[Phpmyadmin-git] [SCM] phpMyAdmin branch, master, updated. RELEASE_3_4_3_1-14434-ga84fd8e

Marc Delisle lem9 at users.sourceforge.net
Thu Aug 11 13:58:41 CEST 2011


The branch, master has been updated
       via  a84fd8e151225beb59f4c4dd480ddd847b1bc4b2 (commit)
       via  e3d069f6536e90dd743fb85b0ee5003efea5d9ca (commit)
       via  ba5598a190ff39225ef3d7527eecc54dfdf50760 (commit)
       via  e65fc63e71496e338e549e14b1c3a958566c1277 (commit)
      from  341b75a99013570eafde267e1f04ae41bb28365b (commit)


- Log -----------------------------------------------------------------
commit a84fd8e151225beb59f4c4dd480ddd847b1bc4b2
Merge: 341b75a e3d069f
Author: Marc Delisle <marc at infomarc.info>
Date:   Thu Aug 11 07:57:30 2011 -0400

    Merge commit 'e3d069f6536e90dd743fb85b0ee5003efea5d9ca'

commit e3d069f6536e90dd743fb85b0ee5003efea5d9ca
Author: Aris Feryanto <aris_feryanto at yahoo.com>
Date:   Thu Aug 11 14:08:37 2011 +0800

    Add more comments in makegrid.js

commit ba5598a190ff39225ef3d7527eecc54dfdf50760
Author: Aris Feryanto <aris_feryanto at yahoo.com>
Date:   Thu Aug 11 13:35:51 2011 +0800

    Refactor makegrid.js

commit e65fc63e71496e338e549e14b1c3a958566c1277
Author: Aris Feryanto <aris_feryanto at yahoo.com>
Date:   Wed Aug 10 22:49:23 2011 +0800

    Incomplete inline edit feature removal

-----------------------------------------------------------------------

Summary of changes:
 js/makegrid.js | 2998 +++++++++++++++++++++++++++++---------------------------
 js/sql.js      |   11 +-
 sql.php        |  126 +---
 3 files changed, 1567 insertions(+), 1568 deletions(-)

diff --git a/js/makegrid.js b/js/makegrid.js
index 91834ac..b8ca5fa 100644
--- a/js/makegrid.js
+++ b/js/makegrid.js
@@ -1,828 +1,856 @@
-(function ($) {
-    $.grid = function(t) {
-        // prepare the grid
-        var g = {
-            // constant
-            minColWidth: 15,
-
-            // variables, assigned with default value, changed later
-            actionSpan: 5,
-            colOrder: new Array(),      // array of column order
-            colVisib: new Array(),      // array of column visibility
-            tableCreateTime: null,      // table creation time, only available in "Browse tab"
-            qtip: null,                 // qtip API
-            reorderHint: '',            // string, hint for column reordering
-            sortHint: '',               // string, hint for column sorting
-            markHint: '',               // string, hint for column marking
-            colVisibHint: '',           // string, hint for column visibility drop-down
-            showReorderHint: false,
-            showSortHint: false,
-            showMarkHint: false,
-            showColVisibHint: false,
-            showAllColText: '',         // string, text for "show all" button under column visibility list
-            visibleHeadersCount: 0,     // number of visible data headers
-            isCellEditActive: false,    // true if current focus is in edit cell
-            isEditCellTextEditable: false,  // true if current edit cell is editable in the text input box (not textarea)
-            currentEditCell: null,      // reference to <td> that currently being edited
-            inEditMode: false,          // true if grid is in edit mode
-            cellEditHint: '',           // hint shown when doing grid edit
-            gotoLinkText: 'Go to link', // "Go to link" text
-            wasEditedCellNull: false,   // true if last value of the edited cell was NULL
-            maxTruncatedLen: 0,         // number of characters that can be displayed in a cell
-            saveCellsAtOnce: false,     // $cfg[saveCellsAtOnce]
-            isCellEdited: false,        // true if at least one cell has been edited
-            saveCellWarning: '',        // string, warning text when user want to leave a page with unsaved edited data
-            lastXHR : null,             // last XHR object used in AJAX request
-            isSaving: false,            // true when currently saving edited data, used to handle double posting caused by pressing ENTER in grid edit text box in Chrome browser
-            alertNonUnique: '',         // string, alert shown when saving edited nonunique table
+/**
+ * Create advanced table (resize, reorder, and show/hide columns; and also grid editing).
+ * This function is designed mainly for table DOM generated from browsing a table in the database.
+ * For using this function in other table DOM, you may need to:
+ * - add "draggable" class in the table header <th>, in order to make it resizable, sortable or hidable
+ * - have at least one non-"draggable" header in the table DOM for placing column visibility drop-down arrow
+ * - pass the value "false" for the parameter "enableGridEdit"
+ * - adjust other parameter value, to select which features that will be enabled
+ *
+ * @param t the table DOM element
+ * @param enableResize Optional, if false, column resizing feature will be disabled
+ * @param enableReorder Optional, if false, column reordering feature will be disabled
+ * @param enableVisib Optional, if false, show/hide column feature will be disabled
+ * @param enableGridEdit Optional, if false, grid editing feature will be disabled
+ */
+function PMA_makegrid(t, enableResize, enableReorder, enableVisib, enableGridEdit) {
+    var g = {
+        /***********
+         * Constant
+         ***********/
+        minColWidth: 15,
+        
+        
+        /***********
+         * Variables, assigned with default value, changed later
+         ***********/
+        actionSpan: 5,              // number of colspan in Actions header in a table
+        tableCreateTime: null,      // table creation time, used for saving column order and visibility to server, only available in "Browse tab"
+        
+        // Column reordering variables
+        colOrder: new Array(),      // array of column order
+        
+        // Column visibility variables
+        colVisib: new Array(),      // array of column visibility
+        showAllColText: '',         // string, text for "show all" button under column visibility list
+        visibleHeadersCount: 0,     // number of visible data headers
+        
+        // Table hint variables
+        qtip: null,                 // qtip API
+        reorderHint: '',            // string, hint for column reordering
+        sortHint: '',               // string, hint for column sorting
+        markHint: '',               // string, hint for column marking
+        colVisibHint: '',           // string, hint for column visibility drop-down
+        showReorderHint: false,
+        showSortHint: false,
+        showMarkHint: false,
+        showColVisibHint: false,
+        
+        // Grid editing
+        isCellEditActive: false,    // true if current focus is in edit cell
+        isEditCellTextEditable: false,  // true if current edit cell is editable in the text input box (not textarea)
+        currentEditCell: null,      // reference to <td> that currently being edited
+        cellEditHint: '',           // hint shown when doing grid edit
+        gotoLinkText: '',           // "Go to link" text
+        wasEditedCellNull: false,   // true if last value of the edited cell was NULL
+        maxTruncatedLen: 0,         // number of characters that can be displayed in a cell
+        saveCellsAtOnce: false,     // $cfg[saveCellsAtOnce]
+        isCellEdited: false,        // true if at least one cell has been edited
+        saveCellWarning: '',        // string, warning text when user want to leave a page with unsaved edited data
+        lastXHR : null,             // last XHR object used in AJAX request
+        isSaving: false,            // true when currently saving edited data, used to handle double posting caused by pressing ENTER in grid edit text box in Chrome browser
+        alertNonUnique: '',         // string, alert shown when saving edited nonunique table
+        
+        // Common hidden inputs
+        token: null,
+        server: null,
+        db: null,
+        table: null,
+        
+        
+        /************
+         * Functions
+         ************/
+
+        /**
+         * Start to resize column. Called when clicking on column separator.
+         *
+         * @param e event
+         * @param obj dragged div object
+         */
+        dragStartRsz: function(e, obj) {
+            var n = $(g.cRsz).find('div').index(obj);    // get the index of separator (i.e., column index)
+            g.colRsz = {
+                x0: e.pageX,
+                n: n,
+                obj: obj,
+                objLeft: $(obj).position().left,
+                objWidth: $(g.t).find('th.draggable:visible:eq(' + n + ') span').outerWidth()
+            };
+            $('body').css('cursor', 'col-resize');
+            $('body').noSelect();
+            if (g.isCellEditActive) {
+                g.hideEditCell();
+            }
+        },
+        
+        /**
+         * Start to reorder column. Called when clicking on table header.
+         *
+         * @param e event
+         * @param obj table header object
+         */
+        dragStartReorder: function(e, obj) {
+            // prepare the cCpy (column copy) and cPointer (column pointer) from the dragged column
+            $(g.cCpy).text($(obj).text());
+            var objPos = $(obj).position();
+            $(g.cCpy).css({
+                top: objPos.top + 20,
+                left: objPos.left,
+                height: $(obj).height(),
+                width: $(obj).width()
+            });
+            $(g.cPointer).css({
+                top: objPos.top
+            });
             
-            // common hidden inputs
-            token: null,
-            server: null,
-            db: null,
-            table: null,
+            // get the column index, zero-based
+            var n = g.getHeaderIdx(obj);
             
-            // functions
-            dragStartRsz: function(e, obj) {    // start column resize
-                var n = $(this.cRsz).find('div').index(obj);
-                this.colRsz = {
-                    x0: e.pageX,
-                    n: n,
-                    obj: obj,
-                    objLeft: $(obj).position().left,
-                    objWidth: $(this.t).find('th.draggable:visible:eq(' + n + ') span').outerWidth()
-                };
-                $('body').css('cursor', 'col-resize');
-                $('body').noSelect();
-                if (g.isCellEditActive) {
-                    g.hideEditCell();
-                }
-            },
-
-            dragStartMove: function(e, obj) {   // start column move
-                // prepare the cCpy and cPointer from the dragged column
-                $(this.cCpy).text($(obj).text());
-                var objPos = $(obj).position();
-                $(this.cCpy).css({
-                    top: objPos.top + 20,
-                    left: objPos.left,
-                    height: $(obj).height(),
-                    width: $(obj).width()
-                });
-                $(this.cPointer).css({
-                    top: objPos.top
-                });
-
-                // get the column index, zero-based
-                var n = this.getHeaderIdx(obj);
-
-                this.colMov = {
-                    x0: e.pageX,
-                    y0: e.pageY,
-                    n: n,
-                    newn: n,
-                    obj: obj,
-                    objTop: objPos.top,
-                    objLeft: objPos.left
-                };
-                this.qtip.hide();
-                $('body').css('cursor', 'move');
-                $('body').noSelect();
-                if (g.isCellEditActive) {
-                    g.hideEditCell();
-                }
-            },
-
-            dragMove: function(e) {
-                if (this.colRsz) {
-                    var dx = e.pageX - this.colRsz.x0;
-                    if (this.colRsz.objWidth + dx > this.minColWidth) {
-                        $(this.colRsz.obj).css('left', this.colRsz.objLeft + dx + 'px');
-                    }
-                } else if (this.colMov) {
-                    // dragged column animation
-                    var dx = e.pageX - this.colMov.x0;
-                    $(this.cCpy)
-                        .css('left', this.colMov.objLeft + dx)
-                        .show();
-
-                    // pointer animation
-                    var hoveredCol = this.getHoveredCol(e);
-                    if (hoveredCol) {
-                        var newn = this.getHeaderIdx(hoveredCol);
-                        this.colMov.newn = newn;
-                        if (newn != this.colMov.n) {
-                            // show the column pointer in the right place
-                            var colPos = $(hoveredCol).position();
-                            var newleft = newn < this.colMov.n ?
-                                          colPos.left :
-                                          colPos.left + $(hoveredCol).outerWidth();
-                            $(this.cPointer)
-                                .css({
-                                    left: newleft,
-                                    visibility: 'visible'
-                                });
-                        } else {
-                            // no movement to other column, hide the column pointer
-                            $(this.cPointer).css('visibility', 'hidden');
-                        }
-                    }
+            g.colReorder = {
+                x0: e.pageX,
+                y0: e.pageY,
+                n: n,
+                newn: n,
+                obj: obj,
+                objTop: objPos.top,
+                objLeft: objPos.left
+            };
+            g.hideHint();
+            $('body').css('cursor', 'move');
+            $('body').noSelect();
+            if (g.isCellEditActive) {
+                g.hideEditCell();
+            }
+        },
+        
+        /**
+         * Handle mousemove event when dragging.
+         *
+         * @param e event
+         */
+        dragMove: function(e) {
+            if (g.colRsz) {
+                var dx = e.pageX - g.colRsz.x0;
+                if (g.colRsz.objWidth + dx > g.minColWidth) {
+                    $(g.colRsz.obj).css('left', g.colRsz.objLeft + dx + 'px');
                 }
-            },
-
-            dragEnd: function(e) {
-                if (this.colRsz) {
-                    var dx = e.pageX - this.colRsz.x0;
-                    var nw = this.colRsz.objWidth + dx;
-                    if (nw < this.minColWidth) {
-                        nw = this.minColWidth;
-                    }
-                    var n = this.colRsz.n;
-                    // do the resizing
-                    this.resize(n, nw);
-
-                    $('body').css('cursor', 'default');
-                    this.reposRsz();
-                    this.reposDrop();
-                    this.colRsz = false;
-                } else if (this.colMov) {
-                    // shift columns
-                    if (this.colMov.newn != this.colMov.n) {
-                        this.shiftCol(this.colMov.n, this.colMov.newn);
-                        // assign new position
-                        var objPos = $(this.colMov.obj).position();
-                        this.colMov.objTop = objPos.top;
-                        this.colMov.objLeft = objPos.left;
-                        this.colMov.n = this.colMov.newn;
-                        // send request to server to remember the column order
-                        if (this.tableCreateTime) {
-                            this.sendColPrefs();
-                        }
-                        this.refreshRestoreButton();
+            } else if (g.colReorder) {
+                // dragged column animation
+                var dx = e.pageX - g.colReorder.x0;
+                $(g.cCpy)
+                    .css('left', g.colReorder.objLeft + dx)
+                    .show();
+                
+                // pointer animation
+                var hoveredCol = g.getHoveredCol(e);
+                if (hoveredCol) {
+                    var newn = g.getHeaderIdx(hoveredCol);
+                    g.colReorder.newn = newn;
+                    if (newn != g.colReorder.n) {
+                        // show the column pointer in the right place
+                        var colPos = $(hoveredCol).position();
+                        var newleft = newn < g.colReorder.n ?
+                                      colPos.left :
+                                      colPos.left + $(hoveredCol).outerWidth();
+                        $(g.cPointer)
+                            .css({
+                                left: newleft,
+                                visibility: 'visible'
+                            });
+                    } else {
+                        // no movement to other column, hide the column pointer
+                        $(g.cPointer).css('visibility', 'hidden');
                     }
-
-                    // animate new column position
-                    $(this.cCpy).stop(true, true)
-                        .animate({
-                            top: g.colMov.objTop,
-                            left: g.colMov.objLeft
-                        }, 'fast')
-                        .fadeOut();
-                    $(this.cPointer).css('visibility', 'hidden');
-
-                    this.colMov = false;
                 }
-                $('body').noSelect(false);
-            },
-
-            /**
-             * Resize column n to new width "nw"
-             */
-            resize: function(n, nw) {
-                $(this.t).find('tr').each(function() {
-                    $(this).find('th.draggable:visible:eq(' + n + ') span,' +
-                                 'td:visible:eq(' + (g.actionSpan + n) + ') span')
-                           .css('width', nw);
-                });
-            },
-
-            /**
-             * Reposition column resize bars.
-             */
-            reposRsz: function() {
-                $(this.cRsz).find('div').hide();
-                var $firstRowCols = $(this.t).find('tr:first th.draggable:visible');
-                for (var n = 0; n < $firstRowCols.length; n++) {
-                    $this = $($firstRowCols[n]);
-                    $cb = $(g.cRsz).find('div:eq(' + n + ')');   // column border
-                    $cb.css('left', $this.position().left + $this.outerWidth(true))
-                       .show();
+            }
+        },
+        
+        /**
+         * Stop the dragging action.
+         *
+         * @param e event
+         */
+        dragEnd: function(e) {
+            if (g.colRsz) {
+                var dx = e.pageX - g.colRsz.x0;
+                var nw = g.colRsz.objWidth + dx;
+                if (nw < g.minColWidth) {
+                    nw = g.minColWidth;
                 }
-                $(this.cRsz).css('height', $(this.t).height());
-            },
-
-            /**
-             * Shift column from index oldn to newn.
-             */
-            shiftCol: function(oldn, newn) {
-                $(this.t).find('tr').each(function() {
-                    if (newn < oldn) {
-                        $(this).find('th.draggable:eq(' + newn + '),' +
-                                     'td:eq(' + (g.actionSpan + newn) + ')')
-                               .before($(this).find('th.draggable:eq(' + oldn + '),' +
-                                                    'td:eq(' + (g.actionSpan + oldn) + ')'));
-                    } else {
-                        $(this).find('th.draggable:eq(' + newn + '),' +
-                                     'td:eq(' + (g.actionSpan + newn) + ')')
-                               .after($(this).find('th.draggable:eq(' + oldn + '),' +
-                                                   'td:eq(' + (g.actionSpan + oldn) + ')'));
+                var n = g.colRsz.n;
+                // do the resizing
+                g.resize(n, nw);
+                
+                g.reposRsz();
+                g.reposDrop();
+                g.colRsz = false;
+            } else if (g.colReorder) {
+                // shift columns
+                if (g.colReorder.newn != g.colReorder.n) {
+                    g.shiftCol(g.colReorder.n, g.colReorder.newn);
+                    // assign new position
+                    var objPos = $(g.colReorder.obj).position();
+                    g.colReorder.objTop = objPos.top;
+                    g.colReorder.objLeft = objPos.left;
+                    g.colReorder.n = g.colReorder.newn;
+                    // send request to server to remember the column order
+                    if (g.tableCreateTime) {
+                        g.sendColPrefs();
                     }
-                });
-                // reposition the column resize bars
-                this.reposRsz();
-
-                // adjust the column visibility list
+                    g.refreshRestoreButton();
+                }
+                
+                // animate new column position
+                $(g.cCpy).stop(true, true)
+                    .animate({
+                        top: g.colReorder.objTop,
+                        left: g.colReorder.objLeft
+                    }, 'fast')
+                    .fadeOut();
+                $(g.cPointer).css('visibility', 'hidden');
+
+                g.colReorder = false;
+            }
+            $('body').css('cursor', 'inherit');
+            $('body').noSelect(false);
+        },
+        
+        /**
+         * Resize column n to new width "nw"
+         *
+         * @param n zero-based column index
+         * @param nw new width of the column in pixel
+         */
+        resize: function(n, nw) {
+            $(g.t).find('tr').each(function() {
+                $(this).find('th.draggable:visible:eq(' + n + ') span,' +
+                             'td:visible:eq(' + (g.actionSpan + n) + ') span')
+                       .css('width', nw);
+            });
+        },
+        
+        /**
+         * Reposition column resize bars.
+         */
+        reposRsz: function() {
+            $(g.cRsz).find('div').hide();
+            var $firstRowCols = $(g.t).find('tr:first th.draggable:visible');
+            for (var n = 0; n < $firstRowCols.length; n++) {
+                var $col = $($firstRowCols[n]);
+                $cb = $(g.cRsz).find('div:eq(' + n + ')');   // column border
+                $cb.css('left', $col.position().left + $col.outerWidth(true))
+                   .show();
+            }
+            $(g.cRsz).css('height', $(g.t).height());
+        },
+        
+        /**
+         * Shift column from index oldn to newn.
+         *
+         * @param oldn old zero-based column index
+         * @param newn new zero-based column index
+         */
+        shiftCol: function(oldn, newn) {
+            $(g.t).find('tr').each(function() {
                 if (newn < oldn) {
-                    $(g.cList).find('.lDiv div:eq(' + newn + ')')
-                              .before($(g.cList).find('.lDiv div:eq(' + oldn + ')'));
+                    $(this).find('th.draggable:eq(' + newn + '),' +
+                                 'td:eq(' + (g.actionSpan + newn) + ')')
+                           .before($(this).find('th.draggable:eq(' + oldn + '),' +
+                                                'td:eq(' + (g.actionSpan + oldn) + ')'));
                 } else {
-                    $(g.cList).find('.lDiv div:eq(' + newn + ')')
-                              .after($(g.cList).find('.lDiv div:eq(' + oldn + ')'));
+                    $(this).find('th.draggable:eq(' + newn + '),' +
+                                 'td:eq(' + (g.actionSpan + newn) + ')')
+                           .after($(this).find('th.draggable:eq(' + oldn + '),' +
+                                               'td:eq(' + (g.actionSpan + oldn) + ')'));
                 }
-                // adjust the colOrder
-                var tmp = this.colOrder[oldn];
-                this.colOrder.splice(oldn, 1);
-                this.colOrder.splice(newn, 0, tmp);
-                // adjust the colVisib
-                var tmp = this.colVisib[oldn];
-                this.colVisib.splice(oldn, 1);
-                this.colVisib.splice(newn, 0, tmp);
-            },
-
-            /**
-             * Find currently hovered table column's header (excluding actions column).
-             * @return the hovered column's th object or undefined if no hovered column found.
-             */
-            getHoveredCol: function(e) {
-                var hoveredCol;
-                $headers = $(this.t).find('th.draggable:visible');
-                $headers.each(function() {
-                    var left = $(this).offset().left;
-                    var right = left + $(this).outerWidth();
-                    if (left <= e.pageX && e.pageX <= right) {
-                        hoveredCol = this;
-                    }
-                });
-                return hoveredCol;
-            },
-
-            /**
-             * Get a zero-based index from a <th class="draggable"> tag in a table.
-             */
-            getHeaderIdx: function(obj) {
-                return $(obj).parents('tr').find('th.draggable').index(obj);
-            },
-
-            /**
-             * Reposition the table back to normal order.
-             */
-            restoreColOrder: function() {
-                // use insertion sort, since we already have shiftCol function
-                for (var i = 1; i < this.colOrder.length; i++) {
-                    var x = this.colOrder[i];
-                    var j = i - 1;
-                    while (j >= 0 && x < this.colOrder[j]) {
-                        j--;
-                    }
-                    if (j != i - 1) {
-                        this.shiftCol(i, j + 1);
-                    }
+            });
+            // reposition the column resize bars
+            g.reposRsz();
+                
+            // adjust the column visibility list
+            if (newn < oldn) {
+                $(g.cList).find('.lDiv div:eq(' + newn + ')')
+                          .before($(g.cList).find('.lDiv div:eq(' + oldn + ')'));
+            } else {
+                $(g.cList).find('.lDiv div:eq(' + newn + ')')
+                          .after($(g.cList).find('.lDiv div:eq(' + oldn + ')'));
+            }
+            // adjust the colOrder
+            var tmp = g.colOrder[oldn];
+            g.colOrder.splice(oldn, 1);
+            g.colOrder.splice(newn, 0, tmp);
+            // adjust the colVisib
+            if (g.colVisib.length > 0) {
+                var tmp = g.colVisib[oldn];
+                g.colVisib.splice(oldn, 1);
+                g.colVisib.splice(newn, 0, tmp);
+            }
+        },
+        
+        /**
+         * Find currently hovered table column's header (excluding actions column).
+         *
+         * @param e event
+         * @return the hovered column's th object or undefined if no hovered column found.
+         */
+        getHoveredCol: function(e) {
+            var hoveredCol;
+            $headers = $(g.t).find('th.draggable:visible');
+            $headers.each(function() {
+                var left = $(this).offset().left;
+                var right = left + $(this).outerWidth();
+                if (left <= e.pageX && e.pageX <= right) {
+                    hoveredCol = this;
                 }
-                if (this.tableCreateTime) {
-                    // send request to server to remember the column order
-                    this.sendColPrefs();
+            });
+            return hoveredCol;
+        },
+        
+        /**
+         * Get a zero-based index from a <th class="draggable"> tag in a table.
+         *
+         * @param obj table header <th> object
+         * @return zero-based index of the specified table header in the set of table headers (visible or not)
+         */
+        getHeaderIdx: function(obj) {
+            return $(obj).parents('tr').find('th.draggable').index(obj);
+        },
+        
+        /**
+         * Reposition the columns back to normal order.
+         */
+        restoreColOrder: function() {
+            // use insertion sort, since we already have shiftCol function
+            for (var i = 1; i < g.colOrder.length; i++) {
+                var x = g.colOrder[i];
+                var j = i - 1;
+                while (j >= 0 && x < g.colOrder[j]) {
+                    j--;
                 }
-                this.refreshRestoreButton();
-            },
-
-            /**
-             * Send column preferences (column order and visibility) to the server.
-             */
-            sendColPrefs: function() {
-                $.post('sql.php', {
-                    ajax_request: true,
-                    db: g.db,
-                    table: g.table,
-                    token: g.token,
-                    server: g.server,
-                    set_col_prefs: true,
-                    col_order: this.colOrder.toString(),
-                    col_visib: this.colVisib.toString(),
-                    table_create_time: this.tableCreateTime
-                });
-            },
-
-            /**
-             * Refresh restore button state.
-             * Make restore button disabled if the table is similar with initial state.
-             */
-            refreshRestoreButton: function() {
-                // check if table state is as initial state
-                var isInitial = true;
-                for (var i = 0; i < this.colOrder.length; i++) {
-                    if (this.colOrder[i] != i) {
-                        isInitial = false;
-                        break;
-                    }
+                if (j != i - 1) {
+                    g.shiftCol(i, j + 1);
                 }
-                // check if only one visible column left
-                var isOneColumn = this.visibleHeadersCount == 1;
-                // enable or disable restore button
-                if (isInitial || isOneColumn) {
-                    $('.restore_column').hide();
-                } else {
-                    $('.restore_column').show();
+            }
+            if (g.tableCreateTime) {
+                // send request to server to remember the column order
+                g.sendColPrefs();
+            }
+            g.refreshRestoreButton();
+        },
+        
+        /**
+         * Send column preferences (column order and visibility) to the server.
+         */
+        sendColPrefs: function() {
+            var post_params = {
+                ajax_request: true,
+                db: g.db,
+                table: g.table,
+                token: g.token,
+                server: g.server,
+                set_col_prefs: true,
+                table_create_time: g.tableCreateTime
+            };
+            if (g.colOrder.length > 0) {
+                $.extend(post_params, { col_order: g.colOrder.toString() });
+            }
+            if (g.colVisib.length > 0) {
+                $.extend(post_params, { col_visib: g.colVisib.toString() });
+            }
+            $.post('sql.php', post_params);
+        },
+        
+        /**
+         * Refresh restore button state.
+         * Make restore button disabled if the table is similar with initial state.
+         */
+        refreshRestoreButton: function() {
+            // check if table state is as initial state
+            var isInitial = true;
+            for (var i = 0; i < g.colOrder.length; i++) {
+                if (g.colOrder[i] != i) {
+                    isInitial = false;
+                    break;
                 }
-            },
-
-            /**
-             * Update current hint using the boolean values (showReorderHint, showSortHint, etc.).
-             * It will hide the hint if all the boolean values is false.
-             */
-            updateHint: function(e) {
-                if (!this.colRsz && !this.colMov) {     // if not resizing or dragging
-                    var text = '';
-                    if (this.showReorderHint && this.reorderHint) {
-                        text += this.reorderHint;
-                    }
-                    if (this.showSortHint && this.sortHint) {
-                        text += text.length > 0 ? '<br />' : '';
-                        text += this.sortHint;
-                    }
-                    if (this.showMarkHint && this.markHint &&
-                        !this.showSortHint      // we do not show mark hint, when sort hint is shown
-                    ) {
-                        text += text.length > 0 ? '<br />' : '';
-                        text += this.markHint;
-                    }
-                    if (this.showColVisibHint && this.colVisibHint) {
-                        text += text.length > 0 ? '<br />' : '';
-                        text += this.colVisibHint;
-                    }
-
-                    // hide the hint if no text
-                    this.qtip.disable(!text && e.type == 'mouseenter');
-
-                    this.qtip.updateContent(text, false);
-                } else {
-                    this.qtip.disable(true);
+            }
+            // check if only one visible column left
+            var isOneColumn = g.visibleHeadersCount == 1;
+            // enable or disable restore button
+            if (isInitial || isOneColumn) {
+                $('.restore_column').hide();
+            } else {
+                $('.restore_column').show();
+            }
+        },
+        
+        /**
+         * Update current hint using the boolean values (showReorderHint, showSortHint, etc.).
+         * It will hide the hint if all the boolean values is false.
+         *
+         * @param e event
+         */
+        updateHint: function(e) {
+            if (!g.colRsz && !g.colReorder) {     // if not resizing or dragging
+                var text = '';
+                if (g.showReorderHint && g.reorderHint) {
+                    text += g.reorderHint;
                 }
-            },
-
-            /**
-             * Toggle column's visibility.
-             * After calling this function and it returns true, afterToggleCol() must be called.
-             *
-             * @return boolean True if the column is toggled successfully.
-             */
-            toggleCol: function(n) {
-                if (this.colVisib[n]) {
-                    // can hide if more than one column is visible
-                    if (this.visibleHeadersCount > 1) {
-                        $(this.t).find('tr').each(function() {
-                            $(this).find('th.draggable:eq(' + n + '),' +
-                                         'td:eq(' + (g.actionSpan + n) + ')')
-                                   .hide();
-                        });
-                        this.colVisib[n] = 0;
-                        $(this.cList).find('.lDiv div:eq(' + n + ') input').removeAttr('checked');
-                    } else {
-                        // cannot hide, force the checkbox to stay checked
-                        $(this.cList).find('.lDiv div:eq(' + n + ') input').attr('checked', 'checked');
-                        return false;
-                    }
-                } else {    // column n is not visible
-                    $(this.t).find('tr').each(function() {
+                if (g.showSortHint && g.sortHint) {
+                    text += text.length > 0 ? '<br />' : '';
+                    text += g.sortHint;
+                }
+                if (g.showMarkHint && g.markHint &&
+                    !g.showSortHint      // we do not show mark hint, when sort hint is shown
+                ) {
+                    text += text.length > 0 ? '<br />' : '';
+                    text += g.markHint;
+                }
+                if (g.showColVisibHint && g.colVisibHint) {
+                    text += text.length > 0 ? '<br />' : '';
+                    text += g.colVisibHint;
+                }
+                
+                // hide the hint if no text and the event is mouseenter
+                g.qtip.disable(!text && e.type == 'mouseenter');
+                
+                g.qtip.updateContent(text, false);
+            } else {
+                g.hideHint();
+            }
+        },
+        
+        hideHint: function() {
+            if (g.qtip) {
+                g.qtip.hide();
+                g.qtip.disable(true);
+            }
+        },
+        
+        /**
+         * Toggle column's visibility.
+         * After calling this function and it returns true, afterToggleCol() must be called.
+         *
+         * @return boolean True if the column is toggled successfully.
+         */
+        toggleCol: function(n) {
+            if (g.colVisib[n]) {
+                // can hide if more than one column is visible
+                if (g.visibleHeadersCount > 1) {
+                    $(g.t).find('tr').each(function() {
                         $(this).find('th.draggable:eq(' + n + '),' +
                                      'td:eq(' + (g.actionSpan + n) + ')')
-                               .show();
+                               .hide();
                     });
-                    this.colVisib[n] = 1;
-                    $(this.cList).find('.lDiv div:eq(' + n + ') input').attr('checked', 'checked');
+                    g.colVisib[n] = 0;
+                    $(g.cList).find('.lDiv div:eq(' + n + ') input').removeAttr('checked');
+                } else {
+                    // cannot hide, force the checkbox to stay checked
+                    $(g.cList).find('.lDiv div:eq(' + n + ') input').attr('checked', 'checked');
+                    return false;
                 }
-                return true;
-            },
-
-            /**
-             * This must be called after calling toggleCol() and the return value is true.
-             *
-             * This function is separated from toggleCol because, sometimes, we want to toggle
-             * some columns together at one time and do one adjustment after it, e.g. in showAllColumns().
-             */
-            afterToggleCol: function() {
-                // some adjustments after hiding column
-                this.reposRsz();
-                this.reposDrop();
-                this.sendColPrefs();
-
-                // check visible first row headers count
-                this.visibleHeadersCount = $(this.t).find('tr:first th.draggable:visible').length;
-                this.refreshRestoreButton();
-            },
-
-            /**
-             * Show columns' visibility list.
-             */
-            showColList: function(obj) {
-                // only show when not resizing or reordering
-                if (!this.colRsz && !this.colMov) {
-                    var pos = $(obj).position();
-                    // check if the list position is too right
-                    if (pos.left + $(this.cList).outerWidth(true) > $(document).width()) {
-                        pos.left = $(document).width() - $(this.cList).outerWidth(true);
-                    }
-                    $(this.cList).css({
-                            left: pos.left,
-                            top: pos.top + $(obj).outerHeight(true)
-                        })
-                        .show();
-                    $(obj).addClass('coldrop-hover');
+            } else {    // column n is not visible
+                $(g.t).find('tr').each(function() {
+                    $(this).find('th.draggable:eq(' + n + '),' +
+                                 'td:eq(' + (g.actionSpan + n) + ')')
+                           .show();
+                });
+                g.colVisib[n] = 1;
+                $(g.cList).find('.lDiv div:eq(' + n + ') input').attr('checked', 'checked');
+            }
+            return true;
+        },
+        
+        /**
+         * This must be called if toggleCol() returns is true.
+         *
+         * This function is separated from toggleCol because, sometimes, we want to toggle
+         * some columns together at one time and do just one adjustment after it, e.g. in showAllColumns().
+         */
+        afterToggleCol: function() {
+            // some adjustments after hiding column
+            g.reposRsz();
+            g.reposDrop();
+            g.sendColPrefs();
+            
+            // check visible first row headers count
+            g.visibleHeadersCount = $(g.t).find('tr:first th.draggable:visible').length;
+            g.refreshRestoreButton();
+        },
+        
+        /**
+         * Show columns' visibility list.
+         *
+         * @param obj The drop down arrow of column visibility list
+         */
+        showColList: function(obj) {
+            // only show when not resizing or reordering
+            if (!g.colRsz && !g.colReorder) {
+                var pos = $(obj).position();
+                // check if the list position is too right
+                if (pos.left + $(g.cList).outerWidth(true) > $(document).width()) {
+                    pos.left = $(document).width() - $(g.cList).outerWidth(true);
                 }
-            },
-
-            /**
-             * Hide columns' visibility list.
-             */
-            hideColList: function() {
-                $(this.cList).hide();
-                $(g.cDrop).find('.coldrop-hover').removeClass('coldrop-hover');
-            },
-
-            /**
-             * Reposition the column visibility drop-down arrow.
-             */
-            reposDrop: function() {
-                $th = $(t).find('th:not(.draggable)');
-                for (var i = 0; i < $th.length; i++) {
-                    var $cd = $(this.cDrop).find('div:eq(' + i + ')');   // column drop-down arrow
-                    var pos = $($th[i]).position();
-                    $cd.css({
-                            left: pos.left + $($th[i]).width() - $cd.width(),
-                            top: pos.top
-                        });
+                $(g.cList).css({
+                        left: pos.left,
+                        top: pos.top + $(obj).outerHeight(true)
+                    })
+                    .show();
+                $(obj).addClass('coldrop-hover');
+            }
+        },
+        
+        /**
+         * Hide columns' visibility list.
+         */
+        hideColList: function() {
+            $(g.cList).hide();
+            $(g.cDrop).find('.coldrop-hover').removeClass('coldrop-hover');
+        },
+        
+        /**
+         * Reposition the column visibility drop-down arrow.
+         */
+        reposDrop: function() {
+            $th = $(t).find('th:not(.draggable)');
+            for (var i = 0; i < $th.length; i++) {
+                var $cd = $(g.cDrop).find('div:eq(' + i + ')');   // column drop-down arrow
+                var pos = $($th[i]).position();
+                $cd.css({
+                        left: pos.left + $($th[i]).width() - $cd.width(),
+                        top: pos.top
+                    });
+            }
+        },
+        
+        /**
+         * Show all hidden columns.
+         */
+        showAllColumns: function() {
+            for (var i = 0; i < g.colVisib.length; i++) {
+                if (!g.colVisib[i]) {
+                    g.toggleCol(i);
                 }
-            },
-
-            /**
-             * Show all hidden columns.
-             */
-            showAllColumns: function() {
-                for (var i = 0; i < this.colVisib.length; i++) {
-                    if (!this.colVisib[i]) {
-                        this.toggleCol(i);
-                    }
+            }
+            g.afterToggleCol();
+        },
+        
+        /**
+         * Show edit cell, if it can be shown
+         *
+         * @param cell <td> element to be edited
+         */
+        showEditCell: function(cell) {
+            if ($(cell).is('.grid_edit') &&
+                !g.colRsz && !g.colReorder)
+            {
+                if (!g.isCellEditActive) {
+                    $cell = $(cell);
+                    // remove all edit area and hide it
+                    $(g.cEdit).find('.edit_area').empty().hide();
+                    // reposition the cEdit element
+                    $(g.cEdit).css({
+                            top: $cell.position().top,
+                            left: $cell.position().left,
+                        })
+                        .show()
+                        .find('input')
+                        .css({
+                            width: $cell.outerWidth(),
+                            height: $cell.outerHeight()
+                        });
+                    // fill the cell edit with text from <td>, if it is not null
+                    var value = $cell.is(':not(.null)') ? PMA_getCellValue(cell) : '';
+                    $(g.cEdit).find('input')
+                        .val(value);
+                    
+                    g.currentEditCell = cell;
+                    $(g.cEdit).find('input[type=text]').focus();
+                    $(g.cEdit).find('*').removeAttr('disabled');
                 }
-                this.afterToggleCol();
-            },
-            
-            /**
-             * Show edit cell, if it can be shown or it is forced.
-             */
-            showEditCell: function(cell, force) {
-                if ($(cell).is('.grid_edit') &&
-                    !g.colRsz && !g.colMov)
-                {
-                    if (!g.isCellEditActive || force) {
-                        $cell = $(cell);
-                        // remove all edit area and hide it
-                        $(g.cEdit).find('.edit_area').empty().hide();
-                        // reposition the cEdit element
-                        $(g.cEdit).css({
-                                top: $cell.position().top,
-                                left: $cell.position().left,
-                            })
-                            .show()
-                            .find('input')
-                            .css({
-                                width: $cell.outerWidth(),
-                                height: $cell.outerHeight()
-                            });
-                        // fill the cell edit with text from <td>, if it is not null
-                        var value = $cell.is(':not(.null)') ? PMA_getCellValue(cell) : '';
-                        $(g.cEdit).find('input')
-                            .val(value);
-                        
-                        g.currentEditCell = cell;
-                        $(g.cEdit).find('input[type=text]').focus();
-                        $(g.cEdit).find('*').removeAttr('disabled');
-                    }
-                } else {
+            } else {
+                if (g.isCellEditActive) {
                     g.hideEditCell();
                 }
-            },
+            }
+        },
+        
+        /**
+         * Remove edit cell and the edit area, if it is shown.
+         *
+         * @param force Optional, force to hide edit cell without saving edited field.
+         * @param data  Optional, data from the POST AJAX request to save the edited field
+         *              or just specify "true", if we want to replace the edited field with the new value.
+         * @param field Optional, the edited <td>. If not specified, the function will
+         *              use currently edited <td> from g.currentEditCell.
+         */
+        hideEditCell: function(force, data, field) {
+            if (g.isCellEditActive && !force) {
+                // cell is being edited, post the edited data
+                g.saveOrPostEditedCell();
+                return;
+            }
             
-            /**
-             * Remove edit cell and the edit area, if it is shown.
-             *
-             * @param force Optional, force to hide edit cell without saving edited field.
-             * @param data  Optional, data from the POST AJAX request to save the edited field
-             *              or just specify "true", if we want to replace the edited field with the new value.
-             * @param field Optional, the edited <td>. If not specified, the function will
-             *              use currently edited <td> from g.currentEditCell.
-             */
-            hideEditCell: function(force, data, field) {
-                if (g.isCellEditActive && !force) {
-                    // cell is being edited, post the edited data
-                    g.saveOrPostEditedCell();
-                    return;
-                }
-                
-                // cancel any previous request
-                if (g.lastXHR != null) {
-                    g.lastXHR.abort();
-                    g.lastXHR = null;
-                }
-                
-                if (data) {
-                    if (g.currentEditCell) {    // save value of currently edited cell
-                        // replace current edited field with the new value
-                        var $this_field = $(g.currentEditCell);
-                        var new_html = $this_field.data('value');
-                        var is_null = $this_field.data('value') == null;
-                        if (is_null) {
-                            $this_field.find('span').html('NULL');
-                            $this_field.addClass('null');
-                        } else {
-                            $this_field.removeClass('null');
-                            if ($this_field.is('.truncated')) {
-                                if (new_html.length > g.maxTruncatedLen) {
-                                    new_html = new_html.substring(0, g.maxTruncatedLen) + '...';
-                                }
+            // cancel any previous request
+            if (g.lastXHR != null) {
+                g.lastXHR.abort();
+                g.lastXHR = null;
+            }
+            
+            if (data) {
+                if (g.currentEditCell) {    // save value of currently edited cell
+                    // replace current edited field with the new value
+                    var $this_field = $(g.currentEditCell);
+                    var new_html = $this_field.data('value');
+                    var is_null = $this_field.data('value') == null;
+                    if (is_null) {
+                        $this_field.find('span').html('NULL');
+                        $this_field.addClass('null');
+                    } else {
+                        $this_field.removeClass('null');
+                        if ($this_field.is('.truncated')) {
+                            if (new_html.length > g.maxTruncatedLen) {
+                                new_html = new_html.substring(0, g.maxTruncatedLen) + '...';
                             }
-                            // replace '\n' with <br>
-                            new_html = new_html.replace(/\n/g, '<br />');
-                            $this_field.find('span').html(new_html);
                         }
+                        // replace '\n' with <br>
+                        new_html = new_html.replace(/\n/g, '<br />');
+                        $this_field.find('span').html(new_html);
                     }
-                    if (data.transformations != undefined) {
-                        $.each(data.transformations, function(cell_index, value) {
-                            var $this_field = $(g.t).find('.to_be_saved:eq(' + cell_index + ')');
-                            $this_field.find('span').html(value);
-                        });
-                    }
-                    if (data.relations != undefined) {
-                        $.each(data.relations, function(cell_index, value) {
-                            var $this_field = $(g.t).find('.to_be_saved:eq(' + cell_index + ')');
-                            $this_field.find('span').html(value);
-                        });
-                    }
-                    
-                    // refresh the grid
-                    this.reposRsz();
-                    this.reposDrop();
+                }
+                if (data.transformations != undefined) {
+                    $.each(data.transformations, function(cell_index, value) {
+                        var $this_field = $(g.t).find('.to_be_saved:eq(' + cell_index + ')');
+                        $this_field.find('span').html(value);
+                    });
+                }
+                if (data.relations != undefined) {
+                    $.each(data.relations, function(cell_index, value) {
+                        var $this_field = $(g.t).find('.to_be_saved:eq(' + cell_index + ')');
+                        $this_field.find('span').html(value);
+                    });
                 }
                 
-                // hide the cell editing area
-                $(g.cEdit).hide();
-                $(g.cEdit).find('input[type=text]').blur();
-                g.isCellEditActive = false;
-                g.currentEditCell = null;
-            },
+                // refresh the grid
+                g.reposRsz();
+                g.reposDrop();
+            }
             
-            /**
-             * Show drop-down edit area when edit cell is clicked.
-             */
-            showEditArea: function() {
-                if (!this.isCellEditActive) {   // make sure we don't have focus on other edit cell
-                    g.isCellEditActive = true;
-                    g.isEditCellTextEditable = false;
-                    var $td = $(g.currentEditCell);
-                    var $editArea = $(this.cEdit).find('.edit_area');
-                    var where_clause = $td.parent('tr').find('.where_clause').val();
-                    /**
-                     * @var field_name  String containing the name of this field.
-                     * @see getFieldName()
-                     */
-                    var field_name = getFieldName($td);
-                    /**
-                     * @var relation_curr_value String current value of the field (for fields that are foreign keyed).
-                     */
-                    var relation_curr_value = $td.text();
-                    /**
-                     * @var relation_key_or_display_column String relational key if in 'Relational display column' mode,
-                     * relational display column if in 'Relational key' mode (for fields that are foreign keyed).
-                     */
-                    var relation_key_or_display_column = $td.find('a').attr('title');
-                    /**
-                     * @var curr_value String current value of the field (for fields that are of type enum or set).
-                     */
-                    var curr_value = $td.find('span').text();
-                    
-                    // empty all edit area, then rebuild it based on $td classes
-                    $editArea.empty();
+            // hide the cell editing area
+            $(g.cEdit).hide();
+            $(g.cEdit).find('input[type=text]').blur();
+            g.isCellEditActive = false;
+            g.currentEditCell = null;
+        },
+        
+        /**
+         * Show drop-down edit area when edit cell is focused.
+         */
+        showEditArea: function() {
+            if (!g.isCellEditActive) {   // make sure the edit area has not been shown
+                g.isCellEditActive = true;
+                g.isEditCellTextEditable = false;
+                var $td = $(g.currentEditCell);
+                var $editArea = $(g.cEdit).find('.edit_area');
+                var where_clause = $td.parent('tr').find('.where_clause').val();
+                /**
+                 * @var field_name  String containing the name of this field.
+                 * @see getFieldName()
+                 */
+                var field_name = getFieldName($td);
+                /**
+                 * @var relation_curr_value String current value of the field (for fields that are foreign keyed).
+                 */
+                var relation_curr_value = $td.text();
+                /**
+                 * @var relation_key_or_display_column String relational key if in 'Relational display column' mode,
+                 * relational display column if in 'Relational key' mode (for fields that are foreign keyed).
+                 */
+                var relation_key_or_display_column = $td.find('a').attr('title');
+                /**
+                 * @var curr_value String current value of the field (for fields that are of type enum or set).
+                 */
+                var curr_value = $td.find('span').text();
+                
+                // empty all edit area, then rebuild it based on $td classes
+                $editArea.empty();
+                
+                // add goto link, if this cell contains a link
+                if ($td.find('a').length > 0) {
+                    var gotoLink = document.createElement('div');
+                    gotoLink.className = 'goto_link';
+                    $(gotoLink).append(g.gotoLinkText + ': ')
+                        .append($td.find('a').clone());
+                    $editArea.append(gotoLink);
+                }
+                
+                g.wasEditedCellNull = false;
+                if ($td.is(':not(.not_null)')) {
+                    // append a null checkbox
+                    $editArea.append('<div class="null_div">Null :<input type="checkbox"></div>');
+                    var $checkbox = $editArea.find('.null_div input');
+                    // check if current <td> is NULL
+                    if ($td.is('.null')) {
+                        $checkbox.attr('checked', true);
+                        g.wasEditedCellNull = true;
+                    }
                     
-                    // add goto link, if this cell contains a link
-                    if ($td.find('a').length > 0) {
-                        var gotoLink = document.createElement('div');
-                        gotoLink.className = 'goto_link';
-                        $(gotoLink).append(g.gotoLinkText + ': ')
-                            .append($td.find('a').clone());
-                        $editArea.append(gotoLink);
+                    // if the select/editor is changed un-check the 'checkbox_null_<field_name>_<row_index>'.
+                    if ($td.is('.enum, .set')) {
+                        $editArea.find('select').live('change', function(e) {
+                            $checkbox.attr('checked', false);
+                        })
+                    } else if ($td.is('.relation')) {
+                        $editArea.find('select').live('change', function(e) {
+                            $checkbox.attr('checked', false);
+                        })
+                        $editArea.find('.browse_foreign').live('click', function(e) {
+                            $checkbox.attr('checked', false);
+                        })
+                    } else {
+                        $(g.cEdit).find('input[type=text]').live('change', function(e) {
+                            $checkbox.attr('checked', false);
+                        })
+                        $editArea.find('textarea').live('keydown', function(e) {
+                            $checkbox.attr('checked', false);
+                        })
                     }
                     
-                    g.wasEditedCellNull = false;
-                    if ($td.is(':not(.not_null)')) {
-                        // append a null checkbox
-                        $editArea.append('<div class="null_div">Null :<input type="checkbox"></div>');
-                        var $checkbox = $editArea.find('.null_div input');
-                        // check if current <td> is NULL
-                        if ($td.is('.null')) {
-                            $checkbox.attr('checked', true);
-                            g.wasEditedCellNull = true;
-                        }
-                        
-                        // if the select/editor is changed un-check the 'checkbox_null_<field_name>_<row_index>'.
-                        if ($td.is('.enum, .set')) {
-                            $editArea.find('select').live('change', function(e) {
-                                $checkbox.attr('checked', false);
+                    // if 'checkbox_null_<field_name>_<row_index>' is clicked empty the corresponding select/editor.
+                    $checkbox.click(function(e) {
+                        if ($td.is('.enum')) {
+                            $editArea.find('select').attr('value', '');
+                        } else if ($td.is('.set')) {
+                            $editArea.find('select').find('option').each(function() {
+                                var $option = $(this);
+                                $option.attr('selected', false);
                             })
                         } else if ($td.is('.relation')) {
-                            $editArea.find('select').live('change', function(e) {
-                                $checkbox.attr('checked', false);
-                            })
-                            $editArea.find('.browse_foreign').live('click', function(e) {
-                                $checkbox.attr('checked', false);
-                            })
-                        } else {
-                            $(g.cEdit).find('input[type=text]').live('change', function(e) {
-                                $checkbox.attr('checked', false);
-                            })
-                            $editArea.find('textarea').live('keydown', function(e) {
-                                $checkbox.attr('checked', false);
-                            })
-                        }
-                        
-                        // if 'checkbox_null_<field_name>_<row_index>' is clicked empty the corresponding select/editor.
-                        $checkbox.click(function(e) {
-                            if ($td.is('.enum')) {
+                            // if the dropdown is there to select the foreign value
+                            if ($editArea.find('select').length > 0) {
                                 $editArea.find('select').attr('value', '');
-                            } else if ($td.is('.set')) {
-                                $editArea.find('select').find('option').each(function() {
-                                    var $option = $(this);
-                                    $option.attr('selected', false);
-                                })
-                            } else if ($td.is('.relation')) {
-                                // if the dropdown is there to select the foreign value
-                                if ($editArea.find('select').length > 0) {
-                                    $editArea.find('select').attr('value', '');
-                                }
-                            } else {
-                                $editArea.find('textarea').val('');
                             }
-                            $(g.cEdit).find('input[type=text]').val('');
-                        })
-                    }
-                    
-                    if($td.is('.relation')) {
-                        /** @lends jQuery */
-                        //handle relations
-                        $editArea.addClass('edit_area_loading');
-
-                        // initialize the original data
-                        $td.data('original_data', null);
-                        
-                        /**
-                         * @var post_params Object containing parameters for the POST request
-                         */
-                        var post_params = {
-                                'ajax_request' : true,
-                                'get_relational_values' : true,
-                                'server' : g.server,
-                                'db' : g.db,
-                                'table' : g.table,
-                                'column' : field_name,
-                                'token' : g.token,
-                                'curr_value' : relation_curr_value,
-                                'relation_key_or_display_column' : relation_key_or_display_column
+                        } else {
+                            $editArea.find('textarea').val('');
                         }
+                        $(g.cEdit).find('input[type=text]').val('');
+                    })
+                }
+                
+                if($td.is('.relation')) {
+                    /** @lends jQuery */
+                    //handle relations
+                    $editArea.addClass('edit_area_loading');
 
-                        g.lastXHR = $.post('sql.php', post_params, function(data) {
-                            g.lastXHR = null;
-                            $editArea.removeClass('edit_area_loading');
-                            // save original_data
-                            var value = $(data.dropdown).val();
-                            $td.data('original_data', value);
-                            // update the text input field, in case where the "Relational display column" is checked
-                            $(g.cEdit).find('input[type=text]').val(value);
-                            
-                            $editArea.append(data.dropdown);
-                            $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
-                        }) // end $.post()
-                        
-                        $editArea.find('select').live('change', function(e) {
-                            $(g.cEdit).find('input[type=text]').val($(this).val());
-                        })
+                    // initialize the original data
+                    $td.data('original_data', null);
+                    
+                    /**
+                     * @var post_params Object containing parameters for the POST request
+                     */
+                    var post_params = {
+                            'ajax_request' : true,
+                            'get_relational_values' : true,
+                            'server' : g.server,
+                            'db' : g.db,
+                            'table' : g.table,
+                            'column' : field_name,
+                            'token' : g.token,
+                            'curr_value' : relation_curr_value,
+                            'relation_key_or_display_column' : relation_key_or_display_column
                     }
-                    else if($td.is('.enum')) {
-                        /** @lends jQuery */
-                        //handle enum fields
-                        $editArea.addClass('edit_area_loading');
 
-                        /**
-                         * @var post_params Object containing parameters for the POST request
-                         */
-                        var post_params = {
-                                'ajax_request' : true,
-                                'get_enum_values' : true,
-                                'server' : g.server,
-                                'db' : g.db,
-                                'table' : g.table,
-                                'column' : field_name,
-                                'token' : g.token,
-                                'curr_value' : curr_value
-                        }
-                        g.lastXHR = $.post('sql.php', post_params, function(data) {
-                            g.lastXHR = null;
-                            $editArea.removeClass('edit_area_loading');
-                            $editArea.append(data.dropdown);
-                            $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
-                        }) // end $.post()
+                    g.lastXHR = $.post('sql.php', post_params, function(data) {
+                        g.lastXHR = null;
+                        $editArea.removeClass('edit_area_loading');
+                        // save original_data
+                        var value = $(data.dropdown).val();
+                        $td.data('original_data', value);
+                        // update the text input field, in case where the "Relational display column" is checked
+                        $(g.cEdit).find('input[type=text]').val(value);
                         
-                        $editArea.find('select').live('change', function(e) {
-                            $(g.cEdit).find('input[type=text]').val($(this).val());
-                        })
-                    }
-                    else if($td.is('.set')) {
-                        /** @lends jQuery */
-                        //handle set fields
-                        $editArea.addClass('edit_area_loading');
-
-                        /**
-                         * @var post_params Object containing parameters for the POST request
-                         */
-                        var post_params = {
-                                'ajax_request' : true,
-                                'get_set_values' : true,
-                                'server' : g.server,
-                                'db' : g.db,
-                                'table' : g.table,
-                                'column' : field_name,
-                                'token' : g.token,
-                                'curr_value' : curr_value
-                        }
+                        $editArea.append(data.dropdown);
+                        $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
+                    }) // end $.post()
+                    
+                    $editArea.find('select').live('change', function(e) {
+                        $(g.cEdit).find('input[type=text]').val($(this).val());
+                    })
+                }
+                else if($td.is('.enum')) {
+                    /** @lends jQuery */
+                    //handle enum fields
+                    $editArea.addClass('edit_area_loading');
 
-                        g.lastXHR = $.post('sql.php', post_params, function(data) {
-                            g.lastXHR = null;
-                            $editArea.removeClass('edit_area_loading');
-                            $editArea.append(data.select);
-                            $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
-                        }) // end $.post()
-                        
-                        $editArea.find('select').live('change', function(e) {
-                            $(g.cEdit).find('input[type=text]').val($(this).val());
-                        })
+                    /**
+                     * @var post_params Object containing parameters for the POST request
+                     */
+                    var post_params = {
+                            'ajax_request' : true,
+                            'get_enum_values' : true,
+                            'server' : g.server,
+                            'db' : g.db,
+                            'table' : g.table,
+                            'column' : field_name,
+                            'token' : g.token,
+                            'curr_value' : curr_value
                     }
-                    else if($td.is('.truncated, .transformed')) {
-                        if ($td.is('.to_be_saved')) {   // cell has been edited
-                            var value = $td.data('value');
-                            $(g.cEdit).find('input[type=text]').val(value);
-                            $editArea.append('<textarea>'+value+'</textarea>');
-                            $editArea.find('textarea').live('keyup', function(e) {
-                                $(g.cEdit).find('input[type=text]').val($(this).val());
-                            });
-                            $(g.cEdit).find('input[type=text]').live('keyup', function(e) {
-                                $editArea.find('textarea').val($(this).val());
-                            });
-                            $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
-                        } else {
-                            /** @lends jQuery */
-                            //handle truncated/transformed values values
-                            $editArea.addClass('edit_area_loading');
+                    g.lastXHR = $.post('sql.php', post_params, function(data) {
+                        g.lastXHR = null;
+                        $editArea.removeClass('edit_area_loading');
+                        $editArea.append(data.dropdown);
+                        $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
+                    }) // end $.post()
+                    
+                    $editArea.find('select').live('change', function(e) {
+                        $(g.cEdit).find('input[type=text]').val($(this).val());
+                    })
+                }
+                else if($td.is('.set')) {
+                    /** @lends jQuery */
+                    //handle set fields
+                    $editArea.addClass('edit_area_loading');
 
-                            // initialize the original data
-                            $td.data('original_data', null);
+                    /**
+                     * @var post_params Object containing parameters for the POST request
+                     */
+                    var post_params = {
+                            'ajax_request' : true,
+                            'get_set_values' : true,
+                            'server' : g.server,
+                            'db' : g.db,
+                            'table' : g.table,
+                            'column' : field_name,
+                            'token' : g.token,
+                            'curr_value' : curr_value
+                    }
 
-                            /**
-                             * @var sql_query   String containing the SQL query used to retrieve value of truncated/transformed data
-                             */
-                            var sql_query = 'SELECT `' + field_name + '` FROM `' + g.table + '` WHERE ' + PMA_urldecode(where_clause);
-                            
-                            // Make the Ajax call and get the data, wrap it and insert it
-                            g.lastXHR = $.post('sql.php', {
-                                'token' : g.token,
-                                'server' : g.server,
-                                'db' : g.db,
-                                'ajax_request' : true,
-                                'sql_query' : sql_query,
-                                'grid_edit' : true
-                            }, function(data) {
-                                g.lastXHR = null;
-                                $editArea.removeClass('edit_area_loading');
-                                if(data.success == true) {
-                                    if ($td.is('.truncated')) {
-                                        // get the truncated data length
-                                        g.maxTruncatedLen = $(g.currentEditCell).text().length - 3;
-                                    }
-                                    
-                                    $td.data('original_data', data.value);
-                                    $(g.cEdit).find('input[type=text]').val(data.value);
-                                    $editArea.append('<textarea>'+data.value+'</textarea>');
-                                    $editArea.find('textarea').live('keyup', function(e) {
-                                        $(g.cEdit).find('input[type=text]').val($(this).val());
-                                    });
-                                    $(g.cEdit).find('input[type=text]').live('keyup', function(e) {
-                                        $editArea.find('textarea').val($(this).val());
-                                    });
-                                    $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
-                                }
-                                else {
-                                    PMA_ajaxShowMessage(data.error);
-                                }
-                            }) // end $.post()
-                        }
-                        g.isEditCellTextEditable = true;
-                    } else {
-                        $editArea.append('<textarea>' + PMA_getCellValue(g.currentEditCell) + '</textarea>');
+                    g.lastXHR = $.post('sql.php', post_params, function(data) {
+                        g.lastXHR = null;
+                        $editArea.removeClass('edit_area_loading');
+                        $editArea.append(data.select);
+                        $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
+                    }) // end $.post()
+                    
+                    $editArea.find('select').live('change', function(e) {
+                        $(g.cEdit).find('input[type=text]').val($(this).val());
+                    })
+                }
+                else if($td.is('.truncated, .transformed')) {
+                    if ($td.is('.to_be_saved')) {   // cell has been edited
+                        var value = $td.data('value');
+                        $(g.cEdit).find('input[type=text]').val(value);
+                        $editArea.append('<textarea>'+value+'</textarea>');
                         $editArea.find('textarea').live('keyup', function(e) {
                             $(g.cEdit).find('input[type=text]').val($(this).val());
                         });
@@ -830,603 +858,489 @@
                             $editArea.find('textarea').val($(this).val());
                         });
                         $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
-                        g.isEditCellTextEditable = true;
-                    }
-                    
-                    $editArea.show();
-                }
-            },
-            
-            /**
-             * Post the content of edited cell.
-             */
-            postEditedCell: function() {
-                if (g.isSaving) {
-                    return;
-                }
-                g.isSaving = true;
-                
-                /**
-                 * @var relation_fields Array containing the name/value pairs of relational fields
-                 */
-                var relation_fields = {};
-                /**
-                 * @var relational_display string 'K' if relational key, 'D' if relational display column
-                 */
-                var relational_display = $("#relational_display_K").attr('checked') ? 'K' : 'D';
-                /**
-                 * @var transform_fields    Array containing the name/value pairs for transformed fields
-                 */
-                var transform_fields = {};
-                /**
-                 * @var transformation_fields   Boolean, if there are any transformed fields in the edited cells
-                 */
-                var transformation_fields = false;
-                /**
-                 * @var full_sql_query String containing the complete SQL query to update this table
-                 */
-                var full_sql_query = '';
-                /**
-                 * @var rel_fields_list  String, url encoded representation of {@link relations_fields}
-                 */
-                var rel_fields_list = '';
-                /**
-                 * @var transform_fields_list  String, url encoded representation of {@link transform_fields}
-                 */
-                var transform_fields_list = '';
-                /**
-                 * @var where_clause Array containing where clause for updated fields
-                 */
-                var full_where_clause = Array();
-                /**
-                 * @var is_unique   Boolean, whether the rows in this table is unique or not
-                 */
-                var is_unique = $('.edit_row_anchor').is('.nonunique') ? 0 : 1;
-                /**
-                 * multi edit variables
-                 */
-                var me_fields_name = Array();
-                var me_fields = Array();
-                var me_fields_null = Array();
-                
-                // alert user if edited table is not unique
-                if (!is_unique) {
-                    alert(g.alertNonUnique);
-                }
-                
-                // loop each edited row
-                $('.to_be_saved').parents('tr').each(function() {
-                    var $tr = $(this);
-                    var where_clause = $tr.find('.where_clause').val();
-                    full_where_clause.push(PMA_urldecode(where_clause));
-                    var condition_array = jQuery.parseJSON($tr.find('.condition_array').val());
-                    
-                    /**
-                     * multi edit variables, for current row
-                     * @TODO array indices are still not correct, they should be md5 of field's name
-                     */
-                    var fields_name = Array();
-                    var fields = Array();
-                    var fields_null = Array();
-
-                    // loop each edited cell in a row
-                    $tr.find('.to_be_saved').each(function() {
-                        /**
-                         * @var $this_field    Object referring to the td that is being edited
-                         */
-                        var $this_field = $(this);
-                        
-                        /**
-                         * @var field_name  String containing the name of this field.
-                         * @see getFieldName()
-                         */
-                        var field_name = getFieldName($this_field);
+                    } else {
+                        /** @lends jQuery */
+                        //handle truncated/transformed values values
+                        $editArea.addClass('edit_area_loading');
 
-                        /**
-                         * @var this_field_params   Array temporary storage for the name/value of current field
-                         */
-                        var this_field_params = {};
+                        // initialize the original data
+                        $td.data('original_data', null);
 
-                        if($this_field.is('.transformed')) {
-                            transformation_fields =  true;
-                        }
-                        this_field_params[field_name] = $this_field.data('value');
-                        
                         /**
-                         * @var is_null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked.
+                         * @var sql_query   String containing the SQL query used to retrieve value of truncated/transformed data
                          */
-                        var is_null = this_field_params[field_name] === null;
+                        var sql_query = 'SELECT `' + field_name + '` FROM `' + g.table + '` WHERE ' + PMA_urldecode(where_clause);
                         
-                        fields_name.push(field_name);
-                        
-                        if (is_null) {
-                            fields_null.push('on');
-                            fields.push('');
-                        } else {
-                            fields_null.push('');
-                            fields.push($this_field.data('value'));
-                            
-                            var cell_index = $this_field.index('.to_be_saved');
-                            if($this_field.is(":not(.relation, .enum, .set, .bit)")) {
-                                if($this_field.is('.transformed')) {
-                                    transform_fields[cell_index] = {};
-                                    $.extend(transform_fields[cell_index], this_field_params);
+                        // Make the Ajax call and get the data, wrap it and insert it
+                        g.lastXHR = $.post('sql.php', {
+                            'token' : g.token,
+                            'server' : g.server,
+                            'db' : g.db,
+                            'ajax_request' : true,
+                            'sql_query' : sql_query,
+                            'grid_edit' : true
+                        }, function(data) {
+                            g.lastXHR = null;
+                            $editArea.removeClass('edit_area_loading');
+                            if(data.success == true) {
+                                if ($td.is('.truncated')) {
+                                    // get the truncated data length
+                                    g.maxTruncatedLen = $(g.currentEditCell).text().length - 3;
                                 }
-                            } else if($this_field.is('.relation')) {
-                                relation_fields[cell_index] = {};
-                                $.extend(relation_fields[cell_index], this_field_params);
+                                
+                                $td.data('original_data', data.value);
+                                $(g.cEdit).find('input[type=text]').val(data.value);
+                                $editArea.append('<textarea>'+data.value+'</textarea>');
+                                $editArea.find('textarea').live('keyup', function(e) {
+                                    $(g.cEdit).find('input[type=text]').val($(this).val());
+                                });
+                                $(g.cEdit).find('input[type=text]').live('keyup', function(e) {
+                                    $editArea.find('textarea').val($(this).val());
+                                });
+                                $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
                             }
-                        }
-                        // check if edited field appears in WHERE clause
-                        if (where_clause.indexOf(PMA_urlencode(field_name)) > -1) {
-                            var field_str = '`' + g.table + '`.' + '`' + field_name + '`';
-                            for (var field in condition_array) {
-                                if (field.indexOf(field_str) > -1) {
-                                    condition_array[field] = is_null ? 'IS NULL' : "= '" + this_field_params[field_name].replace(/'/g,"''") + "'";
-                                    break;
-                                }
+                            else {
+                                PMA_ajaxShowMessage(data.error);
                             }
-                        }
-                        
-                    }); // end of loop for every edited cells in a row
-                    
-                    // save new_clause
-                    var new_clause = '';
-                    for (var field in condition_array) {
-                        new_clause += field + ' ' + condition_array[field] + ' AND ';
+                        }) // end $.post()
                     }
-                    new_clause = new_clause.substring(0, new_clause.length - 5); // remove the last AND
-                    new_clause = PMA_urlencode(new_clause);
-                    $tr.data('new_clause', new_clause);
-                    // save condition_array
-                    $tr.find('.condition_array').val(JSON.stringify(condition_array));
-                    
-                    me_fields_name.push(fields_name);
-                    me_fields.push(fields);
-                    me_fields_null.push(fields_null);
-                
-                }); // end of loop for every edited rows
-                
-                rel_fields_list = $.param(relation_fields);
-                transform_fields_list = $.param(transform_fields);
-                
-                // Make the Ajax post after setting all parameters
-                /**
-                 * @var post_params Object containing parameters for the POST request
-                 */
-                var post_params = {'ajax_request' : true,
-                                'sql_query' : full_sql_query,
-                                'token' : g.token,
-                                'server' : g.server,
-                                'db' : g.db,
-                                'table' : g.table,
-                                'clause_is_unique' : is_unique,
-                                'where_clause' : full_where_clause,
-                                'fields[multi_edit]' : me_fields,
-                                'fields_name[multi_edit]' : me_fields_name,
-                                'fields_null[multi_edit]' : me_fields_null,
-                                'rel_fields_list' : rel_fields_list,
-                                'do_transformations' : transformation_fields,
-                                'transform_fields_list' : transform_fields_list,
-                                'relational_display' : relational_display,
-                                'goto' : 'sql.php',
-                                'submit_type' : 'save'
-                              };
-                
-                if (!g.saveCellsAtOnce) {
-                    $(g.cEdit).find('*').attr('disabled', 'disabled');
-                    var $editArea = $(g.cEdit).find('.edit_area');
-                    $editArea.addClass('edit_area_posting');
+                    g.isEditCellTextEditable = true;
                 } else {
-                    $('.save_edited').addClass('saving_edited_data')
-                        .find('input').attr('disabled', 'disabled');    // disable the save button
+                    $editArea.append('<textarea>' + PMA_getCellValue(g.currentEditCell) + '</textarea>');
+                    $editArea.find('textarea').live('keyup', function(e) {
+                        $(g.cEdit).find('input[type=text]').val($(this).val());
+                    });
+                    $(g.cEdit).find('input[type=text]').live('keyup', function(e) {
+                        $editArea.find('textarea').val($(this).val());
+                    });
+                    $editArea.append('<div class="cell_edit_hint">' + g.cellEditHint + '</div>');
+                    g.isEditCellTextEditable = true;
                 }
                 
-                $.ajax({
-                    type: 'POST',
-                    url: 'tbl_replace.php',
-                    data: post_params,
-                    success:
-                        function(data) {
-                            g.isSaving = false;
-                            if (!g.saveCellsAtOnce) {
-                                $(g.cEdit).find('*').removeAttr('disabled');
-                                $editArea.removeClass('edit_area_posting');
-                            } else {
-                                $('.save_edited').removeClass('saving_edited_data')
-                                    .find('input').removeAttr('disabled');  // enable the save button back
-                            }
-                            if(data.success == true) {
-                                PMA_ajaxShowMessage(data.message);
-                                $('.to_be_saved').each(function() {
-                                    var new_clause = $(this).parent('tr').data('new_clause');
-                                    if (new_clause != '') {
-                                        var $where_clause = $(this).parent('tr').find('.where_clause');
-                                        var old_clause = $where_clause.attr('value');
-                                        var decoded_old_clause = PMA_urldecode(old_clause);
-                                        var decoded_new_clause = PMA_urldecode(new_clause);
-                                        
-                                        $where_clause.attr('value', new_clause);
-                                        // update Edit, Copy, and Delete links also
-                                        $(this).parent('tr').find('a').each(function() {
-                                            $(this).attr('href', $(this).attr('href').replace(old_clause, new_clause));
-                                            // update delete confirmation in Delete link
-                                            if ($(this).attr('href').indexOf('DELETE') > -1) {
-                                                $(this).removeAttr('onclick')
-                                                    .unbind('click')
-                                                    .bind('click', function() {
-                                                        return confirmLink(this, 'DELETE FROM `' + g.db + '`.`' + g.table + '` WHERE ' +
-                                                               decoded_new_clause + (is_unique ? '' : ' LIMIT 1'));
-                                                    });
-                                            }
-                                        });
-                                        // update the multi edit checkboxes
-                                        $(this).parent('tr').find('input[type=checkbox]').each(function() {
-                                            var $checkbox = $(this);
-                                            var checkbox_name = $checkbox.attr('name');
-                                            var checkbox_value = $checkbox.attr('value');
-                                            
-                                            $checkbox.attr('name', checkbox_name.replace(old_clause, new_clause));
-                                            $checkbox.attr('value', checkbox_value.replace(decoded_old_clause, decoded_new_clause));
-                                        });
-                                    }
-                                });
-                                // remove possible previous feedback message
-                                $('#result_query').remove();
-                                if (typeof data.sql_query != 'undefined') {
-                                    // display feedback
-                                    $('#sqlqueryresults').prepend(data.sql_query);
-                                }
-                                g.hideEditCell(true, data);
-                                
-                                // remove the "Save edited cells" button
-                                $('.save_edited').hide();
-                                // update saved fields
-                                $(g.t).find('.to_be_saved')
-                                    .removeClass('to_be_saved')
-                                    .data('value', null)
-                                    .data('original_data', null);
-                                
-                                g.isCellEdited = false;
-                            } else {
-                                PMA_ajaxShowMessage(data.error);
-                            }
-                        }
-                }) // end $.ajax()
-            },
+                $editArea.show();
+            }
+        },
+        
+        /**
+         * Post the content of edited cell.
+         */
+        postEditedCell: function() {
+            if (g.isSaving) {
+                return;
+            }
+            g.isSaving = true;
+            
+            /**
+             * @var relation_fields Array containing the name/value pairs of relational fields
+             */
+            var relation_fields = {};
+            /**
+             * @var relational_display string 'K' if relational key, 'D' if relational display column
+             */
+            var relational_display = $("#relational_display_K").attr('checked') ? 'K' : 'D';
+            /**
+             * @var transform_fields    Array containing the name/value pairs for transformed fields
+             */
+            var transform_fields = {};
+            /**
+             * @var transformation_fields   Boolean, if there are any transformed fields in the edited cells
+             */
+            var transformation_fields = false;
+            /**
+             * @var full_sql_query String containing the complete SQL query to update this table
+             */
+            var full_sql_query = '';
+            /**
+             * @var rel_fields_list  String, url encoded representation of {@link relations_fields}
+             */
+            var rel_fields_list = '';
+            /**
+             * @var transform_fields_list  String, url encoded representation of {@link transform_fields}
+             */
+            var transform_fields_list = '';
+            /**
+             * @var where_clause Array containing where clause for updated fields
+             */
+            var full_where_clause = Array();
+            /**
+             * @var is_unique   Boolean, whether the rows in this table is unique or not
+             */
+            var is_unique = $('.edit_row_anchor').is('.nonunique') ? 0 : 1;
+            /**
+             * multi edit variables
+             */
+            var me_fields_name = Array();
+            var me_fields = Array();
+            var me_fields_null = Array();
             
-            // save edited cell, so it can be posted later
-            saveEditedCell: function() {
-                /**
-                 * @var $this_field    Object referring to the td that is being edited
-                 */
-                var $this_field = $(g.currentEditCell);
-                var $test_element = ''; // to test the presence of a element
-
-                var need_to_post = false;
-
+            // alert user if edited table is not unique
+            if (!is_unique) {
+                alert(g.alertNonUnique);
+            }
+            
+            // loop each edited row
+            $('.to_be_saved').parents('tr').each(function() {
+                var $tr = $(this);
+                var where_clause = $tr.find('.where_clause').val();
+                full_where_clause.push(PMA_urldecode(where_clause));
+                var condition_array = jQuery.parseJSON($tr.find('.condition_array').val());
+                
                 /**
-                 * @var field_name  String containing the name of this field.
-                 * @see getFieldName()
+                 * multi edit variables, for current row
+                 * @TODO array indices are still not correct, they should be md5 of field's name
                  */
-                var field_name = getFieldName($this_field);
+                var fields_name = Array();
+                var fields = Array();
+                var fields_null = Array();
 
-                /**
-                 * @var this_field_params   Array temporary storage for the name/value of current field
-                 */
-                var this_field_params = {};
+                // loop each edited cell in a row
+                $tr.find('.to_be_saved').each(function() {
+                    /**
+                     * @var $this_field    Object referring to the td that is being edited
+                     */
+                    var $this_field = $(this);
+                    
+                    /**
+                     * @var field_name  String containing the name of this field.
+                     * @see getFieldName()
+                     */
+                    var field_name = getFieldName($this_field);
 
-                /**
-                 * @var is_null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked.
-                 */
-                var is_null = $(g.cEdit).find('input:checkbox').is(':checked');
-                var value;
+                    /**
+                     * @var this_field_params   Array temporary storage for the name/value of current field
+                     */
+                    var this_field_params = {};
 
-                if ($(g.cEdit).find('.edit_area').is('.edit_area_loading')) {
-                    need_to_post = false;
-                } else if (is_null) {
-                    if (!g.wasEditedCellNull) {
-                        this_field_params[field_name] = null;
-                        need_to_post = true;
+                    if($this_field.is('.transformed')) {
+                        transformation_fields =  true;
                     }
-                } else {
-                    if($this_field.is(":not(.relation, .enum, .set, .bit)")) {
-                        this_field_params[field_name] = $(g.cEdit).find('textarea').val();
-                    } else if ($this_field.is('.bit')) {
-                        this_field_params[field_name] = '0b' + $(g.cEdit).find('textarea').val();
-                    } else if ($this_field.is('.set')) {
-                        $test_element = $(g.cEdit).find('select');
-                        this_field_params[field_name] = $test_element.map(function(){
-                            return $(this).val();
-                        }).get().join(",");
+                    this_field_params[field_name] = $this_field.data('value');
+                    
+                    /**
+                     * @var is_null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked.
+                     */
+                    var is_null = this_field_params[field_name] === null;
+                    
+                    fields_name.push(field_name);
+                    
+                    if (is_null) {
+                        fields_null.push('on');
+                        fields.push('');
                     } else {
-                        // results from a drop-down
-                        $test_element = $(g.cEdit).find('select');
-                        if ($test_element.length != 0) {
-                            this_field_params[field_name] = $test_element.val();
-                        }
-
-                        // results from Browse foreign value
-                        $test_element = $(g.cEdit).find('span.curr_value');
-                        if ($test_element.length != 0) {
-                            this_field_params[field_name] = $test_element.text();
+                        fields_null.push('');
+                        fields.push($this_field.data('value'));
+                        
+                        var cell_index = $this_field.index('.to_be_saved');
+                        if($this_field.is(":not(.relation, .enum, .set, .bit)")) {
+                            if($this_field.is('.transformed')) {
+                                transform_fields[cell_index] = {};
+                                $.extend(transform_fields[cell_index], this_field_params);
+                            }
+                        } else if($this_field.is('.relation')) {
+                            relation_fields[cell_index] = {};
+                            $.extend(relation_fields[cell_index], this_field_params);
                         }
                     }
-                    if (g.wasEditedCellNull || this_field_params[field_name] != PMA_getCellValue(g.currentEditCell)) {
-                        need_to_post = true;
+                    // check if edited field appears in WHERE clause
+                    if (where_clause.indexOf(PMA_urlencode(field_name)) > -1) {
+                        var field_str = '`' + g.table + '`.' + '`' + field_name + '`';
+                        for (var field in condition_array) {
+                            if (field.indexOf(field_str) > -1) {
+                                condition_array[field] = is_null ? 'IS NULL' : "= '" + this_field_params[field_name].replace(/'/g,"''") + "'";
+                                break;
+                            }
+                        }
                     }
-                }
+                    
+                }); // end of loop for every edited cells in a row
                 
-                if (need_to_post) {
-                    $(g.currentEditCell).addClass('to_be_saved')
-                        .data('value', this_field_params[field_name]);
-                    if (g.saveCellsAtOnce) {
-                        $('.save_edited').show();
-                    }
-                    g.isCellEdited = true;
+                // save new_clause
+                var new_clause = '';
+                for (var field in condition_array) {
+                    new_clause += field + ' ' + condition_array[field] + ' AND ';
                 }
+                new_clause = new_clause.substring(0, new_clause.length - 5); // remove the last AND
+                new_clause = PMA_urlencode(new_clause);
+                $tr.data('new_clause', new_clause);
+                // save condition_array
+                $tr.find('.condition_array').val(JSON.stringify(condition_array));
                 
-                return need_to_post;
-            },
+                me_fields_name.push(fields_name);
+                me_fields.push(fields);
+                me_fields_null.push(fields_null);
             
-            // save or post edited cell, depending on the configuration
-            saveOrPostEditedCell: function() {
-                var saved = g.saveEditedCell();
-                if (!g.saveCellsAtOnce) {
-                    if (saved) {
-                        g.postEditedCell();
-                    } else {
-                        g.hideEditCell(true);
-                    }
-                } else {
-                    if (saved) {
-                        g.hideEditCell(true, true);
-                    } else {
-                        g.hideEditCell(true);
-                    }
-                }
-            },
+            }); // end of loop for every edited rows
+            
+            rel_fields_list = $.param(relation_fields);
+            transform_fields_list = $.param(transform_fields);
+            
+            // Make the Ajax post after setting all parameters
+            /**
+             * @var post_params Object containing parameters for the POST request
+             */
+            var post_params = {'ajax_request' : true,
+                            'sql_query' : full_sql_query,
+                            'token' : g.token,
+                            'server' : g.server,
+                            'db' : g.db,
+                            'table' : g.table,
+                            'clause_is_unique' : is_unique,
+                            'where_clause' : full_where_clause,
+                            'fields[multi_edit]' : me_fields,
+                            'fields_name[multi_edit]' : me_fields_name,
+                            'fields_null[multi_edit]' : me_fields_null,
+                            'rel_fields_list' : rel_fields_list,
+                            'do_transformations' : transformation_fields,
+                            'transform_fields_list' : transform_fields_list,
+                            'relational_display' : relational_display,
+                            'goto' : 'sql.php',
+                            'submit_type' : 'save'
+                          };
             
-            // initialize grid editing feature
-            initGridEdit: function() {
-                $(t).find('td.data')
-                    .click(function(e) {
-                        if (g.isCellEditActive) {
-                            g.saveOrPostEditedCell();
-                            e.stopPropagation();
+            if (!g.saveCellsAtOnce) {
+                $(g.cEdit).find('*').attr('disabled', 'disabled');
+                var $editArea = $(g.cEdit).find('.edit_area');
+                $editArea.addClass('edit_area_posting');
+            } else {
+                $('.save_edited').addClass('saving_edited_data')
+                    .find('input').attr('disabled', 'disabled');    // disable the save button
+            }
+            
+            $.ajax({
+                type: 'POST',
+                url: 'tbl_replace.php',
+                data: post_params,
+                success:
+                    function(data) {
+                        g.isSaving = false;
+                        if (!g.saveCellsAtOnce) {
+                            $(g.cEdit).find('*').removeAttr('disabled');
+                            $editArea.removeClass('edit_area_posting');
                         } else {
-                            g.showEditCell(this);
-                            e.stopPropagation();
+                            $('.save_edited').removeClass('saving_edited_data')
+                                .find('input').removeAttr('disabled');  // enable the save button back
                         }
-                        // prevent default action when clicking on "link" in a table
-                        if ($(e.target).is('a')) {
-                            e.preventDefault();
+                        if(data.success == true) {
+                            PMA_ajaxShowMessage(data.message);
+                            $('.to_be_saved').each(function() {
+                                var new_clause = $(this).parent('tr').data('new_clause');
+                                if (new_clause != '') {
+                                    var $where_clause = $(this).parent('tr').find('.where_clause');
+                                    var old_clause = $where_clause.attr('value');
+                                    var decoded_old_clause = PMA_urldecode(old_clause);
+                                    var decoded_new_clause = PMA_urldecode(new_clause);
+                                    
+                                    $where_clause.attr('value', new_clause);
+                                    // update Edit, Copy, and Delete links also
+                                    $(this).parent('tr').find('a').each(function() {
+                                        $(this).attr('href', $(this).attr('href').replace(old_clause, new_clause));
+                                        // update delete confirmation in Delete link
+                                        if ($(this).attr('href').indexOf('DELETE') > -1) {
+                                            $(this).removeAttr('onclick')
+                                                .unbind('click')
+                                                .bind('click', function() {
+                                                    return confirmLink(this, 'DELETE FROM `' + g.db + '`.`' + g.table + '` WHERE ' +
+                                                           decoded_new_clause + (is_unique ? '' : ' LIMIT 1'));
+                                                });
+                                        }
+                                    });
+                                    // update the multi edit checkboxes
+                                    $(this).parent('tr').find('input[type=checkbox]').each(function() {
+                                        var $checkbox = $(this);
+                                        var checkbox_name = $checkbox.attr('name');
+                                        var checkbox_value = $checkbox.attr('value');
+                                        
+                                        $checkbox.attr('name', checkbox_name.replace(old_clause, new_clause));
+                                        $checkbox.attr('value', checkbox_value.replace(decoded_old_clause, decoded_new_clause));
+                                    });
+                                }
+                            });
+                            // remove possible previous feedback message
+                            $('#result_query').remove();
+                            if (typeof data.sql_query != 'undefined') {
+                                // display feedback
+                                $('#sqlqueryresults').prepend(data.sql_query);
+                            }
+                            g.hideEditCell(true, data);
+                            
+                            // remove the "Save edited cells" button
+                            $('.save_edited').hide();
+                            // update saved fields
+                            $(g.t).find('.to_be_saved')
+                                .removeClass('to_be_saved')
+                                .data('value', null)
+                                .data('original_data', null);
+                            
+                            g.isCellEdited = false;
+                        } else {
+                            PMA_ajaxShowMessage(data.error);
                         }
-                    });
-                $(g.cEdit).find('input[type=text]').focus(function(e) {
-                    g.showEditArea();
-                });
-                $(g.cEdit).find('input[type=text], select').live('keydown', function(e) {
-                    if (e.which == 13) {
-                        // post on pressing "Enter"
-                        e.preventDefault();
-                        g.saveOrPostEditedCell();
-                    }
-                });
-                $(g.cEdit).keydown(function(e) {
-                    if (!g.isEditCellTextEditable) {
-                        // prevent text editing
-                        e.preventDefault();
-                    }
-                });
-                $('html').click(function(e) {
-                    // hide edit cell if the click is not from g.cEdit
-                    if ($(e.target).parents().index(g.cEdit) == -1) {
-                        g.hideEditCell();
-                    }
-                });
-                $('html').keydown(function(e) {
-                    if (e.which == 27 && g.isCellEditActive) {
-
-                        // cancel on pressing "Esc"
-                        g.hideEditCell(true);
-                    }
-                });
-                $('.save_edited').click(function() {
-                    g.hideEditCell();
-                    g.postEditedCell();
-                });
-                $(window).bind('beforeunload', function(e) {
-                    if (g.isCellEdited) {
-                        return g.saveCellWarning;
                     }
-                });
-            }
-        }
-
-        // wrap all data cells, except actions cell, with span
-        $(t).find('th, td:not(:has(span))')
-            .wrapInner('<span />');
-
-        g.gDiv = document.createElement('div');     // create global div
-        g.cRsz = document.createElement('div');     // column resizer
-        g.cCpy = document.createElement('div');     // column copy, to store copy of dragged column header
-        g.cPointer = document.createElement('div'); // column pointer, used when reordering column
-        g.cDrop = document.createElement('div');    // column drop-down arrows
-        g.cList = document.createElement('div');    // column visibility list
-        g.cEdit = document.createElement('div');    // cell edit
-        
-        // adjust g.cCpy
-        g.cCpy.className = 'cCpy';
-        $(g.cCpy).hide();
-
-        // adjust g.cPoint
-        g.cPointer.className = 'cPointer';
-        $(g.cPointer).css('visibility', 'hidden');
-
-        // adjust g.cDrop
-        g.cDrop.className = 'cDrop';
-
-        // adjust g.cList
-        g.cList.className = 'cList';
-        $(g.cList).hide();
-        
-        // adjust g.cEdit
-        g.cEdit.className = 'cEdit';
-        $(g.cEdit).html('<input type="text" /><div class="edit_area" />');
-        $(g.cEdit).hide();
+            }) // end $.ajax()
+        },
         
-        // chain table and grid together
-        t.grid = g;
-        g.t = t;
+        /**
+         * Save edited cell, so it can be posted later.
+         */
+        saveEditedCell: function() {
+            /**
+             * @var $this_field    Object referring to the td that is being edited
+             */
+            var $this_field = $(g.currentEditCell);
+            var $test_element = ''; // to test the presence of a element
 
-        // get first row data columns
-        var $firstRowCols = $(t).find('tr:first th.draggable');
+            var need_to_post = false;
 
-        // initialize g.visibleHeadersCount
-        g.visibleHeadersCount = $firstRowCols.filter(':visible').length;
+            /**
+             * @var field_name  String containing the name of this field.
+             * @see getFieldName()
+             */
+            var field_name = getFieldName($this_field);
 
-        // assign first column (actions) span
-        if (! $(t).find('tr:first th:first').hasClass('draggable')) {  // action header exist
-            g.actionSpan = $(t).find('tr:first th:first').prop('colspan');
-        } else {
-            g.actionSpan = 0;
-        }
+            /**
+             * @var this_field_params   Array temporary storage for the name/value of current field
+             */
+            var this_field_params = {};
 
-        // assign table create time
-        // #table_create_time will only available if we are in "Browse" tab
-        g.tableCreateTime = $('#table_create_time').val();
+            /**
+             * @var is_null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked.
+             */
+            var is_null = $(g.cEdit).find('input:checkbox').is(':checked');
+            var value;
+
+            if ($(g.cEdit).find('.edit_area').is('.edit_area_loading')) {
+                need_to_post = false;
+            } else if (is_null) {
+                if (!g.wasEditedCellNull) {
+                    this_field_params[field_name] = null;
+                    need_to_post = true;
+                }
+            } else {
+                if($this_field.is(":not(.relation, .enum, .set, .bit)")) {
+                    this_field_params[field_name] = $(g.cEdit).find('textarea').val();
+                } else if ($this_field.is('.bit')) {
+                    this_field_params[field_name] = '0b' + $(g.cEdit).find('textarea').val();
+                } else if ($this_field.is('.set')) {
+                    $test_element = $(g.cEdit).find('select');
+                    this_field_params[field_name] = $test_element.map(function(){
+                        return $(this).val();
+                    }).get().join(",");
+                } else {
+                    // results from a drop-down
+                    $test_element = $(g.cEdit).find('select');
+                    if ($test_element.length != 0) {
+                        this_field_params[field_name] = $test_element.val();
+                    }
 
-        // assign column reorder & column sort hint
-        g.reorderHint = PMA_messages['strColOrderHint'];
-        g.sortHint = PMA_messages['strSortHint'];
-        g.markHint = PMA_messages['strColMarkHint'];
-        g.colVisibHint = PMA_messages['strColVisibHint'];
-        g.showAllColText = PMA_messages['strShowAllCol'];
-        
-        // assign cell editing hint
-        g.cellEditHint = PMA_messages['strCellEditHint'];
-        g.saveCellWarning = PMA_messages['strSaveCellWarning'];
-        g.alertNonUnique = PMA_messages['strAlertNonUnique'];
-        
-        // initialize cell editing configuration
-        g.saveCellsAtOnce = $('#save_cells_at_once').val();
-        
-        // assign common hidden inputs
-        var $common_hidden_inputs = $('.common_hidden_inputs');
-        g.token = $common_hidden_inputs.find('input[name=token]').val();
-        g.server = $common_hidden_inputs.find('input[name=server]').val();
-        g.db = $common_hidden_inputs.find('input[name=db]').val();
-        g.table = $common_hidden_inputs.find('input[name=table]').val();
-        
-        // initialize column order
-        $col_order = $('#col_order');
-        if ($col_order.length > 0) {
-            g.colOrder = $col_order.val().split(',');
-            for (var i = 0; i < g.colOrder.length; i++) {
-                g.colOrder[i] = parseInt(g.colOrder[i]);
-            }
-        } else {
-            g.colOrder = new Array();
-            for (var i = 0; i < $firstRowCols.length; i++) {
-                g.colOrder.push(i);
+                    // results from Browse foreign value
+                    $test_element = $(g.cEdit).find('span.curr_value');
+                    if ($test_element.length != 0) {
+                        this_field_params[field_name] = $test_element.text();
+                    }
+                }
+                if (g.wasEditedCellNull || this_field_params[field_name] != PMA_getCellValue(g.currentEditCell)) {
+                    need_to_post = true;
+                }
             }
-        }
-
-        // initialize column visibility
-        $col_visib = $('#col_visib');
-        if ($col_visib.length > 0) {
-            g.colVisib = $col_visib.val().split(',');
-            for (var i = 0; i < g.colVisib.length; i++) {
-                g.colVisib[i] = parseInt(g.colVisib[i]);
+            
+            if (need_to_post) {
+                $(g.currentEditCell).addClass('to_be_saved')
+                    .data('value', this_field_params[field_name]);
+                if (g.saveCellsAtOnce) {
+                    $('.save_edited').show();
+                }
+                g.isCellEdited = true;
             }
-        } else {
-            g.colVisib = new Array();
-            for (var i = 0; i < $firstRowCols.length; i++) {
-                g.colVisib.push(1);
+            
+            return need_to_post;
+        },
+        
+        /**
+         * Save or post currently edited cell, depending on the "saveCellsAtOnce" configuration.
+         */
+        saveOrPostEditedCell: function() {
+            var saved = g.saveEditedCell();
+            if (!g.saveCellsAtOnce) {
+                if (saved) {
+                    g.postEditedCell();
+                } else {
+                    g.hideEditCell(true);
+                }
+            } else {
+                if (saved) {
+                    g.hideEditCell(true, true);
+                } else {
+                    g.hideEditCell(true);
+                }
             }
-        }
-
-        if ($firstRowCols.length > 1) {
-            // create column drop-down arrow(s)
-            $(t).find('th:not(.draggable)').each(function() {
-                var cd = document.createElement('div'); // column drop-down arrow
-                var pos = $(this).position();
-                $(cd).addClass('coldrop')
-                    .css({
-                        left: pos.left + $(this).width() - $(cd).width(),
-                        top: pos.top
-                    })
-                    .click(function() {
-                        if (g.cList.style.display == 'none') {
-                            g.showColList(this);
-                        } else {
-                            g.hideColList();
-                        }
+        },
+        
+        /**
+         * Initialize column resize feature.
+         */
+        initColResize: function() {
+            // create column resizer div
+            g.cRsz = document.createElement('div');
+            g.cRsz.className = 'cRsz';
+            
+            // get data columns in the first row of the table
+            var $firstRowCols = $(g.t).find('tr:first th.draggable');
+            
+            // create column borders
+            $firstRowCols.each(function() {
+                var cb = document.createElement('div'); // column border
+                $(cb).addClass('colborder')
+                    .mousedown(function(e) {
+                        g.dragStartRsz(e, this);
                     });
-                $(g.cDrop).append(cd);
-            });
-
-            // add column visibility control
-            g.cList.innerHTML = '<div class="lDiv"></div>';
-            var $listDiv = $(g.cList).find('div');
-            for (var i = 0; i < $firstRowCols.length; i++) {
-                var currHeader = $firstRowCols[i];
-                var listElmt = document.createElement('div');
-                $(listElmt).text($(currHeader).text())
-                    .prepend('<input type="checkbox" ' + (g.colVisib[i] ? 'checked="checked" ' : '') + '/>');
-                $listDiv.append(listElmt);
-                // add event on click
-                $(listElmt).click(function() {
-                    if ( g.toggleCol($(this).index()) ) {
-                        g.afterToggleCol();
-                    }
-                });
-            }
-            // add "show all column" button
-            var showAll = document.createElement('div');
-            $(showAll).addClass('showAllColBtn')
-                .text(g.showAllColText);
-            $(g.cList).append(showAll);
-            $(showAll).click(function() {
-                g.showAllColumns();
+                $(g.cRsz).append(cb);
             });
-            // prepend "show all column" button at top if the list is too long
-            if ($firstRowCols.length > 10) {
-                var clone = showAll.cloneNode(true);
-                $(g.cList).prepend(clone);
-                $(clone).click(function() {
-                    g.showAllColumns();
-                });
+            g.reposRsz();
+            
+            // attach to global div
+            $(g.gDiv).prepend(g.cRsz);
+        },
+        
+        /**
+         * Initialize column reordering feature.
+         */
+        initColReorder: function() {
+            g.cCpy = document.createElement('div');     // column copy, to store copy of dragged column header
+            g.cPointer = document.createElement('div'); // column pointer, used when reordering column
+            
+            // adjust g.cCpy
+            g.cCpy.className = 'cCpy';
+            $(g.cCpy).hide();
+            
+            // adjust g.cPointer
+            g.cPointer.className = 'cPointer';
+            $(g.cPointer).css('visibility', 'hidden');  // set visibility to hidden instead of calling hide() to force browsers to cache the image in cPointer class
+            
+            // assign column reordering hint
+            g.reorderHint = PMA_messages['strColOrderHint'];
+            
+            // get data columns in the first row of the table
+            var $firstRowCols = $(g.t).find('tr:first th.draggable');
+            
+            // initialize column order
+            $col_order = $('#col_order');   // check if column order is passed from PHP
+            if ($col_order.length > 0) {
+                g.colOrder = $col_order.val().split(',');
+                for (var i = 0; i < g.colOrder.length; i++) {
+                    g.colOrder[i] = parseInt(g.colOrder[i]);
+                }
+            } else {
+                g.colOrder = new Array();
+                for (var i = 0; i < $firstRowCols.length; i++) {
+                    g.colOrder.push(i);
+                }
             }
-        }
-
-        // create column borders
-        $firstRowCols.each(function() {
-            $this = $(this);
-            var cb = document.createElement('div'); // column border
-            $(cb).addClass('colborder')
-                .mousedown(function(e) {
-                    g.dragStartRsz(e, this);
-                });
-            $(g.cRsz).append(cb);
-        });
-        g.reposRsz();
-
-        // bind event to update currently hovered qtip API
-        $(t).find('th').mouseenter(function(e) {
-            g.qtip = $(this).qtip('api');
-        });
-
-        // create qtip for each <th> with draggable class
-        PMA_createqTip($(t).find('th.draggable'));
-
-        // register events
-        if (g.reorderHint) {    // make sure columns is reorderable
+            
+            // register events
             $(t).find('th.draggable')
                 .mousedown(function(e) {
                     if (g.visibleHeadersCount > 1) {
-                        g.dragStartMove(e, this);
+                        g.dragStartReorder(e, this);
                     }
                 })
                 .mouseenter(function(e) {
@@ -1436,131 +1350,333 @@
                     } else {
                         $(this).css('cursor', 'inherit');
                     }
-                    g.updateHint(e);
                 })
                 .mouseleave(function(e) {
                     g.showReorderHint = false;
-                    g.updateHint(e);
                 });
-        }
-        if ($firstRowCols.length > 1) {
-            var $colVisibTh = $(t).find('th:not(.draggable)');
-
-            PMA_createqTip($colVisibTh);
-            $colVisibTh.mouseenter(function(e) {
-                    g.showColVisibHint = true;
-                    g.updateHint(e);
-                })
-                .mouseleave(function(e) {
-                    g.showColVisibHint = false;
-                    g.updateHint(e);
-                });
-        }
-        $(t).find('th.draggable a')
-            .attr('title', '')          // hide default tooltip for sorting
-            .mouseenter(function(e) {
-                g.showSortHint = true;
-                g.updateHint(e);
-            })
-            .mouseleave(function(e) {
-                g.showSortHint = false;
-                g.updateHint(e);
+            // restore column order when the restore button is clicked
+            $('.restore_column').click(function() {
+                g.restoreColOrder();
             });
-        $(t).find('th.marker')
-            .mouseenter(function(e) {
-                g.showMarkHint = true;
-                g.updateHint(e);
-            })
-            .mouseleave(function(e) {
-                g.showMarkHint = false;
-                g.updateHint(e);
+            
+            // attach to global div
+            $(g.gDiv).append(g.cPointer);
+            $(g.gDiv).append(g.cCpy);
+            
+            // prevent default "dragstart" event when dragging a link
+            $(t).find('th a').bind('dragstart', function() {
+                return false;
             });
-        $(document).mousemove(function(e) {
-            g.dragMove(e);
-        });
-        $(document).mouseup(function(e) {
-            g.dragEnd(e);
-        });
-        $('.restore_column').click(function() {
-            g.restoreColOrder();
-        });
-        $(t).find('td, th.draggable').mouseenter(function() {
-            g.hideColList();
-        });
-        // edit cell event
-        if ($(t).is('.ajax')) {
-            g.initGridEdit();
-        }
+            
+            // refresh the restore column button state
+            g.refreshRestoreButton();
+        },
         
-        // add table class
-        $(t).addClass('pma_table');
-
-        // link all divs
-        $(t).before(g.gDiv);
-        $(g.gDiv).append(t);
-        $(g.gDiv).prepend(g.cRsz);
-        $(g.gDiv).append(g.cPointer);
-        $(g.gDiv).append(g.cDrop);
-        $(g.gDiv).append(g.cList);
-        $(g.gDiv).append(g.cCpy);
-        $(g.gDiv).append(g.cEdit);
-
-        // some adjustment
-        g.refreshRestoreButton();
-        g.cRsz.className = 'cRsz';
-        $(t).removeClass('data');
-        $(g.gDiv).addClass('data');
-        $(g.cRsz).css('height', $(t).height());
-        $(t).find('th a').bind('dragstart', function() {
-            return false;
-        });
-    };
-
-    // document ready checking
-    var docready = false;
-    $(document).ready(function() {
-        docready = true;
-    });
-
-    // Additional jQuery functions
-    /**
-     * Make resizable, reorderable grid.
-     */
-    $.fn.makegrid = function() {
-        return this.each(function() {
-            if (!docready) {
-                var t = this;
-                $(document).ready(function() {
-                    $.grid(t);
-                    t.grid.reposDrop();
-                });
+        /**
+         * Initialize column visibility feature.
+         */
+        initColVisib: function() {
+            g.cDrop = document.createElement('div');    // column drop-down arrows
+            g.cList = document.createElement('div');    // column visibility list
+            
+            // adjust g.cDrop
+            g.cDrop.className = 'cDrop';
+            
+            // adjust g.cList
+            g.cList.className = 'cList';
+            $(g.cList).hide();
+            
+            // assign column visibility related hints
+            g.colVisibHint = PMA_messages['strColVisibHint'];
+            g.showAllColText = PMA_messages['strShowAllCol'];
+            
+            // get data columns in the first row of the table
+            var $firstRowCols = $(g.t).find('tr:first th.draggable');
+            
+            // initialize column visibility
+            $col_visib = $('#col_visib');   // check if column visibility is passed from PHP
+            if ($col_visib.length > 0) {
+                g.colVisib = $col_visib.val().split(',');
+                for (var i = 0; i < g.colVisib.length; i++) {
+                    g.colVisib[i] = parseInt(g.colVisib[i]);
+                }
             } else {
-                $.grid(this);
-                this.grid.reposDrop();
+                g.colVisib = new Array();
+                for (var i = 0; i < $firstRowCols.length; i++) {
+                    g.colVisib.push(1);
+                }
             }
-        });
-    };
-    /**
-     * Refresh grid. This must be called after changing the grid's content.
-     */
-    $.fn.refreshgrid = function() {
-        return this.each(function() {
-            if (!docready) {
-                var t = this;
-                $(document).ready(function() {
-                    if (t.grid) {
-                        t.grid.reposRsz();
-                        t.grid.reposDrop();
-                    }
+            
+            // get data columns in the first row of the table
+            var $firstRowCols = $(t).find('tr:first th.draggable');
+            
+            // make sure we have more than one column
+            if ($firstRowCols.length > 1) {
+                var $colVisibTh = $(g.t).find('th:not(.draggable)');
+                PMA_createqTip($colVisibTh);
+                
+                // create column visibility drop-down arrow(s)
+                $colVisibTh.each(function() {
+                        var $th = $(this);
+                        var cd = document.createElement('div'); // column drop-down arrow
+                        var pos = $th.position();
+                        $(cd).addClass('coldrop')
+                            .click(function() {
+                                if (g.cList.style.display == 'none') {
+                                    g.showColList(this);
+                                } else {
+                                    g.hideColList();
+                                }
+                            });
+                        $(g.cDrop).append(cd);
+                    })
+                    .mouseenter(function(e) {
+                        g.showColVisibHint = true;
+                    })
+                    .mouseleave(function(e) {
+                        g.showColVisibHint = false;
+                    });
+                
+                // add column visibility control
+                g.cList.innerHTML = '<div class="lDiv"></div>';
+                var $listDiv = $(g.cList).find('div');
+                for (var i = 0; i < $firstRowCols.length; i++) {
+                    var currHeader = $firstRowCols[i];
+                    var listElmt = document.createElement('div');
+                    $(listElmt).text($(currHeader).text())
+                        .prepend('<input type="checkbox" ' + (g.colVisib[i] ? 'checked="checked" ' : '') + '/>');
+                    $listDiv.append(listElmt);
+                    // add event on click
+                    $(listElmt).click(function() {
+                        if ( g.toggleCol($(this).index()) ) {
+                            g.afterToggleCol();
+                        }
+                    });
+                }
+                // add "show all column" button
+                var showAll = document.createElement('div');
+                $(showAll).addClass('showAllColBtn')
+                    .text(g.showAllColText);
+                $(g.cList).append(showAll);
+                $(showAll).click(function() {
+                    g.showAllColumns();
                 });
-            } else {
-                if (this.grid) {
-                    this.grid.reposRsz();
-                    this.grid.reposDrop();
+                // prepend "show all column" button at top if the list is too long
+                if ($firstRowCols.length > 10) {
+                    var clone = showAll.cloneNode(true);
+                    $(g.cList).prepend(clone);
+                    $(clone).click(function() {
+                        g.showAllColumns();
+                    });
                 }
             }
+            
+            // hide column visibility list if we move outside the list
+            $(t).find('td, th.draggable').mouseenter(function() {
+                g.hideColList();
+            });
+            
+            // attach to global div
+            $(g.gDiv).append(g.cDrop);
+            $(g.gDiv).append(g.cList);
+            
+            // some adjustment
+            g.reposDrop();
+        },
+        
+        /**
+         * Initialize grid editing feature.
+         */
+        initGridEdit: function() {
+            // create cell edit wrapper element
+            g.cEdit = document.createElement('div');
+            
+            // adjust g.cEdit
+            g.cEdit.className = 'cEdit';
+            $(g.cEdit).html('<input type="text" /><div class="edit_area" />');
+            $(g.cEdit).hide();
+            
+            // assign cell editing hint
+            g.cellEditHint = PMA_messages['strCellEditHint'];
+            g.saveCellWarning = PMA_messages['strSaveCellWarning'];
+            g.alertNonUnique = PMA_messages['strAlertNonUnique'];
+            
+            // initialize cell editing configuration
+            g.saveCellsAtOnce = $('#save_cells_at_once').val();
+            
+            // register events
+            $(t).find('td.data')
+                .click(function(e) {
+                    if (g.isCellEditActive) {
+                        g.saveOrPostEditedCell();
+                        e.stopPropagation();
+                    } else {
+                        g.showEditCell(this);
+                        e.stopPropagation();
+                    }
+                    // prevent default action when clicking on "link" in a table
+                    if ($(e.target).is('a')) {
+                        e.preventDefault();
+                    }
+                });
+            $(g.cEdit).find('input[type=text]').focus(function(e) {
+                g.showEditArea();
+            });
+            $(g.cEdit).find('input[type=text], select').live('keydown', function(e) {
+                if (e.which == 13) {
+                    // post on pressing "Enter"
+                    e.preventDefault();
+                    g.saveOrPostEditedCell();
+                }
+            });
+            $(g.cEdit).keydown(function(e) {
+                if (!g.isEditCellTextEditable) {
+                    // prevent text editing
+                    e.preventDefault();
+                }
+            });
+            $('html').click(function(e) {
+                // hide edit cell if the click is not from g.cEdit
+                if ($(e.target).parents().index(g.cEdit) == -1) {
+                    g.hideEditCell();
+                }
+            });
+            $('html').keydown(function(e) {
+                if (e.which == 27 && g.isCellEditActive) {
+
+                    // cancel on pressing "Esc"
+                    g.hideEditCell(true);
+                }
+            });
+            $('.save_edited').click(function() {
+                g.hideEditCell();
+                g.postEditedCell();
+            });
+            $(window).bind('beforeunload', function(e) {
+                if (g.isCellEdited) {
+                    return g.saveCellWarning;
+                }
+            });
+            
+            // attach to global div
+            $(g.gDiv).append(g.cEdit);
+        }
+    }
+    
+    /******************
+     * Initialize grid
+     ******************/
+    
+    // wrap all data cells, except actions cell, with span
+    $(t).find('th, td:not(:has(span))')
+        .wrapInner('<span />');
+    
+    // create grid elements
+    g.gDiv = document.createElement('div');     // create global div
+    
+    // initialize the table variable
+    g.t = t;
+    
+    // get data columns in the first row of the table
+    var $firstRowCols = $(t).find('tr:first th.draggable');
+    
+    // initialize visible headers count
+    g.visibleHeadersCount = $firstRowCols.filter(':visible').length;
+    
+    // assign first column (actions) span
+    if (! $(t).find('tr:first th:first').hasClass('draggable')) {  // action header exist
+        g.actionSpan = $(t).find('tr:first th:first').prop('colspan');
+    } else {
+        g.actionSpan = 0;
+    }
+    
+    // assign table create time
+    // #table_create_time will only available if we are in "Browse" tab
+    g.tableCreateTime = $('#table_create_time').val();
+    
+    // assign the hints
+    g.sortHint = PMA_messages['strSortHint'];
+    g.markHint = PMA_messages['strColMarkHint'];
+    
+    // assign common hidden inputs
+    var $common_hidden_inputs = $('.common_hidden_inputs');
+    g.token = $common_hidden_inputs.find('input[name=token]').val();
+    g.server = $common_hidden_inputs.find('input[name=server]').val();
+    g.db = $common_hidden_inputs.find('input[name=db]').val();
+    g.table = $common_hidden_inputs.find('input[name=table]').val();
+    
+    // add table class
+    $(t).addClass('pma_table');
+    
+    // link the global div
+    $(t).before(g.gDiv);
+    $(g.gDiv).append(t);
+
+    // FEATURES
+    enableResize    = enableResize == undefined ? true : enableResize;
+    enableReorder   = enableReorder == undefined ? true : enableReorder;
+    enableVisib     = enableVisib == undefined ? true : enableVisib;
+    enableGridEdit  = enableGridEdit == undefined ? true : enableGridEdit;
+    if (enableResize) {
+        g.initColResize();
+    }
+    if (enableReorder &&
+        $('.navigation').length > 0)    // disable reordering for result from EXPLAIN or SHOW syntax, which do not have a table navigation panel
+    {
+        g.initColReorder();
+    }
+    if (enableVisib) {
+        g.initColVisib();
+    }
+    if (enableGridEdit &&
+        $(t).is('.ajax'))   // make sure AjaxEnable is enabled in Settings
+    {
+        g.initGridEdit();
+    }
+    
+    // create qtip for each <th> with draggable class
+    PMA_createqTip($(t).find('th.draggable'));
+    
+    // register events for hint tooltip
+    $(t).find('th.draggable a')
+        .attr('title', '')          // hide default tooltip for sorting
+        .mouseenter(function(e) {
+            g.showSortHint = true;
+            g.updateHint(e);
+        })
+        .mouseleave(function(e) {
+            g.showSortHint = false;
+            g.updateHint(e);
+        });
+    $(t).find('th.marker')
+        .mouseenter(function(e) {
+            g.showMarkHint = true;
+        })
+        .mouseleave(function(e) {
+            g.showMarkHint = false;
+        });
+        });  .showMarkHint = false;  / hide default tooltip for sorting  s  m EXPLAIN or SHOW syntax, which do not have a table navigation panel   in cPointer class  browser  er                     ’ÐÅL+  €{Õÿ  € òÅL+          €u{Õÿ         Pt{Õÿ  k³ÄL+  6                    è     è!     è!      €{Õÿ  €{Õÿ  xÄÏÅL+          €v{Õÿ          ’ò²ÄL+          †³ÄL+   €{Õÿ  `ò²ÄL+  _€{Õÿ         À¿3ÆL+  uú²ÄL+   °3ÆL+  Ø´3ÆL+  ¨¹3ÆL+  €´ÄL+          xÄÏÅL+          €v{Õÿ          Pu{Õÿ  k³ÄL+  à!     à!      €{Õÿ  €{Õÿ  HŽ¯ÅL+          `w{Õÿ         ’ò²ÄL+          †³ÄL+   €{Õÿ  `ò²ÄL+  _€{Õÿ  P€{Õÿ  H€{Õÿ  8ÔÄL+  Œp˜        €{Õÿ  uú²ÄL+  €´ÄL+                  `w{Õÿ         0v{Õÿ  k³ÄL+          G       H   I       J   K           M   N   O       P    €{Õÿ  €{Õÿ  HE†ÅL+           y{Õÿ         ’ò²ÄL+          †³ÄL+   €{Õÿ         0¿3ÆL+  uú²ÄL+   ÐÏÅL+   ycÅL+   °3ÆL+  Ø´3ÆL+  ¨¹3ÆL+  €´ÄL+          HE†ÅL+           y{Õÿ         @w{Õÿ  k³ÄL+  8E†ÅL+           y{Õÿ         pw{Õÿ  k³ÄL+  (E†ÅL+           y{Õÿ          w{Õÿ  k³ÄL+  E†ÅL+           y{Õÿ         Ðw{Õÿ  k³ÄL+  	       ¨¾3ÆL+  uú²ÄL+         ¨¹3ÆL+  Pr{Õÿ          ŠÈcÅL+  eù²ÄL+         Ø´3ÆL+  px{Õÿ          yÈcÅL+  eù²ÄL+          °3ÆL+   x{Õÿ          ZÈcÅL+  eù²ÄL+  ØtcÅL+   ÐÏÅL+   ycÅL+   °3ÆL+  ØÔÏÅL+  Ø´3ÆL+  ¨¹3ÆL+  €´ÄL+          8ÔÄL+                        =/ÅL+  €{Õÿ  pcÅL+          Pz{Õÿ           y{Õÿ  k³ÄL+      5   6   7   8   :   <   =   >       ?       @   B   D        €{Õÿ  €{Õÿ  à=,ÅL+                 €{Õÿ  uú²ÄL+         ÔÄL+  Ðx{Õÿ          =/ÅL+  eù²ÄL+  ÔÄL+          Õ_ at fÆS         Qöl±íÁSûÄL+  €{Õÿ  à=,ÅL+          `{{Õÿ         0z{Õÿ  k³ÄL+  u]ÞÓ	4Q ÷U^Qʉ§ ¶uª¹ñìò*Ä“v €{Õÿ  €{Õÿ  HŽøÄL+          Ô4ÅL+          Ё{Õÿ  ¦à–ÆL+  ¦à–ÆL+         &2ÅL+  _€{Õÿ  {Õÿ  H€{Õÿ  °|{Õÿ  ßÏÅL+  uú²ÄL+  ØÔÏÅL+  ƒ{Õÿ  ¢à–ÆL+  à{Õÿ          p}{Õÿ                 ÿÿÿÿÿÿÿÿ¢à–ÆL+          ˜€{Õÿ                   // register events for dragging-related feature
+    if (enableResize || enableReorder) {
+        $(document).mousemove(function(e) {
+            g.dragMove(e);
+        });
+        $(document).mouseup(function(e) {
+            g.dragEnd(e);
         });
     }
-
-})(jQuery);
+    
+    // bind event to update currently hovered qtip API
+    $(t).find('th')
+        .mouseenter(function(e) {
+            g.qtip = $(this).qtip('api');
+            g.updateHint(e);
+        })
+        .mouseleave(function(e) {
+            g.updateHint(e);
+        });
+    
+    // some adjustment
+    $(t).removeClass('data');
+    $(g.gDiv).addClass('data');
+};
 
diff --git a/js/sql.js b/js/sql.js
index 4386dce..f4a7475 100644
--- a/js/sql.js
+++ b/js/sql.js
@@ -94,16 +94,7 @@ $(document).ready(function() {
      * @memberOf    jQuery
      */
     $("#sqlqueryresults").live('makegrid', function() {
-        $('#table_results').makegrid();
-    })
-
-    /**
-     * Attach the {@link refreshgrid} function to a custom event, which will be
-     * triggered manually everytime the table of results is manipulated
-     * @memberOf    jQuery
-     */
-    $("#sqlqueryresults").live('refreshgrid', function() {
-        $('#table_results').refreshgrid();
+        PMA_makegrid($('#table_results')[0]);
     })
 
     /**
diff --git a/sql.php b/sql.php
index e41b602..fef6eb1 100644
--- a/sql.php
+++ b/sql.php
@@ -169,14 +169,19 @@ if (isset($_REQUEST['get_set_values']) && $_REQUEST['get_set_values'] == true) {
  */
 if (isset($_REQUEST['set_col_prefs']) && $_REQUEST['set_col_prefs'] == true) {
     $pmatable = new PMA_Table($table, $db);
+    $retval = false;
 
     // set column order
-    $col_order = explode(',', $_REQUEST['col_order']);
-    $retval = $pmatable->setUiProp(PMA_Table::PROP_COLUMN_ORDER, $col_order, $_REQUEST['table_create_time']);
+    if (isset($_REQUEST['col_order'])) {
+        $col_order = explode(',', $_REQUEST['col_order']);
+        $retval = $pmatable->setUiProp(PMA_Table::PROP_COLUMN_ORDER, $col_order, $_REQUEST['table_create_time']);
+    }
 
     // set column visibility
-    $col_visib = explode(',', $_REQUEST['col_visib']);
-    $retval &= $pmatable->setUiProp(PMA_Table::PROP_COLUMN_VISIB, $col_visib, $_REQUEST['table_create_time']);
+    if (isset($_REQUEST['col_visib'])) {
+        $col_visib = explode(',', $_REQUEST['col_visib']);
+        $retval &= $pmatable->setUiProp(PMA_Table::PROP_COLUMN_VISIB, $col_visib, $_REQUEST['table_create_time']);
+    }
 
     PMA_ajaxResponse(NULL, ($retval == true));
 }
@@ -697,119 +702,6 @@ if (0 == $num_rows || $is_affected) {
     }
 
     if ($GLOBALS['is_ajax_request'] == true) {
-
-        /**
-         * If we are in grid editing, we need to process the relational and
-         * transformed fields, if they were edited. After that, output the correct
-         * link/transformed value and exit
-         *
-         * Logic taken from libraries/display_tbl.lib.php
-         */
-
-        if (isset($_REQUEST['rel_fields_list']) && $_REQUEST['rel_fields_list'] != '') {
-            //handle relations work here for updated row.
-            require_once './libraries/relation.lib.php';
-
-            $map = PMA_getForeigners($db, $table, '', 'both');
-
-            $rel_fields = array();
-            parse_str($_REQUEST['rel_fields_list'], $rel_fields);
-
-            foreach ( $rel_fields as $rel_field => $rel_field_value) {
-
-                $where_comparison = "='" . $rel_field_value . "'";
-                $display_field = PMA_getDisplayField($map[$rel_field]['foreign_db'], $map[$rel_field]['foreign_table']);
-
-                // Field to display from the foreign table?
-                if (isset($display_field) && strlen($display_field)) {
-                    $dispsql     = 'SELECT ' . PMA_backquote($display_field)
-                        . ' FROM ' . PMA_backquote($map[$rel_field]['foreign_db'])
-                        . '.' . PMA_backquote($map[$rel_field]['foreign_table'])
-                        . ' WHERE ' . PMA_backquote($map[$rel_field]['foreign_field'])
-                        . $where_comparison;
-                    $dispresult  = PMA_DBI_try_query($dispsql, null, PMA_DBI_QUERY_STORE);
-                    if ($dispresult && PMA_DBI_num_rows($dispresult) > 0) {
-                        list($dispval) = PMA_DBI_fetch_row($dispresult, 0);
-                    } else {
-                        //$dispval = __('Link not found');
-                    }
-                    @PMA_DBI_free_result($dispresult);
-                } else {
-                    $dispval     = '';
-                } // end if... else...
-
-                if ('K' == $_SESSION['tmp_user_values']['relational_display']) {
-                    // user chose "relational key" in the display options, so
-                    // the title contains the display field
-                    $title = (! empty($dispval))? ' title="' . htmlspecialchars($dispval) . '"' : '';
-                } else {
-                    $title = ' title="' . htmlspecialchars($rel_field_value) . '"';
-                }
-
-                $_url_params = array(
-                    'db'    => $map[$rel_field]['foreign_db'],
-                    'table' => $map[$rel_field]['foreign_table'],
-                    'pos'   => '0',
-                    'sql_query' => 'SELECT * FROM '
-                                        . PMA_backquote($map[$rel_field]['foreign_db']) . '.' . PMA_backquote($map[$rel_field]['foreign_table'])
-                                        . ' WHERE ' . PMA_backquote($map[$rel_field]['foreign_field'])
-                                        . $where_comparison
-                );
-                $output = '<a href="sql.php' . PMA_generate_common_url($_url_params) . '"' . $title . '>';
-
-                if ('D' == $_SESSION['tmp_user_values']['relational_display']) {
-                    // user chose "relational display field" in the
-                    // display options, so show display field in the cell
-                    $output .= (!empty($dispval)) ? htmlspecialchars($dispval) : '';
-                } else {
-                    // otherwise display data in the cell
-                    $output .= htmlspecialchars($rel_field_value);
-                }
-                $output .= '</a>';
-                $extra_data['relations'][$rel_field] = $output;
-            }
-        }
-
-        if (isset($_REQUEST['do_transformations']) && $_REQUEST['do_transformations'] == true ) {
-            require_once './libraries/transformations.lib.php';
-            //if some posted fields need to be transformed, generate them here.
-            $mime_map = PMA_getMIME($db, $table);
-
-            if ($mime_map === false) {
-                $mime_map = array();
-            }
-
-            $edited_values = array();
-            parse_str($_REQUEST['transform_fields_list'], $edited_values);
-
-            foreach($mime_map as $transformation) {
-                $include_file = PMA_securePath($transformation['transformation']);
-                $column_name = $transformation['column_name'];
-                $column_data = $edited_values[$column_name];
-
-                $_url_params = array(
-                    'db'            => $db,
-                    'table'         => $table,
-                    'where_clause'  => $_REQUEST['where_clause'],
-                    'transform_key' => $column_name,
-                );
-
-                if (file_exists('./libraries/transformations/' . $include_file)) {
-                    $transformfunction_name = str_replace('.inc.php', '', $transformation['transformation']);
-
-                    require_once './libraries/transformations/' . $include_file;
-
-                    if (function_exists('PMA_transformation_' . $transformfunction_name)) {
-                        $transform_function = 'PMA_transformation_' . $transformfunction_name;
-                        $transform_options  = PMA_transformation_getOptions((isset($transformation['transformation_options']) ? $transformation['transformation_options'] : ''));
-                        $transform_options['wrapper_link'] = PMA_generate_common_url($_url_params);
-                    }
-                }
-
-                $extra_data['transformations'][$column_name] = $transform_function($column_data, $transform_options);
-            }
-        }
-
         if ($cfg['ShowSQL']) {
             $extra_data['sql_query'] = PMA_showMessage($message, $GLOBALS['sql_query'], 'success');
         }


hooks/post-receive
-- 
phpMyAdmin




More information about the Git mailing list