2016-10-22 14:57:10 +00:00
/* vim: set expandtab sw=4 ts=4 sts=4: */
/ * *
* 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
* /
2018-04-14 09:18:00 +00:00
function PMA _makegrid ( t , enableResize , enableReorder , enableVisib , enableGridEdit ) {
2016-10-22 14:57:10 +00:00
var g = {
2018-04-14 09:18:00 +00:00
/ * * * * * * * * * * *
2016-10-22 14:57:10 +00:00
* Constant
* * * * * * * * * * * /
minColWidth : 15 ,
2018-04-14 09:18:00 +00:00
/ * * * * * * * * * * *
2016-10-22 14:57:10 +00:00
* 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 : [ ] , // array of column order
// Column visibility variables
colVisib : [ ] , // 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
reorderHint : '' , // string, hint for column reordering
sortHint : '' , // string, hint for column sorting
markHint : '' , // string, hint for column marking
copyHint : '' , // string, hint for copy column name
showReorderHint : false ,
showSortHint : false ,
showMarkHint : 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 ,
2018-04-14 09:18:00 +00:00
/ * * * * * * * * * * * *
2016-10-22 14:57:10 +00:00
* 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)
$ ( obj ) . addClass ( 'colborder_active' ) ;
g . colRsz = {
x0 : e . pageX ,
n : n ,
obj : obj ,
objLeft : $ ( obj ) . position ( ) . left ,
objWidth : $ ( g . t ) . find ( 'th.draggable:visible:eq(' + n + ') span' ) . outerWidth ( )
} ;
$ ( document . body ) . css ( 'cursor' , 'col-resize' ) . 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
} ) ;
// get the column index, zero-based
var n = g . getHeaderIdx ( obj ) ;
g . colReorder = {
x0 : e . pageX ,
y0 : e . pageY ,
n : n ,
newn : n ,
obj : obj ,
objTop : objPos . top ,
objLeft : objPos . left
} ;
$ ( document . body ) . css ( 'cursor' , 'move' ) . 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' ) ;
}
} 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 ;
2018-04-14 09:18:00 +00:00
if ( newn !== g . colReorder . n ) {
2016-10-22 14:57:10 +00:00
// show the column pointer in the right place
var colPos = $ ( hoveredCol ) . position ( ) ;
var newleft = newn < g . colReorder . n ?
2018-04-14 09:18:00 +00:00
colPos . left :
colPos . left + $ ( hoveredCol ) . outerWidth ( ) ;
2016-10-22 14:57:10 +00:00
$ ( g . cPointer )
. css ( {
left : newleft ,
visibility : 'visible'
} ) ;
} else {
// no movement to other column, hide the column pointer
$ ( g . cPointer ) . css ( 'visibility' , 'hidden' ) ;
}
}
}
} ,
/ * *
* 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 ;
}
var n = g . colRsz . n ;
// do the resizing
g . resize ( n , nw ) ;
g . reposRsz ( ) ;
g . reposDrop ( ) ;
g . colRsz = false ;
$ ( g . cRsz ) . find ( 'div' ) . removeClass ( 'colborder_active' ) ;
rearrangeStickyColumns ( $ ( t ) . prev ( '.sticky_columns' ) , $ ( t ) ) ;
} else if ( g . colReorder ) {
// shift columns
2018-04-14 09:18:00 +00:00
if ( g . colReorder . newn !== g . colReorder . n ) {
2016-10-22 14:57:10 +00:00
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 ( ) ;
}
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 ;
rearrangeStickyColumns ( $ ( t ) . prev ( '.sticky_columns' ) , $ ( t ) ) ;
}
$ ( document . body ) . css ( 'cursor' , 'inherit' ) . 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' )
2018-04-14 09:18:00 +00:00
. css ( 'width' , nw ) ;
2016-10-22 14:57:10 +00:00
} ) ;
} ,
/ * *
* Reposition column resize bars .
* /
reposRsz : function ( ) {
$ ( g . cRsz ) . find ( 'div' ) . hide ( ) ;
var $firstRowCols = $ ( g . t ) . find ( 'tr:first th.draggable:visible' ) ;
var $resizeHandles = $ ( g . cRsz ) . find ( 'div' ) . removeClass ( 'condition' ) ;
$ ( g . t ) . find ( 'table.pma_table' ) . find ( 'thead th:first' ) . removeClass ( 'before-condition' ) ;
for ( var n = 0 , l = $firstRowCols . length ; n < l ; n ++ ) {
var $col = $ ( $firstRowCols [ n ] ) ;
var colWidth ;
2018-04-14 09:18:00 +00:00
if ( navigator . userAgent . toLowerCase ( ) . indexOf ( 'safari' ) !== - 1 ) {
2016-10-22 14:57:10 +00:00
colWidth = $col . outerWidth ( ) ;
} else {
colWidth = $col . outerWidth ( true ) ;
}
$ ( $resizeHandles [ n ] ) . css ( 'left' , $col . position ( ) . left + colWidth )
2018-04-14 09:18:00 +00:00
. show ( ) ;
2016-10-22 14:57:10 +00:00
if ( $col . hasClass ( 'condition' ) ) {
$ ( $resizeHandles [ n ] ) . addClass ( 'condition' ) ;
if ( n > 0 ) {
$ ( $resizeHandles [ n - 1 ] ) . addClass ( 'condition' ) ;
}
}
}
if ( $ ( $resizeHandles [ 0 ] ) . hasClass ( 'condition' ) ) {
$ ( g . t ) . find ( 'thead th:first' ) . addClass ( 'before-condition' ) ;
}
$ ( 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 ) {
$ ( this ) . find ( 'th.draggable:eq(' + newn + '),' +
'td:eq(' + ( g . actionSpan + newn ) + ')' )
2018-04-14 09:18:00 +00:00
. before ( $ ( this ) . find ( 'th.draggable:eq(' + oldn + '),' +
2016-10-22 14:57:10 +00:00
'td:eq(' + ( g . actionSpan + oldn ) + ')' ) ) ;
} else {
$ ( this ) . find ( 'th.draggable:eq(' + newn + '),' +
'td:eq(' + ( g . actionSpan + newn ) + ')' )
2018-04-14 09:18:00 +00:00
. after ( $ ( this ) . find ( 'th.draggable:eq(' + oldn + '),' +
2016-10-22 14:57:10 +00:00
'td:eq(' + ( g . actionSpan + oldn ) + ')' ) ) ;
}
} ) ;
// reposition the column resize bars
g . reposRsz ( ) ;
// adjust the column visibility list
if ( newn < oldn ) {
$ ( g . cList ) . find ( '.lDiv div:eq(' + newn + ')' )
2018-04-14 09:18:00 +00:00
. before ( $ ( g . cList ) . find ( '.lDiv div:eq(' + oldn + ')' ) ) ;
2016-10-22 14:57:10 +00:00
} else {
$ ( g . cList ) . find ( '.lDiv div:eq(' + newn + ')' )
2018-04-14 09:18:00 +00:00
. after ( $ ( g . cList ) . find ( '.lDiv div:eq(' + oldn + ')' ) ) ;
2016-10-22 14:57:10 +00:00
}
// 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 ) {
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 ;
}
} ) ;
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 -- ;
}
2018-04-14 09:18:00 +00:00
if ( j !== i - 1 ) {
2016-10-22 14:57:10 +00:00
g . shiftCol ( i , j + 1 ) ;
}
}
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 ( ) {
if ( $ ( g . t ) . is ( '.ajax' ) ) { // only send preferences if ajax class
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 ) {
2018-04-14 09:18:00 +00:00
$ . extend ( post _params , { col _order : g . colOrder . toString ( ) } ) ;
2016-10-22 14:57:10 +00:00
}
if ( g . colVisib . length > 0 ) {
2018-04-14 09:18:00 +00:00
$ . extend ( post _params , { col _visib : g . colVisib . toString ( ) } ) ;
2016-10-22 14:57:10 +00:00
}
$ . post ( 'sql.php' , post _params , function ( data ) {
if ( data . success !== true ) {
var $temp _div = $ ( document . createElement ( 'div' ) ) ;
$temp _div . html ( data . error ) ;
2018-04-14 09:18:00 +00:00
$temp _div . addClass ( 'error' ) ;
2016-10-22 14:57:10 +00:00
PMA _ajaxShowMessage ( $temp _div , false ) ;
}
} ) ;
}
} ,
/ * *
* 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 ++ ) {
2018-04-14 09:18:00 +00:00
if ( g . colOrder [ i ] !== i ) {
2016-10-22 14:57:10 +00:00
isInitial = false ;
break ;
}
}
// check if only one visible column left
2018-04-14 09:18:00 +00:00
var isOneColumn = g . visibleHeadersCount === 1 ;
2016-10-22 14:57:10 +00:00
// enable or disable restore button
if ( isInitial || isOneColumn ) {
$ ( g . o ) . find ( 'div.restore_column' ) . hide ( ) ;
} else {
$ ( g . o ) . find ( 'div.restore_column' ) . show ( ) ;
}
} ,
/ * *
* Update current hint using the boolean values ( showReorderHint , showSortHint , etc . ) .
*
* /
updateHint : function ( ) {
var text = '' ;
if ( ! g . colRsz && ! g . colReorder ) { // if not resizing or dragging
if ( g . visibleHeadersCount > 1 ) {
g . showReorderHint = true ;
}
if ( $ ( t ) . find ( 'th.marker' ) . length > 0 ) {
g . showMarkHint = true ;
}
if ( g . showSortHint && g . sortHint ) {
text += text . length > 0 ? '<br />' : '' ;
text += '- ' + g . sortHint ;
}
if ( g . showMultiSortHint && g . strMultiSortHint ) {
text += text . length > 0 ? '<br />' : '' ;
text += '- ' + g . strMultiSortHint ;
}
if ( g . showMarkHint &&
g . markHint &&
! g . showSortHint && // we do not show mark hint, when sort hint is shown
g . showReorderHint &&
g . reorderHint
) {
text += text . length > 0 ? '<br />' : '' ;
text += '- ' + g . reorderHint ;
text += text . length > 0 ? '<br />' : '' ;
text += '- ' + g . markHint ;
text += text . length > 0 ? '<br />' : '' ;
text += '- ' + g . copyHint ;
}
}
return text ;
} ,
/ * *
* 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 ) + ')' )
2018-04-14 09:18:00 +00:00
. hide ( ) ;
2016-10-22 14:57:10 +00:00
} ) ;
g . colVisib [ n ] = 0 ;
$ ( g . cList ) . find ( '.lDiv div:eq(' + n + ') input' ) . prop ( 'checked' , false ) ;
} else {
// cannot hide, force the checkbox to stay checked
$ ( g . cList ) . find ( '.lDiv div:eq(' + n + ') input' ) . prop ( 'checked' , true ) ;
return false ;
}
} else { // column n is not visible
$ ( g . t ) . find ( 'tr' ) . each ( function ( ) {
$ ( this ) . find ( 'th.draggable:eq(' + n + '),' +
'td:eq(' + ( g . actionSpan + n ) + ')' )
2018-04-14 09:18:00 +00:00
. show ( ) ;
2016-10-22 14:57:10 +00:00
} ) ;
g . colVisib [ n ] = 1 ;
$ ( g . cList ) . find ( '.lDiv div:eq(' + n + ') input' ) . prop ( 'checked' , true ) ;
}
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 ) ;
}
$ ( g . cList ) . css ( {
2018-04-14 09:18:00 +00:00
left : pos . left ,
top : pos . top + $ ( obj ) . outerHeight ( true )
} )
2016-10-22 14:57:10 +00:00
. 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 ( ) {
var $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 ( {
2018-04-14 09:18:00 +00:00
left : pos . left + $ ( $th [ i ] ) . width ( ) - $cd . width ( ) ,
top : pos . top
} ) ;
2016-10-22 14:57:10 +00:00
}
} ,
/ * *
* Show all hidden columns .
* /
showAllColumns : function ( ) {
for ( var i = 0 ; i < g . colVisib . length ; i ++ ) {
if ( ! g . colVisib [ i ] ) {
g . 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' ) &&
2018-04-14 09:18:00 +00:00
! g . colRsz && ! g . colReorder ) {
2016-10-22 14:57:10 +00:00
if ( ! g . isCellEditActive ) {
var $cell = $ ( cell ) ;
if ( 'string' === $cell . attr ( 'data-type' ) ||
2018-04-14 09:18:00 +00:00
'blob' === $cell . attr ( 'data-type' ) ||
'json' === $cell . attr ( 'data-type' )
2016-10-22 14:57:10 +00:00
) {
g . cEdit = g . cEditTextarea ;
} else {
g . cEdit = g . cEditStd ;
}
// remove all edit area and hide it
$ ( g . cEdit ) . find ( '.edit_area' ) . empty ( ) . hide ( ) ;
// reposition the cEdit element
$ ( g . cEdit ) . css ( {
2018-04-14 09:18:00 +00:00
top : $cell . position ( ) . top ,
left : $cell . position ( ) . left
} )
2016-10-22 14:57:10 +00:00
. show ( )
. find ( '.edit_box' )
. css ( {
width : $cell . outerWidth ( ) ,
height : $cell . outerHeight ( )
} ) ;
// fill the cell edit with text from <td>
var value = PMA _getCellValue ( cell ) ;
2018-04-14 09:18:00 +00:00
if ( $cell . attr ( 'data-type' ) === 'json' ) {
value = JSON . stringify ( JSON . parse ( value ) , null , 4 ) ;
}
2016-10-22 14:57:10 +00:00
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( value ) ;
g . currentEditCell = cell ;
$ ( g . cEdit ) . find ( '.edit_box' ) . focus ( ) ;
moveCursorToEnd ( $ ( g . cEdit ) . find ( '.edit_box' ) ) ;
$ ( g . cEdit ) . find ( '*' ) . prop ( 'disabled' , false ) ;
}
}
2018-04-14 09:18:00 +00:00
function moveCursorToEnd ( input ) {
2016-10-22 14:57:10 +00:00
var originalValue = input . val ( ) ;
var originallength = originalValue . length ;
input . val ( '' ) ;
input . blur ( ) . focus ( ) . val ( originalValue ) ;
input [ 0 ] . setSelectionRange ( originallength , originallength ) ;
}
} ,
/ * *
* 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 .
* @ param field Optional , this object contains a boolean named move ( true , if called from move * functions )
* and a < td > to which the grid _edit should move
* /
hideEditCell : function ( force , data , field , options ) {
if ( g . isCellEditActive && ! force ) {
// cell is being edited, save or post the edited data
if ( options !== undefined ) {
g . saveOrPostEditedCell ( options ) ;
} else {
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 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' ) ;
var value = data . isNeedToRecheck
? data . truncatableFieldValue
: $this _field . data ( 'value' ) ;
// Truncates the text.
$this _field . removeClass ( 'truncated' ) ;
if ( PMA _commonParams . get ( 'pftext' ) === 'P' && value . length > g . maxTruncatedLen ) {
$this _field . addClass ( 'truncated' ) ;
value = value . substring ( 0 , g . maxTruncatedLen ) + '...' ;
}
2018-04-14 09:18:00 +00:00
// Add <br> before carriage return.
2016-10-22 14:57:10 +00:00
new _html = escapeHtml ( value ) ;
new _html = new _html . replace ( /\n/g , '<br>\n' ) ;
2018-04-14 09:18:00 +00:00
// remove decimal places if column type not supported
if ( ( $this _field . attr ( 'data-decimals' ) === 0 ) && ( $this _field . attr ( 'data-type' ) . indexOf ( 'time' ) !== - 1 ) ) {
2016-10-22 14:57:10 +00:00
new _html = new _html . substring ( 0 , new _html . indexOf ( '.' ) ) ;
}
2018-04-14 09:18:00 +00:00
// remove addtional decimal places
if ( ( $this _field . attr ( 'data-decimals' ) > 0 ) && ( $this _field . attr ( 'data-type' ) . indexOf ( 'time' ) !== - 1 ) ) {
2016-10-22 14:57:10 +00:00
new _html = new _html . substring ( 0 , new _html . length - ( 6 - $this _field . attr ( 'data-decimals' ) ) ) ;
}
var selector = 'span' ;
if ( $this _field . hasClass ( 'hex' ) && $this _field . find ( 'a' ) . length ) {
selector = 'a' ;
}
// Updates the code keeping highlighting (if any).
var $target = $this _field . find ( selector ) ;
if ( ! PMA _updateCode ( $target , new _html , value ) ) {
$target . html ( new _html ) ;
}
}
if ( $this _field . is ( '.bit' ) ) {
$this _field . find ( 'span' ) . text ( $this _field . data ( 'value' ) ) ;
}
}
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
g . reposRsz ( ) ;
g . reposDrop ( ) ;
}
// hide the cell editing area
$ ( g . cEdit ) . hide ( ) ;
$ ( g . cEdit ) . find ( '.edit_box' ) . blur ( ) ;
g . isCellEditActive = false ;
g . currentEditCell = null ;
// destroy datepicker in edit area, if exist
var $dp = $ ( g . cEdit ) . find ( '.hasDatepicker' ) ;
if ( $dp . length > 0 ) {
$ ( document ) . bind ( 'mousedown' , $ . datepicker . _checkExternalClick ) ;
$dp . datepicker ( 'destroy' ) ;
// change the cursor in edit box back to normal
// (the cursor become a hand pointer when we add datepicker)
$ ( g . cEdit ) . find ( '.edit_box' ) . css ( 'cursor' , 'inherit' ) ;
}
} ,
/ * *
* 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 current edited cell
* /
var $td = $ ( g . currentEditCell ) ;
/ * *
* @ var $editArea the editing area
* /
var $editArea = $ ( g . cEdit ) . find ( '.edit_area' ) ;
/ * *
* @ var where _clause WHERE clause for the edited cell
* /
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 ( $ ( t ) , $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 ( ) ;
// remember this instead of testing more than once
var is _null = $td . is ( '.null' ) ;
// 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
2018-04-14 09:18:00 +00:00
$editArea . append ( '<div class="null_div"><label>Null:<input type="checkbox"></label></div>' ) ;
2016-10-22 14:57:10 +00:00
var $checkbox = $editArea . find ( '.null_div input' ) ;
// check if current <td> is NULL
if ( is _null ) {
$checkbox . prop ( '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 . on ( 'change' , 'select' , function ( ) {
$checkbox . prop ( 'checked' , false ) ;
} ) ;
} else if ( $td . is ( '.relation' ) ) {
$editArea . on ( 'change' , 'select' , function ( ) {
$checkbox . prop ( 'checked' , false ) ;
} ) ;
$editArea . on ( 'click' , '.browse_foreign' , function ( ) {
$checkbox . prop ( 'checked' , false ) ;
} ) ;
} else {
$ ( g . cEdit ) . on ( 'keypress change paste' , '.edit_box' , function ( ) {
$checkbox . prop ( 'checked' , false ) ;
} ) ;
// Capture ctrl+v (on IE and Chrome)
$ ( g . cEdit ) . on ( 'keydown' , '.edit_box' , function ( e ) {
2018-04-14 09:18:00 +00:00
if ( e . ctrlKey && e . which === 86 ) {
2016-10-22 14:57:10 +00:00
$checkbox . prop ( 'checked' , false ) ;
}
} ) ;
$editArea . on ( 'keydown' , 'textarea' , function ( ) {
$checkbox . prop ( 'checked' , false ) ;
} ) ;
}
// if null checkbox is clicked empty the corresponding select/editor.
$checkbox . click ( function ( ) {
if ( $td . is ( '.enum' ) ) {
$editArea . find ( 'select' ) . val ( '' ) ;
} else if ( $td . is ( '.set' ) ) {
$editArea . find ( 'select' ) . find ( 'option' ) . each ( function ( ) {
var $option = $ ( this ) ;
$option . prop ( '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' ) . val ( '' ) ;
}
} else {
$editArea . find ( 'textarea' ) . val ( '' ) ;
}
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( '' ) ;
} ) ;
}
2018-04-14 09:18:00 +00:00
// reset the position of the edit_area div after closing datetime picker
$ ( g . cEdit ) . find ( '.edit_area' ) . css ( { 'top' : '0' , 'position' : '' } ) ;
2016-10-22 14:57:10 +00:00
if ( $td . is ( '.relation' ) ) {
2018-04-14 09:18:00 +00:00
// handle relations
2016-10-22 14:57:10 +00:00
$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 ,
'curr_value' : relation _curr _value ,
'relation_key_or_display_column' : relation _key _or _display _column
} ;
g . lastXHR = $ . post ( 'sql.php' , post _params , function ( data ) {
g . lastXHR = null ;
$editArea . removeClass ( 'edit_area_loading' ) ;
if ( $ ( data . dropdown ) . is ( 'select' ) ) {
// 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 ( '.edit_box' ) . val ( value ) ;
}
$editArea . append ( data . dropdown ) ;
$editArea . append ( '<div class="cell_edit_hint">' + g . cellEditHint + '</div>' ) ;
// for 'Browse foreign values' options,
// hide the value next to 'Browse foreign values' link
$editArea . find ( 'span.curr_value' ) . hide ( ) ;
// handle update for new values selected from new window
$editArea . find ( 'span.curr_value' ) . change ( function ( ) {
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( $ ( this ) . text ( ) ) ;
} ) ;
} ) ; // end $.post()
$editArea . show ( ) ;
$editArea . on ( 'change' , 'select' , function ( ) {
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
g . isEditCellTextEditable = true ;
2018-04-14 09:18:00 +00:00
} else if ( $td . is ( '.enum' ) ) {
// handle enum fields
2016-10-22 14:57:10 +00:00
$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 ,
'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()
$editArea . show ( ) ;
$editArea . on ( 'change' , 'select' , function ( ) {
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
2018-04-14 09:18:00 +00:00
} else if ( $td . is ( '.set' ) ) {
// handle set fields
2016-10-22 14:57:10 +00:00
$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 ,
'curr_value' : curr _value
} ;
// if the data is truncated, get the full data
if ( $td . is ( '.truncated' ) ) {
post _params . get _full _values = true ;
2017-04-20 10:55:30 +00:00
post _params . where _clause = where _clause ;
2016-10-22 14:57:10 +00:00
}
g . lastXHR = $ . post ( 'sql.php' , post _params , function ( data ) {
g . lastXHR = null ;
$editArea . removeClass ( 'edit_area_loading' ) ;
$editArea . append ( data . select ) ;
$td . data ( 'original_data' , $ ( data . select ) . val ( ) . join ( ) ) ;
$editArea . append ( '<div class="cell_edit_hint">' + g . cellEditHint + '</div>' ) ;
} ) ; // end $.post()
$editArea . show ( ) ;
$editArea . on ( 'change' , 'select' , function ( ) {
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
2018-04-14 09:18:00 +00:00
} else if ( $td . is ( '.truncated, .transformed' ) ) {
2016-10-22 14:57:10 +00:00
if ( $td . is ( '.to_be_saved' ) ) { // cell has been edited
var value = $td . data ( 'value' ) ;
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( value ) ;
$editArea . append ( '<textarea></textarea>' ) ;
$editArea . find ( 'textarea' ) . val ( value ) ;
$editArea
. on ( 'keyup' , 'textarea' , function ( ) {
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
$ ( g . cEdit ) . on ( 'keyup' , '.edit_box' , function ( ) {
$editArea . find ( 'textarea' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
$editArea . append ( '<div class="cell_edit_hint">' + g . cellEditHint + '</div>' ) ;
} else {
2018-04-14 09:18:00 +00:00
// handle truncated/transformed values values
2016-10-22 14:57:10 +00:00
$editArea . addClass ( 'edit_area_loading' ) ;
// initialize the original data
$td . data ( 'original_data' , null ) ;
/ * *
* @ var sql _query String containing the SQL query used to retrieve value of truncated / transformed data
* /
2017-04-20 10:55:30 +00:00
var sql _query = 'SELECT `' + field _name + '` FROM `' + g . table + '` WHERE ' + where _clause ;
2016-10-22 14:57:10 +00:00
// Make the Ajax call and get the data, wrap it and insert it
g . lastXHR = $ . post ( 'sql.php' , {
'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 ( typeof data !== 'undefined' && data . success === true ) {
$td . data ( 'original_data' , data . value ) ;
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( data . value ) ;
2018-04-14 09:18:00 +00:00
$editArea . append ( '<textarea rows="15"></textarea>' ) ;
$editArea . find ( 'textarea' ) . val ( data . value ) ;
$editArea . on ( 'keyup' , 'textarea' , function ( ) {
$ ( g . cEdit ) . find ( '.edit_box' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
$ ( g . cEdit ) . on ( 'keyup' , '.edit_box' , function ( ) {
$editArea . find ( 'textarea' ) . val ( $ ( this ) . val ( ) ) ;
} ) ;
$editArea . append ( '<div class="cell_edit_hint">' + g . cellEditHint + '</div>' ) ;
$editArea . show ( ) ;
2016-10-22 14:57:10 +00:00
} else {
PMA _ajaxShowMessage ( data . error , false ) ;
}
} ) ; // end $.post()
}
g . isEditCellTextEditable = true ;
} else if ( $td . is ( '.timefield, .datefield, .datetimefield, .timestampfield' ) ) {
var $input _field = $ ( g . cEdit ) . find ( '.edit_box' ) ;
// remember current datetime value in $input_field, if it is not null
var datetime _value = ! is _null ? $input _field . val ( ) : '' ;
var showMillisec = false ;
var showMicrosec = false ;
var timeFormat = 'HH:mm:ss' ;
// check for decimal places of seconds
2018-04-14 09:18:00 +00:00
if ( ( $td . attr ( 'data-decimals' ) > 0 ) && ( $td . attr ( 'data-type' ) . indexOf ( 'time' ) !== - 1 ) ) {
2016-10-22 14:57:10 +00:00
if ( datetime _value && datetime _value . indexOf ( '.' ) === false ) {
datetime _value += '.' ;
}
if ( $td . attr ( 'data-decimals' ) > 3 ) {
showMillisec = true ;
showMicrosec = true ;
timeFormat = 'HH:mm:ss.lc' ;
if ( datetime _value ) {
datetime _value += '000000' ;
var datetime _value = datetime _value . substring ( 0 , datetime _value . indexOf ( '.' ) + 7 ) ;
$input _field . val ( datetime _value ) ;
}
} else {
showMillisec = true ;
timeFormat = 'HH:mm:ss.l' ;
if ( datetime _value ) {
datetime _value += '000' ;
var datetime _value = datetime _value . substring ( 0 , datetime _value . indexOf ( '.' ) + 4 ) ;
$input _field . val ( datetime _value ) ;
}
}
}
// add datetime picker
PMA _addDatepicker ( $input _field , $td . attr ( 'data-type' ) , {
showMillisec : showMillisec ,
showMicrosec : showMicrosec ,
timeFormat : timeFormat
} ) ;
2017-04-20 10:55:30 +00:00
$input _field . on ( 'keyup' , function ( e ) {
2018-04-14 09:18:00 +00:00
if ( e . which === 13 ) {
2017-04-20 10:55:30 +00:00
// post on pressing "Enter"
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
g . saveOrPostEditedCell ( ) ;
2018-04-14 09:18:00 +00:00
} else if ( e . which === 27 ) {
2017-04-20 10:55:30 +00:00
} else {
toggleDatepickerIfInvalid ( $td , $input _field ) ;
}
} ) ;
2018-04-14 09:18:00 +00:00
$input _field . datepicker ( 'show' ) ;
2017-04-20 10:55:30 +00:00
toggleDatepickerIfInvalid ( $td , $input _field ) ;
2016-10-22 14:57:10 +00:00
// unbind the mousedown event to prevent the problem of
// datepicker getting closed, needs to be checked for any
// change in names when updating
2018-04-14 09:18:00 +00:00
$ ( document ) . off ( 'mousedown' , $ . datepicker . _checkExternalClick ) ;
2016-10-22 14:57:10 +00:00
2018-04-14 09:18:00 +00:00
// move ui-datepicker-div inside cEdit div
2016-10-22 14:57:10 +00:00
var datepicker _div = $ ( '#ui-datepicker-div' ) ;
2018-04-14 09:18:00 +00:00
datepicker _div . css ( { 'top' : 0 , 'left' : 0 , 'position' : 'relative' } ) ;
2016-10-22 14:57:10 +00:00
$ ( g . cEdit ) . append ( datepicker _div ) ;
// cancel any click on the datepicker element
$editArea . find ( '> *' ) . click ( function ( e ) {
e . stopPropagation ( ) ;
} ) ;
g . isEditCellTextEditable = true ;
} else {
g . isEditCellTextEditable = true ;
// only append edit area hint if there is a null checkbox
if ( $editArea . children ( ) . length > 0 ) {
$editArea . append ( '<div class="cell_edit_hint">' + g . cellEditHint + '</div>' ) ;
}
}
if ( $editArea . children ( ) . length > 0 ) {
$editArea . show ( ) ;
}
}
} ,
/ * *
* Post the content of edited cell .
*
* @ param field Optional , this object contains a boolean named move ( true , if called from move * functions )
* and a < td > to which the grid _edit should move
* /
postEditedCell : function ( options ) {
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
* /
2018-04-14 09:18:00 +00:00
var relational _display = $ ( g . o ) . find ( 'input[name=relational_display]:checked' ) . val ( ) ;
2016-10-22 14:57:10 +00:00
/ * *
* @ 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 = [ ] ;
/ * *
* @ var is _unique Boolean , whether the rows in this table is unique or not
* /
var is _unique = $ ( g . t ) . find ( 'td.edit_row_anchor' ) . is ( '.nonunique' ) ? 0 : 1 ;
/ * *
* multi edit variables
* /
var me _fields _name = [ ] ;
var me _fields _type = [ ] ;
var me _fields = [ ] ;
var me _fields _null = [ ] ;
// alert user if edited table is not unique
if ( ! is _unique ) {
alert ( g . alertNonUnique ) ;
}
// loop each edited row
$ ( g . t ) . find ( 'td.to_be_saved' ) . parents ( 'tr' ) . each ( function ( ) {
var $tr = $ ( this ) ;
var where _clause = $tr . find ( '.where_clause' ) . val ( ) ;
if ( typeof where _clause === 'undefined' ) {
where _clause = '' ;
}
2017-04-20 10:55:30 +00:00
full _where _clause . push ( where _clause ) ;
var condition _array = JSON . parse ( $tr . find ( '.condition_array' ) . val ( ) ) ;
2016-10-22 14:57:10 +00:00
/ * *
* multi edit variables , for current row
* @ TODO array indices are still not correct , they should be md5 of field ' s name
* /
var fields _name = [ ] ;
var fields _type = [ ] ;
var fields = [ ] ;
var fields _null = [ ] ;
// 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 ( $ ( g . t ) , $this _field ) ;
/ * *
* @ var this _field _params Array temporary storage for the name / value of current field
* /
var this _field _params = { } ;
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 is _null = this _field _params [ field _name ] === null ;
fields _name . push ( field _name ) ;
if ( is _null ) {
fields _null . push ( 'on' ) ;
fields . push ( '' ) ;
} else {
if ( $this _field . is ( '.bit' ) ) {
fields _type . push ( 'bit' ) ;
} else if ( $this _field . hasClass ( 'hex' ) ) {
fields _type . push ( 'hex' ) ;
}
fields _null . push ( '' ) ;
2017-04-20 10:55:30 +00:00
// Convert \n to \r\n to be consistent with form submitted value.
// The internal browser representation has to be just \n
// while form submitted value \r\n, see specification:
// https://www.w3.org/TR/html5/forms.html#the-textarea-element
fields . push ( $this _field . data ( 'value' ) . replace ( /\n/g , '\r\n' ) ) ;
2016-10-22 14:57:10 +00:00
var cell _index = $this _field . index ( '.to_be_saved' ) ;
2018-04-14 09:18:00 +00:00
if ( $this _field . is ( ':not(.relation, .enum, .set, .bit)' ) ) {
2016-10-22 14:57:10 +00:00
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 ) ;
}
}
// 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 ) {
2018-04-14 09:18:00 +00:00
condition _array [ field ] = is _null ? 'IS NULL' : '= \'' + this _field _params [ field _name ] . replace ( /'/g , '\'\'' ) + '\'' ;
2016-10-22 14:57:10 +00:00
break ;
}
}
}
} ) ; // 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 ' ;
}
new _clause = new _clause . substring ( 0 , new _clause . length - 5 ) ; // remove the last AND
$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 _type . push ( fields _type ) ;
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
* /
2018-04-14 09:18:00 +00:00
var post _params = { 'ajax_request' : true ,
'sql_query' : full _sql _query ,
'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_type[multi_edit]' : me _fields _type ,
'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'
} ;
2016-10-22 14:57:10 +00:00
if ( ! g . saveCellsAtOnce ) {
$ ( g . cEdit ) . find ( '*' ) . prop ( 'disabled' , true ) ;
$ ( g . cEdit ) . find ( '.edit_box' ) . addClass ( 'edit_box_posting' ) ;
} else {
$ ( g . o ) . find ( 'div.save_edited' ) . addClass ( 'saving_edited_data' )
. find ( 'input' ) . prop ( 'disabled' , true ) ; // 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 ( '*' ) . prop ( 'disabled' , false ) ;
$ ( g . cEdit ) . find ( '.edit_box' ) . removeClass ( 'edit_box_posting' ) ;
} else {
$ ( g . o ) . find ( 'div.save_edited' ) . removeClass ( 'saving_edited_data' )
. find ( 'input' ) . prop ( 'disabled' , false ) ; // enable the save button back
}
if ( typeof data !== 'undefined' && data . success === true ) {
if ( typeof options === 'undefined' || ! options . move ) {
PMA _ajaxShowMessage ( data . message ) ;
}
// update where_clause related data in each edited row
$ ( g . t ) . find ( 'td.to_be_saved' ) . parents ( 'tr' ) . each ( function ( ) {
var new _clause = $ ( this ) . data ( 'new_clause' ) ;
var $where _clause = $ ( this ) . find ( '.where_clause' ) ;
var old _clause = $where _clause . val ( ) ;
2017-04-20 10:55:30 +00:00
var decoded _old _clause = old _clause ;
var decoded _new _clause = new _clause ;
2016-10-22 14:57:10 +00:00
$where _clause . val ( new _clause ) ;
// update Edit, Copy, and Delete links also
$ ( this ) . 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' )
2018-04-14 09:18:00 +00:00
. off ( 'click' )
. on ( 'click' , function ( ) {
2016-10-22 14:57:10 +00:00
return confirmLink ( this , 'DELETE FROM `' + g . db + '`.`' + g . table + '` WHERE ' +
decoded _new _clause + ( is _unique ? '' : ' LIMIT 1' ) ) ;
} ) ;
}
} ) ;
// update the multi edit checkboxes
$ ( this ) . find ( 'input[type=checkbox]' ) . each ( function ( ) {
var $checkbox = $ ( this ) ;
var checkbox _name = $checkbox . attr ( 'name' ) ;
var checkbox _value = $checkbox . val ( ) ;
$checkbox . attr ( 'name' , checkbox _name . replace ( old _clause , new _clause ) ) ;
$checkbox . val ( checkbox _value . replace ( decoded _old _clause , decoded _new _clause ) ) ;
} ) ;
} ) ;
// update the display of executed SQL query command
2018-04-14 09:18:00 +00:00
if ( typeof data . sql _query !== 'undefined' ) {
// extract query box
2016-10-22 14:57:10 +00:00
var $result _query = $ ( $ . parseHTML ( data . sql _query ) ) ;
var sqlOuter = $result _query . find ( '.sqlOuter' ) . wrap ( '<p>' ) . parent ( ) . html ( ) ;
var tools = $result _query . find ( '.tools' ) . wrap ( '<p>' ) . parent ( ) . html ( ) ;
// sqlOuter and tools will not be present if 'Show SQL queries' configuration is off
2018-04-14 09:18:00 +00:00
if ( typeof sqlOuter !== 'undefined' && typeof tools !== 'undefined' ) {
2016-10-22 14:57:10 +00:00
$ ( g . o ) . find ( '.result_query:not(:last)' ) . remove ( ) ;
var $existing _query = $ ( g . o ) . find ( '.result_query' ) ;
// If two query box exists update query in second else add a second box
if ( $existing _query . find ( 'div.sqlOuter' ) . length > 1 ) {
2018-04-14 09:18:00 +00:00
$existing _query . children ( ':nth-child(4)' ) . remove ( ) ;
$existing _query . children ( ':nth-child(4)' ) . remove ( ) ;
2016-10-22 14:57:10 +00:00
$existing _query . append ( sqlOuter + tools ) ;
} else {
$existing _query . append ( sqlOuter + tools ) ;
}
PMA _highlightSQL ( $existing _query ) ;
}
}
// hide and/or update the successfully saved cells
g . hideEditCell ( true , data ) ;
// remove the "Save edited cells" button
$ ( g . o ) . find ( 'div.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 , false ) ;
if ( ! g . saveCellsAtOnce ) {
$ ( g . t ) . find ( '.to_be_saved' )
. removeClass ( 'to_be_saved' ) ;
}
}
}
2018-04-14 09:18:00 +00:00
} ) . done ( function ( ) {
2016-10-22 14:57:10 +00:00
if ( options !== undefined && options . move ) {
g . showEditCell ( options . cell ) ;
}
} ) ; // end $.ajax()
} ,
/ * *
* 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 ;
/ * *
* @ var field _name String containing the name of this field .
* @ see getFieldName ( )
* /
var field _name = getFieldName ( $ ( g . t ) , $this _field ) ;
/ * *
* @ var this _field _params Array temporary storage for the name / value of current field
* /
var this _field _params = { } ;
/ * *
* @ var is _null String capturing whether 'checkbox_null_<field_name>_<row_index>' is checked .
* /
var is _null = $ ( g . cEdit ) . find ( 'input:checkbox' ) . is ( ':checked' ) ;
if ( $ ( g . cEdit ) . find ( '.edit_area' ) . is ( '.edit_area_loading' ) ) {
// the edit area is still loading (retrieving cell data), no need to post
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 ( '.bit' ) ) {
this _field _params [ field _name ] = $ ( g . cEdit ) . find ( '.edit_box' ) . 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 ( ) ;
2018-04-14 09:18:00 +00:00
} ) . get ( ) . join ( ',' ) ;
2016-10-22 14:57:10 +00:00
} else if ( $this _field . is ( '.relation, .enum' ) ) {
// for relation and enumeration, take the results from edit box value,
// because selected value from drop-down, new window or multiple
// selection list will always be updated to the edit box
this _field _params [ field _name ] = $ ( g . cEdit ) . find ( '.edit_box' ) . val ( ) ;
} else if ( $this _field . hasClass ( 'hex' ) ) {
2018-04-14 09:18:00 +00:00
if ( $ ( g . cEdit ) . find ( '.edit_box' ) . val ( ) . match ( /^(0x)?[a-f0-9]*$/i ) !== null ) {
2016-10-22 14:57:10 +00:00
this _field _params [ field _name ] = $ ( g . cEdit ) . find ( '.edit_box' ) . val ( ) ;
} else {
var hexError = '<div class="error">' + PMA _messages . strEnterValidHex + '</div>' ;
PMA _ajaxShowMessage ( hexError , false ) ;
this _field _params [ field _name ] = PMA _getCellValue ( g . currentEditCell ) ;
}
} else {
this _field _params [ field _name ] = $ ( g . cEdit ) . find ( '.edit_box' ) . val ( ) ;
}
2018-04-14 09:18:00 +00:00
if ( g . wasEditedCellNull || this _field _params [ field _name ] !== PMA _getCellValue ( g . currentEditCell ) ) {
2016-10-22 14:57:10 +00:00
need _to _post = true ;
}
}
if ( need _to _post ) {
$ ( g . currentEditCell ) . addClass ( 'to_be_saved' )
. data ( 'value' , this _field _params [ field _name ] ) ;
if ( g . saveCellsAtOnce ) {
$ ( g . o ) . find ( 'div.save_edited' ) . show ( ) ;
}
g . isCellEdited = true ;
}
return need _to _post ;
} ,
/ * *
* Save or post currently edited cell , depending on the "saveCellsAtOnce" configuration .
*
* @ param field Optional , this object contains a boolean named move ( true , if called from move * functions )
* and a < td > to which the grid _edit should move
* /
saveOrPostEditedCell : function ( options ) {
var saved = g . saveEditedCell ( ) ;
// Check if $cfg['SaveCellsAtOnce'] is false
if ( ! g . saveCellsAtOnce ) {
// Check if need_to_post is true
if ( saved ) {
// Check if this function called from 'move' functions
if ( options !== undefined && options . move ) {
g . postEditedCell ( options ) ;
} else {
g . postEditedCell ( ) ;
}
// need_to_post is false
} else {
// Check if this function called from 'move' functions
if ( options !== undefined && options . move ) {
g . hideEditCell ( true ) ;
g . showEditCell ( options . cell ) ;
// NOT called from 'move' functions
} else {
g . hideEditCell ( true ) ;
}
}
// $cfg['SaveCellsAtOnce'] is true
} else {
// If need_to_post
if ( saved ) {
// If this function called from 'move' functions
if ( options !== undefined && options . move ) {
g . hideEditCell ( true , true , false , options ) ;
g . showEditCell ( options . cell ) ;
// NOT called from 'move' functions
} else {
g . hideEditCell ( true , true ) ;
}
} else {
// If this function called from 'move' functions
if ( options !== undefined && options . move ) {
g . hideEditCell ( true , false , false , options ) ;
g . showEditCell ( options . cell ) ;
// NOT called from 'move' functions
} else {
g . hideEditCell ( true ) ;
}
}
}
} ,
/ * *
* 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 . cRsz ) . append ( cb ) ;
} ) ;
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 = $ ( g . o ) . find ( '.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 ] , 10 ) ;
}
} else {
g . colOrder = [ ] ;
for ( var i = 0 ; i < $firstRowCols . length ; i ++ ) {
g . colOrder . push ( i ) ;
}
}
// register events
$ ( g . t ) . find ( 'th.draggable' )
. mousedown ( function ( e ) {
2018-04-14 09:18:00 +00:00
$ ( g . o ) . addClass ( 'turnOffSelect' ) ;
2016-10-22 14:57:10 +00:00
if ( g . visibleHeadersCount > 1 ) {
g . dragStartReorder ( e , this ) ;
}
} )
. mouseenter ( function ( ) {
if ( g . visibleHeadersCount > 1 ) {
$ ( this ) . css ( 'cursor' , 'move' ) ;
} else {
$ ( this ) . css ( 'cursor' , 'inherit' ) ;
}
} )
. mouseleave ( function ( ) {
g . showReorderHint = false ;
2018-04-14 09:18:00 +00:00
$ ( this ) . tooltip ( 'option' , {
2016-10-22 14:57:10 +00:00
content : g . updateHint ( )
} ) ;
} )
. dblclick ( function ( e ) {
e . preventDefault ( ) ;
2018-04-14 09:18:00 +00:00
$ ( '<div/>' )
. prop ( 'title' , PMA _messages . strColNameCopyTitle )
. addClass ( 'modal-copy' )
. text ( PMA _messages . strColNameCopyText )
. append (
$ ( '<input/>' )
. prop ( 'readonly' , true )
. val ( $ ( this ) . data ( 'column' ) )
2016-10-22 14:57:10 +00:00
)
2018-04-14 09:18:00 +00:00
. dialog ( {
resizable : false ,
modal : true
} )
. find ( 'input' ) . focus ( ) . select ( ) ;
2016-10-22 14:57:10 +00:00
} ) ;
$ ( g . t ) . find ( 'th.draggable a' )
. dblclick ( function ( e ) {
e . stopPropagation ( ) ;
} ) ;
// restore column order when the restore button is clicked
$ ( g . o ) . find ( 'div.restore_column' ) . click ( function ( ) {
g . restoreColOrder ( ) ;
} ) ;
// attach to global div
$ ( g . gDiv ) . append ( g . cPointer ) ;
$ ( g . gDiv ) . append ( g . cCpy ) ;
// prevent default "dragstart" event when dragging a link
2018-04-14 09:18:00 +00:00
$ ( g . t ) . find ( 'th a' ) . on ( 'dragstart' , function ( ) {
2016-10-22 14:57:10 +00:00
return false ;
} ) ;
// refresh the restore column button state
g . refreshRestoreButton ( ) ;
} ,
/ * *
* 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 . showAllColText = PMA _messages . strShowAllCol ;
// get data columns in the first row of the table
var $firstRowCols = $ ( g . t ) . find ( 'tr:first th.draggable' ) ;
var i ;
// initialize column visibility
var $col _visib = $ ( g . o ) . find ( '.col_visib' ) ; // check if column visibility is passed from PHP
if ( $col _visib . length > 0 ) {
g . colVisib = $col _visib . val ( ) . split ( ',' ) ;
for ( i = 0 ; i < g . colVisib . length ; i ++ ) {
g . colVisib [ i ] = parseInt ( g . colVisib [ i ] , 10 ) ;
}
} else {
g . colVisib = [ ] ;
for ( i = 0 ; i < $firstRowCols . length ; i ++ ) {
g . colVisib . push ( 1 ) ;
}
}
// make sure we have more than one column
if ( $firstRowCols . length > 1 ) {
var $colVisibTh = $ ( g . t ) . find ( 'th:not(.draggable)' ) ;
PMA _tooltip (
$colVisibTh ,
'th' ,
PMA _messages . strColVisibHint
) ;
// create column visibility drop-down arrow(s)
$colVisibTh . each ( function ( ) {
2018-04-14 09:18:00 +00:00
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 ) ;
} ) ;
2016-10-22 14:57:10 +00:00
// add column visibility control
g . cList . innerHTML = '<div class="lDiv"></div>' ;
var $listDiv = $ ( g . cList ) . find ( 'div' ) ;
var tempClick = function ( ) {
if ( g . toggleCol ( $ ( this ) . index ( ) ) ) {
g . afterToggleCol ( ) ;
}
} ;
for ( 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 ( tempClick ) ;
}
// 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 ( ) ;
} ) ;
// 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
$ ( g . 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 ( ) ;
} ,
/ * *
* Move currently Editing Cell to Up
* /
2018-04-14 09:18:00 +00:00
moveUp : function ( e ) {
2016-10-22 14:57:10 +00:00
e . preventDefault ( ) ;
var $this _field = $ ( g . currentEditCell ) ;
var field _name = getFieldName ( $ ( g . t ) , $this _field ) ;
var where _clause = $this _field . parents ( 'tr' ) . first ( ) . find ( '.where_clause' ) . val ( ) ;
if ( typeof where _clause === 'undefined' ) {
where _clause = '' ;
}
var found = false ;
var $found _row ;
var $prev _row ;
var j = 0 ;
2018-04-14 09:18:00 +00:00
$this _field . parents ( 'tr' ) . first ( ) . parents ( 'tbody' ) . children ( ) . each ( function ( ) {
if ( $ ( this ) . find ( '.where_clause' ) . val ( ) === where _clause ) {
2016-10-22 14:57:10 +00:00
found = true ;
$found _row = $ ( this ) ;
}
if ( ! found ) {
$prev _row = $ ( this ) ;
}
} ) ;
var new _cell ;
if ( found && $prev _row ) {
2018-04-14 09:18:00 +00:00
$prev _row . children ( 'td' ) . each ( function ( ) {
if ( getFieldName ( $ ( g . t ) , $ ( this ) ) === field _name ) {
2016-10-22 14:57:10 +00:00
new _cell = this ;
}
} ) ;
}
if ( new _cell ) {
2018-04-14 09:18:00 +00:00
g . hideEditCell ( false , false , false , { move : true , cell : new _cell } ) ;
2016-10-22 14:57:10 +00:00
}
} ,
/ * *
* Move currently Editing Cell to Down
* /
2018-04-14 09:18:00 +00:00
moveDown : function ( e ) {
2016-10-22 14:57:10 +00:00
e . preventDefault ( ) ;
var $this _field = $ ( g . currentEditCell ) ;
var field _name = getFieldName ( $ ( g . t ) , $this _field ) ;
var where _clause = $this _field . parents ( 'tr' ) . first ( ) . find ( '.where_clause' ) . val ( ) ;
if ( typeof where _clause === 'undefined' ) {
where _clause = '' ;
}
var found = false ;
var $found _row ;
var $next _row ;
var j = 0 ;
var next _row _found = false ;
2018-04-14 09:18:00 +00:00
$this _field . parents ( 'tr' ) . first ( ) . parents ( 'tbody' ) . children ( ) . each ( function ( ) {
if ( $ ( this ) . find ( '.where_clause' ) . val ( ) === where _clause ) {
2016-10-22 14:57:10 +00:00
found = true ;
$found _row = $ ( this ) ;
}
if ( found ) {
if ( j >= 1 && ! next _row _found ) {
$next _row = $ ( this ) ;
next _row _found = true ;
} else {
j ++ ;
}
}
} ) ;
var new _cell ;
if ( found && $next _row ) {
2018-04-14 09:18:00 +00:00
$next _row . children ( 'td' ) . each ( function ( ) {
if ( getFieldName ( $ ( g . t ) , $ ( this ) ) === field _name ) {
2016-10-22 14:57:10 +00:00
new _cell = this ;
}
} ) ;
}
if ( new _cell ) {
2018-04-14 09:18:00 +00:00
g . hideEditCell ( false , false , false , { move : true , cell : new _cell } ) ;
2016-10-22 14:57:10 +00:00
}
} ,
/ * *
* Move currently Editing Cell to Left
* /
2018-04-14 09:18:00 +00:00
moveLeft : function ( e ) {
2016-10-22 14:57:10 +00:00
e . preventDefault ( ) ;
var $this _field = $ ( g . currentEditCell ) ;
var field _name = getFieldName ( $ ( g . t ) , $this _field ) ;
var where _clause = $this _field . parents ( 'tr' ) . first ( ) . find ( '.where_clause' ) . val ( ) ;
if ( typeof where _clause === 'undefined' ) {
where _clause = '' ;
}
var found = false ;
var $found _row ;
var j = 0 ;
2018-04-14 09:18:00 +00:00
$this _field . parents ( 'tr' ) . first ( ) . parents ( 'tbody' ) . children ( ) . each ( function ( ) {
if ( $ ( this ) . find ( '.where_clause' ) . val ( ) === where _clause ) {
2016-10-22 14:57:10 +00:00
found = true ;
$found _row = $ ( this ) ;
}
} ) ;
var left _cell ;
var cell _found = false ;
if ( found ) {
2018-04-14 09:18:00 +00:00
$found _row . children ( 'td.grid_edit' ) . each ( function ( ) {
2016-10-22 14:57:10 +00:00
if ( getFieldName ( $ ( g . t ) , $ ( this ) ) === field _name ) {
cell _found = true ;
}
if ( ! cell _found ) {
left _cell = this ;
}
} ) ;
}
if ( left _cell ) {
2018-04-14 09:18:00 +00:00
g . hideEditCell ( false , false , false , { move : true , cell : left _cell } ) ;
2016-10-22 14:57:10 +00:00
}
} ,
/ * *
* Move currently Editing Cell to Right
* /
2018-04-14 09:18:00 +00:00
moveRight : function ( e ) {
2016-10-22 14:57:10 +00:00
e . preventDefault ( ) ;
var $this _field = $ ( g . currentEditCell ) ;
var field _name = getFieldName ( $ ( g . t ) , $this _field ) ;
var where _clause = $this _field . parents ( 'tr' ) . first ( ) . find ( '.where_clause' ) . val ( ) ;
if ( typeof where _clause === 'undefined' ) {
where _clause = '' ;
}
var found = false ;
var $found _row ;
var j = 0 ;
2018-04-14 09:18:00 +00:00
$this _field . parents ( 'tr' ) . first ( ) . parents ( 'tbody' ) . children ( ) . each ( function ( ) {
if ( $ ( this ) . find ( '.where_clause' ) . val ( ) === where _clause ) {
2016-10-22 14:57:10 +00:00
found = true ;
$found _row = $ ( this ) ;
}
} ) ;
var right _cell ;
var cell _found = false ;
var next _cell _found = false ;
if ( found ) {
2018-04-14 09:18:00 +00:00
$found _row . children ( 'td.grid_edit' ) . each ( function ( ) {
2016-10-22 14:57:10 +00:00
if ( getFieldName ( $ ( g . t ) , $ ( this ) ) === field _name ) {
cell _found = true ;
}
if ( cell _found ) {
if ( j >= 1 && ! next _cell _found ) {
right _cell = this ;
next _cell _found = true ;
} else {
j ++ ;
}
}
} ) ;
}
if ( right _cell ) {
2018-04-14 09:18:00 +00:00
g . hideEditCell ( false , false , false , { move : true , cell : right _cell } ) ;
2016-10-22 14:57:10 +00:00
}
} ,
/ * *
* Initialize grid editing feature .
* /
initGridEdit : function ( ) {
2018-04-14 09:18:00 +00:00
function startGridEditing ( e , cell ) {
2016-10-22 14:57:10 +00:00
if ( g . isCellEditActive ) {
g . saveOrPostEditedCell ( ) ;
} else {
g . showEditCell ( cell ) ;
}
e . stopPropagation ( ) ;
}
2018-04-14 09:18:00 +00:00
function handleCtrlNavigation ( e ) {
if ( ( e . ctrlKey && e . which === 38 ) || ( e . altKey && e . which === 38 ) ) {
2016-10-22 14:57:10 +00:00
g . moveUp ( e ) ;
2018-04-14 09:18:00 +00:00
} else if ( ( e . ctrlKey && e . which === 40 ) || ( e . altKey && e . which === 40 ) ) {
2016-10-22 14:57:10 +00:00
g . moveDown ( e ) ;
2018-04-14 09:18:00 +00:00
} else if ( ( e . ctrlKey && e . which === 37 ) || ( e . altKey && e . which === 37 ) ) {
2016-10-22 14:57:10 +00:00
g . moveLeft ( e ) ;
2018-04-14 09:18:00 +00:00
} else if ( ( e . ctrlKey && e . which === 39 ) || ( e . altKey && e . which === 39 ) ) {
2016-10-22 14:57:10 +00:00
g . moveRight ( e ) ;
}
}
// create cell edit wrapper element
g . cEditStd = document . createElement ( 'div' ) ;
g . cEdit = g . cEditStd ;
g . cEditTextarea = document . createElement ( 'div' ) ;
// adjust g.cEditStd
g . cEditStd . className = 'cEdit' ;
2018-04-14 09:18:00 +00:00
$ ( g . cEditStd ) . html ( '<input class="edit_box" rows="1" /><div class="edit_area" />' ) ;
2016-10-22 14:57:10 +00:00
$ ( g . cEditStd ) . hide ( ) ;
// adjust g.cEdit
g . cEditTextarea . className = 'cEdit' ;
$ ( g . cEditTextarea ) . html ( '<textarea class="edit_box" rows="1" ></textarea><div class="edit_area" />' ) ;
$ ( g . cEditTextarea ) . hide ( ) ;
// assign cell editing hint
g . cellEditHint = PMA _messages . strCellEditHint ;
g . saveCellWarning = PMA _messages . strSaveCellWarning ;
g . alertNonUnique = PMA _messages . strAlertNonUnique ;
g . gotoLinkText = PMA _messages . strGoToLink ;
// initialize cell editing configuration
g . saveCellsAtOnce = $ ( g . o ) . find ( '.save_cells_at_once' ) . val ( ) ;
g . maxTruncatedLen = PMA _commonParams . get ( 'LimitChars' ) ;
// register events
$ ( g . t ) . find ( 'td.data.click1' )
. click ( function ( e ) {
startGridEditing ( e , this ) ;
// prevent default action when clicking on "link" in a table
if ( $ ( e . target ) . is ( '.grid_edit a' ) ) {
e . preventDefault ( ) ;
}
} ) ;
$ ( g . t ) . find ( 'td.data.click2' )
. click ( function ( e ) {
var $cell = $ ( this ) ;
// In the case of relational link, We want single click on the link
// to goto the link and double click to start grid-editing.
var $link = $ ( e . target ) ;
if ( $link . is ( '.grid_edit.relation a' ) ) {
e . preventDefault ( ) ;
// get the click count and increase
var clicks = $cell . data ( 'clicks' ) ;
clicks = ( typeof clicks === 'undefined' ) ? 1 : clicks + 1 ;
2018-04-14 09:18:00 +00:00
if ( clicks === 1 ) {
2016-10-22 14:57:10 +00:00
// if there are no previous clicks,
// start the single click timer
var timer = setTimeout ( function ( ) {
// temporarily remove ajax class so the page loader will not handle it,
// submit and then add it back
$link . removeClass ( 'ajax' ) ;
AJAX . requestHandler . call ( $link [ 0 ] ) ;
$link . addClass ( 'ajax' ) ;
$cell . data ( 'clicks' , 0 ) ;
} , 700 ) ;
$cell . data ( 'clicks' , clicks ) ;
$cell . data ( 'timer' , timer ) ;
} else {
// this is a double click, cancel the single click timer
// and make the click count 0
clearTimeout ( $cell . data ( 'timer' ) ) ;
$cell . data ( 'clicks' , 0 ) ;
// start grid-editing
startGridEditing ( e , this ) ;
}
}
} )
. dblclick ( function ( e ) {
if ( $ ( e . target ) . is ( '.grid_edit a' ) ) {
e . preventDefault ( ) ;
} else {
startGridEditing ( e , this ) ;
}
} ) ;
$ ( g . cEditStd ) . on ( 'keydown' , 'input.edit_box, select' , handleCtrlNavigation ) ;
$ ( g . cEditStd ) . find ( '.edit_box' ) . focus ( function ( ) {
g . showEditArea ( ) ;
} ) ;
$ ( g . cEditStd ) . on ( 'keydown' , '.edit_box, select' , function ( e ) {
2018-04-14 09:18:00 +00:00
if ( e . which === 13 ) {
2016-10-22 14:57:10 +00:00
// post on pressing "Enter"
e . preventDefault ( ) ;
g . saveOrPostEditedCell ( ) ;
}
} ) ;
$ ( g . cEditStd ) . keydown ( function ( e ) {
if ( ! g . isEditCellTextEditable ) {
// prevent text editing
e . preventDefault ( ) ;
}
} ) ;
$ ( g . cEditTextarea ) . on ( 'keydown' , 'textarea.edit_box, select' , handleCtrlNavigation ) ;
$ ( g . cEditTextarea ) . find ( '.edit_box' ) . focus ( function ( ) {
g . showEditArea ( ) ;
} ) ;
$ ( g . cEditTextarea ) . on ( 'keydown' , '.edit_box, select' , function ( e ) {
2018-04-14 09:18:00 +00:00
if ( e . which === 13 && ! e . shiftKey ) {
2016-10-22 14:57:10 +00:00
// post on pressing "Enter"
e . preventDefault ( ) ;
g . saveOrPostEditedCell ( ) ;
}
} ) ;
$ ( g . cEditTextarea ) . keydown ( function ( e ) {
if ( ! g . isEditCellTextEditable ) {
// prevent text editing
e . preventDefault ( ) ;
}
} ) ;
$ ( 'html' ) . click ( function ( e ) {
// hide edit cell if the click is not fromDat edit area
2018-04-14 09:18:00 +00:00
if ( $ ( e . target ) . parents ( ) . index ( $ ( g . cEdit ) ) === - 1 &&
2016-10-22 14:57:10 +00:00
! $ ( e . target ) . parents ( '.ui-datepicker-header' ) . length &&
2018-04-14 09:18:00 +00:00
! $ ( '.browse_foreign_modal.ui-dialog:visible' ) . length &&
! $ ( e . target ) . closest ( '.dismissable' ) . length
2016-10-22 14:57:10 +00:00
) {
g . hideEditCell ( ) ;
}
} ) . keydown ( function ( e ) {
2018-04-14 09:18:00 +00:00
if ( e . which === 27 && g . isCellEditActive ) {
2016-10-22 14:57:10 +00:00
// cancel on pressing "Esc"
g . hideEditCell ( true ) ;
}
} ) ;
$ ( g . o ) . find ( 'div.save_edited' ) . click ( function ( ) {
g . hideEditCell ( ) ;
g . postEditedCell ( ) ;
} ) ;
2018-04-14 09:18:00 +00:00
$ ( window ) . on ( 'beforeunload' , function ( ) {
2016-10-22 14:57:10 +00:00
if ( g . isCellEdited ) {
return g . saveCellWarning ;
}
} ) ;
// attach to global div
$ ( g . gDiv ) . append ( g . cEditStd ) ;
$ ( g . gDiv ) . append ( g . cEditTextarea ) ;
// add hint for grid editing feature when hovering "Edit" link in each table row
if ( PMA _messages . strGridEditFeatureHint !== undefined ) {
PMA _tooltip (
$ ( g . t ) . find ( '.edit_row_anchor a' ) ,
'a' ,
PMA _messages . strGridEditFeatureHint
) ;
}
}
} ;
2018-04-14 09:18:00 +00:00
/ * * * * * * * * * * * * * * * * * *
2016-10-22 14:57:10 +00:00
* Initialize grid
* * * * * * * * * * * * * * * * * * /
// wrap all truncated data cells with span indicating the original length
// todo update the original length after a grid edit
$ ( t ) . find ( 'td.data.truncated:not(:has(span))' )
2018-04-14 09:18:00 +00:00
. wrapInner ( function ( ) {
2016-10-22 14:57:10 +00:00
return '<span title="' + PMA _messages . strOriginalLength + ' ' +
$ ( this ) . data ( 'originallength' ) + '"></span>' ;
} ) ;
// wrap remaining 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 ;
// enclosing .sqlqueryresults div
g . o = $ ( t ) . parents ( '.sqlqueryresults' ) ;
// 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 = $ ( g . o ) . find ( '.table_create_time' ) . val ( ) ;
// assign the hints
g . sortHint = PMA _messages . strSortHint ;
g . strMultiSortHint = PMA _messages . strMultiSortHint ;
g . markHint = PMA _messages . strColMarkHint ;
g . copyHint = PMA _messages . strColNameCopyHint ;
// assign common hidden inputs
var $common _hidden _inputs = $ ( g . o ) . find ( 'div.common_hidden_inputs' ) ;
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' ) ;
// add relative position to global div so that resize handlers are correctly positioned
$ ( g . gDiv ) . css ( 'position' , 'relative' ) ;
// 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 ( ) ;
}
2018-04-14 09:18:00 +00:00
// disable reordering for result from EXPLAIN or SHOW syntax, which do not have a table navigation panel
2016-10-22 14:57:10 +00:00
if ( enableReorder &&
2018-04-14 09:18:00 +00:00
$ ( g . o ) . find ( 'table.navigation' ) . length > 0 ) {
2016-10-22 14:57:10 +00:00
g . initColReorder ( ) ;
}
if ( enableVisib ) {
g . initColVisib ( ) ;
}
2018-04-14 09:18:00 +00:00
// make sure we have the ajax class
2016-10-22 14:57:10 +00:00
if ( enableGridEdit &&
2018-04-14 09:18:00 +00:00
$ ( t ) . is ( '.ajax' ) ) {
2016-10-22 14:57:10 +00:00
g . initGridEdit ( ) ;
}
// create tooltip for each <th> with draggable class
PMA _tooltip (
2018-04-14 09:18:00 +00:00
$ ( t ) . find ( 'th.draggable' ) ,
'th' ,
g . updateHint ( )
2016-10-22 14:57:10 +00:00
) ;
// register events for hint tooltip (anchors inside draggable th)
$ ( t ) . find ( 'th.draggable a' )
. mouseenter ( function ( ) {
g . showSortHint = true ;
g . showMultiSortHint = true ;
2018-04-14 09:18:00 +00:00
$ ( t ) . find ( 'th.draggable' ) . tooltip ( 'option' , {
2016-10-22 14:57:10 +00:00
content : g . updateHint ( )
} ) ;
} )
. mouseleave ( function ( ) {
g . showSortHint = false ;
g . showMultiSortHint = false ;
2018-04-14 09:18:00 +00:00
$ ( t ) . find ( 'th.draggable' ) . tooltip ( 'option' , {
2016-10-22 14:57:10 +00:00
content : g . updateHint ( )
} ) ;
} ) ;
// register events for dragging-related feature
if ( enableResize || enableReorder ) {
$ ( document ) . mousemove ( function ( e ) {
g . dragMove ( e ) ;
} ) ;
$ ( document ) . mouseup ( function ( e ) {
2018-04-14 09:18:00 +00:00
$ ( g . o ) . removeClass ( 'turnOffSelect' ) ;
2016-10-22 14:57:10 +00:00
g . dragEnd ( e ) ;
} ) ;
}
// some adjustment
$ ( t ) . removeClass ( 'data' ) ;
$ ( g . gDiv ) . addClass ( 'data' ) ;
}
/ * *
* jQuery plugin to cancel selection in HTML code .
* /
( function ( $ ) {
2018-04-14 09:18:00 +00:00
$ . fn . noSelect = function ( p ) { // no select plugin by Paulo P.Marinas
2016-10-22 14:57:10 +00:00
var prevent = ( p === null ) ? true : p ;
var is _msie = navigator . userAgent . indexOf ( 'MSIE' ) > - 1 || ! ! window . navigator . userAgent . match ( /Trident.*rv\:11\./ ) ;
var is _firefox = navigator . userAgent . indexOf ( 'Firefox' ) > - 1 ;
2018-04-14 09:18:00 +00:00
var is _safari = navigator . userAgent . indexOf ( 'Safari' ) > - 1 ;
var is _opera = navigator . userAgent . indexOf ( 'Presto' ) > - 1 ;
2016-10-22 14:57:10 +00:00
if ( prevent ) {
return this . each ( function ( ) {
if ( is _msie || is _safari ) {
2018-04-14 09:18:00 +00:00
$ ( this ) . on ( 'selectstart' , false ) ;
2016-10-22 14:57:10 +00:00
} else if ( is _firefox ) {
$ ( this ) . css ( 'MozUserSelect' , 'none' ) ;
$ ( 'body' ) . trigger ( 'focus' ) ;
} else if ( is _opera ) {
2018-04-14 09:18:00 +00:00
$ ( this ) . on ( 'mousedown' , false ) ;
2016-10-22 14:57:10 +00:00
} else {
$ ( this ) . attr ( 'unselectable' , 'on' ) ;
}
} ) ;
} else {
return this . each ( function ( ) {
if ( is _msie || is _safari ) {
2018-04-14 09:18:00 +00:00
$ ( this ) . off ( 'selectstart' ) ;
2016-10-22 14:57:10 +00:00
} else if ( is _firefox ) {
$ ( this ) . css ( 'MozUserSelect' , 'inherit' ) ;
} else if ( is _opera ) {
2018-04-14 09:18:00 +00:00
$ ( this ) . off ( 'mousedown' ) ;
2016-10-22 14:57:10 +00:00
} else {
$ ( this ) . removeAttr ( 'unselectable' ) ;
}
} ) ;
}
2018-04-14 09:18:00 +00:00
} ; // end noSelect
} ( jQuery ) ) ;