diff options
author | Guillaume (ioguix) de Rorthais | 2009-10-01 23:26:01 +0000 |
---|---|---|
committer | Guillaume (ioguix) de Rorthais | 2009-10-01 23:26:01 +0000 |
commit | a6da6f8ae536a8da187edd2ec5bdbe963452e790 (patch) | |
tree | bba4c0bb6b44df12dafedc8544647f2c1262b3db | |
parent | 6513b379700affca49a95a11a4772f3b66fd9897 (diff) |
Refactor and improve the auto-complete foreign-key support in the insert form
- clean the code
- add support for multi-colmn foreign key
- show all the table column in the choice list
- add pagination in the list to browse prev/next 11 values
- use of jquery lib
-rw-r--r-- | aciur.js | 400 | ||||
-rw-r--r-- | ajax-ac-insert.php | 74 | ||||
-rw-r--r-- | autocomplete.php | 20 | ||||
-rwxr-xr-x | classes/database/Postgres.php | 24 | ||||
-rw-r--r-- | classes/database/Postgres73.php | 8 | ||||
-rw-r--r-- | classes/database/Postgres81.php | 8 | ||||
-rw-r--r-- | js/ac_insert_row.js | 219 | ||||
-rw-r--r-- | lang/english.php | 1 | ||||
-rw-r--r-- | lang/recoded/english.php | 1 | ||||
-rw-r--r-- | tables.php | 128 | ||||
-rw-r--r-- | themes/default/global.css | 45 |
11 files changed, 443 insertions, 485 deletions
diff --git a/aciur.js b/aciur.js deleted file mode 100644 index 8ce0d5a6..00000000 --- a/aciur.js +++ /dev/null @@ -1,400 +0,0 @@ -var nkeycode = 0; -var curopt = 0; -var bnsr = false; -var aSuggests = []; -var otxb = null; -var xo = null; -var acv = false; -var fac_c = 1; -var c_fac_c = 1; -var asg = new Array(); -var rasg = new Array(); -var rasgc = new Array(); -var iMoR = false; -var g_c_ns = ""; -var g_c_tb = ""; -var g_c_fk = ""; -var g_c_sid = ""; -var g_c_db = ""; -var g_c_v = ""; -var g_i_ac = true; - -document.body.onclick=function() {deAC();}; - -function aS(aSuggests) { - if (aSuggests.length > 0) { - tA(aSuggests[curopt]); - } -} - -function hKU(oEvent) { - nkeycode = oEvent.keyCode; - var iKeyCode = oEvent.keyCode; - if(iKeyCode!=13) { - if(iKeyCode==40 && (curopt+1)<=(rasg.length-1)) { - curopt++; - bnsr = true; - tA(rasg[curopt]); - htr(document.getElementById('option_tr_'+(curopt+1))); - } else if(iKeyCode==38 && ((curopt-1)<=(rasg.length-1) && (curopt-1)>=0)) { - curopt--; - bnsr = true; - tA(rasg[curopt]); - htr(document.getElementById('option_tr_'+(curopt+1))); - } else { - if ( (iKeyCode!=8 && iKeyCode <= 32) || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) { - } else { - var sTextboxValue = otxb.value.toLowerCase(); - SG1(sTextboxValue); - bnsr = false; - } - } - } - else { - if(acv) { - hideAC(); - } - return false; - } -} - -function initac(ns,tb,fk,sid,db,v) { - g_c_ns = ns; - g_c_tb = tb; - g_c_fk = fk; - g_c_sid = sid; - g_c_db = db; -} - -function sR(p_start,p_len) { -if(!bnsr) { - if (otxb.createTextRange) { - var oRange = otxb.createTextRange(); - oRange.moveStart("character", p_start); - oRange.moveEnd("character", p_len - otxb.value.length); - oRange.select(); - } else if (otxb.setSelectionRange) { - otxb.setSelectionRange(p_start, p_len); - } -} else { - if (otxb.createTextRange) { - var oRange = otxb.createTextRange(); - oRange.moveStart("character",p_len); - oRange.moveEnd("character",p_len); - oRange.select(); - } - else if (otxb.setSelectionRange) { - otxb.setSelectionRange(p_len,p_len); - } - } - otxb.focus(); -} - -function tA(p_suggestion) { - if(nkeycode!=8) { - if ((otxb.createTextRange || otxb.setSelectionRange) && p_suggestion) { - var p_len = otxb.value.length; - otxb.value = p_suggestion; - sR(p_len, p_suggestion.length); - } - } -} - -function findPosX(obj) { - if(obj) { - var curleft = 0; - if (obj.offsetParent) { - while (obj.offsetParent) - { - curleft += obj.offsetLeft - obj = obj.offsetParent; - } - } - else if (obj.x) - curleft += obj.x; - return curleft; - } -} - -function findPosY(obj) { - if(obj) { - var curtop = 0; - var n = 0; - if (obj.y) { - curtop += obj.y; - } - else if (obj.offsetParent) { - while (obj.offsetParent) { - curtop += obj.offsetTop; - obj = obj.offsetParent; - } - } - return curtop; - } -} - -function hideAC() { - var d = document.getElementById('ac'); - d.innerHTML=''; - d.style.zIndex=0; - d.style.border=0; - d.style.visibility='hidden'; - acv = false; -} - -function deAC() { - if(acv) { - hideAC(); - } -} - -function makenormaltr(id) { - var tr = document.getElementById(id); - tr.style.backgroundColor='white';tr.style.color='black'; -} - -function fillinval(val) { - var tx = document.getElementById('fac' + c_fac_c); - tx.value=val; - hideAC(); -} - -function showAC() { - var d = document.getElementById('ac'); - d.innerHTML=''; - var tb = document.getElementById('fac'+c_fac_c+'_ph'); - d.style.top=findPosY(tb)+'px'; - d.style.visibility='visible'; - acv = true; - d.style.left=findPosX(tb)+'px'; - d.style.border='1px solid black'; - d.style.position='absolute'; - d.style.width=(document.getElementById('aciwp' + c_fac_c).offsetWidth-3) + "px"; - d.style.zIndex=20; - d.style.backgroundColor='white'; - d.style.margin='0px'; - d.style.padding='0px'; - d.style.textAlign='left'; - document.getElementById("ac_form").onsubmit=function(){return false;}; - return d; -} - -function aftr() { - for(i=0;i<rasg.length;i++) { - ftr(document.getElementById('option_tr_'+(i+1))); - } -} - -function htr(obj) { - aftr(); - obj.style.backgroundColor='#3d80df'; - obj.style.color='white'; -} - -function ftr(obj) { - obj.style.backgroundColor='white'; - obj.style.color='black'; -} - -function buildSelectOptions() { - if(rasg.length>0) { - var t = document.createElement('table'); - var tb = document.createElement('tbody'); - var d = showAC(); - t.style.width='100%'; - t.style.margin='0px'; - t.style.padding='0px'; - t.cellPadding='0px'; - t.cellSpacing='0px'; - t.style.zIndex=6; - t.style.visibility='visible'; - for(i=0;i<rasg.length;i++) { - var tr = document.createElement('tr'); - var td = document.createElement('td'); - var tx = document.createTextNode(rasg[i]); - if(i==0) { - td.style.color='white'; - td.id='option_tr_1'; - td.style.backgroundColor='#3d80df'; - } else { - td.id='option_tr_' + (i+1); - td.style.color='black'; - td.style.backgroundColor='white'; - } - td.onmouseover=function() { htr(this); this.style.cursor='pointer'; }; - td.onmouseout=function() { ftr(this); this.style.cursor='normal'; }; - td.onclick=function() { fillinval(this.firstChild.innerHTML); }; - td.style.paddingLeft='10px'; - td.style.fontSize='9pt'; - td.style.fontFamily='Arial'; - td.appendChild(tx); - tr.appendChild(td); - tb.appendChild(tr); - } - t.appendChild(tb); - d.appendChild(t); - } else { - hideAC(); - } -} - -function dF(v) { - g_i_ac = document.getElementById(v).checked; -} - -function makeAC(tx,n,ns,tb,fk,sid,db) { - otxb = document.getElementById(tx); - c_fac_c = n; - if(document.getElementById('no_ac').checked) { - if(acv) { - hideAC(); - } - otxb = document.getElementById(tx); - v = otxb.value; - curopt = 0; - bnsr = false; - otxb.onkeyup = function (oEvent) { - if (!oEvent) { oEvent = window.event; } hKU(oEvent); - }; - initac(ns, tb,fk,sid,db,v); - } else { - otxb.onkeyup = function() {}; - } -} - -function gfacc(s) { - return s.substr(3); -} - -function iA(parent, node, referenceNode) { - parent.insertBefore(node, referenceNode.nextSibling); -} - -var asg = new Array(); -var rasg = new Array(); -var rasgc = new Array(); - -function rS(tx) { - rasg = []; - rasgc = []; - var sTextboxValue = otxb.value.toLowerCase(); - if (sTextboxValue.length > 0) { - for (var i=0;i<asg.length;i++) { - var c = asg[i]; - if (c.indexOf(sTextboxValue) == 0) { - rasg.push(asg[i]); - } - } - buildSelectOptions(); - } else { - hideAC(); - } -} - -function SG1(s) { - if(!iMoR && s.length>0) { - pr("autocomplete.php","ns="+g_c_ns+"&tb="+g_c_tb+"&database="+g_c_db+"&server="+g_c_sid+"&fk="+g_c_fk+"&v="+escapeHTML(s)+""); - } else if(!s.length) { - hideAC(); - } -} - -function escapeHTML(s) { - s = escape(s); - s = s.replace(/\//g,"%2F"); - s = s.replace(/\?/g,"%3F"); - s = s.replace(/=/g,"%3D"); - s = s.replace(/&/g,"%26"); - s = s.replace(/@/g,"%40"); - return s; -} - -function rEB(v) { - var il = document.getElementsByTagName('input'); - for(i in il) { - if(il[i].className=='ac_field' || il[i].className=='normal_field') { - if(v==false) { - il[i].className='normal_field'; - il[i].setAttribute("autocomplete",'on'); - } else { - il[i].className='ac_field'; - il[i].setAttribute("autocomplete",'off'); - } - } - } -} - -function pgsg(v) { - prgsp(v); -} - -function prgsp(v) { - var d = v.split("PPA_EOF;|"); // pondered using RegExp here, but overhead to gain ratio is not convincing - var l = d.length; - asg = []; - for(i=0;i<l;i++) { - asg.push(d[i]); - } - rS(otxb); - aS(rasg); -} - -function rS(tx) { - rasg = []; - rasgc = []; - var sTextboxValue = otxb.value.toLowerCase(); - if (sTextboxValue.length > 0) { - for (var i=0;i<asg.length;i++) { - var c = asg[i]; - if (c.indexOf(sTextboxValue) == 0) { - rasg.push(asg[i]); - } - } - buildSelectOptions(); - } else { - hideAC(); - } -} - -function pr(pg,sr) { - mii(); - xo.open("POST",pg,true); - xo.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - xo.onreadystatechange=xoc; - xo.send(sr); -} - -function mii() { - if(!xo) { - /*@cc_on @*//*@if (@_jscript_version >= 5) try { - xo = new ActiveXObject("Msxml2.XMLHTTP"); - } - catch (e) { - try { - xo = new ActiveXObject("Microsoft.XMLHTTP"); - } - catch (E) { - xo = false; - } - } - @end @*/if (!xo && typeof XMLHttpRequest!='undefined') { - xo = new XMLHttpRequest(); - } - } -} - -function xoc(){ - iMoR = true; - szr = ""; - if (xo.readyState==4) { - if(xo.status==200) { - pgsg(xo.responseText); - iMoR = false; - } - else { - alert("Oops, something unexpected happened, try again"); - window.location.reload(); - } - } -} - diff --git a/ajax-ac-insert.php b/ajax-ac-insert.php new file mode 100644 index 00000000..ee338688 --- /dev/null +++ b/ajax-ac-insert.php @@ -0,0 +1,74 @@ +<?php + include_once('./libraries/lib.inc.php'); + + if(isset($_POST['offset'])) + $offset = " OFFSET {$_POST['offset']}"; + else { + $_POST['offset'] = 0; + $offset = " OFFSET 0"; + } + $keyspos = array_combine($_POST['fkeynames'], $_POST['keys']); + $keysnames = array_combine($_POST['fkeynames'], $_POST['keynames']); + + $q = "SELECT * + FROM \"{$_POST['f_schema']}\".\"{$_POST['f_table']}\" + WHERE \"{$_POST['fkeynames'][$_POST['fattpos']]}\"::text LIKE '{$_POST['fvalue']}%' + ORDER BY \"{$_POST['fkeynames'][$_POST['fattpos']]}\" LIMIT 12 {$offset};"; + + $res = $data->selectSet($q); + + if (!$res->EOF) { + echo "<table class=\"ac_values\">"; + echo '<tr>'; + foreach (array_keys($res->fields) as $h) { + echo '<th>'; + + if (in_array($h,$_POST['fkeynames'])) + echo '<img src="'. $misc->icon('ForeignKey') .'" alt="[referenced key]" />'; + + echo htmlentities($h), '</th>'; + + } + echo "</tr>\n"; + $i=0; + while ((!$res->EOF) && ($i < 11)) { + echo "<tr class=\"acline\">"; + foreach ($res->fields as $n => $v) { + if (in_array($n,$_POST['fkeynames'])) + echo "<td><a href=\"javascript:void(0)\" class=\"fkval\" name=\"{$keyspos[$n]}\">",htmlentities($v), "</a></td>"; + else + echo "<td><a href=\"javascript:void(0)\">", htmlentities($v), "</a></td>"; + } + echo "</tr>\n"; + $i++; + $res->moveNext(); + } + echo "</table>\n"; + + $page_tests=''; + + $js = "<script type=\"text/javascript\">\n"; + + if ($_POST['offset']) { + echo "<a href=\"javascript:void(0)\" id=\"fkprev\"><< Prev</a>"; + $js.= "fkl_hasprev=true;\n"; + } + else + $js.= "fkl_hasprev=false;\n"; + + if ($res->recordCount() == 12) { + $js.= "fkl_hasnext=true;\n"; + echo " <a href=\"javascript:void(0)\" id=\"fknext\">Next >></a>"; + } + else + $js.= "fkl_hasnext=false;\n"; + + echo $js ."</script>"; + } + else { + printf("<p>{$lang['strnofkref']}</p>", "\"{$_POST['f_schema']}\".\"{$_POST['f_table']}\".\"{$_POST['fkeynames'][$_POST['fattpos']]}\""); + + if ($_POST['offset']) + echo "<a href=\"javascript:void(0)\" class=\"fkprev\">Prev <<</a>"; + } +?> diff --git a/autocomplete.php b/autocomplete.php deleted file mode 100644 index 5a662dfe..00000000 --- a/autocomplete.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - include_once('libraries/lib.inc.php'); - $data->clean($_REQUEST['tb']); - $data->clean($_REQUEST['ns']); - $data->clean($_REQUEST['fk']); - $data->clean($_REQUEST['v']); - - // FIXME: At some point this should be schema qualified - $szSQL = 'SELECT * FROM "' . $_REQUEST['ns'] . '"."' . $_REQUEST['tb'] . '" WHERE "' . $_REQUEST['fk'] - . "\"::text LIKE '" . $_REQUEST['v'] . "%' ORDER BY \"". $_REQUEST['fk'] .'" LIMIT 11'; - - $objRes = $data->selectSet($szSQL); - $arrayRes = array(); - while (!$objRes->EOF) { - $arrayRes[] = $objRes->fields[$_REQUEST['fk']]; - $objRes->moveNext(); - } - - echo implode('PPA_EOF;|', $arrayRes); -?> diff --git a/classes/database/Postgres.php b/classes/database/Postgres.php index 07066d81..e7c88411 100755 --- a/classes/database/Postgres.php +++ b/classes/database/Postgres.php @@ -250,8 +250,9 @@ class Postgres extends ADODB_base { * @param $value The value of the field. Note this could be 'numeric(7,2)' sort of thing... * @param $type The database type of the field * @param $actions An array of javascript action name to the code to execute on that action + * @param $extra Some extra attributes to add. */ - function printField($name, $value, $type, $actions = array(),$szExtra="") { + function printField($name, $value, $type, $actions = array(), $extra='') { global $lang; // Determine actions string @@ -276,7 +277,7 @@ class Postgres extends ADODB_base { echo "</select>\n"; } else { - echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$szExtra} />\n"; + echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$extra} />\n"; } break; case 'bytea': @@ -303,7 +304,7 @@ class Postgres extends ADODB_base { echo "</textarea>\n"; break; default: - echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$szExtra} />\n"; + echo "<input name=\"", htmlspecialchars($name), "\" value=\"", htmlspecialchars($value), "\" size=\"35\"{$action_str} {$extra} />\n"; break; } } @@ -1163,7 +1164,6 @@ class Postgres extends ADODB_base { AND pc.relkind='S' ) IS NOT NULL AS attisserial, pg_catalog.col_description(a.attrelid, a.attnum) AS comment - FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_attrdef adef ON a.attrelid=adef.adrelid @@ -3268,11 +3268,10 @@ class Postgres extends ADODB_base { // get the max number of col used in a constraint for the table $sql = "SELECT DISTINCT - max(SUBSTRING(array_dims(c.conkey) FROM E'^\\\[.*:(.*)\\\]$')) as nb - FROM - pg_catalog.pg_constraint AS c - JOIN pg_catalog.pg_class AS r ON (c.conrelid = r.oid) - JOIN pg_catalog.pg_namespace AS ns ON r.relnamespace=ns.oid + max(SUBSTRING(array_dims(c.conkey) FROM E'^\\\[.*:(.*)\\\]$')) as nb + FROM pg_catalog.pg_constraint AS c + JOIN pg_catalog.pg_class AS r ON (c.conrelid=r.oid) + JOIN pg_catalog.pg_namespace AS ns ON (r.relnamespace=ns.oid) WHERE r.relname = '$table' AND ns.nspname='". $this->_schema ."'"; @@ -3283,10 +3282,11 @@ class Postgres extends ADODB_base { $sql = ' SELECT - c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc, + c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid, true) AS consrc, ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema, - r2.relname as f_table, f1.attname as p_field, f2.attname as f_field, - pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment + r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, f2.attname as f_field, + f2.attnum AS f_attnum, pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment, + c.conrelid, c.confrelid FROM pg_catalog.pg_constraint AS c JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid) diff --git a/classes/database/Postgres73.php b/classes/database/Postgres73.php index 87315b97..1ebc89b4 100644 --- a/classes/database/Postgres73.php +++ b/classes/database/Postgres73.php @@ -426,8 +426,8 @@ class Postgres73 extends Postgres74 { } $sql = " - SELECT contype, conname, consrc, ns1.nspname as p_schema, sub.relname as p_table, - f_schema, f_table, p_field, f_field, indkey + SELECT oid AS conid, contype, conname, consrc, ns1.nspname as p_schema, sub.relname as p_table, + f_schema, f_table, p_field, f_field, indkey, confrelid FROM ( SELECT contype, conname, @@ -436,8 +436,8 @@ class Postgres73 extends Postgres74 { ELSE 'CHECK (' || consrc || ')' END AS consrc, r1.relname, - f1.attname as p_field, ns2.nspname as f_schema, r2.relname as f_table, - conrelid, r1.relnamespace, f2.attname as f_field, NULL AS indkey + f1.attname as p_field, f1.attnum AS p_attnum, ns2.nspname as f_schema, r2.relname as f_table, + conrelid, r1.relnamespace, f2.attname as f_field, f2.attnum AS f_attnum, NULL AS indkey FROM pg_catalog.pg_constraint AS c JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid) diff --git a/classes/database/Postgres81.php b/classes/database/Postgres81.php index f8479a3f..d387592f 100644 --- a/classes/database/Postgres81.php +++ b/classes/database/Postgres81.php @@ -167,10 +167,12 @@ class Postgres81 extends Postgres82 { $sql = ' SELECT - c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid,true) AS consrc, + c.oid AS conid, c.contype, c.conname, pg_catalog.pg_get_constraintdef(c.oid,true) AS consrc, ns1.nspname as p_schema, r1.relname as p_table, ns2.nspname as f_schema, - r2.relname as f_table, f1.attname as p_field, f2.attname as f_field, - pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment + r2.relname as f_table, f1.attname as p_field, f1.attnum AS p_attnum, + f2.attname as f_field, f2.attnum AS f_attnum, + pg_catalog.obj_description(c.oid, \'pg_constraint\') AS constcomment, + confrelid FROM pg_catalog.pg_constraint AS c JOIN pg_catalog.pg_class AS r1 ON (c.conrelid=r1.oid) diff --git a/js/ac_insert_row.js b/js/ac_insert_row.js new file mode 100644 index 00000000..ae062a3d --- /dev/null +++ b/js/ac_insert_row.js @@ -0,0 +1,219 @@ +var fkl_hasnext=false; +var fkl_hasprev=false; + +/* hide the value list */ +function hideAc() { + jQuery.ppa.o=0; + with (jQuery.ppa) { + fklist.hide(); + fkbg.hide(); + } +} + +/* enable/disable auto-complete feature */ +function triggerAc(ac) { + if (ac) { + jQuery.ppa.attrs + .keyup(autocomplete) + .keypress(move) + .addClass('ac_field'); + } + else { + jQuery.ppa.attrs + .removeClass('ac_field') + .unbind('keyup',autocomplete) + .unbind('keypress',move); + } +} + +/* select the given index value and highlight it */ +function selectVal(index) { + if (index == jQuery.ppa.i) + return; + + // we catch the header as well so it takes th index 0 + var trs = jQuery.ppa.fklist.find('tr'); + + // change colors for unselected + if (jQuery.ppa.i > 0) + trs.eq(jQuery.ppa.i).find('*').css({ + 'background-color': '#fff', + 'color': '' + }); + + // change colors for newly selected + trs.eq(index).find('*').css({ + 'background-color': '#3d80df', + 'color': '#fff' + }); + + jQuery.ppa.i = index; +} + +function openlist(e) { + var elt = jQuery(e); + var attnum = elt.attr('id').match(/\d+/)[0]; + var conid = attrs['attr_'+attnum]; + + var constr = constrs["constr_" + conid]; + + // get the changed attribute position in the arrays + for (i=0; (constr.pattnums[i] != attnum);i++); + + var datas = { + fattpos: i, + fvalue: e.value, + database: database, + 'keys[]': constr.pattnums, + 'keynames[]': constr.pattnames, + 'fkeynames[]': constr.fattnames, + f_table: constr.f_table, + f_schema: constr.f_schema, + offset: jQuery.ppa.o + }; + + jQuery.ajax({ + url: 'ajax-ac-insert.php?server=' + server, + type: 'post', + data: datas, + success: function (ret) { + jQuery.ppa.i = 0; + jQuery.ppa.fkbg.show(); + with(jQuery.ppa.fklist) { + html(ret); + appendTo('#row_att_'+ attnum); + css('width',elt.css('width')); + show(); + jQuery.ppa.numrow = find('tr').length; + } + } + }); +} + + +/* move the cursor down or up, + * load available next/prev values if going out of bound */ +function move(event) { + /* selecting next value down. + * if the list is closed, it will open next */ + if(event.keyCode == 40) { + if (jQuery.ppa.fklist[0].style.display == 'block') { + if ((jQuery.ppa.i + 1) < jQuery.ppa.numrow) { + selectVal(jQuery.ppa.i + 1); + } + else if (fkl_hasnext == true) { + jQuery.ppa.o+=11; + openlist(this); + } + } + else { + openlist(this); + } + } + /* selecting prev value up */ + else if(event.keyCode == 38) { + if ((jQuery.ppa.i - 1) > 0) { + selectVal(jQuery.ppa.i - 1); + } + else if ((fkl_hasprev == true) && (jQuery.ppa.i == 1)) { + jQuery.ppa.o-=11; + openlist(this); + } + else { + selectVal(jQuery.ppa.numrow -1); + } + } +} + +/* open/update the value list on keyup event */ +function autocomplete(event) { + + /* if pressing enter, fire a click on the selected line */ + if (event.keyCode == 13) { + if (jQuery.ppa.i > 0) { + jQuery.ppa.fklist.find('tr').eq(jQuery.ppa.i).click(); + } + hideAc(); + return false; + } + /* ignoring 38:up and 40:down */ + else if ( event.keyCode == 38 || event.keyCode == 40 ) { + return false; + } + /* ignoring 9:tab, 37:left, 39:right, 16:shift, ctrl: 17, alt:18, 20:lockmaj */ + else if ( event.keyCode == 9 || event.keyCode == 37 || event.keyCode == 39 + || event.keyCode == 16 || event.keyCode == 17 + || event.keyCode == 18 || event.keyCode == 20) { + return true; + } + /* esc */ + else if (event.keyCode == 27) { + hideAc(); + } + /* request the list of possible values asynchronously */ + else { + openlist(this); + } + + return true; +} + +/* bind actions on values lines: hover for style change, click for select */ +with(jQuery('tr.acline')) { + live('mouseover', function () { + selectVal(jQuery('table.ac_values tr').index(this)); + }); + + live('click', function () { + var a = jQuery(this).find('td > a.fkval'); + + for (i=0; i < a.length; i++) { + jQuery('input[name=values\\['+ a[i].name +'\\]]').val(a[i].innerHTML); + } + hideAc(); + }); +} + +jQuery('#fkprev').live('click', function () { + jQuery.ppa.o-=11; + /* get the field that is the previous html elt from the #fklist + * and trigger its keyup to refresh the list */ + jQuery('#fklist').prev().keyup().focus(); +}); + +jQuery('#fknext').live('click', function () { + jQuery.ppa.o+=11; + /* get the field that is the previous html elt from the #fklist + * and trigger its keyup to refresh the list */ + jQuery('#fklist').prev().keyup().focus(); +}); + +jQuery(document).ready(function () { + /* register some global value in the ppa namespace */ + jQuery.ppa = { + fklist: jQuery('#fklist'), + attrs: jQuery('input[id^=attr_]'), + fkbg: jQuery('#fkbg'), + i:0, // selected value indice + o:0 // offset when navigating prev/next + }; + + /* close the list when clicking outside of it */ + jQuery.ppa.fkbg.click(function (e) { + hideAc(); + }); + + /* do not submit the form when selecting a value by pressing enter */ + jQuery.ppa.attrs + .keydown(function (e) { + if (e.keyCode == 13 && jQuery.ppa.fklist[0].style.display == 'block') + return false; + }); + + /* enable/disable auto-complete according to the checkbox */ + triggerAc( + jQuery('#no_ac').click(function () { + triggerAc(this.checked); + })[0].checked + ); +}); diff --git a/lang/english.php b/lang/english.php index 66c67045..d1834e4f 100644 --- a/lang/english.php +++ b/lang/english.php @@ -214,6 +214,7 @@ $lang['strinsertrow'] = 'Insert row'; $lang['strrowinserted'] = 'Row inserted.'; $lang['strrowinsertedbad'] = 'Row insert failed.'; + $lang['strnofkref'] = 'There is no matching value in the foreign key %s.'; $lang['strrowduplicate'] = 'Row insert failed, attempted to do duplicate insert.'; $lang['streditrow'] = 'Edit row'; $lang['strrowupdated'] = 'Row updated.'; diff --git a/lang/recoded/english.php b/lang/recoded/english.php index 4ebe06b4..c231e2de 100644 --- a/lang/recoded/english.php +++ b/lang/recoded/english.php @@ -214,6 +214,7 @@ $lang['strinsertrow'] = 'Insert row'; $lang['strrowinserted'] = 'Row inserted.'; $lang['strrowinsertedbad'] = 'Row insert failed.'; + $lang['strnofkref'] = 'There is no matching value for this foreign key in %s.'; $lang['strrowduplicate'] = 'Row insert failed, attempted to do duplicate insert.'; $lang['streditrow'] = 'Edit row'; $lang['strrowupdated'] = 'Row updated.'; @@ -458,7 +458,7 @@ global $data, $misc, $conf; global $lang; - $bAllowAC = (($conf['autocomplete'] != 'disable') ? TRUE : FALSE); + $auto_complete = ($conf['autocomplete'] != 'disable'); if ($confirm) { $misc->printTrail('table'); @@ -466,21 +466,75 @@ $misc->printMsg($msg); $attrs = $data->getTableAttributes($_REQUEST['table']); - if($bAllowAC) { - $constraints = $data->getConstraintsWithFields($_REQUEST['table']); - - $arrayLocals = array(); - $arrayRefs = array(); - $nC = 0; - while(!$constraints->EOF) { - // FIXME: add a better support for FKs on multi columns - if ($constraints->fields['contype'] == 'f') { - $arrayLocals[$nC] = $constraints->fields['p_field']; - $arrayRefs[$nC] = array($constraints->fields['f_schema'], $constraints->fields['f_table'], $constraints->fields['f_field']); - $nC++; + $fksprops = array( + 'byconstr' => array(), + 'byfield' => array(), + ); + if($auto_complete) { + $constrs = $data->getConstraintsWithFields($_REQUEST['table']); + + if (!$constrs->EOF) { + $conrelid = $constrs->fields['conrelid']; + while(!$constrs->EOF) { + if ($constrs->fields['contype'] == 'f') { + if (!isset($fksprops['byconstr'][$constrs->fields['conid']])) { + $fksprops['byconstr'][$constrs->fields['conid']] = array ( + 'confrelid' => $constrs->fields['confrelid'], + 'f_table' => $constrs->fields['f_table'], + 'f_schema' => $constrs->fields['f_schema'], + 'pattnums' => array(), + 'pattnames' => array(), + 'fattnames' => array() + ); + } + + $fksprops['byconstr'][$constrs->fields['conid']]['pattnums'][] = $constrs->fields['p_attnum']; + $fksprops['byconstr'][$constrs->fields['conid']]['pattnames'][] = $constrs->fields['p_field']; + $fksprops['byconstr'][$constrs->fields['conid']]['fattnames'][] = $constrs->fields['f_field']; + + if (!isset($fksprops['byfield'][$constrs->fields['p_attnum']])) + $fksprops['byfield'][$constrs->fields['p_attnum']] = array(); + $fksprops['byfield'][$constrs->fields['p_attnum']] = $constrs->fields['conid']; + } + $constrs->moveNext(); + } + + echo "<script type=\"text/javascript\">\n"; + echo "var constrs = {};\n"; + foreach ($fksprops['byconstr'] as $conid => $props) { + echo "constrs.constr_{$conid} = {\n"; + echo 'pattnums: [', implode(',',$props['pattnums']), "],\n"; + echo "f_table:\"", htmlentities($props['f_table']), "\",\n"; + echo "f_schema:\"", htmlentities($props['f_schema']), "\",\n"; + $_=''; + foreach ($props['pattnames'] as $n) { + $_.= ",'". htmlentities($n, ENT_QUOTES) ."'"; + } + echo 'pattnames: [', substr($_, 1), "],\n"; + + $_=''; + foreach ($props['fattnames'] as $n) { + $_.= ",'". htmlentities($n, ENT_QUOTES) ."'"; + } + + echo 'fattnames: [', substr($_, 1), "]\n"; + echo "};\n"; + } + + echo "var attrs = {};\n"; + foreach ($fksprops['byfield'] as $attnum => $cstrs ) { + echo "attrs.attr_{$attnum} = {$fksprops['byfield'][$attnum]};\n"; } - $constraints->moveNext(); + + echo "var table='", htmlentities($_REQUEST['table']), "';"; + echo "var server='", htmlentities($_REQUEST['server']), "';"; + echo "var database='", htmlentities($_REQUEST['database']), "';"; + echo "</script>\n"; + + echo '<div id="fkbg"></div>'; + echo '<div id="fklist"></div>'; } + else $auto_complete = false; } echo "<form action=\"tables.php\" method=\"post\" id=\"ac_form\">\n"; @@ -496,18 +550,6 @@ $fields = array(); while (!$attrs->EOF) { $fields[$attrs->fields['attnum']] = $attrs->fields['attname']; - $szValueName = "values[{$attrs->fields['attnum']}]"; - $szEvents = ''; - $szDivPH = ''; - if($bAllowAC) { - $idxFound = array_search($attrs->fields['attname'], $arrayLocals); - // In PHP < 4.2.0 array_search returns NULL on failure - if ($idxFound !== NULL && $idxFound !== FALSE) { - $szEvent = "makeAC('{$szValueName}',{$i},'{$arrayRefs[$idxFound][0]}','{$arrayRefs[$idxFound][1]}','{$arrayRefs[$idxFound][2]}','{$_REQUEST['server']}','{$_REQUEST['database']}');"; - $szEvents = "onfocus=\"{$szEvent}\" onblur=\"hideAC();document.getElementById('ac_form').onsubmit=function(){return true;};\" onchange=\"{$szEvent}\" id=\"{$szValueName}\" onkeyup=\"{$szEvent}\" autocomplete=\"off\" class='ac_field'"; - $szDivPH = "<div id=\"fac{$i}_ph\"></div>"; - } - } $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']); // Set up default value if there isn't one already if (!isset($_REQUEST['values'][$attrs->fields['attnum']])) @@ -522,32 +564,40 @@ echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">", $misc->printVal($attrs->fields['attname']), "</td>"; echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">\n"; echo $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod'])); - echo "<input type=\"hidden\" name=\"types[", htmlspecialchars($attrs->fields['attnum']), "]\" value=\"", + echo "<input type=\"hidden\" name=\"types[{$attrs->fields['attnum']}]\" value=\"", htmlspecialchars($attrs->fields['type']), "\" /></td>"; echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">\n"; - echo "<select name=\"format[", htmlspecialchars($attrs->fields['attnum']), "]\">\n"; + echo "<select name=\"format[{$attrs->fields['attnum']}]\">\n"; echo "<option value=\"VALUE\"", ($_REQUEST['format'][$attrs->fields['attnum']] == 'VALUE') ? ' selected="selected"' : '', ">{$lang['strvalue']}</option>\n"; echo "<option value=\"EXPRESSION\"", ($_REQUEST['format'][$attrs->fields['attnum']] == 'EXPRESSION') ? ' selected="selected"' : '', ">{$lang['strexpression']}</option>\n"; echo "</select>\n</td>\n"; echo "<td class=\"data{$id}\" style=\"white-space:nowrap;\">"; // Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS) if (!$attrs->fields['attnotnull']) { - echo "<input type=\"checkbox\" name=\"nulls[", htmlspecialchars($attrs->fields['attnum']), "]\"", + echo "<input type=\"checkbox\" name=\"nulls[{$attrs->fields['attnum']}]\"", isset($_REQUEST['nulls'][$attrs->fields['attnum']]) ? ' checked="checked"' : '', " /></td>"; } else { echo " </td>"; } - echo "<td class=\"data{$id}\" id=\"aciwp{$i}\" style=\"white-space:nowrap;\">", $data->printField($szValueName, - $_REQUEST['values'][$attrs->fields['attnum']], $attrs->fields['type'],array(),$szEvents),$szDivPH ,"</td>"; + echo "<td class=\"data{$id}\" id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">"; + if ($auto_complete && isset($fksprops['byfield'][$attrs->fields['attnum']])) { + echo $data->printField("values[{$attrs->fields['attnum']}]", $_REQUEST['values'][$attrs->fields['attnum']], + 'fktype'/*force FK*/,array(), "id=\"attr_{$attrs->fields['attnum']}\" autocomplete=\"off\""); + } + else { + echo $data->printField("values[{$attrs->fields['attnum']}]", $_REQUEST['values'][$attrs->fields['attnum']], + $attrs->fields['type'],array()); + } + echo "</td>\n"; echo "</tr>\n"; $i++; $attrs->moveNext(); } echo "</table>\n"; - if($bAllowAC) { - echo '<script src="aciur.js" type="text/javascript"></script>'; - echo "<div id=\"ac\"></div>"; + if($auto_complete) { + echo "<script src=\"libraries/js/jquery.js\" type=\"text/javascript\"></script>"; + echo "<script src=\"js/ac_insert_row.js\" type=\"text/javascript\"></script>"; } if (!isset($_SESSION['counter'])) { $_SESSION['counter'] = 0; } @@ -559,12 +609,14 @@ echo "<p><input type=\"submit\" name=\"insert\" value=\"{$lang['strinsert']}\" />\n"; echo "<input type=\"submit\" name=\"insertandrepeat\" value=\"{$lang['strinsertandrepeat']}\" />\n"; echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />\n"; - if($bAllowAC) { - $szChecked = $conf['autocomplete'] != 'default off' ? 'checked="checked"' : ''; - echo "<input type=\"checkbox\" name=\"no_ac\" id=\"no_ac\" onclick=\"rEB(this.checked);\" value=\"1\" {$szChecked} /><label for='no_ac' onmouseover='this.style.cursor=\"pointer\";'>{$lang['strac']}</label>\n"; + + if($auto_complete) { + if ($conf['autocomplete'] != 'default off') + echo "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$lang['strac']}</label>\n"; + else + echo "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$lang['strac']}</label>\n"; } echo "</p>\n"; - echo "<script type=\"text/javascript\">rEB(document.getElementById('no_ac').checked);</script>"; } else { echo "<p>{$lang['strnofieldsforinsert']}</p>\n"; diff --git a/themes/default/global.css b/themes/default/global.css index 48522239..1584a9df 100644 --- a/themes/default/global.css +++ b/themes/default/global.css @@ -5,7 +5,6 @@ */ /** ELEMENTS */ - body, td { background-color: #FFFFFF; @@ -275,7 +274,6 @@ table.tabs { .tab .icon { display: block; } - a:active { color: #989973; @@ -411,13 +409,44 @@ pre.data .arg_tr_pc { } -.ac_field { -border:1px solid #D9D95F; +#fkbg { + display:none; + position:fixed; + top:0;left:0; + width:100%; + height:100%; + z-index:10; +} +#fklist { + display:none; + position:absolute; + background:#fff; + border:1px solid #000; + overflow:auto; + z-index:15; +} +#fklist table { + border-collapse:collapse; + border: 1px solid #aaa; +} +#fklist th {border: 1px solid #aaa} +#fklist td, +#fklist th { + padding: 3px 10px; + border-right: 1px solid #aaa; + font-size: 12px; +} +#fklist td a { + display:block; + color:#000; +} +#fklist td a.fkval { + color:red; } - .normal_field { } - .pre { -white-space:pre; -}
\ No newline at end of file + white-space:pre; +} +.ac_values {width:100%} +.ac_field {border:1px solid #D9D95F}
\ No newline at end of file |