1/** 2 * Debounce and throttle function's decorator plugin 1.0.5 3 * 4 * Copyright (c) 2009 Filatov Dmitry ([email protected]) 5 * Dual licensed under the MIT and GPL licenses: 6 * http://www.opensource.org/licenses/mit-license.php 7 * http://www.gnu.org/licenses/gpl.html 8 * 9 */ 10 11(function($) { 12 13$.extend({ 14 15 debounce : function(fn, timeout, invokeAsap, ctx) { 16 17 if(arguments.length == 3 && typeof invokeAsap != 'boolean') { 18 ctx = invokeAsap; 19 invokeAsap = false; 20 } 21 22 var timer; 23 24 return function() { 25 26 var args = arguments; 27 ctx = ctx || this; 28 29 invokeAsap && !timer && fn.apply(ctx, args); 30 31 clearTimeout(timer); 32 33 timer = setTimeout(function() { 34 !invokeAsap && fn.apply(ctx, args); 35 timer = null; 36 }, timeout); 37 38 }; 39 40 }, 41 42 throttle : function(fn, timeout, ctx) { 43 44 var timer, args, needInvoke; 45 46 return function() { 47 48 args = arguments; 49 needInvoke = true; 50 ctx = ctx || this; 51 52 if(!timer) { 53 (function() { 54 if(needInvoke) { 55 fn.apply(ctx, args); 56 needInvoke = false; 57 timer = setTimeout(arguments.callee, timeout); 58 } 59 else { 60 timer = null; 61 } 62 })(); 63 } 64 65 }; 66 67 } 68 69}); 70 71})(jQuery);; 72 73/* vim: set expandtab sw=4 ts=4 sts=4: */ 74/** 75 * Handles the resizing of a menu according to the available screen width 76 * 77 * Uses themes/original/css/resizable-menu.css.php 78 * 79 * To initialise: 80 * $('#myMenu').menuResizer(function () { 81 * // This function will be called to find out how much 82 * // available horizontal space there is for the menu 83 * return $('body').width() - 5; // Some extra margin for good measure 84 * }); 85 * 86 * To trigger a resize operation: 87 * $('#myMenu').menuResizer('resize'); // Bind this to $(window).resize() 88 * 89 * To restore the menu to a state like before it was initialized: 90 * $('#myMenu').menuResizer('destroy'); 91 * 92 * @package PhpMyAdmin 93 */ 94(function ($) { 95 function MenuResizer($container, widthCalculator) { 96 var self = this; 97 self.$container = $container; 98 self.widthCalculator = widthCalculator; 99 // create submenu container 100 var link = $('<a />', {href: '#', 'class': 'tab nowrap'}) 101 .text(PMA_messages.strMore) 102 .bind('click', false); // same as event.preventDefault() 103 var img = $container.find('li img'); 104 if (img.length) { 105 $(PMA_getImage('b_more.png').toString()).prependTo(link); 106 } 107 var $submenu = $('<li />', {'class': 'submenu'}) 108 .append(link) 109 .append($('<ul />')) 110 .mouseenter(function() { 111 if ($(this).find('ul .tabactive').length === 0) { 112 $(this) 113 .addClass('submenuhover') 114 .find('> a') 115 .addClass('tabactive'); 116 } 117 }) 118 .mouseleave(function() { 119 if ($(this).find('ul .tabactive').length === 0) { 120 $(this) 121 .removeClass('submenuhover') 122 .find('> a') 123 .removeClass('tabactive'); 124 } 125 }); 126 $container.children('.clearfloat').remove(); 127 $container.append($submenu).append("<div class='clearfloat'></div>"); 128 setTimeout(function () { 129 self.resize(); 130 }, 4); 131 } 132 MenuResizer.prototype.resize = function () { 133 var wmax = this.widthCalculator.call(this.$container); 134 var $submenu = this.$container.find('.submenu:last'); 135 var submenu_w = $submenu.outerWidth(true); 136 var $submenu_ul = $submenu.find('ul'); 137 var $li = this.$container.find('> li'); 138 var $li2 = $submenu_ul.find('li'); 139 var more_shown = $li2.length > 0; 140 // Calculate the total width used by all the shown tabs 141 var total_len = more_shown ? submenu_w : 0; 142 var l = $li.length - 1; 143 var i; 144 for (i = 0; i < l; i++) { 145 total_len += $($li[i]).outerWidth(true); 146 } 147 // Now hide menu elements that don't fit into the menubar 148 var hidden = false; // Whether we have hidden any tabs 149 while (total_len >= wmax && --l >= 0) { // Process the tabs backwards 150 hidden = true; 151 var el = $($li[l]); 152 var el_width = el.outerWidth(true); 153 el.data('width', el_width); 154 if (! more_shown) { 155 total_len -= el_width; 156 el.prependTo($submenu_ul); 157 total_len += submenu_w; 158 more_shown = true; 159 } else { 160 total_len -= el_width; 161 el.prependTo($submenu_ul); 162 } 163 } 164 // If we didn't hide any tabs, then there might be some space to show some 165 if (! hidden) { 166 // Show menu elements that do fit into the menubar 167 for (i = 0, l = $li2.length; i < l; i++) { 168 total_len += $($li2[i]).data('width'); 169 // item fits or (it is the last item 170 // and it would fit if More got removed) 171 if (total_len < wmax || 172 (i == $li2.length - 1 && total_len - submenu_w < wmax) 173 ) { 174 $($li2[i]).insertBefore($submenu); 175 } else { 176 break; 177 } 178 } 179 } 180 // Show/hide the "More" tab as needed 181 if ($submenu_ul.find('li').length > 0) { 182 $submenu.addClass('shown'); 183 } else { 184 $submenu.removeClass('shown'); 185 } 186 if (this.$container.find('> li').length == 1) { 187 // If there is only the "More" tab left, then we need 188 // to align the submenu to the left edge of the tab 189 $submenu_ul.removeClass().addClass('only'); 190 } else { 191 // Otherwise we align the submenu to the right edge of the tab 192 $submenu_ul.removeClass().addClass('notonly'); 193 } 194 if ($submenu.find('.tabactive').length) { 195 $submenu 196 .addClass('active') 197 .find('> a') 198 .removeClass('tab') 199 .addClass('tabactive'); 200 } else { 201 $submenu 202 .removeClass('active') 203 .find('> a') 204 .addClass('tab') 205 .removeClass('tabactive'); 206 } 207 }; 208 MenuResizer.prototype.destroy = function () { 209 var $submenu = this.$container.find('li.submenu').removeData(); 210 $submenu.find('li').appendTo(this.$container); 211 $submenu.remove(); 212 }; 213 214 /** Public API */ 215 var methods = { 216 init: function(widthCalculator) { 217 return this.each(function () { 218 var $this = $(this); 219 if (! $this.data('menuResizer')) { 220 $this.data( 221 'menuResizer', 222 new MenuResizer($this, widthCalculator) 223 ); 224 } 225 }); 226 }, 227 resize: function () { 228 return this.each(function () { 229 var self = $(this).data('menuResizer'); 230 if (self) { 231 self.resize(); 232 } 233 }); 234 }, 235 destroy: function () { 236 return this.each(function () { 237 var self = $(this).data('menuResizer'); 238 if (self) { 239 self.destroy(); 240 } 241 }); 242 } 243 }; 244 245 /** Extend jQuery */ 246 $.fn.menuResizer = function(method) { 247 if (methods[method]) { 248 return methods[method].call(this); 249 } else if (typeof method === 'function') { 250 return methods.init.apply(this, [method]); 251 } else { 252 $.error('Method ' + method + ' does not exist on jQuery.menuResizer'); 253 } 254 }; 255})(jQuery); 256; 257 258/* vim: set expandtab sw=4 ts=4 sts=4: */ 259/** 260 * Conditionally included if framing is not allowed 261 */ 262if (self == top) { 263 var style_element = document.getElementById("cfs-style"); 264 // check if style_element has already been removed 265 // to avoid frequently reported js error 266 if (typeof(style_element) != 'undefined' && style_element != null) { 267 style_element.parentNode.removeChild(style_element); 268 } 269} else { 270 top.location = self.location; 271} 272; 273 274/* vim: set expandtab sw=4 ts=4 sts=4: */ 275/** 276 * JavaScript functionality for Routines, Triggers and Events. 277 * 278 * @package PhpMyadmin 279 */ 280/** 281 * @var RTE Contains all the JavaScript functionality 282 * for Routines, Triggers and Events 283 */ 284var RTE = { 285 /** 286 * Construct for the object that provides the 287 * functionality for Routines, Triggers and Events 288 */ 289 object: function (type) { 290 $.extend(this, RTE.COMMON); 291 this.editorType = type; 292 293 switch (type) { 294 case 'routine': 295 $.extend(this, RTE.ROUTINE); 296 break; 297 case 'trigger': 298 // nothing extra yet for triggers 299 break; 300 case 'event': 301 $.extend(this, RTE.EVENT); 302 break; 303 default: 304 break; 305 } 306 }, 307 /** 308 * @var string param_template Template for a row in the routine editor
309 */ 310 param_template: '' 311}; 312 313/** 314 * @var RTE.COMMON a JavaScript namespace containing the functionality 315 * for Routines, Triggers and Events 316 * 317 * This namespace is extended by the functionality required 318 * to handle a specific item (a routine, trigger or event) 319 * in the relevant javascript files in this folder 320 */ 321RTE.COMMON = { 322 /** 323 * @var $ajaxDialog Query object containing the reference to the 324 * dialog that contains the editor 325 */ 326 $ajaxDialog: null, 327 /** 328 * @var syntaxHiglighter Reference to the codemirror editor 329 */ 330 syntaxHiglighter: null, 331 /** 332 * @var buttonOptions Object containing options for 333 * the jQueryUI dialog buttons 334 */ 335 buttonOptions: {}, 336 /** 337 * @var editorType Type of the editor 338 */ 339 editorType: null, 340 /** 341 * Validate editor form fields. 342 */ 343 validate: function () { 344 /** 345 * @var $elm a jQuery object containing the reference 346 * to an element that is being validated 347 */ 348 var $elm = null; 349 // Common validation. At the very least the name 350 // and the definition must be provided for an item 351 $elm = $('table.rte_table').last().find('input[name=item_name]'); 352 if ($elm.val() === '') { 353 $elm.focus(); 354 alert(PMA_messages.strFormEmpty); 355 return false; 356 } 357 $elm = $('table.rte_table').find('textarea[name=item_definition]'); 358 if ($elm.val() === '') { 359 if (this.syntaxHiglighter !== null) { 360 this.syntaxHiglighter.focus(); 361 } 362 else { 363 $('textarea[name=item_definition]').last().focus(); 364 } 365 alert(PMA_messages.strFormEmpty); 366 return false; 367 } 368 // The validation has so far passed, so now 369 // we can validate item-specific fields. 370 return this.validateCustom(); 371 }, // end validate() 372 /** 373 * Validate custom editor form fields. 374 * This function can be overridden by 375 * other files in this folder 376 */ 377 validateCustom: function () { 378 return true; 379 }, // end validateCustom() 380 /** 381 * Execute some code after the ajax 382 * dialog for the editor is shown. 383 * This function can be overridden by 384 * other files in this folder 385 */ 386 postDialogShow: function () { 387 // Nothing by default 388 }, // end postDialogShow() 389 390 exportDialog: function ($this) { 391 var $msg = PMA_ajaxShowMessage(); 392 if ($this.hasClass('mult_submit')) { 393 var combined = { 394 success: true, 395 title: PMA_messages.strExport, 396 message: '', 397 error: '' 398 }; 399 // export anchors of all selected rows 400 var export_anchors = $('input.checkall:checked').parents('tr').find('.export_anchor'); 401 var count = export_anchors.length; 402 var returnCount = 0; 403 404 // No routine is exportable (due to privilege issues) 405 if (count === 0) { 406 PMA_ajaxShowMessage(PMA_messages.NoExportable); 407 } 408 409 export_anchors.each(function () { 410 $.get($(this).attr('href'), {'ajax_request': true}, function (data) { 411 returnCount++; 412 if (data.success === true) { 413 combined.message += "\n" + data.message + "\n"; 414 if (returnCount == count) { 415 showExport(combined); 416 } 417 } else { 418 // complain even if one export is failing 419 combined.success = false; 420 combined.error += "\n" + data.error + "\n"; 421 if (returnCount == count) { 422 showExport(combined); 423 } 424 } 425 }); 426 }); 427 } else { 428 $.get($this.attr('href'), {'ajax_request': true}, showExport); 429 } 430 PMA_ajaxRemoveMessage($msg); 431 432 function showExport(data) { 433 if (data.success === true) { 434 PMA_ajaxRemoveMessage($msg); 435 /** 436 * @var button_options Object containing options 437 * for jQueryUI dialog buttons 438 */ 439 var button_options = {}; 440 button_options[PMA_messages.strClose] = function () { 441 $(this).dialog("close").remove(); 442 }; 443 /**
444 * Display the dialog to the user 445 */ 446 data.message = '<textarea cols="40" rows="15" style="width: 100%;">' + data.message + '</textarea>'; 447 var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({ 448 width: 500, 449 buttons: button_options, 450 title: data.title 451 }); 452 // Attach syntax highlighted editor to export dialog 453 /** 454 * @var $elm jQuery object containing the reference 455 * to the Export textarea. 456 */ 457 var $elm = $ajaxDialog.find('textarea'); 458 PMA_getSQLEditor($elm); 459 } else { 460 PMA_ajaxShowMessage(data.error, false); 461 } 462 } // end showExport() 463 }, // end exportDialog() 464 editorDialog: function (is_new, $this) { 465 var that = this; 466 /** 467 * @var $edit_row jQuery object containing the reference to 468 * the row of the the item being edited 469 * from the list of items 470 */ 471 var $edit_row = null; 472 if ($this.hasClass('edit_anchor')) { 473 // Remeber the row of the item being edited for later, 474 // so that if the edit is successful, we can replace the 475 // row with info about the modified item. 476 $edit_row = $this.parents('tr'); 477 } 478 /** 479 * @var $msg jQuery object containing the reference to 480 * the AJAX message shown to the user 481 */ 482 var $msg = PMA_ajaxShowMessage(); 483 $.get($this.attr('href'), {'ajax_request': true}, function (data) { 484 if (data.success === true) { 485 // We have successfully fetched the editor form 486 PMA_ajaxRemoveMessage($msg); 487 // Now define the function that is called when 488 // the user presses the "Go" button 489 that.buttonOptions[PMA_messages.strGo] = function () { 490 // Move the data from the codemirror editor back to the 491 // textarea, where it can be used in the form submission. 492 if (typeof CodeMirror != 'undefined') { 493 that.syntaxHiglighter.save(); 494 } 495 // Validate editor and submit request, if passed. 496 if (that.validate()) { 497 /** 498 * @var data Form data to be sent in the AJAX request 499 */ 500 var data = $('form.rte_form').last().serialize(); 501 $msg = PMA_ajaxShowMessage( 502 PMA_messages.strProcessingRequest 503 ); 504 var url = $('form.rte_form').last().attr('action'); 505 $.post(url, data, function (data) { 506 if (data.success === true) { 507 // Item created successfully 508 PMA_ajaxRemoveMessage($msg); 509 PMA_slidingMessage(data.message); 510 that.$ajaxDialog.dialog('close'); 511 // If we are in 'edit' mode, we must 512 // remove the reference to the old row. 513 if (mode === 'edit' && $edit_row !== null ) { 514 $edit_row.remove(); 515 } 516 // Sometimes, like when moving a trigger from 517 // a table to another one, the new row should 518 // not be inserted into the list. In this case 519 // "data.insert" will be set to false. 520 if (data.insert) { 521 // Insert the new row at the correct 522 // location in the list of items 523 /** 524 * @var text Contains the name of an item from 525 * the list that is used in comparisons 526 * to find the correct location where 527 * to insert a new row. 528 */ 529 var text = ''; 530 /** 531 * @var inserted Whether a new item has been 532 * inserted in the list or not 533 */ 534 var inserted = false;
535 $('table.data').find('tr').each(function () { 536 text = $(this) 537 .children('td') 538 .eq(0) 539 .find('strong') 540 .text() 541 .toUpperCase(); 542 text = $.trim(text); 543 if (text !== '' && text > data.name) { 544 $(this).before(data.new_row); 545 inserted = true; 546 return false; 547 } 548 }); 549 if (! inserted) { 550 // If we didn't manage to insert the row yet, 551 // it must belong at the end of the list, 552 // so we insert it there. 553 $('table.data').append(data.new_row); 554 } 555 // Fade-in the new row 556 $('tr.ajaxInsert') 557 .show('slow') 558 .removeClass('ajaxInsert'); 559 } else if ($('table.data').find('tr').has('td').length === 0) { 560 // If we are not supposed to insert the new row, 561 // we will now check if the table is empty and 562 // needs to be hidden. This will be the case if 563 // we were editing the only item in the list, 564 // which we removed and will not be inserting 565 // something else in its place. 566 $('table.data').hide("slow", function () { 567 $('#nothing2display').show("slow"); 568 }); 569 } 570 // Now we have inserted the row at the correct 571 // position, but surely at least some row classes 572 // are wrong now. So we will itirate throught 573 // all rows and assign correct classes to them 574 /** 575 * @var ct Count of processed rows 576 */ 577 var ct = 0; 578 /** 579 * @var rowclass Class to be attached to the row 580 * that is being processed 581 */ 582 var rowclass = ''; 583 $('table.data').find('tr').has('td').each(function () { 584 rowclass = (ct % 2 === 0) ? 'odd' : 'even'; 585 $(this).removeClass().addClass(rowclass); 586 ct++; 587 }); 588 // If this is the first item being added, remove 589 // the "No items" message and show the list. 590 if ($('table.data').find('tr').has('td').length > 0 && 591 $('#nothing2display').is(':visible') 592 ) { 593 $('#nothing2display').hide("slow", function () { 594 $('table.data').show("slow"); 595 }); 596 } 597 PMA_reloadNavigation(); 598 } else { 599 PMA_ajaxShowMessage(data.error, false); 600 } 601 }); // end $.post() 602 } // end "if (that.validate())" 603 }; // end of function that handles the submission of the Editor 604 that.buttonOptions[PMA_messages.strClose] = function () { 605 $(this).dialog("close"); 606 }; 607 /**
608 * Display the dialog to the user 609 */ 610 that.$ajaxDialog = $('<div id="rteDialog">' + data.message + '</div>').dialog({ 611 width: 700, 612 minWidth: 500, 613 maxHeight: $(window).height(), 614 buttons: that.buttonOptions, 615 title: data.title, 616 modal: true, 617 open: function () { 618 if ($('#rteDialog').parents('.ui-dialog').height() > $(window).height()) { 619 $('#rteDialog').dialog("option", "height", $(window).height()); 620 } 621 $(this).find('input[name=item_name]').focus(); 622 $(this).find('input.datefield').each(function () { 623 PMA_addDatepicker($(this).css('width', '95%'), 'date'); 624 }); 625 $(this).find('input.datetimefield').each(function () { 626 PMA_addDatepicker($(this).css('width', '95%'), 'datetime'); 627 }); 628 $.datepicker.initialized = false; 629 }, 630 close: function () { 631 $(this).remove(); 632 } 633 }); 634 /** 635 * @var mode Used to remeber whether the editor is in 636 * "Edit" or "Add" mode 637 */ 638 var mode = 'add'; 639 if ($('input[name=editor_process_edit]').length > 0) { 640 mode = 'edit'; 641 } 642 // Attach syntax highlighted editor to the definition 643 /** 644 * @var elm jQuery object containing the reference to 645 * the Definition textarea. 646 */ 647 var $elm = $('textarea[name=item_definition]').last(); 648 var linterOptions = {}; 649 linterOptions[that.editorType + '_editor'] = true; 650 that.syntaxHiglighter = PMA_getSQLEditor($elm, {}, null, linterOptions); 651 652 // Execute item-specific code 653 that.postDialogShow(data); 654 } else { 655 PMA_ajaxShowMessage(data.error, false); 656 } 657 }); // end $.get() 658 }, 659 660 dropDialog: function ($this) { 661 /** 662 * @var $curr_row Object containing reference to the current row 663 */ 664 var $curr_row = $this.parents('tr'); 665 /** 666 * @var question String containing the question to be asked for c
666onfirmation 667 */ 668 var question = $('<div/>').text( 669 $curr_row.children('td').children('.drop_sql').html() 670 ); 671 // We ask for confirmation first here, before submitting the ajax request 672 $this.PMA_confirm(question, $this.attr('href'), function (url) { 673 /** 674 * @var msg jQuery object containing the reference to 675 * the AJAX message shown to the user 676 */ 677 var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 678 $.post(url, {'is_js_confirmed': 1, 'ajax_request': true}, function (data) { 679 if (data.success === true) { 680 /** 681 * @var $table Object containing reference 682 * to the main list of elements 683 */ 684 var $table = $curr_row.parent(); 685 // Check how many rows will be left after we remove 686 // the one that the user has requested us to remove 687 if ($table.find('tr').length === 3) { 688 // If there are two rows left, it means that they are 689 // the header of the table and the rows that we are 690 // about to remove, so after the removal there will be 691 // nothing to show in the table, so we hide it. 692 $table.hide("slow", function () { 693 $(this).find('tr.even, tr.odd').remove(); 694 $('.withSelected').remove(); 695 $('#nothing2display').show("slow"); 696 }); 697 } else { 698 $curr_row.hide("slow", function () { 699 $(this).remove(); 700 // Now we have removed the row from the list, but maybe 701 // some row classes are wrong now. So we will itirate 702 // throught all rows and assign correct classes to them. 703 /** 704 * @var ct Count of processed rows 705 */ 706 var ct = 0; 707 /** 708 * @var rowclass Class to be attached to the row 709 * that is being processed 710 */ 711 var rowclass = ''; 712 $table.find('tr').has('td').each(function () { 713 rowclass = (ct % 2 === 1) ? 'odd' : 'even'; 714 $(this).removeClass().addClass(rowclass); 715 ct++; 716 }); 717 }); 718 } 719 // Get rid of the "Loading" message 720 PMA_ajaxRemoveMessage($msg); 721 // Show the query that we just executed 722 PMA_slidingMessage(data.sql_query); 723 PMA_reloadNavigation(); 724 } else { 725 PMA_ajaxShowMessage(data.error, false); 726 } 727 }); // end $.post() 728 }); // end $.PMA_confirm() 729 }, 730 731 dropMultipleDialog: function ($this) { 732 // We ask for confirmation here 733 $this.PMA_confirm(PMA_messages.strDropRTEitems, '', function (url) { 734 /** 735 * @var msg jQuery object containing the reference to 736 * the AJAX message shown to the user 737 */ 738 var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 739 740 // drop anchors of all selected rows 741 var drop_anchors = $('input.checkall:checked').parents('tr').find('.drop_anchor'); 742 var success = true; 743 var count = drop_anchors.length; 744 var returnCount = 0; 745 746 drop_anchors.each(function () { 747 var $anchor = $(this); 748 /** 749 * @var $curr_row Object containing reference to the current row 750 */ 751 var $curr_row = $anchor.parents('tr'); 752 $.post($anchor.attr('href'), {'is_js_confirmed': 1, 'ajax_request': true}, function (data) { 753 returnCount++; 754 if (data.success === true) { 755 /** 756 * @var $table Object containing reference 757 * to the main list of elements 758 */ 759 var $table = $curr_row.parent(); 760 // Check how many rows will be left after we remove 761 // the one that the user has requested us to remove 762 if ($table.find('tr').length === 3) { 763 // If there are two rows left, it means that they are 764 // the header of the table and the rows that we are 765 // about to remove, so after the removal there will be 766 // nothing to show in the table, so we hide it. 767 $table.hide("slow", function () { 768 $(this).find('tr.even, tr.odd').remove(); 769 $('.withSelected').remove(); 770 $('#nothing2display').show("slow"); 771 }); 772 } else { 773 $curr_row.hide("fast", function () { 774 $(this).remove(); 775 // Now we have removed the row from the list, but maybe 776 // some row classes are wrong now. So we will itirate 777 // throught all rows and assign correct classes to them. 778 /** 779 * @var ct Count of processed rows 780 */ 781 var ct = 0; 782 /** 783 * @var rowclass Class to be attached to the row 784 * that is being processed 785 */ 786 var rowclass = ''; 787 $table.find('tr').has('td').each(function () { 788 rowclass = (ct % 2 === 1) ? 'odd' : 'even'; 789 $(this).removeClass().addClass(rowclass); 790 ct++; 791 }); 792 }); 793 } 794 if (returnCount == count) { 795 if (success) { 796 // Get rid of the "Loading" message 797 PMA_ajaxRemoveMessage($msg); 798 $('#rteListForm_checkall').prop({checked: fal
798se, indeterminate: false}); 799 } 800 PMA_reloadNavigation(); 801 } 802 } else { 803 PMA_ajaxShowMessage(data.error, false); 804 success = false; 805 if (returnCount == count) { 806 PMA_reloadNavigation(); 807 } 808 } 809 }); // end $.post() 810 }); // end drop_anchors.each() 811 }); // end $.PMA_confirm() 812 } 813}; // end RTE namespace 814 815/** 816 * @var RTE.EVENT JavaScript functionality for events 817 */ 818RTE.EVENT = { 819 validateCustom: function () { 820 /** 821 * @var elm a jQuery object containing the reference 822 * to an element that is being validated 823 */ 824 var $elm = null; 825 if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'RECURRING') { 826 // The interval field must not be empty for recurring events 827 $elm = this.$ajaxDialog.find('input[name=item_interval_value]'); 828 if ($elm.val() === '') { 829 $elm.focus(); 830 alert(PMA_messages.strFormEmpty); 831 return false; 832 } 833 } else { 834 // The execute_at field must not be empty for "once off" events 835 $elm = this.$ajaxDialog.find('input[name=item_execute_at]'); 836 if ($elm.val() === '') { 837 $elm.focus(); 838 alert(PMA_messages.strFormEmpty); 839 return false; 840 } 841 } 842 return true; 843 } 844}; 845 846/** 847 * @var RTE.ROUTINE JavaScript functionality for routines 848 */ 849RTE.ROUTINE = { 850 /** 851 * Overriding the postDialogShow() function defined in common.js 852 * 853 * @param data JSON-encoded data from the ajax request 854 */ 855 postDialogShow: function (data) { 856 // Cache the template for a parameter table row 857 RTE.param_template = data.param_template; 858 var that = this; 859 // Make adjustments in the dialog to make it AJAX compatible 860 $('td.routine_param_remove').show(); 861 $('input[name=routine_removeparameter]').remove(); 862 $('input[name=routine_addparameter]').css('width', '100%'); 863 // Enable/disable the 'options' dropdowns for parameters as necessary 864 $('table.routine_params_table').last().find('th[colspan=2]').attr('colspan', '1'); 865 $('table.routine_params_table').last().find('tr').has('td').each(function () { 866 that.setOptionsForParameter( 867 $(this).find('select[name^=item_param_type]'), 868 $(this).find('input[name^=item_param_length]'), 869 $(this).find('select[name^=item_param_opts_text]'), 870 $(this).find('select[name^=item_param_opts_num]') 871 ); 872 }); 873 // Enable/disable the 'options' dropdowns for 874 // function return value as necessary 875 this.setOptionsForParameter( 876 $('table.rte_table').last().find('select[name=item_returntype]'), 877 $('table.rte_table').last().find('input[name=item_returnlength]'), 878 $('table.rte_table').last().find('select[name=item_returnopts_text]'), 879 $('table.rte_table').last().find('select[name=item_returnopts_num]') 880 ); 881 // Allow changing parameter order 882 $('.routine_params_table tbody').sortable({ 883 containment: '.routine_params_table tbody', 884 handle: '.dragHandle', 885 stop: function(event, ui) { 886 that.reindexParameters(); 887 }, 888 }); 889 }, 890 /** 891 * Reindexes the parameters after dropping a parameter or reordering parameters 892 */ 893 reindexParameters: function () { 894 /** 895 * @var index Counter used for reindexing the input 896 * fields in the routine parameters table 897 */ 898 var index = 0; 899 $('table.routine_params_table tbody').find('tr').each(function () { 900 $(this).find(':input').each(function () { 901 /** 902 * @var inputname The value of the name attribute of 903 * the input field being reindexed 904 */ 905 var inputname = $(this).attr('name');
906 if (inputname.substr(0, 14) === 'item_param_dir') { 907 $(this).attr('name', inputname.substr(0, 14) + '[' + index + ']'); 908 } else if (inputname.substr(0, 15) === 'item_param_name') { 909 $(this).attr('name', inputname.substr(0, 15) + '[' + index + ']'); 910 } else if (inputname.substr(0, 15) === 'item_param_type') { 911 $(this).attr('name', inputname.substr(0, 15) + '[' + index + ']'); 912 } else if (inputname.substr(0, 17) === 'item_param_length') { 913 $(this).attr('name', inputname.substr(0, 17) + '[' + index + ']'); 914 $(this).attr('id', 'item_param_length_' + index); 915 } else if (inputname.substr(0, 20) === 'item_param_opts_text') { 916 $(this).attr('name', inputname.substr(0, 20) + '[' + index + ']'); 917 } else if (inputname.substr(0, 19) === 'item_param_opts_num') { 918 $(this).attr('name', inputname.substr(0, 19) + '[' + index + ']'); 919 } 920 }); 921 index++; 922 }); 923 }, 924 /** 925 * Overriding the validateCustom() function defined in common.js 926 */ 927 validateCustom: function () { 928 /** 929 * @var isSuccess Stores the outcome of the validation 930 */ 931 var isSuccess = true; 932 /** 933 * @var inputname The value of the "name" attribute for 934 * the field that is being processed 935 */ 936 var inputname = ''; 937 this.$ajaxDialog.find('table.routine_params_table').last().find('tr').each(function () { 938 // Every parameter of a routine must have 939 // a non-empty direction, name and type 940 if (isSuccess) { 941 $(this).find(':input').each(function () { 942 inputname = $(this).attr('name'); 943 if (inputname.substr(0, 14) === 'item_param_dir' || 944 inputname.substr(0, 15) === 'item_param_name' || 945 inputname.substr(0, 15) === 'item_param_type') { 946 if ($(this).val() === '') { 947 $(this).focus(); 948 isSuccess = false; 949 return false; 950 } 951 } 952 }); 953 } else { 954 return false; 955 } 956 }); 957 if (! isSuccess) { 958 alert(PMA_messages.strFormEmpty); 959 return false; 960 } 961 this.$ajaxDialog.find('table.routine_params_table').last().find('tr').each(function () { 962 // SET, ENUM, VARCHAR and VARBINARY fields must have length/values 963 var $inputtyp = $(this).find('select[name^=item_param_type]'); 964 var $inputlen = $(this).find('input[name^=item_param_length]'); 965 if ($inputtyp.length && $inputlen.length) { 966 if (($inputtyp.val() === 'ENUM' || $inputtyp.val() === 'SET' || $inputtyp.val().substr(0, 3) === 'VAR') && 967 $inputlen.val() === '' 968 ) { 969 $inputlen.focus(); 970 isSuccess = false; 971 return false; 972 } 973 } 974 }); 975 if (! isSuccess) { 976 alert(PMA_messages.strFormEmpty); 977 return false; 978 } 979 if (this.$ajaxDialog.find('select[name=item_type]').find(':selected').val() === 'FUNCTION') { 980 // The length/values of return variable for functions must 981 // be set, if the type is SET, ENUM, VARCHAR or VARBINARY. 982 var $returntyp = this.$ajaxDialog.find('select[name=item_returntype]'); 983 var $returnlen = this.$ajaxDialog.find('input[name=item_returnlength]'); 984 if (($returntyp.val() === 'ENUM' || $returntyp.val() === 'SET' || $returntyp.val().substr(0, 3) === 'VAR') && 985 $returnlen.val() === '' 986 ) { 987 $returnlen.focus(); 988 alert(PMA_messages.strFormEmpty); 989 return false; 990 } 991 } 992 if ($('select[name=item_type]').find(':selected').val() === 'FUNCTION') { 993 // A function must contain a RETURN statement in its definition 994 if (this.$ajaxDialog.find('table.rte_table').find('textarea[name=item_definition]').val().toUpperCase().indexOf('RETURN') < 0) { 995 this.syntaxHiglighter.focus(); 996 alert(PMA_messages.MissingReturn); 997 return false;
998 } 999 } 1000 return true; 1001 }, 1002 /** 1003 * Enable/disable the "options" dropdown and "length" input for 1004 * parameters and the return variable in the routine editor 1005 * as necessary. 1006 * 1007 * @param type a jQuery object containing the reference 1008 * to the "Type" dropdown box 1009 * @param len a jQuery object containing the reference 1010 * to the "Length" input box 1011 * @param text a jQuery object containing the reference 1012 * to the dropdown box with options for 1013 * parameters of text type 1014 * @param num a jQuery object containing the reference 1015 * to the dropdown box with options for 1016 * parameters of numeric type 1017 */ 1018 setOptionsForParameter: function ($type, $len, $text, $num) { 1019 /** 1020 * @var no_opts a jQuery object containing the reference 1021 * to an element to be displayed when no 1022 * options are available 1023 */ 1024 var $no_opts = $text.parent().parent().find('.no_opts'); 1025 /** 1026 * @var no_len a jQuery object containing the reference 1027 * to an element to be displayed when no 1028 * "length/values" field is available 1029 */ 1030 var $no_len = $len.parent().parent().find('.no_len'); 1031 1032 // Process for parameter options 1033 switch ($type.val()) { 1034 case 'TINYINT': 1035 case 'SMALLINT': 1036 case 'MEDIUMINT': 1037 case 'INT': 1038 case 'BIGINT': 1039 case 'DECIMAL': 1040 case 'FLOAT': 1041 case 'DOUBLE': 1042 case 'REAL': 1043 $text.parent().hide(); 1044 $num.parent().show(); 1045 $no_opts.hide(); 1046 break; 1047 case 'TINYTEXT': 1048 case 'TEXT': 1049 case 'MEDIUMTEXT': 1050 case 'LONGTEXT': 1051 case 'CHAR': 1052 case 'VARCHAR': 1053 case 'SET': 1054 case 'ENUM': 1055 $text.parent().show(); 1056 $num.parent().hide(); 1057 $no_opts.hide(); 1058 break; 1059 default: 1060 $text.parent().hide(); 1061 $num.parent().hide(); 1062 $no_opts.show(); 1063 break; 1064 } 1065 // Process for parameter length 1066 switch ($type.val()) { 1067 case 'DATE': 1068 case 'TINYBLOB': 1069 case 'TINYTEXT': 1070 case 'BLOB': 1071 case 'TEXT': 1072 case 'MEDIUMBLOB': 1073 case 'MEDIUMTEXT': 1074 case 'LONGBLOB': 1075 case 'LONGTEXT': 1076 $text.closest('tr').find('a:first').hide(); 1077 $len.parent().hide(); 1078 $no_len.show(); 1079 break; 1080 default: 1081 if ($type.val() == 'ENUM' || $type.val() == 'SET') { 1082 $text.closest('tr').find('a:first').show(); 1083 } else { 1084 $text.closest('tr').find('a:first').hide(); 1085 } 1086 $len.parent().show(); 1087 $no_len.hide(); 1088 break; 1089 } 1090 }, 1091 executeDialog: function ($this) { 1092 var that = this; 1093 /** 1094 * @var msg jQuery object containing the reference to 1095 * the AJAX message shown to the user 1096 */ 1097 var $msg = PMA_ajaxShowMessage(); 1098 $.post($this.attr('href'), {'ajax_request': true}, function (data) { 1099 if (data.success === true) { 1100 PMA_ajaxRemoveMessage($msg); 1101 // If 'data.dialog' is true we show a dialog with a form 1102 // to get the input parameters for routine, otherwise 1103 // we just show the results of the query 1104 if (data.dialog) { 1105 // Define the function that is called when 1106 // the user presses the "Go" button 1107 that.buttonOptions[PMA_messages.strGo] = function () { 1108 /** 1109 * @var data Form data to be sent in the AJAX request 1110 */ 1111 var data = $('form.rte_form').last().serialize(); 1112 $msg = PMA_ajaxShowMessage( 1113 PMA_messages.strProcessingRequest 1114 ); 1115 $.post('db_routines.php', data, function (data) { 1116 if (data.success === true) { 1117 // Routine executed successfully 1118 PMA_ajaxRemoveMessage($msg); 1119 PMA_slidingMessage(data.message); 1120 $ajaxDialog.dialog('close'); 1121 } else { 1122 PMA_ajaxShowMessage(data.error, false); 1123 } 1124 }); 1125 }; 1126 that.buttonOptions[PMA_messages.strClose] = function () { 1127 $(this).dialog("close"); 1128 }; 1129 /**
1130 * Display the dialog to the user 1131 */ 1132 var $ajaxDialog = $('<div>' + data.message + '</div>').dialog({ 1133 width: 650, 1134 buttons: that.buttonOptions, 1135 title: data.title, 1136 modal: true, 1137 close: function () { 1138 $(this).remove(); 1139 } 1140 }); 1141 $ajaxDialog.find('input[name^=params]').first().focus(); 1142 /** 1143 * Attach the datepickers to the relevant form fields 1144 */ 1145 $ajaxDialog.find('input.datefield, input.datetimefield').each(function () { 1146 PMA_addDatepicker($(this).css('width', '95%')); 1147 }); 1148 /* 1149 * Define the function if the user presses enter 1150 */ 1151 $('form.rte_form').on('keyup', function (event) { 1152 event.preventDefault(); 1153 if (event.keyCode === 13) { 1154 /** 1155 * @var data Form data to be sent in the AJAX request 1156 */ 1157 var data = $(this).serialize(); 1158 $msg = PMA_ajaxShowMessage( 1159 PMA_messages.strProcessingRequest 1160 ); 1161 var url = $(this).attr('action'); 1162 $.post(url, data, function (data) { 1163 if (data.success === true) { 1164 // Routine executed successfully 1165 PMA_ajaxRemoveMessage($msg); 1166 PMA_slidingMessage(data.message); 1167 $('form.rte_form').off('keyup'); 1168 $ajaxDialog.remove(); 1169 } else { 1170 PMA_ajaxShowMessage(data.error, false); 1171 } 1172 }); 1173 } 1174 }); 1175 } else { 1176 // Routine executed successfully 1177 PMA_slidingMessage(data.message); 1178 } 1179 } else { 1180 PMA_ajaxShowMessage(data.error, false); 1181 } 1182 }); // end $.post() 1183 } 1184}; 1185 1186/** 1187 * Attach Ajax event handlers for the Routines, Triggers and Events editor 1188 */ 1189$(function () { 1190 /** 1191 * Attach Ajax event handlers for the Add/Edit functionality. 1192 */ 1193 $(document).on('click', 'a.ajax.add_anchor, a.ajax.edit_anchor', function (event) { 1194 event.preventDefault(); 1195 var type = $(this).attr('href').substr(0, $(this).attr('href').indexOf('?')); 1196 if (type.indexOf('routine') != -1) { 1197 type = 'routine'; 1198 } else if (type.indexOf('trigger') != -1) { 1199 type = 'trigger'; 1200 } else if (type.indexOf('event') != -1) { 1201 type = 'event'; 1202 } else { 1203 type = ''; 1204 } 1205 var dialog = new RTE.object(type); 1206 dialog.editorDialog($(this).hasClass('add_anchor'), $(this)); 1207 }); // end $(document).on() 1208 1209 /** 1210 * Attach Ajax event handlers for the Execute routine functionality 1211 */ 1212 $(document).on('click', 'a.ajax.exec_anchor', function (event) { 1213 event.preventDefault(); 1214 var dialog = new RTE.object('routine'); 1215 dialog.executeDialog($(this)); 1216 }); // end $(document).on() 1217 1218 /** 1219 * Attach Ajax event handlers for Export of Routines, Triggers and Events 1220 */ 1221 $(document).on('click', 'a.ajax.export_anchor', function (event) { 1222 event.preventDefault(); 1223 var dialog = new RTE.object(); 1224 dialog.exportDialog($(this)); 1225 }); // end $(document).on() 1226 1227 $(document).on('click', '#rteListForm.ajax .mult_submit[value="export"]', function (event) { 1228 event.preventDefault(); 1229 var dialog = new RTE.object(); 1230 dialog.exportDialog($(this)); 1231 }); // end $(document).on() 1232 1233 /** 1234 * Attach Ajax event handlers for Drop functionality 1235 * of Routines, Triggers and Events. 1236 */ 1237 $(document).on('click', 'a.ajax.drop_anchor', function (event) { 1238 event.preventDefault(); 1239 var dialog = new RTE.object(); 1240 dialog.dropDialog($(this)); 1241 }); // end $(document).on() 1242 1243 $(document).on('click', '#rteListForm.ajax .mult_submit[value="drop"]', function (event) { 1244 event.preventDefault(); 1245 var dialog = new RTE.object(); 1246 dialog.dropMultipleDialog($(this)); 1247 }); // end $(document).on() 1248 1249 /** 1250 * Attach Ajax event handlers for the "Change event/routine type" 1251 * functionality in the events editor, so that the correct 1252 * rows are shown in the editor when changing the event type 1253 */ 1254 $(document).on('change', 'select[name=item_type]', function () { 1255 $(this) 1256 .closest('table') 1257 .find('tr.recurring_event_row, tr.onetime_event_row, tr.routine_return_row, .routine_direction_cell')
1258 .toggle(); 1259 }); // end $(document).on() 1260 1261 /** 1262 * Attach Ajax event handlers for the "Change parameter type" 1263 * functionality in the routines editor, so that the correct 1264 * option/length fields, if any, are shown when changing 1265 * a parameter type 1266 */ 1267 $(document).on('change', 'select[name^=item_param_type]', function () { 1268 /** 1269 * @var row jQuery object containing the reference to 1270 * a row in the routine parameters table 1271 */ 1272 var $row = $(this).parents('tr').first(); 1273 var rte = new RTE.object('routine'); 1274 rte.setOptionsForParameter( 1275 $row.find('select[name^=item_param_type]'), 1276 $row.find('input[name^=item_param_length]'), 1277 $row.find('select[name^=item_param_opts_text]'), 1278 $row.find('select[name^=item_param_opts_num]') 1279 ); 1280 }); // end $(document).on() 1281 1282 /** 1283 * Attach Ajax event handlers for the "Change the type of return 1284 * variable of function" functionality, so that the correct fields, 1285 * if any, are shown when changing the function return type type 1286 */ 1287 $(document).on('change', 'select[name=item_returntype]', function () { 1288 var rte = new RTE.object('routine'); 1289 var $table = $(this).closest('table.rte_table'); 1290 rte.setOptionsForParameter( 1291 $table.find('select[name=item_returntype]'), 1292 $table.find('input[name=item_returnlength]'), 1293 $table.find('select[name=item_returnopts_text]'), 1294 $table.find('select[name=item_returnopts_num]') 1295 ); 1296 }); // end $(document).on() 1297 1298 /** 1299 * Attach Ajax event handlers for the "Add parameter to routine" functionality 1300 */ 1301 $(document).on('click', 'input[name=routine_addparameter]', function (event) { 1302 event.preventDefault(); 1303 /** 1304 * @var routine_params_table jQuery object containing the reference 1305 * to the routine parameters table 1306 */ 1307 var $routine_params_table = $(this).closest('div.ui-dialog').find('.routine_params_table'); 1308 /** 1309 * @var new_param_row A string containing the HTML code for the 1310 * new row for the routine parameters table 1311 */ 1312 var new_param_row = RTE.param_template.replace(/%s/g, $routine_params_table.find('tr').length - 1); 1313 // Append the new row to the parameters table 1314 $routine_params_table.append(new_param_row); 1315 // Make sure that the row is correctly shown according to the type of routine 1316 if ($(this).closest('div.ui-dialog').find('table.rte_table select[name=item_type]').val() === 'FUNCTION') { 1317 $('tr.routine_return_row').show(); 1318 $('td.routine_direction_cell').hide(); 1319 } 1320 /** 1321 * @var newrow jQuery object containing the reference to the newly 1322 * inserted row in the routine parameters table 1323 */ 1324 var $newrow = $(this).closest('div.ui-dialog').find('table.routine_params_table').find('tr').has('td').last(); 1325 // Enable/disable the 'options' dropdowns for parameters as necessary 1326 var rte = new RTE.object('routine'); 1327 rte.setOptionsForParameter( 1328 $newrow.find('select[name^=item_param_type]'), 1329 $newrow.find('input[name^=item_param_length]'), 1330 $newrow.find('select[name^=item_param_opts_text]'), 1331 $newrow.find('select[name^=item_param_opts_num]') 1332 ); 1333 }); // end $(document).on() 1334 1335 /** 1336 * Attach Ajax event handlers for the 1337 * "Remove parameter from routine" functionality 1338 */ 1339 $(document).on('click', 'a.routine_param_remove_anchor', function (event) { 1340 event.preventDefault(); 1341 $(this).parent().parent().remove(); 1342 // After removing a parameter, the indices of the name attributes in 1343 // the input fields lose the correct order and need to be reordered. 1344 RTE.ROUTINE.reindexParameters(); 1345 }); // end $(document).on() 1346}); // end of $() 1347; 1348 1349/** 1350 * https://github.com/csnover/TraceKit 1351 * @license MIT 1352 * @namespace TraceKit 1353 */ 1354(function(window, undefined) { 1355if (!window) { 1356 return; 1357} 1358 1359var TraceKit = {}; 1360var _oldTraceKit = window.TraceKit; 1361 1362// global reference to slice 1363var _slice = [].slice; 1364var UNKNOWN_FUNCTION = '?'; 1365 1366/** 1367 * A better form of hasOwnProperty<br/>
1368 * Example: `_has(MainHostObject, property) === true/false` 1369 * 1370 * @param {Object} object to check property 1371 * @param {string} key to check 1372 * @return {Boolean} true if the object has the key and it is not inherited 1373 */ 1374function _has(object, key) { 1375 return Object.prototype.hasOwnProperty.call(object, key); 1376} 1377 1378/** 1379 * Returns true if the parameter is undefined<br/> 1380 * Example: `_isUndefined(val) === true/false` 1381 * 1382 * @param {*} what Value to check 1383 * @return {Boolean} true if undefined and false otherwise 1384 */ 1385function _isUndefined(what) { 1386 return typeof what === 'undefined'; 1387} 1388 1389/** 1390 * Export TraceKit out to another variable<br/> 1391 * Example: `var TK = TraceKit.noConflict()` 1392 * @return {Object} The TraceKit object 1393 * @memberof TraceKit 1394 */ 1395TraceKit.noConflict = function noConflict() { 1396 window.TraceKit = _oldTraceKit; 1397 return TraceKit; 1398}; 1399 1400/** 1401 * Wrap any function in a TraceKit reporter<br/> 1402 * Example: `func = TraceKit.wrap(func);` 1403 * 1404 * @param {Function} func Function to be wrapped 1405 * @return {Function} The wrapped func 1406 * @memberof TraceKit 1407 */ 1408TraceKit.wrap = function traceKitWrapper(func) { 1409 function wrapped() { 1410 try { 1411 return func.apply(this, arguments); 1412 } catch (e) { 1413 TraceKit.report(e); 1414 throw e; 1415 } 1416 } 1417 return wrapped; 1418}; 1419 1420/** 1421 * Cross-browser processing of unhandled exceptions 1422 * 1423 * Syntax: 1424 * ```js 1425 * TraceKit.report.subscribe(function(stackInfo) { ... }) 1426 * TraceKit.report.unsubscribe(function(stackInfo) { ... }) 1427 * TraceKit.report(exception) 1428 * try { ...code... } catch(ex) { TraceKit.report(ex); } 1429 * ``` 1430 * 1431 * Supports: 1432 * - Firefox: full stack trace with line numbers, plus column number 1433 * on top frame; column number is not guaranteed 1434 * - Opera: full stack trace with line and column numbers 1435 * - Chrome: full stack trace with line and column numbers 1436 * - Safari: line and column number for the top frame only; some frames 1437 * may be missing, and column number is not guaranteed 1438 * - IE: line and column number for the top frame only; some frames 1439 * may be missing, and column number is not guaranteed 1440 * 1441 * In theory, TraceKit should work on all of the following versions: 1442 * - IE5.5+ (only 8.0 tested) 1443 * - Firefox 0.9+ (only 3.5+ tested) 1444 * - Opera 7+ (only 10.50 tested; versions 9 and earlier may require 1445 * Exceptions Have Stacktrace to be enabled in opera:config) 1446 * - Safari 3+ (only 4+ tested) 1447 * - Chrome 1+ (only 5+ tested) 1448 * - Konqueror 3.5+ (untested) 1449 * 1450 * Requires TraceKit.computeStackTrace. 1451 * 1452 * Tries to catch all unhandled exceptions and report them to the 1453 * subscribed handlers. Please note that TraceKit.report will rethrow the 1454 * exception. This is REQUIRED in order to get a useful stack trace in IE. 1455 * If the exception does not reach the top of the browser, you will only 1456 * get a stack trace from the point where TraceKit.report was called. 1457 * 1458 * Handlers receive a TraceKit.StackTrace object as described in the 1459 * TraceKit.computeStackTrace docs. 1460 * 1461 * @memberof TraceKit 1462 * @namespace 1463 */ 1464TraceKit.report = (function reportModuleWrapper() { 1465 var handlers = [], 1466 lastArgs = null, 1467 lastException = null, 1468 lastExceptionStack = null; 1469 1470 /** 1471 * Add a crash handler. 1472 * @param {Function} handler 1473 * @memberof TraceKit.report 1474 */ 1475 function subscribe(handler) { 1476 installGlobalHandler(); 1477 handlers.push(handler); 1478 } 1479 1480 /** 1481 * Remove a crash handler. 1482 * @param {Function} handler 1483 * @memberof TraceKit.report 1484 */ 1485 function unsubscribe(handler) { 1486 for (var i = handlers.length - 1; i >= 0; --i) { 1487 if (handlers[i] === handler) { 1488 handlers.splice(i, 1); 1489 } 1490 } 1491 } 1492 1493 /** 1494 * Dispatch stack information to all handlers. 1495 * @param {TraceKit.StackTrace} stack 1496 * @param {boolean} isWindowError Is this a top-level window error? 1497 * @memberof TraceKit.report 1498 * @throws An exception if an error occurs while calling an handler. 1499 */ 1500 function notifyHandlers(stack, isWindowError) { 1501 var exception = null; 1502 if (isWindowError && !TraceKit.collectWindowErrors) { 1503 return; 1504 } 1505 for (var i in handlers) { 1506 if (_has(handlers, i)) { 1507 try { 1508 handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2))); 1509 } catch (inner) { 1510 exception = inner; 1511 } 1512 } 1513 } 1514 1515 if (exception) { 1516 throw exception; 1517 } 1518 } 1519 1520 var _oldOnerrorHandler, _onErrorHandlerInstalled; 1521 1522 /** 1523 * Ensures all global unhandled exceptions are recorded. 1524 * Supported by Gecko and IE. 1525 * @param {string} message Error message. 1526 * @param {string} url URL of script that generated the exception. 1527 * @param {(number|string)} lineNo The line number at which the error occurred. 1528 * @param {(number|string)=} columnNo The column number at which the error occurred. 1529 * @param {Error=} errorObj The actual Error object. 1530 * @memberof TraceKit.report 1531 */ 1532 function traceKitWindowOnError(message, url, lineNo, columnNo, errorObj) { 1533 var stack = null; 1534 1535 if (lastExceptionStack) { 1536 TraceKit.computeStackTrace.augmentStackTraceWithInitialElement(lastExceptionStack, url, lineNo, message); 1537 processLastException(); 1538 } else if (errorObj) { 1539 stack = TraceKit.computeStackTrace(errorObj); 1540 notifyHandlers(stack, true); 1541 } else { 1542 var location = { 1543 'url': url, 1544 'line': lineNo, 1545 'column': columnNo 1546 }; 1547 location.func = TraceKit.computeStackTrace.guessFunctionName(location.url, location.line); 1548 location.context = TraceKit.computeStackTrace.gatherContext(location.url, location.line); 1549 stack = { 1550 'mode': 'onerror', 1551 'message': message, 1552 'stack': [location] 1553 }; 1554 1555 notifyHandlers(stack, true); 1556 } 1557 1558 if (_oldOnerrorHandler) { 1559 return _oldOnerrorHandler.apply(this, arguments); 1560 } 1561 1562 return false;
1563 } 1564 1565 /** 1566 * Install a global onerror handler 1567 * @memberof TraceKit.report 1568 */ 1569 function installGlobalHandler () { 1570 if (_onErrorHandlerInstalled === true) { 1571 return; 1572 } 1573 _oldOnerrorHandler = window.onerror; 1574 window.onerror = traceKitWindowOnError; 1575 _onErrorHandlerInstalled = true; 1576 } 1577 1578 /** 1579 * Process the most recent exception 1580 * @memberof TraceKit.report 1581 */ 1582 function processLastException() { 1583 var _lastExceptionStack = lastExceptionStack, 1584 _lastArgs = lastArgs; 1585 lastArgs = null; 1586 lastExceptionStack = null; 1587 lastException = null; 1588 notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs)); 1589 } 1590 1591 /** 1592 * Reports an unhandled Error to TraceKit. 1593 * @param {Error} ex 1594 * @memberof TraceKit.report 1595 * @throws An exception if an incomplete stack trace is detected (old IE browsers). 1596 */ 1597 function report(ex) { 1598 if (lastExceptionStack) { 1599 if (lastException === ex) { 1600 return; // already caught by an inner catch block, ignore 1601 } else { 1602 processLastException(); 1603 } 1604 } 1605 1606 var stack = TraceKit.computeStackTrace(ex); 1607 lastExceptionStack = stack; 1608 lastException = ex; 1609 lastArgs = _slice.call(arguments, 1); 1610 1611 // If the stack trace is incomplete, wait for 2 seconds for 1612 // slow slow IE to see if onerror occurs or not before reporting 1613 // this exception; otherwise, we will end up with an incomplete 1614 // stack trace 1615 window.setTimeout(function () { 1616 if (lastException === ex) { 1617 processLastException(); 1618 } 1619 }, (stack.incomplete ? 2000 : 0)); 1620 1621 throw ex; // re-throw to propagate to the top level (and cause window.onerror) 1622 } 1623 1624 report.subscribe = subscribe; 1625 report.unsubscribe = unsubscribe; 1626 return report; 1627}()); 1628 1629/** 1630 * An object representing a single stack frame. 1631 * @typedef {Object} StackFrame 1632 * @property {string} url The JavaScript or HTML file URL. 1633 * @property {string} func The function name, or empty for anonymous functions (if guessing did not work). 1634 * @property {string[]?} args The arguments passed to the function, if known. 1635 * @property {number=} line The line number, if known. 1636 * @property {number=} column The column number, if known. 1637 * @property {string[]} context An array of source code lines; the middle element corresponds to the correct line#. 1638 * @memberof TraceKit 1639 */ 1640 1641/** 1642 * An object representing a JavaScript stack trace. 1643 * @typedef {Object} StackTrace 1644 * @property {string} name The name of the thrown exception. 1645 * @property {string} message The exception error message. 1646 * @property {TraceKit.StackFrame[]} stack An array of stack frames. 1647 * @property {string} mode 'stack', 'stacktrace', 'multiline', 'callers', 'onerror', or 'failed' -- method used to collect the stack trace. 1648 * @memberof TraceKit 1649 */ 1650 1651/** 1652 * TraceKit.computeStackTrace: cross-browser stack traces in JavaScript 1653 * 1654 * Syntax: 1655 * ```js 1656 * s = TraceKit.computeStackTrace.ofCaller([depth]) 1657 * s = TraceKit.computeStackTrace(exception) // consider using TraceKit.report instead (see below) 1658 * ``` 1659 * 1660 * Supports: 1661 * - Firefox: full stack trace with line numbers and unreliable column 1662 * number on top frame 1663 * - Opera 10: full stack trace with line and column numbers 1664 * - Opera 9-: full stack trace with line numbers 1665 * - Chrome: full stack trace with line and column numbers 1666 * - Safari: line and column number for the topmost stacktrace element 1667 * only 1668 * - IE: no line numbers whatsoever 1669 * 1670 * Tries to guess names of anonymous functions by looking for assignments 1671 * in the source code. In IE and Safari, we have to guess source file names 1672 * by searching for function bodies inside all page scripts. This will not 1673 * work for scripts that are loaded cross-domain. 1674 * Here be dragons: some function names may be guessed incorrectly, and 1675 * duplicate functions may be mismatched. 1676 * 1677 * TraceKit.computeStackTrace should only be used for tracing purposes. 1678 * Logging of unhandled exceptions should be done with TraceKit.report, 1679 * which builds on top of TraceKit.computeStackTrace and provides better 1680 * IE support by utilizing the window.onerror event to retrieve information 1681 * about the top of the stack. 1682 *
1683 * Note: In IE and Safari, no stack trace is recorded on the Error object, 1684 * so computeStackTrace instead walks its *own* chain of callers. 1685 * This means that: 1686 * * in Safari, some methods may be missing from the stack trace; 1687 * * in IE, the topmost function in the stack trace will always be the 1688 * caller of computeStackTrace. 1689 * 1690 * This is okay for tracing (because you are likely to be calling 1691 * computeStackTrace from the function you want to be the topmost element 1692 * of the stack trace anyway), but not okay for logging unhandled 1693 * exceptions (because your catch block will likely be far away from the 1694 * inner function that actually caused the exception). 1695 * 1696 * Tracing example: 1697 * ```js 1698 * function trace(message) { 1699 * var stackInfo = TraceKit.computeStackTrace.ofCaller(); 1700 * var data = message + "\n"; 1701 * for(var i in stackInfo.stack) { 1702 * var item = stackInfo.stack[i]; 1703 * data += (item.func || '[anonymous]') + "() in " + item.url + ":" + (item.line || '0') + "\n"; 1704 * } 1705 * if (window.console) 1706 * console.info(data); 1707 * else 1708 * alert(data); 1709 * } 1710 * ``` 1711 * @memberof TraceKit 1712 * @namespace 1713 */ 1714TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 1715 var debug = false, 1716 sourceCache = {}; 1717 1718 /** 1719 * Attempts to retrieve source code via XMLHttpRequest, which is used 1720 * to look up anonymous function names. 1721 * @param {string} url URL of source code. 1722 * @return {string} Source contents. 1723 * @memberof TraceKit.computeStackTrace 1724 */ 1725 function loadSource(url) { 1726 if (!TraceKit.remoteFetching) { //Only attempt request if remoteFetching is on. 1727 return ''; 1728 } 1729 try { 1730 var getXHR = function() { 1731 try { 1732 return new window.XMLHttpRequest(); 1733 } catch (e) { 1734 // explicitly bubble up the exception if not found 1735 return new window.ActiveXObject('Microsoft.XMLHTTP'); 1736 } 1737 }; 1738 1739 var request = getXHR(); 1740 request.open('GET', url, false); 1741 request.send(''); 1742 return request.responseText; 1743 } catch (e) { 1744 return ''; 1745 } 1746 } 1747 1748 /** 1749 * Retrieves source code from the source code cache. 1750 * @param {string} url URL of source code. 1751 * @return {Array.<string>} Source contents. 1752 * @memberof TraceKit.computeStackTrace 1753 */ 1754 function getSource(url) { 1755 if (typeof url !== 'string') { 1756 return []; 1757 } 1758 1759 if (!_has(sourceCache, url)) { 1760 // URL needs to be able to fetched within the acceptable domain. Otherwise, 1761 // cross-domain errors will be triggered. 1762 /* 1763 Regex matches: 1764 0 - Full Url 1765 1 - Protocol 1766 2 - Domain 1767 3 - Port (Useful for internal applications) 1768 4 - Path 1769 */ 1770 var source = ''; 1771 var domain = ''; 1772 try { domain = window.document.domain; } catch (e) { } 1773 var match = /(.*)\:\/\/([^:\/]+)([:\d]*)\/{0,1}([\s\S]*)/.exec(url); 1774 if (match && match[2] === domain) { 1775 source = loadSource(url); 1776 } 1777 sourceCache[url] = source ? source.split('\n') : []; 1778 } 1779 1780 return sourceCache[url]; 1781 } 1782 1783 /** 1784 * Tries to use an externally loaded copy of source code to determine 1785 * the name of a function by looking at the name of the variable it was 1786 * assigned to, if any. 1787 * @param {string} url URL of source code. 1788 * @param {(string|number)} lineNo Line number in source code. 1789 * @return {string} The function name, if discoverable. 1790 * @memberof TraceKit.computeStackTrace 1791 */ 1792 function guessFunctionName(url, lineNo) { 1793 var reFunctionArgNames = /function ([^(]*)\(([^)]*)\)/, 1794 reGuessFunction = /['"]?([0-9A-Za-z$_]+)['"]?\s*[:=]\s*(function|eval|new Function)/, 1795 line = '', 1796 maxLines = 10, 1797 source = getSource(url), 1798 m; 1799 1800 if (!source.length) { 1801 return UNKNOWN_FUNCTION; 1802 } 1803 1804 // Walk backwards from the first line in the function until we find the line which 1805 // matches the pattern above, which is the function definition 1806 for (var i = 0; i < maxLines; ++i) { 1807 line = source[lineNo - i] + line; 1808 1809 if (!_isUndefined(line)) { 1810 if ((m = reGuessFunction.exec(line))) { 1811 return m[1]; 1812 } else if ((m = reFunctionArgNames.exec(line))) { 1813 return m[1]; 1814 } 1815 } 1816 } 1817 1818 return UNKNOWN_FUNCTION; 1819 } 1820 1821 /** 1822 * Retrieves the surrounding lines from where an exception occurred. 1823 * @param {string} url URL of source code. 1824 * @param {(string|number)} line Line number in source code to centre 1825 * around for context. 1826 * @return {?Array.<string>} Lines of source code. 1827 * @memberof TraceKit.computeStackTrace 1828 */ 1829 function gatherContext(url, line) { 1830 var source = getSource(url); 1831 1832 if (!source.length) { 1833 return null; 1834 } 1835 1836 var context = [], 1837 // linesBefore & linesAfter are inclusive with the offending line. 1838 // if linesOfContext is even, there will be one extra line 1839 // *before* the offending line. 1840 linesBefore = Math.floor(TraceKit.linesOfContext / 2), 1841 // Add one extra line if linesOfContext is odd 1842 linesAfter = linesBefore + (TraceKit.linesOfContext
vendor: 6,747 bytes, lines 1842-2039
1842% 2), 1843 start = Math.max(0, line - linesBefore - 1), 1844 end = Math.min(source.length, line + linesAfter - 1); 1845 1846 line -= 1; // convert to 0-based index 1847 1848 for (var i = start; i < end; ++i) { 1849 if (!_isUndefined(source[i])) { 1850 context.push(source[i]); 1851 } 1852 } 1853 1854 return context.length > 0 ? context : null; 1855 } 1856 1857 /** 1858 * Escapes special characters, except for whitespace, in a string to be 1859 * used inside a regular expression as a string literal. 1860 * @param {string} text The string. 1861 * @return {string} The escaped string literal. 1862 * @memberof TraceKit.computeStackTrace 1863 */ 1864 function escapeRegExp(text) { 1865 return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&'); 1866 } 1867 1868 /** 1869 * Escapes special characters in a string to be used inside a regular 1870 * expression as a string literal. Also ensures that HTML entities will 1871 * be matched the same as their literal friends. 1872 * @param {string} body The string. 1873 * @return {string} The escaped string. 1874 * @memberof TraceKit.computeStackTrace 1875 */ 1876 function escapeCodeAsRegExpForMatchingInsideHTML(body) { 1877 return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('"', '(?:"|")').replace(/\s+/g, '\\s+'); 1878 } 1879 1880 /** 1881 * Determines where a code fragment occurs in the source code. 1882 * @param {RegExp} re The function definition. 1883 * @param {Array.<string>} urls A list of URLs to search. 1884 * @return {?Object.<string, (string|number)>} An object containing 1885 * the url, line, and column number of the defined function. 1886 * @memberof TraceKit.computeStackTrace 1887 */ 1888 function findSourceInUrls(re, urls) { 1889 var source, m; 1890 for (var i = 0, j = urls.length; i < j; ++i) { 1891 // console.log('searching', urls[i]); 1892 if ((source = getSource(urls[i])).length) { 1893 source = source.join('\n'); 1894 if ((m = re.exec(source))) { 1895 // console.log('Found function in ' + urls[i]); 1896 1897 return { 1898 'url': urls[i], 1899 'line': source.substring(0, m.index).split('\n').length, 1900 'column': m.index - source.lastIndexOf('\n', m.index) - 1 1901 }; 1902 } 1903 } 1904 } 1905 1906 // console.log('no match'); 1907 1908 return null; 1909 } 1910 1911 /** 1912 * Determines at which column a code fragment occurs on a line of the 1913 * source code. 1914 * @param {string} fragment The code fragment. 1915 * @param {string} url The URL to search. 1916 * @param {(string|number)} line The line number to examine. 1917 * @return {?number} The column number. 1918 * @memberof TraceKit.computeStackTrace 1919 */ 1920 function findSourceInLine(fragment, url, line) { 1921 var source = getSource(url), 1922 re = new RegExp('\\b' + escapeRegExp(fragment) + '\\b'), 1923 m; 1924 1925 line -= 1; 1926 1927 if (source && source.length > line && (m = re.exec(source[line]))) { 1928 return m.index; 1929 } 1930 1931 return null; 1932 } 1933 1934 /** 1935 * Determines where a function was defined within the source code. 1936 * @param {(Function|string)} func A function reference or serialized 1937 * function definition. 1938 * @return {?Object.<string, (string|number)>} An object containing 1939 * the url, line, and column number of the defined function. 1940 * @memberof TraceKit.computeStackTrace 1941 */ 1942 function findSourceByFunctionBody(func) { 1943 if (_isUndefined(window && window.document)) { 1944 return; 1945 } 1946 1947 var urls = [window.location.href], 1948 scripts = window.document.getElementsByTagName('script'), 1949 body, 1950 code = '' + func, 1951 codeRE = /^function(?:\s+([\w$]+))?\s*\(([\w\s,]*)\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/, 1952 eventRE = /^function on([\w$]+)\s*\(event\)\s*\{\s*(\S[\s\S]*\S)\s*\}\s*$/, 1953 re, 1954 parts, 1955 result; 1956 1957 for (var i = 0; i < scripts.length; ++i) { 1958 var script = scripts[i]; 1959 if (script.src) { 1960 urls.push(script.src); 1961 } 1962 } 1963 1964 if (!(parts = codeRE.exec(code))) { 1965 re = new RegExp(escapeRegExp(code).replace(/\s+/g, '\\s+')); 1966 } 1967 1968 // not sure if this is really necessary, but I don’t have a test 1969 // corpus large enough to confirm that and it was in the original. 1970 else { 1971 var name = parts[1] ? '\\s+' + parts[1] : '', 1972 args = parts[2].split(',').join('\\s*,\\s*'); 1973 1974 body = escapeRegExp(parts[3]).replace(/;$/, ';?'); // semicolon is inserted if the function ends with a comment.replace(/\s+/g, '\\s+'); 1975 re = new RegExp('function' + name + '\\s*\\(\\s*' + args + '\\s*\\)\\s*{\\s*' + body + '\\s*}'); 1976 } 1977 1978 // look for a normal function definition 1979 if ((result = findSourceInUrls(re, urls))) { 1980 return result; 1981 } 1982 1983 // look for an old-school event handler function 1984 if ((parts = eventRE.exec(code))) { 1985 var event = parts[1]; 1986 body = escapeCodeAsRegExpForMatchingInsideHTML(parts[2]); 1987 1988 // look for a function defined in HTML as an onXXX handler 1989 re = new RegExp('on' + event + '=[\\\'"]\\s*' + body + '\\s*[\\\'"]', 'i'); 1990 1991 if ((result = findSourceInUrls(re, urls[0]))) { 1992 return result; 1993 } 1994 1995 // look for ??? 1996 re = new RegExp(body); 1997 1998 if ((result = findSourceInUrls(re, urls))) { 1999 return result; 2000 } 2001 } 2002 2003 return null; 2004 } 2005 2006 // Contents of Exception in various browsers. 2007 // 2008 // SAFARI: 2009 // ex.message = Can't find variable: qq 2010 // ex.line = 59 2011 // ex.sourceId = 580238192 2012 // ex.sourceURL = http://... 2013 // ex.expressionBeginOffset = 96 2014 // ex.expressionCaretOffset = 98 2015 // ex.expressionEndOffset = 98 2016 // ex.name = ReferenceError 2017 // 2018 // FIREFOX: 2019 // ex.message = qq is not defined 2020 // ex.fileName = http://... 2021 // ex.lineNumber = 59 2022 // ex.columnNumber = 69 2023 // ex.stack = ...stack trace... (see the example below) 2024 // ex.name = ReferenceError 2025 // 2026 // CHROME: 2027 // ex.message = qq is not defined 2028 // ex.name = ReferenceError 2029 // ex.type = not_defined 2030 // ex.arguments = ['aa'] 2031 // ex.stack = ...stack trace... 2032 // 2033 // INTERNET EXPLORER: 2034 // ex.message = ... 2035 // ex.name = ReferenceError 2036 // 2037 // OPERA: 2038 // ex.message = ...message... (see the example below) 2039 // ex.name = ReferenceError
2040 // ex.opera#sourceloc = 11 (pretty much useless, duplicates the info in ex.message) 2041 // ex.stacktrace = n/a; see 'opera:config#UserPrefs|Exceptions Have Stacktrace' 2042 2043 /** 2044 * Computes stack trace information from the stack property. 2045 * Chrome and Gecko use this property. 2046 * @param {Error} ex 2047 * @return {?TraceKit.StackTrace} Stack trace information. 2048 * @memberof TraceKit.computeStackTrace 2049 */ 2050 function computeStackTraceFromStackProp(ex) { 2051 if (!ex.stack) { 2052 return null; 2053 } 2054 2055 var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|webpack|eval).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, 2056 gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i, 2057 winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, 2058 lines = ex.stack.split('\n'), 2059 stack = [], 2060 parts, 2061 element, 2062 reference = /^(.*) is undefined$/.exec(ex.message); 2063 2064 for (var i = 0, j = lines.length; i < j; ++i) { 2065 if ((parts = chrome.exec(lines[i]))) { 2066 var isNative = parts[2] && parts[2].indexOf('native') !== -1; 2067 element = { 2068 'url': !isNative ? parts[2] : null, 2069 'func': parts[1] || UNKNOWN_FUNCTION, 2070 'args': isNative ? [parts[2]] : [], 2071 'line': parts[3] ? +parts[3] : null, 2072 'column': parts[4] ? +parts[4] : null 2073 }; 2074 } else if ( parts = winjs.exec(lines[i]) ) { 2075 element = { 2076 'url': parts[2], 2077 'func': parts[1] || UNKNOWN_FUNCTION, 2078 'args': [], 2079 'line': +parts[3], 2080 'column': parts[4] ? +parts[4] : null 2081 }; 2082 } else if ((parts = gecko.exec(lines[i]))) { 2083 element = { 2084 'url': parts[3], 2085 'func': parts[1] || UNKNOWN_FUNCTION, 2086 'args': parts[2] ? parts[2].split(',') : [], 2087 'line': parts[4] ? +parts[4] : null, 2088 'column': parts[5] ? +parts[5] : null 2089 }; 2090 } else { 2091 continue; 2092 } 2093 2094 if (!element.func && element.line) { 2095 element.func = guessFunctionName(element.url, element.line); 2096 } 2097 2098 if (element.line) { 2099 element.context = gatherContext(element.url, element.line); 2100 } 2101 2102 stack.push(element); 2103 } 2104 2105 if (!stack.length) { 2106 return null; 2107 } 2108 2109 if (stack[0] && stack[0].line && !stack[0].column && reference) { 2110 stack[0].column = findSourceInLine(reference[1], stack[0].url, stack[0].line); 2111 } else if (!stack[0].column && !_isUndefined(ex.columnNumber)) { 2112 // FireFox uses this awesome columnNumber property for its top frame 2113 // Also note, Firefox's column number is 0-based and everything else expects 1-based, 2114 // so adding 1 2115 stack[0].column = ex.columnNumber + 1; 2116 } 2117 2118 return { 2119 'mode': 'stack', 2120 'name': ex.name, 2121 'message': ex.message, 2122 'stack': stack 2123 }; 2124 } 2125 2126 /** 2127 * Computes stack trace information from the stacktrace property. 2128 * Opera 10+ uses this property. 2129 * @param {Error} ex 2130 * @return {?TraceKit.StackTrace} Stack trace information. 2131 * @memberof TraceKit.computeStackTrace 2132 */ 2133 function computeStackTraceFromStacktraceProp(ex) { 2134 // Access and store the stacktrace property before doing ANYTHING 2135 // else to it because Opera is not very good at providing it 2136 // reliably in other circumstances. 2137 var stacktrace = ex.stacktrace; 2138 if (!stacktrace) { 2139 return; 2140 } 2141 2142 var opera10Regex = / line (\d+).*script (?:in )?(\S+)(?:: in function (\S+))?$/i, 2143 opera11Regex = / line (\d+), column (\d+)\s*(?:in (?:<anonymous function: ([^>]+)>|([^\)]+))\((.*)\))? in (.*):\s*$/i, 2144 lines = stacktrace.split('\n'), 2145 stack = [], 2146 parts; 2147 2148 for (var line = 0; line < lines.length; line += 2) { 2149 var element = null; 2150 if ((parts = opera10Regex.exec(lines[line]))) { 2151 element = { 2152 'url': parts[2], 2153 'line': +parts[1], 2154 'column': null, 2155 'func': parts[3], 2156 'args':[] 2157 }; 2158 } else if ((parts = opera11Regex.exec(lines[line]))) { 2159 element = { 2160 'url': parts[6], 2161 'line': +parts[1], 2162 'column': +parts[2], 2163 'func': parts[3] || parts[4], 2164 'args': parts[5] ? parts[5].split(',') : [] 2165 }; 2166 } 2167 2168 if (element) { 2169 if (!element.func && element.line) { 2170 element.func = guessFunctionName(element.url, element.line); 2171 } 2172 if (element.line) { 2173 try { 2174 element.context = gatherContext(element.url, element.line); 2175 } catch (exc) {} 2176 } 2177 2178 if (!element.context) { 2179 element.context = [lines[line + 1]]; 2180 } 2181 2182 stack.push(element); 2183 } 2184 } 2185 2186 if (!stack.length) { 2187 return null; 2188 } 2189 2190 return { 2191 'mode': 'stacktrace', 2192 'name': ex.name, 2193 'message': ex.message, 2194 'stack': stack 2195 }; 2196 } 2197 2198 /** 2199 * NOT TESTED. 2200 * Computes stack trace information from an error message that includes 2201 * the stack trace. 2202 * Opera 9 and earlier use this method if the option to show stack 2203 * traces is turned on in opera:config. 2204 * @param {Error} ex 2205 * @return {?TraceKit.StackTrace} Stack information. 2206 * @memberof TraceKit.computeStackTrace 2207 */ 2208 function computeStackTraceFromOperaMultiLineMessage(ex) { 2209 // TODO: Clean this function up 2210 // Opera includes a stack trace into the exception message. An example is: 2211 // 2212 // Statement on line 3: Undefined variable: undefinedFunc 2213 // Backtrace: 2214 // Line 3 of linked script file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.js: In function zzz 2215 // undefinedFunc(a); 2216 // Line 7 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function yyy 2217 // zzz(x, y, z); 2218 // Line 3 of inline#1 script in file://localhost/Users/andreyvit/Projects/TraceKit/javascript-client/sample.html: In function xxx 2219 // yyy(a, a, a); 2220 // Line 1 of function script 2221 // try { xxx('hi'); return false; } catch(ex) { TraceKit.report(ex); } 2222 // ... 2223 2224 var lines = ex.message.split('\n'); 2225 if (lines.length < 4) { 2226 return null; 2227 } 2228 2229 var lineRE1 = /^\s*Line (\d+) of linked script ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, 2230 lineRE2 = /^\s*Line (\d+) of inline#(\d+) script in ((?:file|https?|blob)\S+)(?:: in function (\S+))?\s*$/i, 2231 lineRE3 = /^\s*Line (\d+) of function script\s*$/i, 2232 stack = [], 2233 scripts = (window && window.document && window.document.getElementsByTagName('script')), 2234 inlineScriptBlocks = [], 2235 parts; 2236 2237 for (var s in scripts) { 2238 if (_has(scripts, s) && !scripts[s].src) { 2239 inlineScriptBlocks.push(scripts[s]); 2240 } 2241 } 2242 2243 for (var line = 2; line < lines.length; line += 2) { 2244 var item = null; 2245 if ((parts = lineRE1.exec(lines[line]))) { 2246 item = { 2247 'url': parts[2], 2248 'func': parts[3], 2249 'args': [], 2250 'line': +parts[1], 2251 'column': null 2252 }; 2253 } else if ((parts = lineRE2.exec(lines[line]))) { 2254 item = { 2255 'url': parts[3], 2256 'func': parts[4], 2257 'args': [], 2258 'line': +parts[1], 2259 'column': null // TODO: Check to see if inline#1 (+parts[2]) points to the script number or column number. 2260 }; 2261 var relativeLine = (+parts[1]); // relative to the start of the <SCRIPT> block 2262 var script = inlineScriptBlocks[parts[2] - 1]; 2263 if (script) { 2264 var source = getSource(item.url); 2265 if (source) { 2266 source = source.join('\n'); 2267 var pos = source.indexOf(script.innerText); 2268 if (pos >= 0) { 2269 item.line = relativeLine + source.substring(0, pos).split('\n').length; 2270 } 2271 } 2272 } 2273 } else if ((parts = lineRE3.exec(lines[line]))) { 2274 var url = window.location.href.replace(/#.*$/, ''); 2275 var re = new RegExp(escapeCodeAsRegExpForMatchingInsideHTML(lines[line + 1])); 2276 var src = findSourceInUrls(re, [url]); 2277 item = { 2278 'url': url, 2279 'func': '', 2280 'args': [], 2281 'line': src ? src.line : parts[1], 2282 'column': null 2283 }; 2284 } 2285 2286 if (item) { 2287 if (!item.func) { 2288 item.func = guessFunctionName(item.url, item.line); 2289 } 2290 var context = gatherContext(item.url, item.line); 2291 var midline = (context ? context[Math.floor(context.length / 2)] : null); 2292 if (context && midline.replace(/^\s*/, '') === lines[line + 1].replace(/^\s*/, '')) { 2293 item.context = context; 2294 } else { 2295 // if (context) alert("Context mismatch. Correct midline:\n" + lines[i+1] + "\n\nMidline:\n" + midline + "\n\nContext:\n" + context.join("\n") + "\n\nURL:\n" + item.url); 2296 item.context = [lines[line + 1]]; 2297 } 2298 stack.push(item); 2299 } 2300 } 2301 if (!stack.length) { 2302 return null; // could not parse multiline exception message as Opera stack trace 2303 } 2304 2305 return { 2306 'mode': 'multiline', 2307 'name': ex.name, 2308 'message': lines[0], 2309 'stack': stack 2310 }; 2311 } 2312 2313 /** 2314 * Adds information about the first frame to incomplete stack traces. 2315 * Safari and IE require this to get complete data on the first frame. 2316 * @param {TraceKit.StackTrace} stackInfo Stack trace information from 2317 * one of the compute* methods. 2318 * @param {string} url The URL of the script that caused an error. 2319 * @param {(number|string)} lineNo The line number of the script that 2320 * caused an error. 2321 * @param {string=} message The error generated by the browser, which 2322 * hopefully contains the name of the object that caused the error. 2323 * @return {boolean} Whether or not the stack information was 2324 * augmented. 2325 * @memberof TraceKit.computeStackTrace 2326 */ 2327 function augmentStackTraceWithInitialElement(stackInfo, url, lineNo, message) { 2328 var initial = { 2329 'url': url, 2330 'line': lineNo 2331 }; 2332 2333 if (initial.url && initial.line) { 2334 stackInfo.incomplete = false;
2335 2336 if (!initial.func) { 2337 initial.func = guessFunctionName(initial.url, initial.line); 2338 } 2339 2340 if (!initial.context) { 2341 initial.context = gatherContext(initial.url, initial.line); 2342 } 2343 2344 var reference = / '([^']+)' /.exec(message); 2345 if (reference) { 2346 initial.column = findSourceInLine(reference[1], initial.url, initial.line); 2347 } 2348 2349 if (stackInfo.stack.length > 0) { 2350 if (stackInfo.stack[0].url === initial.url) { 2351 if (stackInfo.stack[0].line === initial.line) { 2352 return false; // already in stack trace 2353 } else if (!stackInfo.stack[0].line && stackInfo.stack[0].func === initial.func) { 2354 stackInfo.stack[0].line = initial.line; 2355 stackInfo.stack[0].context = initial.context; 2356 return false; 2357 } 2358 } 2359 } 2360 2361 stackInfo.stack.unshift(initial); 2362 stackInfo.partial = true; 2363 return true; 2364 } else { 2365 stackInfo.incomplete = true; 2366 } 2367 2368 return false; 2369 } 2370 2371 /** 2372 * Computes stack trace information by walking the arguments.caller 2373 * chain at the time the exception occurred. This will cause earlier 2374 * frames to be missed but is the only way to get any stack trace in 2375 * Safari and IE. The top frame is restored by 2376 * {@link augmentStackTraceWithInitialElement}. 2377 * @param {Error} ex 2378 * @return {TraceKit.StackTrace=} Stack trace information. 2379 * @memberof TraceKit.computeStackTrace 2380 */ 2381 function computeStackTraceByWalkingCallerChain(ex, depth) { 2382 var functionName = /function\s+([_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*)?\s*\(/i, 2383 stack = [], 2384 funcs = {}, 2385 recursion = false, 2386 parts, 2387 item, 2388 source; 2389 2390 for (var curr = computeStackTraceByWalkingCallerChain.caller; curr && !recursion; curr = curr.caller) { 2391 if (curr === computeStackTrace || curr === TraceKit.report) { 2392 // console.log('skipping internal function'); 2393 continue; 2394 } 2395 2396 item = { 2397 'url': null, 2398 'func': UNKNOWN_FUNCTION, 2399 'args': [], 2400 'line': null, 2401 'column': null 2402 }; 2403 2404 if (curr.name) { 2405 item.func = curr.name; 2406 } else if ((parts = functionName.exec(curr.toString()))) { 2407 item.func = parts[1]; 2408 } 2409 2410 if (typeof item.func === 'undefined') { 2411 try { 2412 item.func = parts.input.substring(0, parts.input.indexOf('{')); 2413 } catch (e) { } 2414 } 2415 2416 if ((source = findSourceByFunctionBody(curr))) { 2417 item.url = source.url; 2418 item.line = source.line; 2419 2420 if (item.func === UNKNOWN_FUNCTION) { 2421 item.func = guessFunctionName(item.url, item.line); 2422 } 2423 2424 var reference = / '([^']+)' /.exec(ex.message || ex.description); 2425 if (reference) { 2426 item.column = findSourceInLine(reference[1], source.url, source.line); 2427 } 2428 } 2429 2430 if (funcs['' + curr]) { 2431 recursion = true; 2432 }else{ 2433 funcs['' + curr] = true; 2434 } 2435 2436 stack.push(item); 2437 } 2438 2439 if (depth) { 2440 // console.log('depth is ' + depth); 2441 // console.log('stack is ' + stack.length); 2442 stack.splice(0, depth); 2443 } 2444 2445 var result = { 2446 'mode': 'callers', 2447 'name': ex.name, 2448 'message': ex.message, 2449 'stack': stack 2450 }; 2451 augmentStackTraceWithInitialElement(result, ex.sourceURL || ex.fileName, ex.line || ex.lineNumber, ex.message || ex.description); 2452 return result; 2453 } 2454 2455 /** 2456 * Computes a stack trace for an exception. 2457 * @param {Error} ex 2458 * @param {(string|number)=} depth 2459 * @memberof TraceKit.computeStackTrace 2460 */ 2461 function computeStackTrace(ex, depth) { 2462 var stack = null; 2463 depth = (depth == null ? 0 : +depth); 2464 2465 try { 2466 // This must be tried first because Opera 10 *destroys* 2467 // its stacktrace property if you try to access the stack 2468 // property first!! 2469 stack = computeStackTraceFromStacktraceProp(ex); 2470 if (stack) { 2471 return stack; 2472 } 2473 } catch (e) { 2474 if (debug) { 2475 throw e; 2476 } 2477 } 2478 2479 try { 2480 stack = computeStackTraceFromStackProp(ex); 2481 if (stack) { 2482 return stack; 2483 } 2484 } catch (e) { 2485 if (debug) { 2486 throw e; 2487 } 2488 } 2489 2490 try { 2491 stack = computeStackTraceFromOperaMultiLineMessage(ex); 2492 if (stack) { 2493 return stack; 2494 } 2495 } catch (e) { 2496 if (debug) { 2497 throw e; 2498 } 2499 } 2500 2501 try { 2502 stack = computeStackTraceByWalkingCallerChain(ex, depth + 1); 2503 if (stack) { 2504 return stack; 2505 } 2506 } catch (e) { 2507 if (debug) { 2508 throw e; 2509 } 2510 } 2511 2512 return { 2513 'mode': 'failed' 2514 }; 2515 } 2516 2517 /** 2518 * Logs a stacktrace starting from the previous call and working down. 2519 * @param {(number|string)=} depth How many frames deep to trace. 2520 * @return {TraceKit.StackTrace} Stack trace information. 2521 * @memberof TraceKit.computeStackTrace 2522 */ 2523 function computeStackTraceOfCaller(depth) { 2524 depth = (depth == null ? 0 : +depth) + 1; // "+ 1" because "ofCaller" should drop one frame 2525 try { 2526 throw new Error(); 2527 } catch (ex) { 2528 return computeStackTrace(ex, depth + 1); 2529 } 2530 } 2531 2532 computeStackTrace.augmentStackTraceWithInitialElement = augmentStackTraceWithInitialElement; 2533 computeStackTrace.guessFunctionName = guessFunctionName; 2534 computeStackTrace.gatherContext = gatherContext; 2535 computeStackTrace.ofCaller = computeStackTraceOfCaller; 2536 computeStackTrace.getSource = getSource; 2537 2538 return computeStackTrace; 2539}()); 2540 2541/** 2542 * Extends support for global error handling for asynchronous browser 2543 * functions. Adopted from Closure Library's errorhandler.js 2544 * @memberof TraceKit 2545 */ 2546TraceKit.extendToAsynchronousCallbacks = function () { 2547 var _helper = function _helper(fnName) { 2548 var originalFn = window[fnName]; 2549 window[fnName] = function traceKitAsyncExtension() { 2550 // Make a copy of the arguments 2551 var args = _slice.call(arguments); 2552 var originalCallback = args[0]; 2553 if (typeof (originalCallback) === 'function') { 2554 args[0] = TraceKit.wrap(originalCallback); 2555 } 2556 // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it 2557 // also only supports 2 argument and doesn't care what "this" is, so we 2558 // can just call the original function directly. 2559 if (originalFn.apply) { 2560 return originalFn.apply(this, args); 2561 } else { 2562 return originalFn(args[0], args[1]); 2563 } 2564 }; 2565 }; 2566 2567 _helper('setTimeout'); 2568 _helper('setInterval'); 2569}; 2570 2571//Default options: 2572if (!TraceKit.remoteFetching) { 2573 TraceKit.remoteFetching = true; 2574} 2575if (!TraceKit.collectWindowErrors) { 2576 TraceKit.collectWindowErrors = true; 2577} 2578if (!TraceKit.linesOfContext || TraceKit.linesOfContext < 1) { 2579 // 5 lines before, the offending line, 5 lines after 2580 TraceKit.linesOfContext = 11; 2581} 2582 2583// UMD export 2584if (typeof module !== 'undefined' && module.exports && window.module !== module) { 2585 module.exports = TraceKit; 2586} else if (typeof define === 'function' && define.amd) { 2587 define('TraceKit', [], TraceKit); 2588} else { 2589 window.TraceKit = TraceKit; 2590} 2591 2592}(typeof window !== 'undefined' ? window : global)); 2593; 2594 2595/* vim: set expandtab sw=4 ts=4 sts=4: */ 2596/** 2597 * general function, usually for data manipulation pages 2598 * 2599 */ 2600 2601var ErrorReport = { 2602 /** 2603 * @var object stores the last exception info 2604 */ 2605 _last_exception: null, 2606 /** 2607 * handles thrown error exceptions based on user preferences 2608 * 2609 * @return void 2610 */ 2611 error_handler: function (exception) { 2612 if (exception.name === null || typeof(exception.name) == "undefined") { 2613 exception.name = ErrorReport._extractExceptionName(exception); 2614 } 2615 ErrorReport._last_exception = exception; 2616 $.get("error_report.php", { 2617 ajax_request: true, 2618 server: PMA_commonParams.get('server'), 2619 token: PMA_commonParams.get('token'), 2620 get_settings: true, 2621 exception_type: 'js' 2622 }, function (data) { 2623 if (data.success !== true) { 2624 PMA_ajaxShowMessage(data.error, false); 2625 return; 2626 } 2627 if (data.report_setting == "ask") { 2628 ErrorReport._showErrorNotification(); 2629 } else if (data.report_setting == "always") { 2630 report_data = ErrorReport._get_report_data(exception); 2631 post_data = $.extend(report_data, { 2632 send_error_report: true, 2633 automatic: true 2634 }); 2635 $.post("error_report.php", post_data, function (data) { 2636 if (data.success === false) { 2637 //in the case of an error, show the error message returned. 2638 PMA_ajaxShowMessage(data.error, false); 2639 } else { 2640 PMA_ajaxShowMessage(data.message, false); 2641 } 2642 }); 2643 } 2644 }); 2645 }, 2646 /** 2647 * Shows the modal dialog previewing the report 2648 * 2649 * @param exception object error report info 2650 * 2651 * @return void 2652 */ 2653 _showReportDialog: function (exception) { 2654 var report_data = ErrorReport._get_report_data(exception); 2655 2656 /*Remove the hidden dialogs if there are*/ 2657 if ($('#error_report_dialog').length !== 0) { 2658 $('#error_report_dialog').remove(); 2659 }
2660 var $div = $('<div id="error_report_dialog"></div>'); 2661 2662 var button_options = {}; 2663 2664 button_options[PMA_messages.strSendErrorReport] = function () { 2665 var $dialog = $(this); 2666 var post_data = $.extend(report_data, { 2667 send_error_report: true, 2668 description: $("#report_description").val(), 2669 always_send: $("#always_send_checkbox")[0].checked 2670 }); 2671 $.post("error_report.php", post_data, function (data) { 2672 $dialog.dialog('close'); 2673 if (data.success === false) { 2674 //in the case of an error, show the error message returned. 2675 PMA_ajaxShowMessage(data.error, false); 2676 } else { 2677 PMA_ajaxShowMessage(data.message, 3000); 2678 } 2679 }); 2680 }; 2681 2682 button_options[PMA_messages.strCancel] = function () { 2683 $(this).dialog('close'); 2684 }; 2685 2686 $.post("error_report.php", report_data, function (data) { 2687 if (data.success === false) { 2688 //in the case of an error, show the error message returned. 2689 PMA_ajaxShowMessage(data.error, false); 2690 } else { 2691 // Show dialog if the request was successful 2692 $div 2693 .append(data.message) 2694 .dialog({ 2695 title: PMA_messages.strSubmitErrorReport, 2696 width: 650, 2697 modal: true, 2698 buttons: button_options, 2699 close: function () { 2700 $(this).remove(); 2701 } 2702 }); 2703 } 2704 }); // end $.get() 2705 }, 2706 /** 2707 * Shows the small notification that asks for user permission 2708 * 2709 * @return void 2710 */ 2711 _showErrorNotification: function () { 2712 ErrorReport._removeErrorNotification(); 2713 2714 var $div = $( 2715 '<div style="position:fixed;bottom:0;left:0;right:0;margin:0;' + 2716 'z-index:1000" class="error" id="error_notification"></div>' 2717 ).append( 2718 PMA_getImage("s_error.png") + PMA_messages.strErrorOccurred 2719 ); 2720 2721 var $buttons = $('<div class="floatright"></div>'); 2722 2723 var button_html = '<button id="show_error_report">'; 2724 button_html += PMA_messages.strShowReportDetails; 2725 button_html += '</button>'; 2726 2727 button_html += '<a id="change_error_settings">'; 2728 button_html += PMA_getImage('s_cog.png', PMA_messages.strChangeReportSettings); 2729 button_html += '</a>'; 2730 2731 button_html += '<a href="#" id="ignore_error">'; 2732 button_html += PMA_getImage('b_close.png', PMA_messages.strIgnore); 2733 button_html += '</a>'; 2734 2735 $buttons.html(button_html); 2736 2737 $div.append($buttons); 2738 $div.appendTo(document.body); 2739 $("#change_error_settings").on("click", ErrorReport._redirect_to_settings); 2740 $("#show_error_report").on("click", ErrorReport._createReportDialog); 2741 $("#ignore_error").on("click", ErrorReport._removeErrorNotification); 2742 }, 2743 /** 2744 * Removes the notification if it was displayed before 2745 * 2746 * @return void 2747 */ 2748 _removeErrorNotification: function (e) { 2749 if (e) { 2750 // don't remove the hash fragment by navigating to # 2751 e.preventDefault(); 2752 } 2753 $("#error_notification").fadeOut(function () { 2754 $(this).remove(); 2755 }); 2756 }, 2757 /** 2758 * Extracts Exception name from message if it exists 2759 * 2760 * @return String 2761 */ 2762 _extractExceptionName: function (exception) { 2763 if (exception.message === null || typeof(exception.message) == "undefined") { 2764 return ""; 2765 } 2766 2767 var reg = /([a-zA-Z]+):/; 2768 var regex_result = reg.exec(exception.message); 2769 if (regex_result && regex_result.length == 2) { 2770 return regex_result[1]; 2771 } 2772 2773 return ""; 2774 }, 2775 /** 2776 * Shows the modal dialog previewing the report 2777 * 2778 * @return void 2779 */ 2780 _createReportDialog: function () { 2781 ErrorReport._removeErrorNotification(); 2782 ErrorReport._showReportDialog(ErrorReport._last_exception); 2783 }, 2784 /** 2785 * Redirects to the settings page containing error report 2786 * preferences 2787 * 2788 * @return void 2789 */ 2790 _redirect_to_settings: function () { 2791 window.location.href = "prefs_forms.php?token=" + PMA_commonParams.get('token'); 2792 }, 2793 /** 2794 * Returns the report data to send to the server 2795 * 2796 * @param exception object exception info 2797 * 2798 * @return object 2799 */ 2800 _get_report_data: function (exception) { 2801 var report_data = { 2802 "ajax_request": true, 2803 "token": PMA_commonParams.get('token'), 2804 "exception": exception, 2805 "current_url": window.location.href, 2806 "exception_type": 'js' 2807 }; 2808 if (AJAX.scriptHandler._scripts.length > 0) { 2809 report_data.scripts = AJAX.scriptHandler._scripts.map( 2810 function (script) { 2811 return script.name; 2812 } 2813 ); 2814 } 2815 return report_data; 2816 }, 2817 /** 2818 * Wraps all global functions that start with PMA_ 2819 * 2820 * @return void 2821 */ 2822 wrap_global_functions: function () { 2823 for (var key in window) { 2824 if (key.indexOf("PMA_") === 0) { 2825 var global = window[key]; 2826 if (typeof(global) === "function") { 2827 window[key] = ErrorReport.wrap_function(global); 2828 } 2829 } 2830 } 2831 }, 2832 /** 2833 * Wraps given function in error reporting code and returns wrapped function 2834 * 2835 * @param func function to be wrapped 2836 * 2837 * @return function 2838 */ 2839 wrap_function: function (func) { 2840 if (!func.wrapped) { 2841 var new_func = function () { 2842 try { 2843 return func.apply(this, arguments); 2844 } catch (x) { 2845 TraceKit.report(x); 2846 } 2847 }; 2848 new_func.wrapped = true; 2849 //Set guid of wrapped function same as original function, so it can be removed 2850 //See bug#4146 (problem with jquery draggable and sortable) 2851 new_func.guid = func.guid = func.guid || new_func.guid || jQuery.guid++; 2852 return new_func; 2853 } else { 2854 return func; 2855 } 2856 }, 2857 /** 2858 * Automatically wraps the callback in AJAX.registerOnload 2859 * 2860 * @return void 2861 */ 2862 _wrap_ajax_onload_callback: function () { 2863 var oldOnload = AJAX.registerOnload; 2864 AJAX.registerOnload = function (file, func) { 2865 func = ErrorReport.wrap_function(func); 2866 oldOnload.call(this, file, func); 2867 }; 2868 }, 2869 /** 2870 * Automatically wraps the callback in $.fn.on 2871 * 2872 * @return void 2873 */ 2874 _wrap_$_on_callback: function () { 2875 var oldOn = $.fn.on; 2876 $.fn.on = function () { 2877 for (var i = 1; i <= 3; i++) { 2878 if (typeof(arguments[i]) === "function") { 2879 arguments[i] = ErrorReport.wrap_function(arguments[i]); 2880 break; 2881 } 2882 } 2883 return oldOn.apply(this, arguments); 2884 }; 2885 }, 2886 /** 2887 * Wraps all global functions that start with PMA_ 2888 * also automatically wraps the callback in AJAX.registerOnload 2889 * 2890 * @return void 2891 */ 2892 set_up_error_reporting: function () { 2893 ErrorReport.wrap_global_functions(); 2894 ErrorReport._wrap_ajax_onload_callback(); 2895 ErrorReport._wrap_$_on_callback(); 2896 } 2897 2898}; 2899 2900TraceKit.report.subscribe(ErrorReport.error_handler); 2901ErrorReport.set_up_error_reporting(); 2902$(function () { 2903 ErrorReport.wrap_global_functions(); 2904}); 2905; 2906 2907/* vim: set expandtab sw=4 ts=4 sts=4: */ 2908/**
2909 * Functions used in configuration forms and on user preferences pages 2910 */ 2911 2912/** 2913 * checks whether browser supports web storage 2914 * 2915 * @param type the type of storage i.e. localStorage or sessionStorage 2916 * 2917 * @returns bool 2918 */ 2919function isStorageSupported(type, warn) 2920{ 2921 try { 2922 window[type].setItem('PMATest', 'test'); 2923 // Check whether key-value pair was set successfully 2924 if (window[type].getItem('PMATest') === 'test') { 2925 // Supported, remove test variable from storage 2926 window[type].removeItem('PMATest'); 2927 return true; 2928 } 2929 } catch(error) { 2930 // Not supported 2931 if (warn) { 2932 PMA_ajaxShowMessage(PMA_messages.strNoLocalStorage, false); 2933 } 2934 } 2935 return false; 2936} 2937 2938/** 2939 * Unbind all event handlers before tearing down a page 2940 */ 2941AJAX.registerTeardown('config.js', function () { 2942 $('.optbox input[id], .optbox select[id], .optbox textarea[id]').unbind('change').unbind('keyup'); 2943 $('.optbox input[type=button][name=submit_reset]').unbind('click'); 2944 $('div.tabs_contents').undelegate(); 2945 $('#import_local_storage, #export_local_storage').unbind('click'); 2946 $('form.prefs-form').unbind('change').unbind('submit'); 2947 $(document).off('click', 'div.click-hide-message'); 2948 $('#prefs_autoload').find('a').unbind('click'); 2949}); 2950 2951AJAX.registerOnload('config.js', function () { 2952 var $topmenu_upt = $('#topmenu2.user_prefs_tabs'); 2953 $topmenu_upt.find('li.active a').attr('rel', 'samepage'); 2954 $topmenu_upt.find('li:not(.active) a').attr('rel', 'newpage'); 2955}); 2956 2957// default values for fields 2958var defaultValues = {}; 2959 2960/** 2961 * Returns field type 2962 * 2963 * @param {Element} field 2964 */ 2965function getFieldType(field) 2966{ 2967 var $field = $(field); 2968 var tagName = $field.prop('tagName'); 2969 if (tagName == 'INPUT') { 2970 return $field.attr('type'); 2971 } else if (tagName == 'SELECT') { 2972 return 'select'; 2973 } else if (tagName == 'TEXTAREA') { 2974 return 'text'; 2975 } 2976 return ''; 2977} 2978 2979/** 2980 * Enables or disables the "restore default value" button 2981 * 2982 * @param {Element} field 2983 * @param {boolean} display 2984 */ 2985function setRestoreDefaultBtn(field, display) 2986{ 2987 var $el = $(field).closest('td').find('.restore-default img'); 2988 $el[display ? 'show' : 'hide'](); 2989} 2990 2991/** 2992 * Marks field depending on its value (system default or custom) 2993 * 2994 * @param {Element} field 2995 */ 2996function markField(field) 2997{ 2998 var $field = $(field); 2999 var type = getFieldType($field); 3000 var isDefault = checkFieldDefault($field, type); 3001 3002 // checkboxes uses parent <span> for marking 3003 var $fieldMarker = (type == 'checkbox') ? $field.parent() : $field; 3004 setRestoreDefaultBtn($field, !isDefault); 3005 $fieldMarker[isDefault ? 'removeClass' : 'addClass']('custom'); 3006} 3007 3008/** 3009 * Sets field value 3010 * 3011 * value must be of type: 3012 * o undefined (omitted) - restore default value (form default, not PMA default) 3013 * o String - if field_type is 'text' 3014 * o boolean - if field_type is 'checkbox' 3015 * o Array of values - if field_type is 'select' 3016 * 3017 * @param {Element} field 3018 * @param {String} field_type see {@link #getFieldType} 3019 * @param {String|Boolean} [value] 3020 */ 3021function setFieldValue(field, field_type, value) 3022{ 3023 var $field = $(field); 3024 switch (field_type) { 3025 case 'text': 3026 case 'number': 3027 $field.val(value !== undefined ? value : $field.attr('defaultValue')); 3028 break; 3029 case 'checkbox': 3030 $field.prop('checked', (value !== undefined ? value : $field.attr('defaultChecked'))); 3031 break; 3032 case 'select': 3033 var options = $field.prop('options'); 3034 var i, imax = options.length; 3035 if (value === undefined) { 3036 for (i = 0; i < imax; i++) { 3037 options[i].selected = options[i].defaultSelected; 3038 } 3039 } else { 3040 for (i = 0; i < imax; i++) { 3041 options[i].selected = (value.indexOf(options[i].value) != -1); 3042 } 3043 } 3044 break; 3045 } 3046 markField($field); 3047} 3048 3049/** 3050 * Gets field value 3051 * 3052 * Will return one of: 3053 * o String - if type is 'text' 3054 * o boolean - if type is 'checkbox' 3055 * o Array of values - if type is 'select' 3056 * 3057 * @param {Element} field 3058 * @param {String} field_type returned by {@link #getFieldType} 3059 * @type Boolean|String|String[] 3060 */ 3061function getFieldValue(field, field_type) 3062{ 3063 var $field = $(field); 3064 switch (field_type) { 3065 case 'text': 3066 case 'number': 3067 return $field.prop('value'); 3068 case 'checkbox': 3069 return $field.prop('checked'); 3070 case 'select': 3071 var options = $field.prop('options'); 3072 var i, imax = options.length, items = []; 3073 for (i = 0; i < imax; i++) { 3074 if (options[i].selected) { 3075 items.push(options[i].value); 3076 } 3077 } 3078 return items; 3079 } 3080 return null; 3081} 3082 3083/** 3084 * Returns values for all fields in fieldsets 3085 */ 3086function getAllValues() 3087{ 3088 var $elements = $('fieldset input, fieldset select, fieldset textarea'); 3089 var values = {}; 3090 var type, value; 3091 for (var i = 0; i < $elements.length; i++) { 3092 type = getFieldType($elements[i]); 3093 value = getFieldValue($elements[i], type); 3094 if (typeof value != 'undefined') { 3095 // we only have single selects, fatten array 3096 if (type == 'select') { 3097 value = value[0]; 3098 } 3099 values[$elements[i].name] = value; 3100 } 3101 } 3102 return values; 3103} 3104 3105/** 3106 * Checks whether field has its default value 3107 * 3108 * @param {Element} field 3109 * @param {String} type 3110 * @return boolean 3111 */ 3112function checkFieldDefault(field, type) 3113{ 3114 var $field = $(field); 3115 var field_id = $field.attr('id'); 3116 if (typeof defaultValues[field_id] == 'undefined') { 3117 return true; 3118 } 3119 var isDefault = true; 3120 var currentValue = getFieldValue($field, type); 3121 if (type != 'select') { 3122 isDefault = currentValue == defaultValues[field_id]; 3123 } else { 3124 // compare arrays, will work for our representation of select values 3125 if (currentValue.length != defaultValues[field_id].length) { 3126 isDefault = false;
3127 } 3128 else { 3129 for (var i = 0; i < currentValue.length; i++) { 3130 if (currentValue[i] != defaultValues[field_id][i]) { 3131 isDefault = false; 3132 break; 3133 } 3134 } 3135 } 3136 } 3137 return isDefault; 3138} 3139 3140/** 3141 * Returns element's id prefix 3142 * @param {Element} element 3143 */ 3144function getIdPrefix(element) 3145{ 3146 return $(element).attr('id').replace(/[^-]+$/, ''); 3147} 3148 3149// ------------------------------------------------------------------ 3150// Form validation and field operations 3151// 3152 3153// form validator assignments 3154var validate = {}; 3155 3156// form validator list 3157var validators = { 3158 // regexp: numeric value 3159 _regexp_numeric: /^[0-9]+$/, 3160 // regexp: extract parts from PCRE expression 3161 _regexp_pcre_extract: /(.)(.*)\1(.*)?/, 3162 /** 3163 * Validates positive number 3164 * 3165 * @param {boolean} isKeyUp 3166 */ 3167 PMA_validatePositiveNumber: function (isKeyUp) { 3168 if (isKeyUp && this.value === '') { 3169 return true; 3170 } 3171 var result = this.value != '0' && validators._regexp_numeric.test(this.value); 3172 return result ? true : PMA_messages.error_nan_p; 3173 }, 3174 /** 3175 * Validates non-negative number 3176 * 3177 * @param {boolean} isKeyUp 3178 */ 3179 PMA_validateNonNegativeNumber: function (isKeyUp) { 3180 if (isKeyUp && this.value === '') { 3181 return true; 3182 } 3183 var result = validators._regexp_numeric.test(this.value); 3184 return result ? true : PMA_messages.error_nan_nneg; 3185 }, 3186 /** 3187 * Validates port number 3188 * 3189 * @param {boolean} isKeyUp 3190 */ 3191 PMA_validatePortNumber: function (isKeyUp) { 3192 if (this.value === '') { 3193 return true; 3194 } 3195 var result = validators._regexp_numeric.test(this.value) && this.value != '0'; 3196 return result && this.value <= 65535 ? true : PMA_messages.error_incorrect_port; 3197 }, 3198 /** 3199 * Validates value according to given regular expression 3200 * 3201 * @param {boolean} isKeyUp 3202 * @param {string} regexp 3203 */ 3204 PMA_validateByRegex: function (isKeyUp, regexp) { 3205 if (isKeyUp && this.value === '') { 3206 return true; 3207 } 3208 // convert PCRE regexp 3209 var parts = regexp.match(validators._regexp_pcre_extract); 3210 var valid = this.value.match(new RegExp(parts[2], parts[3])) !== null; 3211 return valid ? true : PMA_messages.error_invalid_value; 3212 }, 3213 /** 3214 * Validates upper bound for numeric inputs 3215 * 3216 * @param {boolean} isKeyUp 3217 * @param {int} max_value 3218 */ 3219 PMA_validateUpperBound: function (isKeyUp, max_value) { 3220 var val = parseInt(this.value, 10); 3221 if (isNaN(val)) { 3222 return true; 3223 } 3224 return val <= max_value ? true : PMA_sprintf(PMA_messages.error_value_lte, max_value); 3225 }, 3226 // field validators 3227 _field: { 3228 }, 3229 // fieldset validators 3230 _fieldset: { 3231 } 3232}; 3233 3234/** 3235 * Registers validator for given field 3236 * 3237 * @param {String} id field id 3238 * @param {String} type validator (key in validators object) 3239 * @param {boolean} onKeyUp whether fire on key up 3240 * @param {Array} params validation function parameters 3241 */ 3242function validateField(id, type, onKeyUp, params) 3243{ 3244 if (typeof validators[type] == 'undefined') { 3245 return; 3246 } 3247 if (typeof validate[id] == 'undefined') { 3248 validate[id] = []; 3249 } 3250 validate[id].push([type, params, onKeyUp]); 3251} 3252 3253/** 3254 * Returns validation functions associated with form field 3255 * 3256 * @param {String} field_id form field id 3257 * @param {boolean} onKeyUpOnly see validateField 3258 * @type Array 3259 * @return array of [function, parameters to be passed to function] 3260 */ 3261function getFieldValidators(field_id, onKeyUpOnly) 3262{ 3263 // look for field bound validator 3264 var name = field_id && field_id.match(/[^-]+$/)[0]; 3265 if (typeof validators._field[name] != 'undefined') { 3266 return [[validators._field[name], null]]; 3267 } 3268 3269 // look for registered validators 3270 var functions = []; 3271 if (typeof validate[field_id] != 'undefined') { 3272 // validate[field_id]: array of [type, params, onKeyUp] 3273 for (var i = 0, imax = validate[field_id].length; i < imax; i++) { 3274 if (onKeyUpOnly && !validate[field_id][i][2]) { 3275 continue; 3276 } 3277 functions.push([validators[validate[field_id][i][0]], validate[field_id][i][1]]); 3278 } 3279 } 3280 3281 return functions; 3282} 3283 3284/** 3285 * Displays errors for given form fields 3286 * 3287 * WARNING: created DOM elements must be identical with the ones made by 3288 * display_input() in FormDisplay.tpl.php! 3289 * 3290 * @param {Object} error_list list of errors in the form {field id: error array} 3291 */ 3292function displayErrors(error_list) 3293{ 3294 var tempIsEmpty = function (item) { 3295 return item !== ''; 3296 }; 3297 3298 for (var field_id in error_list) { 3299 var errors = error_list[field_id]; 3300 var $field = $('#' + field_id); 3301 var isFieldset = $field.attr('tagName') == 'FIELDSET'; 3302 var $errorCnt; 3303 if (isFieldset) { 3304 $errorCnt = $field.find('dl.errors'); 3305 } else { 3306 $errorCnt = $field.siblings('.inline_errors'); 3307 } 3308 3309 // remove empty errors (used to clear error list) 3310 errors = $.grep(errors, tempIsEmpty); 3311 3312 // CSS error class 3313 if (!isFieldset) { 3314 // checkboxes uses parent <span> for marking 3315 var $fieldMarker = ($field.attr('type') == 'checkbox') ? $field.parent() : $field; 3316 $fieldMarker[errors.length ? 'addClass' : 'removeClass']('field-error'); 3317 } 3318 3319 if (errors.length) { 3320 // if error container doesn't exist, create it 3321 if ($errorCnt.length === 0) { 3322 if (isFieldset) { 3323 $errorCnt = $('<dl class="errors" />'); 3324 $field.find('table').before($errorCnt); 3325 } else { 3326 $errorCnt = $('<dl class="inline_errors" />'); 3327 $field.closest('td').append($errorCnt); 3328 } 3329 } 3330 3331 var html = ''; 3332 for (var i = 0, imax = errors.length; i < imax; i++) { 3333 html += '<dd>' + errors[i] + '</dd>'; 3334 } 3335 $errorCnt.html(html); 3336 } else if ($errorCnt !== null) { 3337 // remove useless error container 3338 $errorCnt.remove(); 3339 } 3340 } 3341} 3342 3343/** 3344 * Validates fieldset and puts errors in 'errors' object 3345 * 3346 * @param {Element} fieldset 3347 * @param {boolean} isKeyUp 3348 * @param {Object} errors 3349 */ 3350function validate_fieldset(fieldset, isKeyUp, errors) 3351{ 3352 var $fieldset = $(fieldset); 3353 if ($fieldset.length && typeof validators._fieldset[$fieldset.attr('id')] != 'undefined') { 3354 var fieldset_errors = validators._fieldset[$fieldset.attr('id')].apply($fieldset[0], [isKeyUp]); 3355 for (var field_id in fieldset_errors) { 3356 if (typeof errors[field_id] == 'undefined') { 3357 errors[field_id] = []; 3358 } 3359 if (typeof fieldset_errors[field_id] == 'string') { 3360 fieldset_errors[field_id] = [fieldset_errors[field_id]]; 3361 } 3362 $.merge(errors[field_id], fieldset_errors[field_id]); 3363 } 3364 } 3365} 3366 3367/** 3368 * Validates form field and puts errors in 'errors' object 3369 * 3370 * @param {Element} field 3371 * @param {boolean} isKeyUp 3372 * @param {Object} errors 3373 */ 3374function validate_field(field, isKeyUp, errors) 3375{ 3376 var args, result; 3377 var $field = $(field); 3378 var field_id = $field.attr('id'); 3379 errors[field_id] = []; 3380 var functions = getFieldValidators(field_id, isKeyUp); 3381 for (var i = 0; i < functions.length; i++) { 3382 if (typeof functions[i][1] !== 'undefined' && functions[i][1] !== null) { 3383 args = functions[i][1].slice(0); 3384 } else { 3385 args = []; 3386 } 3387 args.unshift(isKeyUp); 3388 result = functions[i][0].apply($field[0], args); 3389 if (result !== true) { 3390 if (typeof result == 'string') { 3391 result = [result]; 3392 } 3393 $.merge(errors[field_id], result); 3394 } 3395 } 3396} 3397 3398/** 3399 * Validates form field and parent fieldset 3400 * 3401 * @param {Element} field 3402 * @param {boolean} isKeyUp 3403 */ 3404function validate_field_and_fieldset(field, isKeyUp) 3405{ 3406 var $field = $(field); 3407 var errors = {}; 3408 validate_field($field, isKeyUp, errors); 3409 validate_fieldset($field.closest('fieldset.optbox'), isKeyUp, errors); 3410 displayErrors(errors); 3411} 3412 3413function loadInlineConfig() { 3414 if (!Array.isArray(configInlineParams)) { 3415 return; 3416 } 3417 for (var i = 0; i < configInlineParams.length; ++i) { 3418 if (typeof configInlineParams[i] === 'function') { 3419 configInlineParams[i](); 3420 } 3421 } 3422} 3423 3424function setupValidation() { 3425 validate = {}; 3426 configScriptLoaded = true; 3427 if (configScriptLoaded && typeof configInlineParams !== "undefined") { 3428 loadInlineConfig(); 3429 } 3430 // register validators and mark custom values
3431 var $elements = $('.optbox input[id], .optbox select[id], .optbox textarea[id]'); 3432 $elements.each(function () { 3433 markField(this); 3434 var $el = $(this); 3435 $el.bind('change', function () { 3436 validate_field_and_fieldset(this, false); 3437 markField(this); 3438 }); 3439 var tagName = $el.attr('tagName'); 3440 // text fields can be validated after each change 3441 if (tagName == 'INPUT' && $el.attr('type') == 'text') { 3442 $el.keyup(function () { 3443 validate_field_and_fieldset($el, true); 3444 markField($el); 3445 }); 3446 } 3447 // disable textarea spellcheck 3448 if (tagName == 'TEXTAREA') { 3449 $el.attr('spellcheck', false); 3450 } 3451 }); 3452 3453 // check whether we've refreshed a page and browser remembered modified 3454 // form values 3455 var $check_page_refresh = $('#check_page_refresh'); 3456 if ($check_page_refresh.length === 0 || $check_page_refresh.val() == '1') { 3457 // run all field validators 3458 var errors = {}; 3459 for (var i = 0; i < $elements.length; i++) { 3460 validate_field($elements[i], false, errors); 3461 } 3462 // run all fieldset validators 3463 $('fieldset.optbox').each(function () { 3464 validate_fieldset(this, false, errors); 3465 }); 3466 3467 displayErrors(errors); 3468 } else if ($check_page_refresh) { 3469 $check_page_refresh.val('1'); 3470 } 3471} 3472 3473AJAX.registerOnload('config.js', function () { 3474 setupValidation(); 3475}); 3476 3477// 3478// END: Form validation and field operations 3479// ------------------------------------------------------------------ 3480 3481// ------------------------------------------------------------------ 3482// Tabbed forms 3483// 3484 3485/** 3486 * Sets active tab 3487 * 3488 * @param {String} tab_id 3489 */ 3490function setTab(tab_id) 3491{ 3492 $('ul.tabs').each(function() { 3493 var $this = $(this); 3494 if (!$this.find('li a[href=#' + tab_id + ']').length) { 3495 return; 3496 } 3497 $this.find('li').removeClass('active').find('a[href=#' + tab_id + ']').parent().addClass('active'); 3498 $this.parent().find('div.tabs_contents fieldset').hide().filter('#' + tab_id).show(); 3499 var hashValue = 'tab_' + tab_id; 3500 location.hash = hashValue; 3501 $this.parent().find('input[name=tab_hash]').val(hashValue); 3502 }); 3503} 3504 3505function setupConfigTabs() { 3506 var forms = $('form.config-form'); 3507 forms.each(function() { 3508 var $this = $(this); 3509 var $tabs = $this.find('ul.tabs'); 3510 if (!$tabs.length) { 3511 return; 3512 } 3513 // add tabs events and activate one tab (the first one or indicated by location hash) 3514 $tabs.find('li').removeClass('active'); 3515 $tabs.find('a') 3516 .click(function (e) { 3517 e.preventDefault(); 3518 setTab($(this).attr('href').substr(1)); 3519 }) 3520 .filter(':first') 3521 .parent() 3522 .addClass('active'); 3523 $this.find('div.tabs_contents fieldset').hide().filter(':first').show(); 3524 }); 3525} 3526 3527AJAX.registerOnload('config.js', function () { 3528 setupConfigTabs(); 3529 3530 // tab links handling, check each 200ms 3531 // (works with history in FF, further browser support here would be an overkill) 3532 var prev_hash; 3533 var tab_check_fnc = function () { 3534 if (location.hash != prev_hash) { 3535 prev_hash = location.hash; 3536 if (prev_hash.match(/^#tab_[a-zA-Z0-9_]+$/)) { 3537 // session ID is sometimes appended here 3538 var hash = prev_hash.substr(5).split('&')[0]; 3539 if ($('#' + hash).length) { 3540 setTab(hash); 3541 } 3542 } 3543 } 3544 }; 3545 tab_check_fnc(); 3546 setInterval(tab_check_fnc, 200); 3547}); 3548 3549// 3550// END: Tabbed forms 3551// ------------------------------------------------------------------ 3552 3553// ------------------------------------------------------------------ 3554// Form reset buttons 3555// 3556 3557AJAX.registerOnload('config.js', function () { 3558 $('.optbox input[type=button][name=submit_reset]').click(function () { 3559 var fields = $(this).closest('fieldset').find('input, select, textarea'); 3560 for (var i = 0, imax = fields.length; i < imax; i++) { 3561 setFieldValue(fields[i], getFieldType(fields[i])); 3562 } 3563 }); 3564}); 3565 3566// 3567// END: Form reset buttons 3568// ------------------------------------------------------------------ 3569 3570// ------------------------------------------------------------------ 3571// "Restore default" and "set value" buttons 3572// 3573 3574/** 3575 * Restores field's default value 3576 * 3577 * @param {String} field_id 3578 */ 3579function restoreField(field_id) 3580{ 3581 var $field = $('#' + field_id); 3582 if ($field.length === 0 || defaultValues[field_id] === undefined) { 3583 return; 3584 } 3585 setFieldValue($field, getFieldType($field), defaultValues[field_id]); 3586} 3587 3588function setupRestoreField() { 3589 $('div.tabs_contents') 3590 .delegate('.restore-default, .set-value', 'mouseenter', function () { 3591 $(this).css('opacity', 1); 3592 }) 3593 .delegate('.restore-default, .set-value', 'mouseleave', function () { 3594 $(this).css('opacity', 0.25); 3595 }) 3596 .delegate('.restore-default, .set-value', 'click', function (e) { 3597 e.preventDefault(); 3598 var href = $(this).attr('href'); 3599 var field_sel; 3600 if ($(this).hasClass('restore-default')) { 3601 field_sel = href; 3602 restoreField(field_sel.substr(1)); 3603 } else { 3604 field_sel = href.match(/^[^=]+/)[0]; 3605 var value = href.match(/\=(.+)$/)[1]; 3606 setFieldValue($(field_sel), 'text', value); 3607 } 3608 $(field_sel).trigger('change'); 3609 }) 3610 .find('.restore-default, .set-value') 3611 // inline-block for IE so opacity inheritance works 3612 .css({display: 'inline-block', opacity: 0.25}); 3613} 3614 3615AJAX.registerOnload('config.js', function () { 3616 setupRestoreField(); 3617}); 3618 3619// 3620// END: "Restore default" and "set value" buttons 3621// ------------------------------------------------------------------ 3622 3623// ------------------------------------------------------------------ 3624// User preferences import/export 3625// 3626 3627AJAX.registerOnload('config.js', function () { 3628 offerPrefsAutoimport(); 3629 var $radios = $('#import_local_storage, #export_local_storage'); 3630 if (!$radios.length) { 3631 return; 3632 } 3633 3634 // enable JavaScript dependent fields 3635 $radios 3636 .prop('disabled', false) 3637 .add('#export_text_file, #import_text_file') 3638 .click(function () { 3639 var enable_id = $(this).attr('id'); 3640 var disable_id; 3641 if (enable_id.match(/local_storage$/)) { 3642 disable_id = enable_id.replace(/local_storage$/, 'text_file'); 3643 } else { 3644 disable_id = enable_id.replace(/text_file$/, 'local_storage'); 3645 } 3646 $('#opts_' + disable_id).addClass('disabled').find('input').prop('disabled', true); 3647 $('#opts_' + enable_id).removeClass('disabled').find('input').prop('disabled', false); 3648 }); 3649 3650 // detect localStorage state 3651 var ls_supported = isStorageSupported('localStorage', true); 3652 var ls_exists = ls_supported ? (window.localStorage.config || false) : false;
3653 $('div.localStorage-' + (ls_supported ? 'un' : '') + 'supported').hide(); 3654 $('div.localStorage-' + (ls_exists ? 'empty' : 'exists')).hide(); 3655 if (ls_exists) { 3656 updatePrefsDate(); 3657 } 3658 $('form.prefs-form').change(function () { 3659 var $form = $(this); 3660 var disabled = false; 3661 if (!ls_supported) { 3662 disabled = $form.find('input[type=radio][value$=local_storage]').prop('checked'); 3663 } else if (!ls_exists && $form.attr('name') == 'prefs_import' && 3664 $('#import_local_storage')[0].checked 3665 ) { 3666 disabled = true; 3667 } 3668 $form.find('input[type=submit]').prop('disabled', disabled); 3669 }).submit(function (e) { 3670 var $form = $(this); 3671 if ($form.attr('name') == 'prefs_export' && $('#export_local_storage')[0].checked) { 3672 e.preventDefault(); 3673 // use AJAX to read JSON settings and save them 3674 savePrefsToLocalStorage($form); 3675 } else if ($form.attr('name') == 'prefs_import' && $('#import_local_storage')[0].checked) { 3676 // set 'json' input and submit form 3677 $form.find('input[name=json]').val(window.localStorage.config); 3678 } 3679 }); 3680 3681 $(document).on('click', 'div.click-hide-message', function () { 3682 $(this) 3683 .hide() 3684 .parent('.group') 3685 .css('height', '') 3686 .next('form') 3687 .show(); 3688 }); 3689}); 3690 3691/** 3692 * Saves user preferences to localStorage 3693 * 3694 * @param {Element} form 3695 */ 3696function savePrefsToLocalStorage(form) 3697{ 3698 $form = $(form); 3699 var submit = $form.find('input[type=submit]'); 3700 submit.prop('disabled', true); 3701 $.ajax({ 3702 url: 'prefs_manage.php', 3703 cache: false, 3704 type: 'POST', 3705 data: { 3706 ajax_request: true, 3707 server: $form.find('input[name=server]').val(), 3708 token: $form.find('input[name=token]').val(), 3709 submit_get_json: true 3710 }, 3711 success: function (data) { 3712 if (typeof data !== 'undefined' && data.success === true) { 3713 window.localStorage.config = data.prefs; 3714 window.localStorage.config_mtime = data.mtime; 3715 window.localStorage.config_mtime_local = (new Date()).toUTCString(); 3716 updatePrefsDate(); 3717 $('div.localStorage-empty').hide(); 3718 $('div.localStorage-exists').show(); 3719 var group = $form.parent('.group'); 3720 group.css('height', group.height() + 'px'); 3721 $form.hide('fast'); 3722 $form.prev('.click-hide-message').show('fast'); 3723 } else { 3724 PMA_ajaxShowMessage(data.error); 3725 } 3726 }, 3727 complete: function () { 3728 submit.prop('disabled', false); 3729 } 3730 }); 3731} 3732 3733/** 3734 * Updates preferences timestamp in Import form 3735 */ 3736function updatePrefsDate() 3737{ 3738 var d = new Date(window.localStorage.config_mtime_local); 3739 var msg = PMA_messages.strSavedOn.replace( 3740 '@DATE@', 3741 PMA_formatDateTime(d) 3742 ); 3743 $('#opts_import_local_storage').find('div.localStorage-exists').html(msg); 3744} 3745 3746/** 3747 * Prepares message which informs that localStorage preferences are available and can be imported 3748 */ 3749function offerPrefsAutoimport() 3750{ 3751 var has_config = (isStorageSupported('localStorage')) && (window.localStorage.config || false); 3752 var $cnt = $('#prefs_autoload'); 3753 if (!$cnt.length || !has_config) { 3754 return; 3755 } 3756 $cnt.find('a').click(function (e) { 3757 e.preventDefault(); 3758 var $a = $(this); 3759 if ($a.attr('href') == '#no') { 3760 $cnt.remove(); 3761 $.post('index.php', { 3762 token: $cnt.find('input[name=token]').val(), 3763 prefs_autoload: 'hide' 3764 }, null, 'html'); 3765 return; 3766 } 3767 $cnt.find('input[name=json]').val(window.localStorage.config); 3768 $cnt.find('form').submit(); 3769 }); 3770 $cnt.show(); 3771} 3772 3773// 3774// END: User preferences import/export 3775// ------------------------------------------------------------------ 3776; 3777 3778/** 3779 * Definition of links to MySQL documentation. 3780 */ 3781 3782var mysql_doc_keyword = { 3783 /* Multi word */ 3784 'CHARACTER SET': Array('charset'), 3785 'SHOW AUTHORS': Array('show-authors'), 3786 'SHOW BINARY LOGS': Array('show-binary-logs'), 3787 'SHOW BINLOG EVENTS': Array('show-binlog-events'), 3788 'SHOW CHARACTER SET': Array('show-character-set'), 3789 'SHOW COLLATION': Array('show-collation'), 3790 'SHOW COLUMNS': Array('show-columns'), 3791 'SHOW CONTRIBUTORS': Array('show-contributors'), 3792 'SHOW CREATE DATABASE': Array('show-create-database'), 3793 'SHOW CREATE EVENT': Array('show-create-event'), 3794 'SHOW CREATE FUNCTION': Array('show-create-function'), 3795 'SHOW CREATE PROCEDURE': Array('show-create-procedure'), 3796 'SHOW CREATE TABLE': Array('show-create-table'), 3797 'SHOW CREATE TRIGGER': Array('show-create-trigger'), 3798 'SHOW CREATE VIEW': Array('show-create-view'), 3799 'SHOW DATABASES': Array('show-databases'), 3800 'SHOW ENGINE': Array('show-engine'), 3801 'SHOW ENGINES': Array('show-engines'), 3802 'SHOW ERRORS': Array('show-errors'), 3803 'SHOW EVENTS': Array('show-events'), 3804 'SHOW FUNCTION CODE': Array('show-function-code'), 3805 'SHOW FUNCTION STATUS': Array('show-function-status'), 3806 'SHOW GRANTS': Array('show-grants'), 3807 'SHOW INDEX': Array('show-index'),
3808 'SHOW MASTER STATUS': Array('show-master-status'), 3809 'SHOW OPEN TABLES': Array('show-open-tables'), 3810 'SHOW PLUGINS': Array('show-plugins'), 3811 'SHOW PRIVILEGES': Array('show-privileges'), 3812 'SHOW PROCEDURE CODE': Array('show-procedure-code'), 3813 'SHOW PROCEDURE STATUS': Array('show-procedure-status'), 3814 'SHOW PROCESSLIST': Array('show-processlist'), 3815 'SHOW PROFILE': Array('show-profile'), 3816 'SHOW PROFILES': Array('show-profiles'), 3817 'SHOW RELAYLOG EVENTS': Array('show-relaylog-events'), 3818 'SHOW SLAVE HOSTS': Array('show-slave-hosts'), 3819 'SHOW SLAVE STATUS': Array('show-slave-status'), 3820 'SHOW STATUS': Array('show-status'), 3821 'SHOW TABLE STATUS': Array('show-table-status'), 3822 'SHOW TABLES': Array('show-tables'), 3823 'SHOW TRIGGERS': Array('show-triggers'), 3824 'SHOW VARIABLES': Array('show-variables'), 3825 'SHOW WARNINGS': Array('show-warnings'), 3826 'LOAD DATA INFILE': Array('load-data'), 3827 'LOAD XML': Array('load-xml'), 3828 'LOCK TABLES': Array('lock-tables'), 3829 'UNLOCK TABLES': Array('lock-tables'), 3830 'ALTER DATABASE': Array('alter-database'), 3831 'ALTER EVENT': Array('alter-event'), 3832 'ALTER LOGFILE GROUP': Array('alter-logfile-group'), 3833 'ALTER FUNCTION': Array('alter-function'), 3834 'ALTER PROCEDURE': Array('alter-procedure'), 3835 'ALTER SERVER': Array('alter-server'), 3836 'ALTER TABLE': Array('alter-table'), 3837 'ALTER TABLESPACE': Array('alter-tablespace'), 3838 'ALTER VIEW': Array('alter-view'), 3839 'CREATE DATABASE': Array('create-database'), 3840 'CREATE EVENT': Array('create-event'), 3841 'CREATE FUNCTION': Array('create-function'), 3842 'CREATE INDEX': Array('create-index'), 3843 'CREATE LOGFILE GROUP': Array('create-logfile-group'), 3844 'CREATE PROCEDURE': Array('create-procedure'), 3845 'CREATE SERVER': Array('create-server'), 3846 'CREATE TABLE': Array('create-table'), 3847 'CREATE TABLESPACE': Array('create-tablespace'), 3848 'CREATE TRIGGER': Array('create-trigger'), 3849 'CREATE VIEW': Array('create-view'), 3850 'DROP DATABASE': Array('drop-database'), 3851 'DROP EVENT': Array('drop-event'), 3852 'DROP FUNCTION': Array('drop-function'), 3853 'DROP INDEX': Array('drop-index'), 3854 'DROP LOGFILE GROUP': Array('drop-logfile-group'), 3855 'DROP PROCEDURE': Array('drop-procedure'), 3856 'DROP SERVER': Array('drop-server'), 3857 'DROP TABLE': Array('drop-table'), 3858 'DROP TABLESPACE': Array('drop-tablespace'), 3859 'DROP TRIGGER': Array('drop-trigger'), 3860 'DROP VIEW': Array('drop-view'), 3861 'RENAME TABLE': Array('rename-table'), 3862 'TRUNCATE TABLE': Array('truncate-table'), 3863 3864 /* Statements */ 3865 'SELECT': Array('select'), 3866 'SET': Array('set'), 3867 'EXPLAIN': Array('explain'), 3868 'DESCRIBE': Array('describe'), 3869 'DELETE': Array('delete'), 3870 'SHOW': Array('show'), 3871 'UPDATE': Array('update'), 3872 'INSERT': Array('insert'), 3873 'REPLACE': Array('replace'), 3874 'CALL': Array('call'), 3875 'DO': Array('do'), 3876 'HANDLER': Array('handler'), 3877 'COLLATE': Array('charset-collations'), 3878 3879 /* Functions */ 3880 'ABS': Array('mathematical-functions', 'function_abs'), 3881 'ACOS': Array('mathematical-functions', 'function_acos'), 3882 'ADDDATE': Array('date-and-time-functions', 'function_adddate'), 3883 'ADDTIME': Array('date-and-time-functions', 'function_addtime'), 3884 'AES_DECRYPT': Array('encryption-functions', 'function_aes_decrypt'), 3885 'AES_ENCRYPT': Array('encryption-functions', 'function_aes_encrypt'), 3886 'AND': Array('logical-operators', 'operator_and'), 3887 'ASCII': Array('string-functions', 'function_ascii'), 3888 'ASIN': Array('mathematical-functions', 'function_asin'), 3889 'ATAN2': Array('mathematical-functions', 'function_atan2'), 3890 'ATAN': Array('mathematical-functions', 'function_atan'), 3891 'AVG': Array('group-by-functions', 'function_avg'), 3892 'BENCHMARK': Array('information-functions', 'function_benchmark'), 3893 'BIN': Array('string-functions', 'function_bin'), 3894 'BINARY': Array('cast-functions', 'operator_binary'), 3895 'BIT_AND': Array('group-by-functions', 'function_bit_and'), 3896 'BIT_COUNT': Array('bit-functions', 'function_bit_count'), 3897 'BIT_LENGTH': Array('string-functions', 'function_bit_length'), 3898 'BIT_OR': Array('group-by-functions', 'function_bit_or'), 3899 'BIT_XOR': Array('group-by-functions', 'function_bit_xor'), 3900 'CASE': Array('control-flow-functions', 'operator_case'), 3901 'CAST': Array('cast-functions', 'function_cast'), 3902 'CEIL': Array('mathematical-functions', 'function_ceil'), 3903 'CEILING': Array('mathematical-functions', 'function_ceiling'), 3904 'CHAR_LENGTH': Array('string-functions', 'function_char_length'), 3905 'CHAR': Array('string-functions', 'function_char'), 3906 'CHARACTER_LENGTH': Array('string-functions', 'function_character_length'), 3907 'CHARSET': Array('information-functions', 'function_charset'), 3908 'COALESCE': Array('comparison-operators', 'function_coalesce'), 3909 'COERCIBILITY': Array('information-functions', 'function_coercibility'), 3910 'COLLATION': Array('information-functions', 'function_collation'), 3911 'COMPRESS': Array('encryption-functions', 'function_compress'), 3912 'CONCAT_WS': Array('string-functions', 'function_concat_ws'), 3913 'CONCAT': Array('string-functions', 'function_concat'), 3914 'CONNECTION_ID': Array('information-functions', 'function_connection_id'), 3915 'CONV': Array('mathematical-functions', 'function_conv'), 3916 'CONVERT_TZ': Array('date-and-time-functions', 'function_convert_tz'), 3917 'Convert': Array('cast-functions', 'function_convert'), 3918 'COS': Array('mathematical-functions', 'function_cos'), 3919 'COT': Array('mathematical-functions', 'function_cot'), 3920 'COUNT': Array('group-by-functions', 'function_count'), 3921 'CRC32': Array('mathematical-functions', 'function_crc32'), 3922 'CURDATE': Array('date-and-time-functions', 'function_curdate'), 3923 'CURRENT_DATE': Array('date-and-time-functions', 'function_current_date'), 3924 'CURRENT_TIME': Array('date-and-time-functions', 'function_current_time'), 3925 'CURRENT_TIMESTAMP': Array('date-and-time-functions', 'function_current_timestamp'), 3926 'CURRENT_USER': Array('information-functions', 'function_current_user'), 3927 'CURTIME': Array('date-and-time-functions', 'function_curtime'), 3928 'DATABASE': Array('information-functions', 'function_database'), 3929 'DATE_ADD': Array('date-and-time-functions', 'function_date_add'), 3930 'DATE_FORMAT': Array('date-and-time-functions', 'function_date_format'), 3931 'DATE_SUB': Array('date-and-time-functions', 'function_date_sub'), 3932 'DATE': Array('date-and-time-functions', 'function_date'), 3933 'DATEDIFF': Array('date-and-time-functions', 'function_datediff'), 3934 'DAY': Array('date-and-time-functions', 'function_day'), 3935 'DAYNAME': Array('date-and-time-functions', 'function_dayname'), 3936 'DAYOFMONTH': Array('date-and-time-functions', 'function_dayofmonth'), 3937 'DAYOFWEEK': Array('date-and-time-functions', 'function_dayofweek'), 3938 'DAYOFYEAR': Array('date-and-time-functions', 'function_dayofyear'), 3939 'DECLARE': Array('declare', 'declare'), 3940 'DECODE': Array('encryption-functions', 'function_decode'), 3941 'DEFAULT': Array('miscellaneous-functions', 'function_default'), 3942 'DEGREES': Array('mathematical-functions', 'function_degrees'), 3943 'DES_DECRYPT': Array('encryption-functions', 'function_des_decrypt'), 3944 'DES_ENCRYPT': Array('encryption-functions', 'function_des_encrypt'), 3945 'DIV': Array('arithmetic-functions', 'operator_div'), 3946 'ELT': Array('string-functions', 'function_elt'), 3947 'ENCODE': Array('encryption-functions', 'function_encode'), 3948 'ENCRYPT': Array('encryption-functions', 'function_encrypt'), 3949 'EXP': Array('mathematical-functions', 'function_exp'), 3950 'EXPORT_SET': Array('string-functions', 'function_export_set'),
3951 'EXTRACT': Array('date-and-time-functions', 'function_extract'), 3952 'ExtractValue': Array('xml-functions', 'function_extractvalue'), 3953 'FIELD': Array('string-functions', 'function_field'), 3954 'FIND_IN_SET': Array('string-functions', 'function_find_in_set'), 3955 'FLOOR': Array('mathematical-functions', 'function_floor'), 3956 'FORMAT': Array('string-functions', 'function_format'), 3957 'FOUND_ROWS': Array('information-functions', 'function_found_rows'), 3958 'FROM_DAYS': Array('date-and-time-functions', 'function_from_days'), 3959 'FROM_UNIXTIME': Array('date-and-time-functions', 'function_from_unixtime'), 3960 'GET_FORMAT': Array('date-and-time-functions', 'function_get_format'), 3961 'GET_LOCK': Array('miscellaneous-functions', 'function_get_lock'), 3962 'GREATEST': Array('comparison-operators', 'function_greatest'), 3963 'GROUP_CONCAT': Array('group-by-functions', 'function_group_concat'), 3964 'HEX': Array('string-functions', 'function_hex'), 3965 'HOUR': Array('date-and-time-functions', 'function_hour'), 3966 'IF': Array('control-flow-functions', 'function_if'), 3967 'IFNULL': Array('control-flow-functions', 'function_ifnull'), 3968 'IN': Array('comparison-operators', 'function_in'), 3969 'INET_ATON': Array('miscellaneous-functions', 'function_inet_aton'), 3970 'INET_NTOA': Array('miscellaneous-functions', 'function_inet_ntoa'), 3971 'INSTR': Array('string-functions', 'function_instr'), 3972 'INTERVAL': Array('comparison-operators', 'function_interval'), 3973 'IS_FREE_LOCK': Array('miscellaneous-functions', 'function_is_free_lock'), 3974 'IS_USED_LOCK': Array('miscellaneous-functions', 'function_is_used_lock'), 3975 'IS': Array('comparison-operators', 'operator_is'), 3976 'ISNULL': Array('comparison-operators', 'function_isnull'), 3977 'LAST_DAY': Array('date-and-time-functions', 'function_last_day'), 3978 'LAST_INSERT_ID': Array('information-functions', 'function_last_insert_id'), 3979 'LCASE': Array('string-functions', 'function_lcase'), 3980 'LEAST': Array('comparison-operators', 'function_least'), 3981 'LEFT': Array('string-functions', 'function_left'), 3982 'LENGTH': Array('string-functions', 'function_length'), 3983 'LIKE': Array('string-comparison-functions', 'operator_like'), 3984 'LN': Array('mathematical-functions', 'function_ln'), 3985 'LOAD_FILE': Array('string-functions', 'function_load_file'), 3986 'LOCALTIME': Array('date-and-time-functions', 'function_localtime'), 3987 'LOCALTIMESTAMP': Array('date-and-time-functions', 'function_localtimestamp'), 3988 'LOCATE': Array('string-functions', 'function_locate'), 3989 'LOG10': Array('mathematical-functions', 'function_log10'), 3990 'LOG2': Array('mathematical-functions', 'function_log2'), 3991 'LOG': Array('mathematical-functions', 'function_log'), 3992 'LOWER': Array('string-functions', 'function_lower'), 3993 'LPAD': Array('string-functions', 'function_lpad'), 3994 'LTRIM': Array('string-functions', 'function_ltrim'), 3995 'MAKE_SET': Array('string-functions', 'function_make_set'), 3996 'MAKEDATE': Array('date-and-time-functions', 'function_makedate'), 3997 'MAKETIME': Array('date-and-time-functions', 'function_maketime'), 3998 'MASTER_POS_WAIT': Array('miscellaneous-functions', 'function_master_pos_wait'), 3999 'MATCH': Array('fulltext-search', 'function_match'), 4000 'MAX': Array('group-by-functions', 'function_max'), 4001 'MD5': Array('encryption-functions', 'function_md5'), 4002 'MICROSECOND': Array('date-and-time-functions', 'function_microsecond'), 4003 'MID': Array('string-functions', 'function_mid'), 4004 'MIN': Array('group-by-functions', 'function_min'), 4005 'MINUTE': Array('date-and-time-functions', 'function_minute'), 4006 'MOD': Array('mathematical-functions', 'function_mod'), 4007 'MONTH': Array('date-and-time-functions', 'function_month'), 4008 'MONTHNAME': Array('date-and-time-functions', 'function_monthname'), 4009 'NAME_CONST': Array('miscellaneous-functions', 'function_name_const'), 4010 'NOT': Array('logical-operators', 'operator_not'), 4011 'NOW': Array('date-and-time-functions', 'function_now'), 4012 'NULLIF': Array('control-flow-functions', 'function_nullif'), 4013 'OCT': Array('mathematical-functions', 'function_oct'), 4014 'OCTET_LENGTH': Array('string-functions', 'function_octet_length'), 4015 'OLD_PASSWORD': Array('encryption-functions', 'function_old_password'), 4016 'OR': Array('logical-operators', 'operator_or'), 4017 'ORD': Array('string-functions', 'function_ord'), 4018 'PASSWORD': Array('encryption-functions', 'function_password'), 4019 'PERIOD_ADD': Array('date-and-time-functions', 'function_period_add'), 4020 'PERIOD_DIFF': Array('date-and-time-functions', 'function_period_diff'), 4021 'PI': Array('mathematical-functions', 'function_pi'), 4022 'POSITION': Array('string-functions', 'function_position'), 4023 'POW': Array('mathematical-functions', 'function_pow'), 4024 'POWER': Array('mathematical-functions', 'function_power'),
4025 'QUARTER': Array('date-and-time-functions', 'function_quarter'), 4026 'QUOTE': Array('string-functions', 'function_quote'), 4027 'RADIANS': Array('mathematical-functions', 'function_radians'), 4028 'RAND': Array('mathematical-functions', 'function_rand'), 4029 'REGEXP': Array('regexp', 'operator_regexp'), 4030 'RELEASE_LOCK': Array('miscellaneous-functions', 'function_release_lock'), 4031 'REPEAT': Array('string-functions', 'function_repeat'), 4032 'REVERSE': Array('string-functions', 'function_reverse'), 4033 'RIGHT': Array('string-functions', 'function_right'), 4034 'RLIKE': Array('regexp', 'operator_rlike'), 4035 'ROUND': Array('mathematical-functions', 'function_round'), 4036 'ROW_COUNT': Array('information-functions', 'function_row_count'), 4037 'RPAD': Array('string-functions', 'function_rpad'), 4038 'RTRIM': Array('string-functions', 'function_rtrim'), 4039 'SCHEMA': Array('information-functions', 'function_schema'), 4040 'SEC_TO_TIME': Array('date-and-time-functions', 'function_sec_to_time'), 4041 'SECOND': Array('date-and-time-functions', 'function_second'), 4042 'SESSION_USER': Array('information-functions', 'function_session_user'), 4043 'SHA': Array('encryption-functions', 'function_sha1'), 4044 'SHA1': Array('encryption-functions', 'function_sha1'), 4045 'SIGN': Array('mathematical-functions', 'function_sign'), 4046 'SIN': Array('mathematical-functions', 'function_sin'), 4047 'SLEEP': Array('miscellaneous-functions', 'function_sleep'), 4048 'SOUNDEX': Array('string-functions', 'function_soundex'), 4049 'SPACE': Array('string-functions', 'function_space'), 4050 'SQRT': Array('mathematical-functions', 'function_sqrt'), 4051 'STD': Array('group-by-functions', 'function_std'), 4052 'STDDEV_POP': Array('group-by-functions', 'function_stddev_pop'), 4053 'STDDEV_SAMP': Array('group-by-functions', 'function_stddev_samp'), 4054 'STDDEV': Array('group-by-functions', 'function_stddev'), 4055 'STR_TO_DATE': Array('date-and-time-functions', 'function_str_to_date'), 4056 'STRCMP': Array('string-comparison-functions', 'function_strcmp'), 4057 'SUBDATE': Array('date-and-time-functions', 'function_subdate'), 4058 'SUBSTR': Array('string-functions', 'function_substr'), 4059 'SUBSTRING_INDEX': Array('string-functions', 'function_substring_index'), 4060 'SUBSTRING': Array('string-functions', 'function_substring'), 4061 'SUBTIME': Array('date-and-time-functions', 'function_subtime'), 4062 'SUM': Array('group-by-functions', 'function_sum'), 4063 'SYSDATE': Array('date-and-time-functions', 'function_sysdate'), 4064 'SYSTEM_USER': Array('information-functions', 'function_system_user'), 4065 'TAN': Array('mathematical-functions', 'function_tan'), 4066 'TIME_FORMAT': Array('date-and-time-functions', 'function_time_format'), 4067 'TIME_TO_SEC': Array('date-and-time-functions', 'function_time_to_sec'), 4068 'TIME': Array('date-and-time-functions', 'function_time'), 4069 'TIMEDIFF': Array('date-and-time-functions', 'function_timediff'), 4070 'TIMESTAMP': Array('date-and-time-functions', 'function_timestamp'), 4071 'TIMESTAMPADD': Array('date-and-time-functions', 'function_timestampadd'), 4072 'TIMESTAMPDIFF': Array('date-and-time-functions', 'function_timestampdiff'), 4073 'TO_DAYS': Array('date-and-time-functions', 'function_to_days'), 4074 'TRIM': Array('string-functions', 'function_trim'), 4075 'TRUNCATE': Array('mathematical-functions', 'function_truncate'), 4076 'UCASE': Array('string-functions', 'function_ucase'), 4077 'UNCOMPRESS': Array('encryption-functions', 'function_uncompress'), 4078 'UNCOMPRESSED_LENGTH': Array('encryption-functions', 'function_uncompressed_length'), 4079 'UNHEX': Array('string-functions', 'function_unhex'), 4080 'UNIX_TIMESTAMP': Array('date-and-time-functions', 'function_unix_timestamp'), 4081 'UpdateXML': Array('xml-functions', 'function_updatexml'), 4082 'UPPER': Array('string-functions', 'function_upper'), 4083 'USER': Array('information-functions', 'function_user'), 4084 'UTC_DATE': Array('date-and-time-functions', 'function_utc_date'), 4085 'UTC_TIME': Array('date-and-time-functions', 'function_utc_time'), 4086 'UTC_TIMESTAMP': Array('date-and-time-functions', 'function_utc_timestamp'), 4087 'UUID_SHORT': Array('miscellaneous-functions', 'function_uuid_short'), 4088 'UUID': Array('miscellaneous-functions', 'function_uuid'), 4089 'VALUES': Array('miscellaneous-functions', 'function_values'), 4090 'VAR_POP': Array('group-by-functions', 'function_var_pop'), 4091 'VAR_SAMP': Array('group-by-functions', 'function_var_samp'),
4092 'VARIANCE': Array('group-by-functions', 'function_variance'), 4093 'VERSION': Array('information-functions', 'function_version'), 4094 'WEEK': Array('date-and-time-functions', 'function_week'), 4095 'WEEKDAY': Array('date-and-time-functions', 'function_weekday'), 4096 'WEEKOFYEAR': Array('date-and-time-functions', 'function_weekofyear'), 4097 'XOR': Array('logical-operators', 'operator_xor'), 4098 'YEAR': Array('date-and-time-functions', 'function_year'), 4099 'YEARWEEK': Array('date-and-time-functions', 'function_yearweek'), 4100 'SOUNDS_LIKE': Array('string-functions', 'operator_sounds-like'), 4101 'IS_NOT_NULL': Array('comparison-operators', 'operator_is-not-null'), 4102 'IS_NOT': Array('comparison-operators', 'operator_is-not'), 4103 'IS_NULL': Array('comparison-operators', 'operator_is-null'), 4104 'NOT_LIKE': Array('string-comparison-functions', 'operator_not-like'), 4105 'NOT_REGEXP': Array('regexp', 'operator_not-regexp'), 4106 'COUNT_DISTINCT': Array('group-by-functions', 'function_count-distinct'), 4107 'NOT_IN': Array('comparison-operators', 'function_not-in') 4108}; 4109 4110var mysql_doc_builtin = { 4111 'TINYINT': Array('numeric-types'), 4112 'SMALLINT': Array('numeric-types'), 4113 'MEDIUMINT': Array('numeric-types'), 4114 'INT': Array('numeric-types'), 4115 'BIGINT': Array('numeric-types'), 4116 'DECIMAL': Array('numeric-types'), 4117 'FLOAT': Array('numeric-types'), 4118 'DOUBLE': Array('numeric-types'), 4119 'REAL': Array('numeric-types'), 4120 'BIT': Array('numeric-types'), 4121 'BOOLEAN': Array('numeric-types'), 4122 'SERIAL': Array('numeric-types'), 4123 'DATE': Array('date-and-time-types'), 4124 'DATETIME': Array('date-and-time-types'), 4125 'TIMESTAMP': Array('date-and-time-types'), 4126 'TIME': Array('date-and-time-types'), 4127 'YEAR': Array('date-and-time-types'), 4128 'CHAR': Array('string-types'), 4129 'VARCHAR': Array('string-types'), 4130 'TINYTEXT': Array('string-types'), 4131 'TEXT': Array('string-types'), 4132 'MEDIUMTEXT': Array('string-types'), 4133 'LONGTEXT': Array('string-types'), 4134 'BINARY': Array('string-types'), 4135 'VARBINARY': Array('string-types'), 4136 'TINYBLOB': Array('string-types'), 4137 'MEDIUMBLOB': Array('string-types'), 4138 'BLOB': Array('string-types'), 4139 'LONGBLOB': Array('string-types'), 4140 'ENUM': Array('string-types'), 4141 'SET': Array('string-types') 4142}; 4143; 4144 4145/* vim: set expandtab sw=4 ts=4 sts=4: */ 4146/** 4147 * general function, usually for data manipulation pages 4148 * 4149 */ 4150 4151/** 4152 * @var sql_box_locked lock for the sqlbox textarea in the querybox 4153 */ 4154var sql_box_locked = false; 4155 4156/** 4157 * @var array holds elements which content should only selected once 4158 */ 4159var only_once_elements = []; 4160 4161/** 4162 * @var int ajax_message_count Number of AJAX messages shown since page load 4163 */ 4164var ajax_message_count = 0; 4165 4166/** 4167 * @var codemirror_editor object containing CodeMirror editor of the query editor in SQL tab 4168 */ 4169var codemirror_editor = false; 4170 4171/** 4172 * @var codemirror_editor object containing CodeMirror editor of the inline query editor 4173 */ 4174var codemirror_inline_editor = false; 4175 4176/** 4177 * @var sql_autocomplete_in_progress bool shows if Table/Column name autocomplete AJAX is in progress 4178 */ 4179var sql_autocomplete_in_progress = false; 4180 4181/** 4182 * @var sql_autocomplete object containing list of columns in each table 4183 */ 4184var sql_autocomplete = false; 4185 4186/**
4187 * @var sql_autocomplete_default_table string containing default table to autocomplete columns 4188 */ 4189var sql_autocomplete_default_table = ''; 4190 4191/** 4192 * @var central_column_list array to hold the columns in central list per db. 4193 */ 4194var central_column_list = []; 4195 4196/** 4197 * @var primary_indexes array to hold 'Primary' index columns. 4198 */ 4199var primary_indexes = []; 4200 4201/** 4202 * @var unique_indexes array to hold 'Unique' index columns. 4203 */ 4204var unique_indexes = []; 4205 4206/** 4207 * @var indexes array to hold 'Index' columns. 4208 */ 4209var indexes = []; 4210 4211/** 4212 * @var fulltext_indexes array to hold 'Fulltext' columns. 4213 */ 4214var fulltext_indexes = []; 4215 4216/** 4217 * @var spatial_indexes array to hold 'Spatial' columns. 4218 */ 4219var spatial_indexes = []; 4220 4221/** 4222 * Make sure that ajax requests will not be cached 4223 * by appending a random variable to their parameters 4224 */ 4225$.ajaxPrefilter(function (options, originalOptions, jqXHR) { 4226 var nocache = new Date().getTime() + "" + Math.floor(Math.random() * 1000000); 4227 if (typeof options.data == "string") { 4228 options.data += "&_nocache=" + nocache; 4229 } else if (typeof options.data == "object") { 4230 options.data = $.extend(originalOptions.data, {'_nocache' : nocache}); 4231 } 4232}); 4233 4234/* 4235 * Adds a date/time picker to an element 4236 * 4237 * @param object $this_element a jQuery object pointing to the element 4238 */ 4239function PMA_addDatepicker($this_element, type, options) 4240{ 4241 var showTimepicker = true; 4242 if (type=="date") { 4243 showTimepicker = false; 4244 } 4245 4246 var defaultOptions = { 4247 showOn: 'button', 4248 buttonImage: themeCalendarImage, // defined in js/messages.php 4249 buttonImageOnly: true, 4250 stepMinutes: 1, 4251 stepHours: 1, 4252 showSecond: true, 4253 showMillisec: true, 4254 showMicrosec: true, 4255 showTimepicker: showTimepicker, 4256 showButtonPanel: false, 4257 dateFormat: 'yy-mm-dd', // yy means year with four digits 4258 timeFormat: 'HH:mm:ss.lc', 4259 constrainInput: false, 4260 altFieldTimeOnly: false, 4261 showAnim: '', 4262 beforeShow: function (input, inst) { 4263 // Remember that we came from the datepicker; this is used 4264 // in tbl_change.js by verificationsAfterFieldChange() 4265 $this_element.data('comes_from', 'datepicker'); 4266 if ($(input).closest('.cEdit').length > 0) { 4267 setTimeout(function () { 4268 inst.dpDiv.css({ 4269 top: 0, 4270 left: 0, 4271 position: 'relative' 4272 }); 4273 }, 0); 4274 } 4275 // Fix wrong timepicker z-index, doesn't work without timeout 4276 setTimeout(function () { 4277 $('#ui-timepicker-div').css('z-index', $('#ui-datepicker-div').css('z-index')); 4278 }, 0); 4279 }, 4280 onSelect: function() { 4281 $this_element.data('datepicker').inline = true; 4282 }, 4283 onClose: function (dateText, dp_inst) { 4284 // The value is no more from the date picker 4285 $this_element.data('comes_from', ''); 4286 if (typeof $this_element.data('datepicker') !== 'undefined') { 4287 $this_element.data('datepicker').inline = false; 4288 } 4289 } 4290 }; 4291 if (type == "datetime" || type == "timestamp") { 4292 $this_element.datetimepicker($.extend(defaultOptions, options)); 4293 } 4294 else if (type == "date") { 4295 $this_element.datetimepicker($.extend(defaultOptions, options)); 4296 } 4297 else if (type == "time") { 4298 $this_element.timepicker($.extend(defaultOptions, options)); 4299 // Add a tip regarding entering MySQL allowed-values for TIME data-type 4300 PMA_tooltip($this_element, 'input', PMA_messages.strMysqlAllowedValuesTip); 4301 } 4302} 4303 4304/** 4305 * Add a date/time picker to each element that needs it 4306 * (only when jquery-ui-timepicker-addon.js is loaded) 4307 */ 4308function addDateTimePicker() { 4309 if ($.timepicker !== undefined) { 4310 $('input.timefield, input.datefield, input.datetimefield').each(function () { 4311 4312 var decimals = $(this).parent().attr('data-decimals'); 4313 var type = $(this).parent().attr('data-type'); 4314 4315 var showMillisec = false;
4316 var showMicrosec = false; 4317 var timeFormat = 'HH:mm:ss'; 4318 // check for decimal places of seconds 4319 if (decimals > 0 && type.indexOf('time') != -1){ 4320 if (decimals > 3) { 4321 showMillisec = true; 4322 showMicrosec = true; 4323 timeFormat = 'HH:mm:ss.lc'; 4324 } else { 4325 showMillisec = true; 4326 timeFormat = 'HH:mm:ss.l'; 4327 } 4328 } 4329 PMA_addDatepicker($(this), type, { 4330 showMillisec: showMillisec, 4331 showMicrosec: showMicrosec, 4332 timeFormat: timeFormat 4333 }); 4334 4335 // Add a tip regarding entering MySQL allowed-values 4336 // for TIME and DATE data-type 4337 if ($(this).hasClass('timefield')) { 4338 PMA_tooltip($(this), 'input', PMA_messages.strMysqlAllowedValuesTipTime); 4339 } else if ($(this).hasClass('datefield')) { 4340 PMA_tooltip($(this), 'input', PMA_messages.strMysqlAllowedValuesTipDate); 4341 } 4342 }); 4343 } 4344} 4345 4346/** 4347 * Handle redirect and reload flags sent as part of AJAX requests 4348 * 4349 * @param data ajax response data 4350 */ 4351function PMA_handleRedirectAndReload(data) { 4352 if (parseInt(data.redirect_flag) == 1) { 4353 // add one more GET param to display session expiry msg 4354 if (window.location.href.indexOf('?') === -1) { 4355 window.location.href += '?session_expired=1'; 4356 } else { 4357 window.location.href += '&session_expired=1'; 4358 } 4359 window.location.reload(); 4360 } else if (parseInt(data.reload_flag) == 1) { 4361 // remove the token param and reload 4362 window.location.href = window.location.href.replace(/&?token=[^&#]*/g, ""); 4363 window.location.reload(); 4364 } 4365} 4366 4367/** 4368 * Creates an SQL editor which supports auto completing etc. 4369 * 4370 * @param $textarea jQuery object wrapping the textarea to be made the editor 4371 * @param options optional options for CodeMirror 4372 * @param resize optional resizing ('vertical', 'horizontal', 'both') 4373 * @param lintOptions additional options for lint 4374 */ 4375function PMA_getSQLEditor($textarea, options, resize, lintOptions) { 4376 if ($textarea.length > 0 && typeof CodeMirror !== 'undefined') { 4377 4378 // merge options for CodeMirror 4379 var defaults = { 4380 lineNumbers: true, 4381 matchBrackets: true, 4382 extraKeys: {"Ctrl-Space": "autocomplete"}, 4383 hintOptions: {"completeSingle": false, "completeOnSingleClick": true}, 4384 indentUnit: 4, 4385 mode: "text/x-mysql", 4386 lineWrapping: true 4387 }; 4388 4389 if (CodeMirror.sqlLint) { 4390 $.extend(defaults, { 4391 gutters: ["CodeMirror-lint-markers"], 4392 lint: { 4393 "getAnnotations": CodeMirror.sqlLint, 4394 "async": true, 4395 "lintOptions": lintOptions 4396 } 4397 }); 4398 } 4399 4400 $.extend(true, defaults, options); 4401 4402 // create CodeMirror editor 4403 var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults); 4404 // allow resizing 4405 if (! resize) { 4406 resize = 'vertical'; 4407 } 4408 var handles = ''; 4409 if (resize == 'vertical') { 4410 handles = 's'; 4411 } 4412 if (resize == 'both') { 4413 handles = 'all'; 4414 } 4415 if (resize == 'horizontal') { 4416 handles = 'e, w'; 4417 } 4418 $(codemirrorEditor.getWrapperElement()) 4419 .css('resize', resize) 4420 .resizable({ 4421 handles: handles, 4422 resize: function() { 4423 codemirrorEditor.setSize($(this).width(), $(this).height()); 4424 } 4425 }); 4426 // enable autocomplete 4427 codemirrorEditor.on("inputRead", codemirrorAutocompleteOnInputRead); 4428 4429 return codemirrorEditor; 4430 } 4431 return null; 4432} 4433 4434/** 4435 * Clear text selection 4436 */ 4437function PMA_clearSelection() { 4438 if (document.selection && document.selection.empty) { 4439 document.selection.empty(); 4440 } else if (window.getSelection) { 4441 var sel = window.getSelection(); 4442 if (sel.empty) { 4443 sel.empty(); 4444 } 4445 if (sel.removeAllRanges) { 4446 sel.removeAllRanges(); 4447 } 4448 } 4449} 4450 4451/** 4452 * Create a jQuery UI tooltip 4453 * 4454 * @param $elements jQuery object representing the elements 4455 * @param item the item 4456 * (see https://api.jqueryui.com/tooltip/#option-items) 4457 * @param myContent content of the tooltip 4458 * @param additionalOptions to override the default options 4459 * 4460 */ 4461function PMA_tooltip($elements, item, myContent, additionalOptions) 4462{ 4463 if ($('#no_hint').length > 0) { 4464 return; 4465 } 4466 4467 var defaultOptions = { 4468 content: myContent, 4469 items: item, 4470 tooltipClass: "tooltip", 4471 track: true, 4472 show: false, 4473 hide: false 4474 }; 4475 4476 $elements.tooltip($.extend(true, defaultOptions, additionalOptions)); 4477} 4478 4479/** 4480 * HTML escaping 4481 */ 4482 4483function escapeHtml(unsafe) { 4484 if (typeof(unsafe) != 'undefined') { 4485 return unsafe 4486 .toString() 4487 .replace(/&/g, "&") 4488 .replace(/</g, "<") 4489 .replace(/>/g, ">") 4490 .replace(/"/g, """) 4491 .replace(/'/g, "'"); 4492 } else { 4493 return false;
4494 } 4495} 4496 4497function escapeJsString(unsafe) { 4498 if (typeof(unsafe) != 'undefined') { 4499 return unsafe 4500 .toString() 4501 .replace("\000", '') 4502 .replace('\\', '\\\\') 4503 .replace('\'', '\\\'') 4504 .replace("'", "\\\'") 4505 .replace('"', '\"') 4506 .replace(""", "\"") 4507 .replace("\n", '\n') 4508 .replace("\r", '\r') 4509 .replace(/<\/script/gi, '</\' + \'script') 4510 } else { 4511 return false; 4512 } 4513} 4514 4515function PMA_sprintf() { 4516 return sprintf.apply(this, arguments); 4517} 4518 4519/** 4520 * Hides/shows the default value input field, depending on the default type 4521 * Ticks the NULL checkbox if NULL is chosen as default value. 4522 */ 4523function PMA_hideShowDefaultValue($default_type) 4524{ 4525 if ($default_type.val() == 'USER_DEFINED') { 4526 $default_type.siblings('.default_value').show().focus(); 4527 } else { 4528 $default_type.siblings('.default_value').hide(); 4529 if ($default_type.val() == 'NULL') { 4530 var $null_checkbox = $default_type.closest('tr').find('.allow_null'); 4531 $null_checkbox.prop('checked', true); 4532 } 4533 } 4534} 4535 4536/** 4537 * Hides/shows the input field for column expression based on whether 4538 * VIRTUAL/PERSISTENT is selected 4539 * 4540 * @param $virtuality virtuality dropdown 4541 */ 4542function PMA_hideShowExpression($virtuality) 4543{ 4544 if ($virtuality.val() === '') { 4545 $virtuality.siblings('.expression').hide(); 4546 } else { 4547 $virtuality.siblings('.expression').show(); 4548 } 4549} 4550 4551/** 4552 * Show notices for ENUM columns; add/hide the default value 4553 * 4554 */ 4555function PMA_verifyColumnsProperties() 4556{ 4557 $("select.column_type").each(function () { 4558 PMA_showNoticeForEnum($(this)); 4559 }); 4560 $("select.default_type").each(function () { 4561 PMA_hideShowDefaultValue($(this)); 4562 }); 4563 $('select.virtuality').each(function () { 4564 PMA_hideShowExpression($(this)); 4565 }); 4566} 4567 4568/** 4569 * Add a hidden field to the form to indicate that this will be an 4570 * Ajax request (only if this hidden field does not exist) 4571 * 4572 * @param $form object the form 4573 */ 4574function PMA_prepareForAjaxRequest($form) 4575{ 4576 if (! $form.find('input:hidden').is('#ajax_request_hidden')) { 4577 $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true" />'); 4578 } 4579} 4580 4581/** 4582 * Generate a new password and copy it to the password input areas 4583 * 4584 * @param passwd_form object the form that holds the password fields 4585 * 4586 * @return boolean always true 4587 */ 4588function suggestPassword(passwd_form) 4589{ 4590 // restrict the password to just letters and numbers to avoid problems: 4591 // "editors and viewers regard the password as multiple words and 4592 // things like double click no longer work" 4593 var pwchars = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZ"; 4594 var passwordlength = 16; // do we want that to be dynamic? no, keep it simple :) 4595 var passwd = passwd_form.generated_pw; 4596 var randomWords = new Int32Array(passwordlength); 4597 4598 passwd.value = ''; 4599 4600 // First we're going to try to use a built-in CSPRNG 4601 if (window.crypto && window.crypto.getRandomValues) { 4602 window.crypto.getRandomValues(randomWords); 4603 } 4604 // Because of course IE calls it msCrypto instead of being standard 4605 else if (window.msCrypto && window.msCrypto.getRandomValues) { 4606 window.msCrypto.getRandomValues(randomWords); 4607 } else { 4608 // Fallback to Math.random 4609 for (var i = 0; i < passwordlength; i++) { 4610 randomWords[i] = Math.floor(Math.random() * pwchars.length); 4611 } 4612 } 4613 4614 for (var i = 0; i < passwordlength; i++) { 4615 passwd.value += pwchars.charAt(Math.abs(randomWords[i]) % pwchars.length); 4616 } 4617 4618 passwd_form.text_pma_pw.value = passwd.value; 4619 passwd_form.text_pma_pw2.value = passwd.value; 4620 return true; 4621} 4622 4623/** 4624 * Version string to integer conversion. 4625 */ 4626function parseVersionString(str) 4627{ 4628 if (typeof(str) != 'string') { return false; } 4629 var add = 0; 4630 // Parse possible alpha/beta/rc/ 4631 var state = str.split('-'); 4632 if (state.length >= 2) { 4633 if (state[1].substr(0, 2) == 'rc') { 4634 add = - 20 - parseInt(state[1].substr(2), 10); 4635 } else if (state[1].substr(0, 4) == 'beta') { 4636 add = - 40 - parseInt(state[1].substr(4), 10); 4637 } else if (state[1].substr(0, 5) == 'alpha') { 4638 add = - 60 - parseInt(state[1].substr(5), 10); 4639 } else if (state[1].substr(0, 3) == 'dev') { 4640 /* We don't handle dev, it's git snapshot */ 4641 add = 0; 4642 } 4643 } 4644 // Parse version 4645 var x = str.split('.'); 4646 // Use 0 for non existing parts 4647 var maj = parseInt(x[0], 10) || 0; 4648 var min = parseInt(x[1], 10) || 0; 4649 var pat = parseInt(x[2], 10) || 0; 4650 var hotfix = parseInt(x[3], 10) || 0; 4651 return maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add; 4652} 4653 4654/** 4655 * Indicates current available version on main page. 4656 */ 4657function PMA_current_version(data) 4658{ 4659 if (data && data.version && data.date) { 4660 var current = parseVersionString($('span.version').text()); 4661 var latest = parseVersionString(data.version);
4662 var url = 'https://web.phpmyadmin.net/files/' + escapeHtml(encodeURIComponent(data.version)) + '/'; 4663 var version_information_message = '<span class="latest">' + 4664 PMA_messages.strLatestAvailable + 4665 ' <a href="' + url + '" class="disableAjax">' + escapeHtml(data.version) + '</a>' + 4666 '</span>'; 4667 if (latest > current) { 4668 var message = PMA_sprintf( 4669 PMA_messages.strNewerVersion, 4670 escapeHtml(data.version), 4671 escapeHtml(data.date) 4672 ); 4673 var htmlClass = 'notice'; 4674 if (Math.floor(latest / 10000) === Math.floor(current / 10000)) { 4675 /* Security update */ 4676 htmlClass = 'error'; 4677 } 4678 $('#newer_version_notice').remove(); 4679 $('#maincontainer').after('<div id="newer_version_notice" class="' + htmlClass + '"><a href="' + url + '" class="disableAjax">' + message + '</a></div>'); 4680 } 4681 if (latest === current) { 4682 version_information_message = ' (' + PMA_messages.strUpToDate + ')'; 4683 } 4684 var $liPmaVersion = $('#li_pma_version'); 4685 $liPmaVersion.find('span.latest').remove(); 4686 $liPmaVersion.append(version_information_message); 4687 } 4688} 4689 4690/** 4691 * Loads Git revision data from ajax for index.php 4692 */ 4693function PMA_display_git_revision() 4694{ 4695 $('#is_git_revision').remove(); 4696 $('#li_pma_version_git').remove(); 4697 $.get( 4698 "index.php", 4699 { 4700 "server": PMA_commonParams.get('server'), 4701 "token": PMA_commonParams.get('token'), 4702 "git_revision": true, 4703 "ajax_request": true, 4704 "no_debug": true 4705 }, 4706 function (data) { 4707 if (typeof data !== 'undefined' && data.success === true) { 4708 $(data.message).insertAfter('#li_pma_version'); 4709 } 4710 } 4711 ); 4712} 4713 4714/** 4715 * for libraries/display_change_password.lib.php 4716 * libraries/user_password.php 4717 * 4718 */ 4719 4720function displayPasswordGenerateButton() 4721{ 4722 $('#tr_element_before_generate_password').parent().append('<tr class="vmiddle"><td>' + PMA_messages.strGeneratePassword + '</td><td><input type="button" class="button" id="button_generate_password" value="' + PMA_messages.strGenerate + '" onclick="suggestPassword(this.form)" /><input type="text" name="generated_pw" id="generated_pw" /></td></tr>'); 4723 $('#div_element_before_generate_password').parent().append('<div class="item"><label for="button_generate_password">' + PMA_messages.strGeneratePassword + ':</label><span class="options"><input type="button" class="button" id="button_generate_password" value="' + PMA_messages.strGenerate + '" onclick="suggestPassword(this.form)" /></span><input type="text" name="generated_pw" id="generated_pw" /></div>'); 4724} 4725 4726/** 4727 * selects the content of a given object, f.e. a textarea 4728 * 4729 * @param element object element of which the content will be selected 4730 * @param lock var variable which holds the lock for this element 4731 * or true, if no lock exists 4732 * @param only_once boolean if true this is only done once 4733 * f.e. only on first focus 4734 */ 4735function selectContent(element, lock, only_once) 4736{ 4737 if (only_once && only_once_elements[element.name]) { 4738 return; 4739 } 4740 4741 only_once_elements[element.name] = true; 4742 4743 if (lock) { 4744 return; 4745 } 4746 4747 element.select(); 4748} 4749 4750/** 4751 * Displays a confirmation box before submitting a "DROP/DELETE/ALTER" query. 4752 * This function is called while clicking links 4753 * 4754 * @param theLink object the link 4755 * @param theSqlQuery object the sql query to submit 4756 * 4757 * @return boolean whether to run the query or not 4758 */ 4759function confirmLink(theLink, theSqlQuery) 4760{ 4761 // Confirmation is not required in the configuration file 4762 // or browser is Opera (crappy js implementation) 4763 if (PMA_messages.strDoYouReally === '' || typeof(window.opera) != 'undefined') { 4764 return true; 4765 } 4766 4767 var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, theSqlQuery)); 4768 if (is_confirmed) { 4769 if ($(theLink).hasClass('formLinkSubmit')) { 4770 var name = 'is_js_confirmed'; 4771 4772 if ($(theLink).attr('href').indexOf('usesubform') != -1) { 4773 var matches = $(theLink).attr('href').substr('#').match(/usesubform\[(\d+)\]/i); 4774 if (matches != null) { 4775 name = 'subform[' + matches[1] + '][is_js_confirmed]'; 4776 } 4777 } 4778 4779 $(theLink).parents('form').append('<input type="hidden" name="' + name + '" value="1" />'); 4780 } else if (typeof(theLink.href) != 'undefined') { 4781 theLink.href += '&is_js_confirmed=1'; 4782 } else if (typeof(theLink.form) != 'undefined') { 4783 theLink.form.action += '?is_js_confirmed=1'; 4784 } 4785 } 4786 4787 return is_confirmed; 4788} // end of the 'confirmLink()' function 4789 4790/**
4791 * Confirms a "DROP/DELETE/ALTER" query before 4792 * submitting it if required. 4793 * This function is called by the 'checkSqlQuery()' js function. 4794 * 4795 * @param theForm1 object the form 4796 * @param sqlQuery1 object the sql query textarea 4797 * 4798 * @return boolean whether to run the query or not 4799 * 4800 * @see checkSqlQuery() 4801 */ 4802function confirmQuery(theForm1, sqlQuery1) 4803{ 4804 // Confirmation is not required in the configuration file 4805 if (PMA_messages.strDoYouReally === '') { 4806 return true; 4807 } 4808 4809 // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement 4810 // 4811 // TODO: find a way (if possible) to use the parser-analyser 4812 // for this kind of verification 4813 // For now, I just added a ^ to check for the statement at 4814 // beginning of expression 4815 4816 var do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|PROCEDURE)\\s', 'i'); 4817 var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i'); 4818 var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i'); 4819 var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i'); 4820 4821 if (do_confirm_re_0.test(sqlQuery1.value) || 4822 do_confirm_re_1.test(sqlQuery1.value) || 4823 do_confirm_re_2.test(sqlQuery1.value) || 4824 do_confirm_re_3.test(sqlQuery1.value)) { 4825 var message; 4826 if (sqlQuery1.value.length > 100) { 4827 message = sqlQuery1.value.substr(0, 100) + '\n ...'; 4828 } else { 4829 message = sqlQuery1.value; 4830 } 4831 var is_confirmed = confirm(PMA_sprintf(PMA_messages.strDoYouReally, message)); 4832 // statement is confirmed -> update the 4833 // "is_js_confirmed" form field so the confirm test won't be 4834 // run on the server side and allows to submit the form 4835 if (is_confirmed) { 4836 theForm1.elements.is_js_confirmed.value = 1; 4837 return true; 4838 } 4839 // statement is rejected -> do not submit the form 4840 else { 4841 window.focus(); 4842 sqlQuery1.focus(); 4843 return false; 4844 } // end if (handle confirm box result) 4845 } // end if (display confirm box) 4846 4847 return true; 4848} // end of the 'confirmQuery()' function 4849 4850/** 4851 * Displays an error message if the user submitted the sql query form with no 4852 * sql query, else checks for "DROP/DELETE/ALTER" statements 4853 * 4854 * @param theForm object the form 4855 * 4856 * @return boolean always false 4857 * 4858 * @see confirmQuery() 4859 */ 4860function checkSqlQuery(theForm) 4861{ 4862 // get the textarea element containing the query 4863 var sqlQuery; 4864 if (codemirror_editor) { 4865 codemirror_editor.save(); 4866 sqlQuery = codemirror_editor.getValue(); 4867 } else { 4868 sqlQuery = theForm.elements.sql_query.value; 4869 } 4870 var isEmpty = 1; 4871 var space_re = new RegExp('\\s+'); 4872 if (typeof(theForm.elements.sql_file) != 'undefined' && 4873 theForm.elements.sql_file.value.replace(space_re, '') !== '') { 4874 return true; 4875 } 4876 if (isEmpty && typeof(theForm.elements.id_bookmark) != 'undefined' && 4877 (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') && 4878 theForm.elements.id_bookmark.selectedIndex !== 0) { 4879 return true; 4880 } 4881 // Checks for "DROP/DELETE/ALTER" statements 4882 if (sqlQuery.replace(space_re, '') !== '') { 4883 return confirmQuery(theForm, sqlQuery); 4884 } 4885 theForm.reset(); 4886 isEmpty = 1; 4887 4888 if (isEmpty) { 4889 alert(PMA_messages.strFormEmpty); 4890 codemirror_editor.focus(); 4891 return false; 4892 } 4893 4894 return true; 4895} // end of the 'checkSqlQuery()' function 4896 4897/** 4898 * Check if a form's element is empty. 4899 * An element containing only spaces is also considered empty 4900 * 4901 * @param object the form 4902 * @param string the name of the form field to put the focus on 4903 * 4904 * @return boolean whether the form field is empty or not 4905 */ 4906function emptyCheckTheField(theForm, theFieldName) 4907{ 4908 var theField = theForm.elements[theFieldName]; 4909 var space_re = new RegExp('\\s+'); 4910 return theField.value.replace(space_re, '') === ''; 4911} // end of the 'emptyCheckTheField()' function 4912 4913/** 4914 * Ensures a value submitted in a form is numeric and is in a range 4915 * 4916 * @param object the form 4917 * @param string the name of the form field to check 4918 * @param integer the minimum authorized value 4919 * @param integer the maximum authorized value 4920 * 4921 * @return boolean whether a valid number has been submitted or not 4922 */ 4923function checkFormElementInRange(theForm, theFieldName, message, min, max) 4924{ 4925 var theField = theForm.elements[theFieldName]; 4926 var val = parseInt(theField.value, 10); 4927 4928 if (typeof(min) == 'undefined') { 4929 min = 0; 4930 } 4931 if (typeof(max) == 'undefined') { 4932 max = Number.MAX_VALUE; 4933 } 4934 4935 // It's not a number 4936 if (isNaN(val)) { 4937 theField.select(); 4938 alert(PMA_messages.strEnterValidNumber); 4939 theField.focus(); 4940 return false;
4941 } 4942 // It's a number but it is not between min and max 4943 else if (val < min || val > max) { 4944 theField.select(); 4945 alert(PMA_sprintf(message, val)); 4946 theField.focus(); 4947 return false; 4948 } 4949 // It's a valid number 4950 else { 4951 theField.value = val; 4952 } 4953 return true; 4954 4955} // end of the 'checkFormElementInRange()' function 4956 4957 4958function checkTableEditForm(theForm, fieldsCnt) 4959{ 4960 // TODO: avoid sending a message if user just wants to add a line 4961 // on the form but has not completed at least one field name 4962 4963 var atLeastOneField = 0; 4964 var i, elm, elm2, elm3, val, id; 4965 4966 for (i = 0; i < fieldsCnt; i++) { 4967 id = "#field_" + i + "_2"; 4968 elm = $(id); 4969 val = elm.val(); 4970 if (val == 'VARCHAR' || val == 'CHAR' || val == 'BIT' || val == 'VARBINARY' || val == 'BINARY') { 4971 elm2 = $("#field_" + i + "_3"); 4972 val = parseInt(elm2.val(), 10); 4973 elm3 = $("#field_" + i + "_1"); 4974 if (isNaN(val) && elm3.val() !== "") { 4975 elm2.select(); 4976 alert(PMA_messages.strEnterValidLength); 4977 elm2.focus(); 4978 return false; 4979 } 4980 } 4981 4982 if (atLeastOneField === 0) { 4983 id = "field_" + i + "_1"; 4984 if (!emptyCheckTheField(theForm, id)) { 4985 atLeastOneField = 1; 4986 } 4987 } 4988 } 4989 if (atLeastOneField === 0) { 4990 var theField = theForm.elements.field_0_1; 4991 alert(PMA_messages.strFormEmpty); 4992 theField.focus(); 4993 return false; 4994 } 4995 4996 // at least this section is under jQuery 4997 var $input = $("input.textfield[name='table']"); 4998 if ($input.val() === "") { 4999 alert(PMA_messages.strFormEmpty); 5000 $input.focus(); 5001 return false; 5002 } 5003 5004 return true; 5005} // enf of the 'checkTableEditForm()' function 5006 5007/** 5008 * True if last click is to check a row. 5009 */ 5010var last_click_checked = false; 5011 5012/** 5013 * Zero-based index of last clicked row. 5014 * Used to handle the shift + click event in the code above. 5015 */ 5016var last_clicked_row = -1; 5017 5018/** 5019 * Zero-based index of last shift clicked row. 5020 */ 5021var last_shift_clicked_row = -1; 5022 5023var _idleSecondsCounter = 0; 5024var IncInterval; 5025var updateTimeout; 5026AJAX.registerTeardown('functions.js', function () { 5027 clearTimeout(updateTimeout); 5028 clearInterval(IncInterval); 5029 $(document).off('mousemove'); 5030}); 5031 5032AJAX.registerOnload('functions.js', function () { 5033 document.onclick = function() { 5034 _idleSecondsCounter = 0; 5035 }; 5036 $(document).on('mousemove',function() { 5037 _idleSecondsCounter = 0; 5038 }); 5039 document.onkeypress = function() { 5040 _idleSecondsCounter = 0; 5041 }; 5042 5043 function SetIdleTime() { 5044 _idleSecondsCounter++; 5045 } 5046 function UpdateIdleTime() { 5047 var href = 'index.php'; 5048 var params = { 5049 'ajax_request' : true, 5050 'token' : PMA_commonParams.get('token'), 5051 'server' : PMA_commonParams.get('server'), 5052 'db' : PMA_commonParams.get('db'), 5053 'access_time':_idleSecondsCounter 5054 }; 5055 $.ajax({ 5056 type: 'POST', 5057 url: href, 5058 data: params, 5059 success: function (data) { 5060 if (data.success) { 5061 var remaining = PMA_commonParams.get('LoginCookieValidity') - _idleSecondsCounter; 5062 if (remaining > 5) { 5063 // max value for setInterval() function 5064 var interval = Math.min(remaining * 1000, Math.pow(2, 31) - 1); 5065 updateTimeout = window.setTimeout(UpdateIdleTime, interval); 5066 } else if (remaining > 0) { 5067 // We're close to session expiry 5068 updateTimeout = window.setTimeout(UpdateIdleTime, 2000); 5069 } 5070 } else { //timeout occurred 5071 if(isStorageSupported('sessionStorage')){ 5072 window.sessionStorage.clear(); 5073 } 5074 window.location.reload(true); 5075 clearInterval(IncInterval); 5076 } 5077 } 5078 }); 5079 } 5080 if (PMA_commonParams.get('logged_in') && PMA_commonParams.get('auth_type') == 'cookie') { 5081 IncInterval = window.setInterval(SetIdleTime, 1000); 5082 var interval = (PMA_commonParams.get('LoginCookieValidity') - 5) * 1000; 5083 if (interval > Math.pow(2, 31) - 1) { // max value for setInterval() function 5084 interval = Math.pow(2, 31) - 1; 5085 } 5086 updateTimeout = window.setTimeout(UpdateIdleTime, interval); 5087 } 5088}); 5089/** 5090 * Unbind all event handlers before tearing down a page 5091 */ 5092AJAX.registerTeardown('functions.js', function () { 5093 $(document).off('click', 'input:checkbox.checkall'); 5094}); 5095AJAX.registerOnload('functions.js', function () { 5096 /** 5097 * Row marking in horizontal mode (use "on" so that it works also for 5098 * next pages reached via AJAX); a tr may have the class noclick to remove 5099 * this behavior. 5100 */ 5101 5102 $(document).on('click', 'input:checkbox.checkall', function (e) { 5103 $this = $(this); 5104 var $tr = $this.closest('tr'); 5105 var $table = $this.closest('table'); 5106 5107 if (!e.shiftKey || last_clicked_row == -1) { 5108 // usual click 5109 5110 var $checkbox = $tr.find(':checkbox.checkall'); 5111 var checked = $this.prop('checked'); 5112 $checkbox.prop('checked', checked).trigger('change'); 5113 if (checked) { 5114 $tr.addClass('marked'); 5115 } else { 5116 $tr.removeClass('marked'); 5117 } 5118 last_click_checked = checked; 5119 5120 // remember the last clicked row 5121 last_clicked_row = last_click_checked ? $table.find('tr.odd:not(.noclick), tr.even:not(.noclick)').index($tr) : -1;
5122 last_shift_clicked_row = -1; 5123 } else { 5124 // handle the shift click 5125 PMA_clearSelection(); 5126 var start, end; 5127 5128 // clear last shift click result 5129 if (last_shift_clicked_row >= 0) { 5130 if (last_shift_clicked_row >= last_clicked_row) { 5131 start = last_clicked_row; 5132 end = last_shift_clicked_row; 5133 } else { 5134 start = last_shift_clicked_row; 5135 end = last_clicked_row; 5136 } 5137 $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)') 5138 .slice(start, end + 1) 5139 .removeClass('marked') 5140 .find(':checkbox') 5141 .prop('checked', false) 5142 .trigger('change'); 5143 } 5144 5145 // handle new shift click 5146 var curr_row = $table.find('tr.odd:not(.noclick), tr.even:not(.noclick)').index($tr); 5147 if (curr_row >= last_clicked_row) { 5148 start = last_clicked_row; 5149 end = curr_row; 5150 } else { 5151 start = curr_row; 5152 end = last_clicked_row; 5153 } 5154 $tr.parent().find('tr.odd:not(.noclick), tr.even:not(.noclick)') 5155 .slice(start, end + 1) 5156 .addClass('marked') 5157 .find(':checkbox') 5158 .prop('checked', true) 5159 .trigger('change'); 5160 5161 // remember the last shift clicked row 5162 last_shift_clicked_row = curr_row; 5163 } 5164 }); 5165 5166 addDateTimePicker(); 5167 5168 /** 5169 * Add attribute to text boxes for iOS devices (based on bugID: 3508912) 5170 */ 5171 if (navigator.userAgent.match(/(iphone|ipod|ipad)/i)) { 5172 $('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off'); 5173 } 5174}); 5175 5176/** 5177 * Row highlighting in horizontal mode (use "on" 5178 * so that it works also for pages reached via AJAX) 5179 */ 5180/*AJAX.registerOnload('functions.js', function () { 5181 $(document).on('hover', 'tr.odd, tr.even',function (event) { 5182 var $tr = $(this); 5183 $tr.toggleClass('hover',event.type=='mouseover'); 5184 $tr.children().toggleClass('hover',event.type=='mouseover'); 5185 }); 5186})*/ 5187 5188/** 5189 * marks all rows and selects its first checkbox inside the given element 5190 * the given element is usually a table or a div containing the table or tables 5191 * 5192 * @param container DOM element 5193 */ 5194function markAllRows(container_id) 5195{ 5196 5197 $("#" + container_id).find("input:checkbox:enabled").prop('checked', true) 5198 .trigger("change") 5199 .parents("tr").addClass("marked"); 5200 return true; 5201} 5202 5203/** 5204 * marks all rows and selects its first checkbox inside the given element 5205 * the given element is usually a table or a div containing the table or tables 5206 * 5207 * @param container DOM element 5208 */ 5209function unMarkAllRows(container_id) 5210{ 5211 5212 $("#" + container_id).find("input:checkbox:enabled").prop('checked', false) 5213 .trigger("change") 5214 .parents("tr").removeClass("marked"); 5215 return true; 5216} 5217 5218/** 5219 * Checks/unchecks all options of a <select> element 5220 * 5221 * @param string the form name 5222 * @param string the element name 5223 * @param boolean whether to check or to uncheck options 5224 * 5225 * @return boolean always true 5226 */ 5227function setSelectOptions(the_form, the_select, do_check) 5228{ 5229 $("form[name='" + the_form + "'] select[name='" + the_select + "']").find("option").prop('selected', do_check); 5230 return true; 5231} // end of the 'setSelectOptions()' function 5232 5233/** 5234 * Sets current value for query box. 5235 */ 5236function setQuery(query) 5237{ 5238 if (codemirror_editor) { 5239 codemirror_editor.setValue(query); 5240 codemirror_editor.focus(); 5241 } else { 5242 document.sqlform.sql_query.value = query; 5243 document.sqlform.sql_query.focus(); 5244 } 5245} 5246 5247/** 5248 * Handles 'Simulate query' button on SQL query box. 5249 * 5250 * @return void 5251 */ 5252function PMA_handleSimulateQueryButton() 5253{ 5254 var update_re = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i'); 5255 var delete_re = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i'); 5256 var query = ''; 5257 5258 if (codemirror_editor) { 5259 query = codemirror_editor.getValue(); 5260 } else { 5261 query = $('#sqlquery').val(); 5262 } 5263 5264 var $simulateDml = $('#simulate_dml'); 5265 if (update_re.test(query) || delete_re.test(query)) { 5266 if (! $simulateDml.length) { 5267 $('#button_submit_query') 5268 .before('<input type="button" id="simulate_dml"' + 5269 'tabindex="199" value="' + 5270 PMA_messages.strSimulateDML + 5271 '" />'); 5272 } 5273 } else { 5274 if ($simulateDml.length) { 5275 $simulateDml.remove(); 5276 } 5277 } 5278} 5279 5280/** 5281 * Create quick sql statements. 5282 * 5283 */ 5284function insertQuery(queryType) 5285{ 5286 if (queryType == "clear") { 5287 setQuery(''); 5288 return; 5289 } else if (queryType == "format") { 5290 if (codemirror_editor) { 5291 $('#querymessage').html(PMA_messages.strFormatting + 5292 ' <img class="ajaxIcon" src="' + 5293 pmaThemeImage + 'ajax_clock_small.gif" alt="">'); 5294 var href = 'db_sql_format.php'; 5295 var params = { 5296 'ajax_request': true, 5297 'token': PMA_commonParams.get('token'), 5298 'sql': codemirror_editor.getValue() 5299 }; 5300 $.ajax({ 5301 type: 'POST', 5302 url: href, 5303 data: params, 5304 success: function (data) { 5305 if (data.success) { 5306 codemirror_editor.setValue(data.sql); 5307 } 5308 $('#querymessage').html(''); 5309 } 5310 }); 5311 } 5312 return; 5313 } else if (queryType == "saved") { 5314 if (isStorageSupported('localStorage') && typeof window.localStorage.auto_saved_sql != 'undefined') { 5315 setQuery(window.localStorage.auto_saved_sql); 5316 } else if ($.cookie('auto_saved_sql')) { 5317 setQuery($.cookie('auto_saved_sql')); 5318 } else { 5319 PMA_ajaxShowMessage(PMA_messages.strNoAutoSavedQuery); 5320 } 5321 return; 5322 } 5323 5324 var query = ""; 5325 var myListBox = document.sqlform.dummy; 5326 var table = document.sqlform.table.value; 5327 5328 if (myListBox.options.length > 0) { 5329 sql_box_locked = true; 5330 var columnsList = ""; 5331 var valDis = "";
5332 var editDis = ""; 5333 var NbSelect = 0; 5334 for (var i = 0; i < myListBox.options.length; i++) { 5335 NbSelect++; 5336 if (NbSelect > 1) { 5337 columnsList += ", "; 5338 valDis += ","; 5339 editDis += ","; 5340 } 5341 columnsList += myListBox.options[i].value; 5342 valDis += "[value-" + NbSelect + "]"; 5343 editDis += myListBox.options[i].value + "=[value-" + NbSelect + "]"; 5344 } 5345 if (queryType == "selectall") { 5346 query = "SELECT * FROM `" + table + "` WHERE 1"; 5347 } else if (queryType == "select") { 5348 query = "SELECT " + columnsList + " FROM `" + table + "` WHERE 1"; 5349 } else if (queryType == "insert") { 5350 query = "INSERT INTO `" + table + "`(" + columnsList + ") VALUES (" + valDis + ")"; 5351 } else if (queryType == "update") { 5352 query = "UPDATE `" + table + "` SET " + editDis + " WHERE 1"; 5353 } else if (queryType == "delete") { 5354 query = "DELETE FROM `" + table + "` WHERE 0"; 5355 } 5356 setQuery(query); 5357 sql_box_locked = false; 5358 } 5359} 5360 5361 5362/** 5363 * Inserts multiple fields. 5364 * 5365 */ 5366function insertValueQuery() 5367{ 5368 var myQuery = document.sqlform.sql_query; 5369 var myListBox = document.sqlform.dummy; 5370 5371 if (myListBox.options.length > 0) { 5372 sql_box_locked = true; 5373 var columnsList = ""; 5374 var NbSelect = 0; 5375 for (var i = 0; i < myListBox.options.length; i++) { 5376 if (myListBox.options[i].selected) { 5377 NbSelect++; 5378 if (NbSelect > 1) { 5379 columnsList += ", "; 5380 } 5381 columnsList += myListBox.options[i].value; 5382 } 5383 } 5384 5385 /* CodeMirror support */ 5386 if (codemirror_editor) { 5387 codemirror_editor.replaceSelection(columnsList); 5388 //IE support 5389 } else if (document.selection) { 5390 myQuery.focus(); 5391 var sel = document.selection.createRange(); 5392 sel.text = columnsList; 5393 document.sqlform.insert.focus(); 5394 } 5395 //MOZILLA/NETSCAPE support 5396 else if (document.sqlform.sql_query.selectionStart || document.sqlform.sql_query.selectionStart == "0") { 5397 var startPos = document.sqlform.sql_query.selectionStart; 5398 var endPos = document.sqlform.sql_query.selectionEnd; 5399 var SqlString = document.sqlform.sql_query.value; 5400 5401 myQuery.value = SqlString.substring(0, startPos) + columnsList + SqlString.substring(endPos, SqlString.length); 5402 } else { 5403 myQuery.value += columnsList; 5404 } 5405 sql_box_locked = false; 5406 } 5407} 5408 5409/** 5410 * Updates the input fields for the parameters based on the query 5411 */ 5412function updateQueryParameters() { 5413 5414 if ($('#parameterized').is(':checked')) { 5415 var query = codemirror_editor ? codemirror_editor.getValue() : $('#sqlquery').val(); 5416 5417 var allParameters = query.match(/:[a-zA-Z0-9_]+/g); 5418 var parameters = []; 5419 // get unique parameters 5420 if (allParameters) { 5421 $.each(allParameters, function(i, parameter){ 5422 if ($.inArray(parameter, parameters) === -1) { 5423 parameters.push(parameter); 5424 } 5425 }); 5426 } else { 5427 $('#parametersDiv').text(PMA_messages.strNoParam); 5428 return; 5429 } 5430 5431 var $temp = $('<div />'); 5432 $temp.append($('#parametersDiv').children()); 5433 $('#parametersDiv').empty(); 5434 5435 $.each(parameters, function (i, parameter) { 5436 var paramName = parameter.substring(1); 5437 var $param = $temp.find('#paramSpan_' + paramName ); 5438 if (! $param.length) { 5439 $param = $('<span class="parameter" id="paramSpan_' + paramName + '" />'); 5440 $('<label for="param_' + paramName + '" />').text(parameter).appendTo($param); 5441 $('<input type="text" name="parameters[' + parameter + ']" id="param_' + paramName + '" />').appendTo($param); 5442 } 5443 $('#parametersDiv').append($param); 5444 }); 5445 } else { 5446 $('#parametersDiv').empty(); 5447 } 5448} 5449 5450/** 5451 * Refresh/resize the WYSIWYG scratchboard 5452 */ 5453function refreshLayout() 5454{ 5455 var $elm = $('#pdflayout'); 5456 var orientation = $('#orientation_opt').val(); 5457 var paper = 'A4'; 5458 var $paperOpt = $('#paper_opt'); 5459 if ($paperOpt.length == 1) { 5460 paper = $paperOpt.val(); 5461 } 5462 var posa = 'y'; 5463 var posb = 'x'; 5464 if (orientation == 'P') { 5465 posa = 'x'; 5466 posb = 'y'; 5467 } 5468 $elm.css('width', pdfPaperSize(paper, posa) + 'px'); 5469 $elm.css('height', pdfPaperSize(paper, posb) + 'px'); 5470} 5471 5472/** 5473 * Initializes positions of elements. 5474 */ 5475function TableDragInit() { 5476 $('.pdflayout_table').each(function () { 5477 var $this = $(this); 5478 var number = $this.data('number'); 5479 var x = $('#c_table_' + number + '_x').val(); 5480 var y = $('#c_table_' + number + '_y').val(); 5481 $this.css('left', x + 'px'); 5482 $this.css('top', y + 'px'); 5483 /* Make elements draggable */ 5484 $this.draggable({ 5485 containment: "parent", 5486 drag: function (evt, ui) { 5487 var number = $this.data('number'); 5488 $('#c_table_' + number + '_x').val(parseInt(ui.position.left,
548810)); 5489 $('#c_table_' + number + '_y').val(parseInt(ui.position.top, 10)); 5490 } 5491 }); 5492 }); 5493} 5494 5495/** 5496 * Resets drag and drop positions. 5497 */ 5498function resetDrag() { 5499 $('.pdflayout_table').each(function () { 5500 var $this = $(this); 5501 var x = $this.data('x'); 5502 var y = $this.data('y'); 5503 $this.css('left', x + 'px'); 5504 $this.css('top', y + 'px'); 5505 }); 5506} 5507 5508/** 5509 * User schema handlers. 5510 */ 5511$(function () { 5512 /* Move in scratchboard on manual change */ 5513 $(document).on('change', '.position-change', function () { 5514 var $this = $(this); 5515 var $elm = $('#table_' + $this.data('number')); 5516 $elm.css($this.data('axis'), $this.val() + 'px'); 5517 }); 5518 /* Refresh on paper size/orientation change */ 5519 $(document).on('change', '.paper-change', function () { 5520 var $elm = $('#pdflayout'); 5521 if ($elm.css('visibility') == 'visible') { 5522 refreshLayout(); 5523 TableDragInit(); 5524 } 5525 }); 5526 /* Show/hide the WYSIWYG scratchboard */ 5527 $(document).on('click', '#toggle-dragdrop', function () { 5528 var $elm = $('#pdflayout'); 5529 if ($elm.css('visibility') == 'hidden') { 5530 refreshLayout(); 5531 TableDragInit(); 5532 $elm.css('visibility', 'visible'); 5533 $elm.css('display', 'block'); 5534 $('#showwysiwyg').val('1'); 5535 } else { 5536 $elm.css('visibility', 'hidden'); 5537 $elm.css('display', 'none'); 5538 $('#showwysiwyg').val('0'); 5539 } 5540 }); 5541 /* Reset scratchboard */ 5542 $(document).on('click', '#reset-dragdrop', function () { 5543 resetDrag(); 5544 }); 5545}); 5546 5547/** 5548 * Returns paper sizes for a given format 5549 */ 5550function pdfPaperSize(format, axis) 5551{ 5552 switch (format.toUpperCase()) { 5553 case '4A0': 5554 if (axis == 'x') { 5555 return 4767.87; 5556 } else { 5557 return 6740.79; 5558 } 5559 break; 5560 case '2A0': 5561 if (axis == 'x') { 5562 return 3370.39; 5563 } else { 5564 return 4767.87; 5565 } 5566 break; 5567 case 'A0': 5568 if (axis == 'x') { 5569 return 2383.94; 5570 } else { 5571 return 3370.39; 5572 } 5573 break; 5574 case 'A1': 5575 if (axis == 'x') { 5576 return 1683.78; 5577 } else { 5578 return 2383.94; 5579 } 5580 break; 5581 case 'A2': 5582 if (axis == 'x') { 5583 return 1190.55; 5584 } else { 5585 return 1683.78; 5586 } 5587 break; 5588 case 'A3': 5589 if (axis == 'x') { 5590 return 841.89; 5591 } else { 5592 return 1190.55; 5593 } 5594 break; 5595 case 'A4': 5596 if (axis == 'x') { 5597 return 595.28; 5598 } else { 5599 return 841.89; 5600 } 5601 break; 5602 case 'A5': 5603 if (axis == 'x') { 5604 return 419.53; 5605 } else { 5606 return 595.28; 5607 } 5608 break; 5609 case 'A6': 5610 if (axis == 'x') { 5611 return 297.64; 5612 } else { 5613 return 419.53; 5614 } 5615 break; 5616 case 'A7': 5617 if (axis == 'x') { 5618 return 209.76; 5619 } else { 5620 return 297.64; 5621 } 5622 break; 5623 case 'A8': 5624 if (axis == 'x') { 5625 return 147.40; 5626 } else { 5627 return 209.76; 5628 } 5629 break; 5630 case 'A9': 5631 if (axis == 'x') { 5632 return 104.88; 5633 } else { 5634 return 147.40; 5635 } 5636 break; 5637 case 'A10': 5638 if (axis == 'x') { 5639 return 73.70; 5640 } else { 5641 return 104.88; 5642 } 5643 break; 5644 case 'B0': 5645 if (axis == 'x') { 5646 return 2834.65; 5647 } else { 5648 return 4008.19; 5649 } 5650 break; 5651 case 'B1': 5652 if (axis == 'x') { 5653 return 2004.09; 5654 } else { 5655 return 2834.65; 5656 } 5657 break; 5658 case 'B2': 5659 if (axis == 'x') { 5660 return 1417.32; 5661 } else { 5662 return 2004.09; 5663 } 5664 break; 5665 case 'B3': 5666 if (axis == 'x') { 5667 return 1000.63; 5668 } else { 5669 return 1417.32; 5670 } 5671 break; 5672 case 'B4': 5673 if (axis == 'x') { 5674 return 708.66; 5675 } else { 5676 return 1000.63; 5677 } 5678 break; 5679 case 'B5': 5680 if (axis == 'x') { 5681 return 498.90; 5682 } else { 5683 return 708.66; 5684 } 5685 break; 5686 case 'B6': 5687 if (axis == 'x') { 5688 return 354.33; 5689 } else { 5690 return 498.90; 5691 } 5692 break; 5693 case 'B7': 5694 if (axis == 'x') { 5695 return 249.45; 5696 } else { 5697 return 354.33; 5698 } 5699 break; 5700 case 'B8': 5701 if (axis == 'x') { 5702 return 175.75; 5703 } else { 5704 return 249.45; 5705 } 5706 break; 5707 case 'B9': 5708 if (axis == 'x') { 5709 return 124.72; 5710 } else { 5711 return 175.75; 5712 } 5713 break; 5714 case 'B10': 5715 if (axis == 'x') { 5716 return 87.87; 5717 } else { 5718 return 124.72; 5719 } 5720 break; 5721 case 'C0': 5722 if (axis == 'x') { 5723 return 2599.37; 5724 } else { 5725 return 3676.54; 5726 } 5727 break; 5728 case 'C1': 5729 if (axis == 'x') { 5730 return 1836.85; 5731 } else { 5732 return 2599.37; 5733 } 5734 break; 5735 case 'C2': 5736 if (axis == 'x') { 5737 return 1298.27; 5738 } else { 5739 return 1836.85; 5740 } 5741 break; 5742 case 'C3': 5743 if (axis == 'x') { 5744 return 918.43; 5745 } else { 5746 return 1298.27; 5747 } 5748 break; 5749 case 'C4': 5750 if (axis == 'x') { 5751 return 649.13; 5752 } else { 5753 return 918.43; 5754 } 5755 break; 5756 case 'C5': 5757 if (axis == 'x') { 5758 return 459.21; 5759 } else { 5760 return 649.13; 5761 } 5762 break; 5763 case 'C6': 5764 if (axis == 'x') { 5765 return 323.15; 5766 } else { 5767 return 459.21; 5768 } 5769 break; 5770 case 'C7': 5771 if (axis == 'x') { 5772 return 229.61; 5773 } else { 5774 return 323.15; 5775 } 5776 break; 5777 case 'C8': 5778 if (axis == 'x') { 5779 return 161.57; 5780 } else { 5781 return 229.61; 5782 } 5783 break; 5784 case 'C9': 5785 if (axis == 'x') { 5786 return 113.39; 5787 } else { 5788 return 161.57; 5789 } 5790 break; 5791 case 'C10': 5792 if (axis == 'x') { 5793 return 79.37; 5794 } else { 5795 return 113.39; 5796 } 5797 break; 5798 case 'RA0': 5799 if (axis == 'x') { 5800 return 2437.80; 5801 } else { 5802 return 3458.27; 5803 } 5804 break; 5805 case 'RA1': 5806 if (axis == 'x') { 5807 return 1729.13; 5808 } else { 5809 return 2437.80; 5810 } 5811 break; 5812 case 'RA2': 5813 if (axis == 'x') { 5814 return 1218.90; 5815 } else { 5816 return 1729.13; 5817 } 5818 break; 5819 case 'RA3': 5820 if (axis == 'x') { 5821 return 864.57; 5822 } else { 5823 return 1218.90; 5824 } 5825 break; 5826 case 'RA4': 5827 if (axis == 'x') { 5828 return 609.45; 5829 } else { 5830 return 864.57; 5831 } 5832 break; 5833 case 'SRA0': 5834 if (axis == 'x') { 5835 return 2551.18; 5836 } else { 5837 return 3628.35; 5838 } 5839 break; 5840 case 'SRA1': 5841 if (axis == 'x') { 5842 return 1814.17; 5843 } else { 5844 return 2551.18; 5845 } 5846 break; 5847 case 'SRA2': 5848 if (axis == 'x') { 5849 return 1275.59; 5850 } else { 5851 return 1814.17; 5852 } 5853 break; 5854 case 'SRA3': 5855 if (axis == 'x') { 5856 return 907.09; 5857 } else { 5858 return 1275.59; 5859 } 5860 break; 5861 case 'SRA4': 5862 if (axis == 'x') { 5863 return 637.80; 5864 } else { 5865 return 907.09; 5866 } 5867 break; 5868 case 'LETTER': 5869 if (axis == 'x') { 5870 return 612.00; 5871 } else { 5872 return 792.00; 5873 } 5874 break; 5875 case 'LEGAL': 5876 if (axis == 'x') { 5877 return 612.00; 5878 } else { 5879 return 1008.00; 5880 } 5881 break; 5882 case 'EXECUTIVE': 5883 if (axis == 'x') { 5884 return 521.86; 5885 } else { 5886 return 756.00; 5887 } 5888 break; 5889 case 'FOLIO': 5890 if (axis == 'x') { 5891 return 612.00; 5892 } else { 5893 return 936.00; 5894 } 5895 break; 5896 } // end switch 5897 5898 return 0; 5899} 5900 5901/** 5902 * Get checkbox for foreign key checks 5903 * 5904 * @return string 5905 */ 5906function getForeignKeyCheckboxLoader() { 5907 var html = ''; 5908 html += '<div>'; 5909 html += '<div class="load-default-fk-check-value">'; 5910 html += PMA_getImage('ajax_clock_small.gif'); 5911 html += '</div>'; 5912 html += '</div>'; 5913 return html; 5914} 5915 5916function loadForeignKeyCheckbox() { 5917 // Load default foreign key check value 5918 var params = { 5919 'ajax_request': true, 5920 'token': PMA_commonParams.get('token'), 5921 'server': PMA_commonParams.get('server'), 5922 'get_default_fk_check_value': true 5923 }; 5924 $.get('sql.php', params, function (data) { 5925 var html = '<input type="hidden" name="fk_checks" value="
59250" />' + 5926 '<input type="checkbox" name="fk_checks" id="fk_checks"' + 5927 (data.default_fk_check_value ? ' checked="checked"' : '') + ' />' + 5928 '<label for="fk_checks">' + PMA_messages.strForeignKeyCheck + '</label>'; 5929 $('.load-default-fk-check-value').replaceWith(html); 5930 }); 5931} 5932 5933function getJSConfirmCommonParam(elem) { 5934 return { 5935 'is_js_confirmed' : 1, 5936 'ajax_request' : true, 5937 'fk_checks': $(elem).find('#fk_checks').is(':checked') ? 1 : 0 5938 }; 5939} 5940 5941/** 5942 * Unbind all event handlers before tearing down a page 5943 */ 5944AJAX.registerTeardown('functions.js', function () { 5945 $(document).off('click', "a.inline_edit_sql"); 5946 $(document).off('click', "input#sql_query_edit_save"); 5947 $(document).off('click', "input#sql_query_edit_discard"); 5948 $('input.sqlbutton').unbind('click'); 5949 if (codemirror_editor) { 5950 codemirror_editor.off('blur'); 5951 } else { 5952 $(document).off('blur', '#sqlquery'); 5953 } 5954 $(document).off('change', '#parameterized'); 5955 $('#sqlquery').unbind('keydown'); 5956 $('#sql_query_edit').unbind('keydown'); 5957 5958 if (codemirror_inline_editor) { 5959 // Copy the sql query to the text area to preserve it. 5960 $('#sql_query_edit').text(codemirror_inline_editor.getValue()); 5961 $(codemirror_inline_editor.getWrapperElement()).unbind('keydown'); 5962 codemirror_inline_editor.toTextArea(); 5963 codemirror_inline_editor = false; 5964 } 5965 if (codemirror_editor) { 5966 $(codemirror_editor.getWrapperElement()).unbind('keydown'); 5967 } 5968}); 5969 5970/** 5971 * Jquery Coding for inline editing SQL_QUERY 5972 */ 5973AJAX.registerOnload('functions.js', function () {
5974 // If we are coming back to the page by clicking forward button 5975 // of the browser, bind the code mirror to inline query editor. 5976 bindCodeMirrorToInlineEditor(); 5977 $(document).on('click', "a.inline_edit_sql", function () { 5978 if ($('#sql_query_edit').length) { 5979 // An inline query editor is already open, 5980 // we don't want another copy of it 5981 return false; 5982 } 5983 5984 var $form = $(this).prev('form'); 5985 var sql_query = $form.find("input[name='sql_query']").val().trim(); 5986 var $inner_sql = $(this).parent().prev().find('code.sql'); 5987 var old_text = $inner_sql.html(); 5988 5989 var new_content = "<textarea name=\"sql_query_edit\" id=\"sql_query_edit\">" + escapeHtml(sql_query) + "</textarea>\n"; 5990 new_content += getForeignKeyCheckboxLoader(); 5991 new_content += "<input type=\"submit\" id=\"sql_query_edit_save\" class=\"button btnSave\" value=\"" + PMA_messages.strGo + "\"/>\n"; 5992 new_content += "<input type=\"button\" id=\"sql_query_edit_discard\" class=\"button btnDiscard\" value=\"" + PMA_messages.strCancel + "\"/>\n"; 5993 var $editor_area = $('div#inline_editor'); 5994 if ($editor_area.length === 0) { 5995 $editor_area = $('<div id="inline_editor_outer"></div>'); 5996 $editor_area.insertBefore($inner_sql); 5997 } 5998 $editor_area.html(new_content); 5999 loadForeignKeyCheckbox(); 6000 $inner_sql.hide(); 6001 6002 bindCodeMirrorToInlineEditor(); 6003 return false; 6004 }); 6005 6006 $(document).on('click', "input#sql_query_edit_save", function () { 6007 $(".success").hide(); 6008 //hide already existing success message 6009 var sql_query; 6010 if (codemirror_inline_editor) { 6011 codemirror_inline_editor.save(); 6012 sql_query = codemirror_inline_editor.getValue(); 6013 } else { 6014 sql_query = $(this).parent().find('#sql_query_edit').val(); 6015 } 6016 var fk_check = $(this).parent().find('#fk_checks').is(':checked'); 6017 6018 var $form = $("a.inline_edit_sql").prev('form'); 6019 var $fake_form = $('<form>', {action: 'import.php', method: 'post'}) 6020 .append($form.find("input[name=server], input[name=db], input[name=table], input[name=token]").clone()) 6021 .append($('<input/>', {type: 'hidden', name: 'show_query', value: 1})) 6022 .append($('<input/>', {type: 'hidden', name: 'is_js_confirmed', value: 0})) 6023 .append($('<input/>', {type: 'hidden', name: 'sql_query', value: sql_query})) 6024 .append($('<input/>', {type: 'hidden', name: 'fk_checks', value: fk_check ? 1 : 0})); 6025 if (! checkSqlQuery($fake_form[0])) { 6026 return false; 6027 } 6028 $fake_form.appendTo($('body')).submit(); 6029 }); 6030 6031 $(document).on('click', "input#sql_query_edit_discard", function () { 6032 var $divEditor = $('div#inline_editor_outer'); 6033 $divEditor.siblings('code.sql').show(); 6034 $divEditor.remove(); 6035 }); 6036 6037 $('input.sqlbutton').click(function (evt) { 6038 insertQuery(evt.target.id); 6039 PMA_handleSimulateQueryButton(); 6040 return false; 6041 }); 6042 6043 $(document).on('change', '#parameterized', updateQueryParameters); 6044 6045 var $inputUsername = $('#input_username'); 6046 if ($inputUsername) { 6047 if ($inputUsername.val() === '') { 6048 $inputUsername.focus(); 6049 } else { 6050 $('#input_password').focus(); 6051 } 6052 } 6053}); 6054 6055/** 6056 * "inputRead" event handler for CodeMirror SQL query editors for autocompletion 6057 */ 6058function codemirrorAutocompleteOnInputRead(instance) { 6059 if (!sql_autocomplete_in_progress 6060 && (!instance.options.hintOptions.tables || !sql_autocomplete)) { 6061 6062 if (!sql_autocomplete) { 6063 // Reset after teardown 6064 instance.options.hintOptions.tables = false; 6065 instance.options.hintOptions.defaultTable = ''; 6066 6067 sql_autocomplete_in_progress = true; 6068 6069 var href = 'db_sql_autocomplete.php'; 6070 var params = { 6071 'ajax_request': true, 6072 'token': PMA_commonParams.get('token'), 6073 'server': PMA_commonParams.get('server'), 6074 'db': PMA_commonParams.get('db'), 6075 'no_debug': true 6076 }; 6077 6078 var columnHintRender = function(elem, self, data) { 6079 $('<div class="autocomplete-column-name">') 6080 .text(data.columnName) 6081 .appendTo(elem); 6082 $('<div class="autocomplete-column-hint">') 6083 .text(data.columnHint) 6084 .appendTo(elem); 6085 }; 6086 6087 $.ajax({ 6088 type: 'POST', 6089 url: href, 6090 data: params, 6091 success: function (data) { 6092 if (data.success) { 6093 var tables = $.parseJSON(data.tables); 6094 sql_autocomplete_default_table = PMA_commonParams.get('table'); 6095 sql_autocomplete = []; 6096 for (var table in tables) { 6097 if (tables.hasOwnProperty(table)) { 6098 var columns = tables[table]; 6099 table = { 6100 text: table, 6101 columns: [] 6102 }; 6103 for (var column in columns) { 6104 if (columns.hasOwnProperty(column)) { 6105 var displayText = columns[column].Type; 6106 if (columns[column].Key == 'PRI') { 6107 displayText += ' | Primary'; 6108 } else if (columns[column].Key == 'UNI') { 6109 displayText += ' | Unique'; 6110 } 6111 table.columns.push({ 6112 text: column, 6113 displayText: column + " | " + displayText, 6114 columnName: column, 6115 columnHint: displayText, 6116 render: columnHintRender 6117 }); 6118 } 6119 } 6120 } 6121 sql_autocomplete.push(table); 6122 } 6123 instance.options.hintOptions.tables = sql_autocomplete; 6124 instance.options.hintOptions.defaultTable = sql_autocomplete_default_table; 6125 } 6126 }, 6127 complete: function () { 6128 sql_autocomplete_in_progress = false;
6129 } 6130 }); 6131 } 6132 else { 6133 instance.options.hintOptions.tables = sql_autocomplete; 6134 instance.options.hintOptions.defaultTable = sql_autocomplete_default_table; 6135 } 6136 } 6137 if (instance.state.completionActive) { 6138 return; 6139 } 6140 var cur = instance.getCursor(); 6141 var token = instance.getTokenAt(cur); 6142 var string = ''; 6143 if (token.string.match(/^[.`\w@]\w*$/)) { 6144 string = token.string; 6145 } 6146 if (string.length > 0) { 6147 CodeMirror.commands.autocomplete(instance); 6148 } 6149} 6150 6151/** 6152 * Remove autocomplete information before tearing down a page 6153 */ 6154AJAX.registerTeardown('functions.js', function () { 6155 sql_autocomplete = false; 6156 sql_autocomplete_default_table = ''; 6157}); 6158 6159/** 6160 * Binds the CodeMirror to the text area used to inline edit a query. 6161 */ 6162function bindCodeMirrorToInlineEditor() { 6163 var $inline_editor = $('#sql_query_edit'); 6164 if ($inline_editor.length > 0) { 6165 if (typeof CodeMirror !== 'undefined') { 6166 var height = $inline_editor.css('height'); 6167 codemirror_inline_editor = PMA_getSQLEditor($inline_editor); 6168 codemirror_inline_editor.getWrapperElement().style.height = height; 6169 codemirror_inline_editor.refresh(); 6170 codemirror_inline_editor.focus(); 6171 $(codemirror_inline_editor.getWrapperElement()) 6172 .bind('keydown', catchKeypressesFromSqlInlineEdit); 6173 } else { 6174 $inline_editor 6175 .focus() 6176 .bind('keydown', catchKeypressesFromSqlInlineEdit); 6177 } 6178 } 6179} 6180 6181function catchKeypressesFromSqlInlineEdit(event) { 6182 // ctrl-enter is 10 in chrome and ie, but 13 in ff 6183 if (event.ctrlKey && (event.keyCode == 13 || event.keyCode == 10)) { 6184 $("#sql_query_edit_save").trigger('click'); 6185 } 6186} 6187 6188/** 6189 * Adds doc link to single highlighted SQL element 6190 */ 6191function PMA_doc_add($elm, params) 6192{ 6193 if (typeof mysql_doc_template == 'undefined') { 6194 return; 6195 } 6196 6197 var url = PMA_sprintf( 6198 decodeURIComponent(mysql_doc_template), 6199 params[0] 6200 ); 6201 if (params.length > 1) { 6202 url += '#' + params[1]; 6203 } 6204 var content = $elm.text(); 6205 $elm.text(''); 6206 $elm.append('<a target="mysql_doc" class="cm-sql-doc" href="' + url + '">' + content + '</a>'); 6207} 6208 6209/** 6210 * Generates doc links for keywords inside highlighted SQL 6211 */ 6212function PMA_doc_keyword(idx, elm) 6213{ 6214 var $elm = $(elm); 6215 /* Skip already processed ones */ 6216 if ($elm.find('a').length > 0) { 6217 return; 6218 } 6219 var keyword = $elm.text().toUpperCase(); 6220 var $next = $elm.next('.cm-keyword'); 6221 if ($next) { 6222 var next_keyword = $next.text().toUpperCase(); 6223 var full = keyword + ' ' + next_keyword; 6224 6225 var $next2 = $next.next('.cm-keyword'); 6226 if ($next2) { 6227 var next2_keyword = $next2.text().toUpperCase(); 6228 var full2 = full + ' ' + next2_keyword; 6229 if (full2 in mysql_doc_keyword) { 6230 PMA_doc_add($elm, mysql_doc_keyword[full2]); 6231 PMA_doc_add($next, mysql_doc_keyword[full2]); 6232 PMA_doc_add($next2, mysql_doc_keyword[full2]); 6233 return; 6234 } 6235 } 6236 if (full in mysql_doc_keyword) { 6237 PMA_doc_add($elm, mysql_doc_keyword[full]); 6238 PMA_doc_add($next, mysql_doc_keyword[full]); 6239 return; 6240 } 6241 } 6242 if (keyword in mysql_doc_keyword) { 6243 PMA_doc_add($elm, mysql_doc_keyword[keyword]); 6244 } 6245} 6246 6247/** 6248 * Generates doc links for builtins inside highlighted SQL 6249 */ 6250function PMA_doc_builtin(idx, elm) 6251{ 6252 var $elm = $(elm); 6253 var builtin = $elm.text().toUpperCase(); 6254 if (builtin in mysql_doc_builtin) { 6255 PMA_doc_add($elm, mysql_doc_builtin[builtin]); 6256 } 6257} 6258 6259/** 6260 * Higlights SQL using CodeMirror. 6261 */ 6262function PMA_highlightSQL($base) 6263{ 6264 var $elm = $base.find('code.sql'); 6265 $elm.each(function () { 6266 var $sql = $(this); 6267 var $pre = $sql.find('pre'); 6268 /* We only care about visible elements to avoid double processing */ 6269 if ($pre.is(":visible")) { 6270 var $highlight = $('<div class="sql-highlight cm-s-default"></div>'); 6271 $sql.append($highlight); 6272 if (typeof CodeMirror != 'undefined') { 6273 CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]); 6274 $pre.hide(); 6275 $highlight.find('.cm-keyword').each(PMA_doc_keyword); 6276 $highlight.find('.cm-builtin').each(PMA_doc_builtin); 6277 } 6278 } 6279 }); 6280} 6281 6282/** 6283 * Updates an element containing code. 6284 * 6285 * @param jQuery Object $base base element which contains the raw and the 6286 * highlighted code. 6287 * 6288 * @param string htmlValue code in HTML format, displayed if code cannot be 6289 * highlighted 6290 * 6291 * @param string rawValue raw code, used as a parameter for highlighter 6292 * 6293 * @return bool whether content was updated or not 6294 */ 6295function PMA_updateCode($base, htmlValue, rawValue) 6296{ 6297 var $code = $base.find('code'); 6298 if ($code.length === 0) { 6299 return false;
6300 } 6301 6302 // Determines the type of the content and appropriate CodeMirror mode. 6303 var type = '', mode = ''; 6304 if ($code.hasClass('json')) { 6305 type = 'json'; 6306 mode = 'application/json'; 6307 } else if ($code.hasClass('sql')) { 6308 type = 'sql'; 6309 mode = 'text/x-mysql'; 6310 } else if ($code.hasClass('xml')) { 6311 type = 'xml'; 6312 mode = 'application/xml'; 6313 } else { 6314 return false; 6315 } 6316 6317 // Element used to display unhighlighted code. 6318 var $notHighlighted = $('<pre>' + htmlValue + '</pre>'); 6319 6320 // Tries to highlight code using CodeMirror. 6321 if (typeof CodeMirror != 'undefined') { 6322 var $highlighted = $('<div class="' + type + '-highlight cm-s-default"></div>'); 6323 CodeMirror.runMode(rawValue, mode, $highlighted[0]); 6324 $notHighlighted.hide(); 6325 $code.html('').append($notHighlighted, $highlighted[0]); 6326 } else { 6327 $code.html('').append($notHighlighted); 6328 } 6329 6330 return true; 6331} 6332 6333/** 6334 * Show a message on the top of the page for an Ajax request 6335 * 6336 * Sample usage: 6337 * 6338 * 1) var $msg = PMA_ajaxShowMessage(); 6339 * This will show a message that reads "Loading...". Such a message will not 6340 * disappear automatically and cannot be dismissed by the user. To remove this 6341 * message either the PMA_ajaxRemoveMessage($msg) function must be called or
6342 * another message must be show with PMA_ajaxShowMessage() function. 6343 * 6344 * 2) var $msg = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 6345 * This is a special case. The behaviour is same as above, 6346 * just with a different message 6347 * 6348 * 3) var $msg = PMA_ajaxShowMessage('The operation was successful'); 6349 * This will show a message that will disappear automatically and it can also 6350 * be dismissed by the user. 6351 * 6352 * 4) var $msg = PMA_ajaxShowMessage('Some error', false); 6353 * This will show a message that will not disappear automatically, but it 6354 * can be dismissed by the user after he has finished reading it. 6355 * 6356 * @param string message string containing the message to be shown. 6357 * optional, defaults to 'Loading...' 6358 * @param mixed timeout number of milliseconds for the message to be visible 6359 * optional, defaults to 5000. If set to 'false', the 6360 * notification will never disappear 6361 * @return jQuery object jQuery Element that holds the message div 6362 * this object can be passed to PMA_aja
6362xRemoveMessage() 6363 * to remove the notification 6364 */ 6365function PMA_ajaxShowMessage(message, timeout) 6366{ 6367 /** 6368 * @var self_closing Whether the notification will automatically disappear 6369 */ 6370 var self_closing = true; 6371 /** 6372 * @var dismissable Whether the user will be able to remove 6373 * the notification by clicking on it 6374 */ 6375 var dismissable = true; 6376 // Handle the case when a empty data.message is passed. 6377 // We don't want the empty message 6378 if (message === '') { 6379 return true; 6380 } else if (! message) { 6381 // If the message is undefined, show the default 6382 message = PMA_messages.strLoading; 6383 dismissable = false; 6384 self_closing = false; 6385 } else if (message == PMA_messages.strProcessingRequest) { 6386 // This is another case where the message should not disappear 6387 dismissable = false; 6388 self_closing = false; 6389 } 6390 // Figure out whether (or after how long) to remove the notification 6391 if (timeout === undefined) { 6392 timeout = 5000; 6393 } else if (timeout === false) { 6394 self_closing = false; 6395 } 6396 // Create a parent element for the AJAX messages, if necessary 6397 if ($('#loading_parent').length === 0) { 6398 $('<div id="loading_parent"></div>') 6399 .prependTo("#page_content"); 6400 } 6401 // Update message count to create distinct message elements every time 6402 ajax_message_count++; 6403 // Remove all old messages, if any 6404 $("span.ajax_notification[id^=ajax_message_num]").remove(); 6405 /** 6406 * @var $retval a jQuery object containing the reference 6407 * to the created AJAX message 6408 */ 6409 var $retval = $( 6410 '<span class="ajax_notification" id="ajax_message_num_' + 6411 ajax_message_count + 6412 '"></span>' 6413 ) 6414 .hide() 6415 .appendTo("#loading_parent") 6416 .html(message) 6417 .show(); 6418 // If the notification is self-closing we should create a callback to remove it 6419 if (self_closing) { 6420 $retval 6421 .delay(timeout) 6422 .fadeOut('medium', function () { 6423 if ($(this).is(':data(tooltip)')) { 6424 $(this).tooltip('destroy'); 6425 } 6426 // Remove the notification 6427 $(this).remove(); 6428 }); 6429 } 6430 // If the notification is dismissable we need to add the relevant class to it 6431 // and add a tooltip so that the users know that it can be removed 6432 if (dismissable) { 6433 $retval.addClass('dismissable').css('cursor', 'pointer'); 6434 /** 6435 * Add a tooltip to the notification to let the user know that (s)he 6436 * can dismiss the ajax notification by clicking on it. 6437 */ 6438 PMA_tooltip( 6439 $retval, 6440 'span', 6441 PMA_messages.strDismiss 6442 ); 6443 } 6444 PMA_highlightSQL($retval); 6445 6446 return $retval; 6447} 6448 6449/** 6450 * Removes the message shown for an Ajax operation when it's completed 6451 * 6452 * @param jQuery object jQuery Element that holds the notification 6453 * 6454 * @return nothing 6455 */ 6456function PMA_ajaxRemoveMessage($this_msgbox) 6457{ 6458 if ($this_msgbox !== undefined && $this_msgbox instanceof jQuery) { 6459 $this_msgbox 6460 .stop(true, true) 6461 .fadeOut('medium'); 6462 if ($this_msgbox.is(':data(tooltip)')) { 6463 $this_msgbox.tooltip('destroy'); 6464 } else { 6465 $this_msgbox.remove(); 6466 } 6467 } 6468} 6469 6470/** 6471 * Requests SQL for previewing before executing. 6472 * 6473 * @param jQuery Object $form Form containing query data 6474 * 6475 * @return void 6476 */ 6477function PMA_previewSQL($form) 6478{ 6479 var form_url = $form.attr('action'); 6480 var form_data = $form.serialize() + 6481 '&do_save_data=1' + 6482 '&preview_sql=1' + 6483 '&ajax_request=1'; 6484 var $msgbox = PMA_ajaxShowMessage(); 6485 $.ajax({ 6486 type: 'POST', 6487 url: form_url, 6488 data: form_data, 6489 success: function (response) { 6490 PMA_ajaxRemoveMessage($msgbox); 6491 if (response.success) { 6492 var $dialog_content = $('<div/>') 6493 .append(response.sql_data); 6494 var button_options = {}; 6495 button_options[PMA_messages.strClose] = function () { 6496 $(this).dialog('close'); 6497 }; 6498 var $response_dialog = $dialog_content.dialog({ 6499 minWidth: 550, 6500 maxHeight: 400, 6501 modal: true, 6502 buttons: button_options, 6503 title: PMA_messages.strPreviewSQL, 6504 close: function () { 6505 $(this).remove(); 6506 }, 6507 open: function () { 6508 // Pretty SQL printing. 6509 PMA_highlightSQL($(this)); 6510 } 6511 }); 6512 } else { 6513 PMA_ajaxShowMessage(response.message); 6514 } 6515 }, 6516 error: function () { 6517 PMA_ajaxShowMessage(PMA_messages.strErrorProcessingRequest); 6518 } 6519 }); 6520} 6521 6522/** 6523 * check for reserved keyword column name 6524 * 6525 * @param jQuery Object $form Form 6526 * 6527 * @returns true|false 6528 */ 6529 6530function PMA_checkReservedWordColumns($form) { 6531 var is_confirmed = true; 6532 $.ajax({ 6533 type: 'POST', 6534 url: "tbl_structure.php", 6535 data: $form.serialize() + '&reserved_word_check=1', 6536 success: function (data) { 6537 if (typeof data.success != 'undefined' && data.success === true) { 6538 is_confirmed = confirm(data.message); 6539 } 6540 }, 6541 async:false 6542 }); 6543 return is_confirmed; 6544} 6545 6546// This event only need to be fired once after the initial page load 6547$(function () { 6548 /** 6549 * Allows the user to dismiss a notification
6550 * created with PMA_ajaxShowMessage() 6551 */ 6552 $(document).on('click', 'span.ajax_notification.dismissable', function () { 6553 PMA_ajaxRemoveMessage($(this)); 6554 }); 6555 /** 6556 * The below two functions hide the "Dismiss notification" tooltip when a user 6557 * is hovering a link or button that is inside an ajax message 6558 */ 6559 $(document).on('mouseover', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () { 6560 if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) { 6561 $(this).parents('span.ajax_notification').tooltip('disable'); 6562 } 6563 }); 6564 $(document).on('mouseout', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () { 6565 if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) { 6566 $(this).parents('span.ajax_notification').tooltip('enable'); 6567 } 6568 }); 6569}); 6570 6571/** 6572 * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected 6573 */ 6574function PMA_showNoticeForEnum(selectElement) 6575{ 6576 var enum_notice_id = selectElement.attr("id").split("_")[1]; 6577 enum_notice_id += "_" + (parseInt(selectElement.attr("id").split("_")[2], 10) + 1); 6578 var selectedType = selectElement.val(); 6579 if (selectedType == "ENUM" || selectedType == "SET") { 6580 $("p#enum_notice_" + enum_notice_id).show(); 6581 } else { 6582 $("p#enum_notice_" + enum_notice_id).hide(); 6583 } 6584} 6585 6586/** 6587 * Creates a Profiling Chart. Used in sql.js 6588 * and in server_status_monitor.js 6589 */ 6590function PMA_createProfilingChart(target, data) 6591{ 6592 // create the chart 6593 var factory = new JQPlotChartFactory(); 6594 var chart = factory.createChart(ChartType.PIE, target); 6595 6596 // create the data table and add columns 6597 var dataTable = new DataTable(); 6598 dataTable.addColumn(ColumnType.STRING, ''); 6599 dataTable.addColumn(ColumnType.NUMBER, ''); 6600 dataTable.setData(data); 6601 6602 // draw the chart and return the chart object 6603 chart.draw(dataTable, { 6604 seriesDefaults: { 6605 rendererOptions: { 6606 showDataLabels: true 6607 } 6608 }, 6609 highlighter: { 6610 tooltipLocation: 'se', 6611 sizeAdjust: 0, 6612 tooltipAxes: 'pieref', 6613 formatString: '%s, %.9Ps' 6614 }, 6615 legend: { 6616 show: true, 6617 location: 'e', 6618 rendererOptions: { 6619 numberColumns: 2 6620 } 6621 }, 6622 // from http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines#Color_Palette 6623 seriesColors: [ 6624 '#fce94f', 6625 '#fcaf3e', 6626 '#e9b96e', 6627 '#8ae234', 6628 '#729fcf', 6629 '#ad7fa8', 6630 '#ef2929', 6631 '#eeeeec', 6632 '#888a85', 6633 '#c4a000', 6634 '#ce5c00', 6635 '#8f5902', 6636 '#4e9a06', 6637 '#204a87', 6638 '#5c3566', 6639 '#a40000', 6640 '#babdb6', 6641 '#2e3436' 6642 ] 6643 }); 6644 return chart; 6645} 6646 6647/** 6648 * Formats a profiling duration nicely (in us and ms time). 6649 * Used in server_status_monitor.js 6650 * 6651 * @param integer Number to be formatted, should be in the range of microsecond to second 6652 * @param integer Accuracy, how many numbers right to the comma should be 6653 * @return string The formatted number 6654 */ 6655function PMA_prettyProfilingNum(num, acc) 6656{ 6657 if (!acc) { 6658 acc = 2; 6659 } 6660 acc = Math.pow(10, acc); 6661 if (num * 1000 < 0.1) { 6662 num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ'; 6663 } else if (num < 0.1) { 6664 num = Math.round(acc * (num * 1000)) / acc + 'm'; 6665 } else { 6666 num = Math.round(acc * num) / acc; 6667 } 6668 6669 return num + 's'; 6670} 6671 6672 6673/** 6674 * Formats a SQL Query nicely with newlines and indentation. Depends on Codemirror and MySQL Mode! 6675 * 6676 * @param string Query to be formatted 6677 * @return string The formatted query 6678 */ 6679function PMA_SQLPrettyPrint(string) 6680{ 6681 if (typeof CodeMirror == 'undefined') { 6682 return string; 6683 } 6684 6685 var mode = CodeMirror.getMode({}, "text/x-mysql"); 6686 var stream = new CodeMirror.StringStream(string); 6687 var state = mode.startState(); 6688 var token, tokens = []; 6689 var output = ''; 6690 var tabs = function (cnt) { 6691 var ret = ''; 6692 for (var i = 0; i < 4 * cnt; i++) { 6693 ret += " ";
6694 } 6695 return ret; 6696 }; 6697 6698 // "root-level" statements 6699 var statements = { 6700 'select': ['select', 'from', 'on', 'where', 'having', 'limit', 'order by', 'group by'], 6701 'update': ['update', 'set', 'where'], 6702 'insert into': ['insert into', 'values'] 6703 }; 6704 // don't put spaces before these tokens 6705 var spaceExceptionsBefore = {';': true, ',': true, '.': true, '(': true}; 6706 // don't put spaces after these tokens 6707 var spaceExceptionsAfter = {'.': true}; 6708 6709 // Populate tokens array 6710 var str = ''; 6711 while (! stream.eol()) { 6712 stream.start = stream.pos; 6713 token = mode.token(stream, state); 6714 if (token !== null) { 6715 tokens.push([token, stream.current().toLowerCase()]); 6716 } 6717 } 6718 6719 var currentStatement = tokens[0][1]; 6720 6721 if (! statements[currentStatement]) { 6722 return string; 6723 } 6724 // Holds all currently opened code blocks (statement, function or generic) 6725 var blockStack = []; 6726 // Holds the type of block from last iteration (the current is in blockStack[0]) 6727 var previousBlock; 6728 // If a new code block is found, newBlock contains its type for one iteration and vice versa for endBlock 6729 var newBlock, endBlock; 6730 // How much to indent in the current line 6731 var indentLevel = 0; 6732 // Holds the "root-level" statements 6733 var statementPart, lastStatementPart = statements[currentStatement][0]; 6734 6735 blockStack.unshift('statement'); 6736 6737 // Iterate through every token and format accordingly 6738 for (var i = 0; i < tokens.length; i++) { 6739 previousBlock = blockStack[0]; 6740 6741 // New block => push to stack 6742 if (tokens[i][1] == '(') { 6743 if (i < tokens.length - 1 && tokens[i + 1][0] == 'statement-verb') { 6744 blockStack.unshift(newBlock = 'statement'); 6745 } else if (i > 0 && tokens[i - 1][0] == 'builtin') { 6746 blockStack.unshift(newBlock = 'function'); 6747 } else { 6748 blockStack.unshift(newBlock = 'generic'); 6749 } 6750 } else { 6751 newBlock = null; 6752 } 6753 6754 // Block end => pop from stack 6755 if (tokens[i][1] == ')') { 6756 endBlock = blockStack[0]; 6757 blockStack.shift(); 6758 } else { 6759 endBlock = null; 6760 } 6761 6762 // A subquery is starting 6763 if (i > 0 && newBlock == 'statement') { 6764 indentLevel++; 6765 output += "\n" + tabs(indentLevel) + tokens[i][1] + ' ' + tokens[i + 1][1].toUpperCase() + "\n" + tabs(indentLevel + 1); 6766 currentStatement = tokens[i + 1][1]; 6767 i++; 6768 continue; 6769 } 6770 6771 // A subquery is ending 6772 if (endBlock == 'statement' && indentLevel > 0) { 6773 output += "\n" + tabs(indentLevel); 6774 indentLevel--; 6775 } 6776 6777 // One less indentation for statement parts (from, where, order by, etc.) and a newline 6778 statementPart = statements[currentStatement].indexOf(tokens[i][1]); 6779 if (statementPart != -1) { 6780 if (i > 0) { 6781 output += "\n"; 6782 } 6783 output += tabs(indentLevel) + tokens[i][1].toUpperCase(); 6784 output += "\n" + tabs(indentLevel + 1); 6785 lastStatementPart = tokens[i][1]; 6786 } 6787 // Normal indentation and spaces for everything else 6788 else { 6789 if (! spaceExceptionsBefore[tokens[i][1]] && 6790 ! (i > 0 && spaceExceptionsAfter[tokens[i - 1][1]]) && 6791 output.charAt(output.length - 1) != ' ') { 6792 output += " "; 6793 } 6794 if (tokens[i][0] == 'keyword') { 6795 output += tokens[i][1].toUpperCase(); 6796 } else { 6797 output += tokens[i][1]; 6798 } 6799 } 6800 6801 // split columns in select and 'update set' clauses, but only inside statements blocks 6802 if ((lastStatementPart == 'select' || lastStatementPart == 'where' || lastStatementPart == 'set') && 6803 tokens[i][1] == ',' && blockStack[0] == 'statement') { 6804 6805 output += "\n" + tabs(indentLevel + 1); 6806 } 6807 6808 // split conditions in where clauses, but only inside statements blocks 6809 if (lastStatementPart == 'where' && 6810 (tokens[i][1] == 'and' || tokens[i][1] == 'or' || tokens[i][1] == 'xor')) { 6811 6812 if (blockStack[0] == 'statement') { 6813 output += "\n" + tabs(indentLevel + 1); 6814 } 6815 // Todo: Also split and or blocks in newlines & indentation++ 6816 //if (blockStack[0] == 'generic') 6817 // output += ... 6818 } 6819 } 6820 return output; 6821} 6822 6823/** 6824 * jQuery function that uses jQueryUI's dialogs to confirm with user. Does not 6825 * return a jQuery object yet and hence cannot be chained 6826 * 6827 * @param string question 6828 * @param string url URL to be passed to the callbackFn to make 6829 * an Ajax call to 6830 * @param function callbackFn callback to execute after user clicks on OK 6831 * @param function openCallback optional callback to run when dialog is shown 6832 */ 6833 6834jQuery.fn.PMA_confirm = function (question, url, callbackFn, openCallback) { 6835 var confirmState = PMA_commonParams.get('confirm'); 6836 if (! confirmState) { 6837 // user does not want to confirm 6838 if ($.isFunction(callbackFn)) { 6839 callbackFn.call(this, url); 6840 return true; 6841 } 6842 } 6843 if (PMA_messages.strDoYouReally === '') { 6844 return true; 6845 } 6846 6847 /** 6848 * @var button_options Object that stores the options passed to
6848jQueryUI 6849 * dialog 6850 */ 6851 var button_options = [ 6852 { 6853 text: PMA_messages.strOK, 6854 'class': 'submitOK', 6855 click: function () { 6856 $(this).dialog("close"); 6857 if ($.isFunction(callbackFn)) { 6858 callbackFn.call(this, url); 6859 } 6860 } 6861 }, 6862 { 6863 text: PMA_messages.strCancel, 6864 'class': 'submitCancel', 6865 click: function () { 6866 $(this).dialog("close"); 6867 } 6868 } 6869 ]; 6870 6871 $('<div/>', {'id': 'confirm_dialog', 'title': PMA_messages.strConfirm}) 6872 .prepend(question) 6873 .dialog({ 6874 buttons: button_options, 6875 close: function () { 6876 $(this).remove(); 6877 }, 6878 open: openCallback, 6879 modal: true 6880 }); 6881}; 6882 6883/** 6884 * jQuery function to sort a table's body after a new row has been appended to it. 6885 * Also fixes the even/odd classes of the table rows at the end. 6886 * 6887 * @param string text_selector string to select the sortKey's text 6888 * 6889 * @return jQuery Object for chaining purposes 6890 */ 6891jQuery.fn.PMA_sort_table = function (text_selector) { 6892 return this.each(function () { 6893 6894 /** 6895 * @var table_body Object referring to the table's <tbody> element 6896 */ 6897 var table_body = $(this); 6898 /** 6899 * @var rows Object referring to the collection of rows in {@link table_body} 6900 */ 6901 var rows = $(this).find('tr').get(); 6902 6903 //get the text of the field that we will sort by 6904 $.each(rows, function (index, row) { 6905 row.sortKey = $.trim($(row).find(text_selector).text().toLowerCase()); 6906 }); 6907 6908 //get the sorted order 6909 rows.sort(function (a, b) { 6910 if (a.sortKey < b.sortKey) { 6911 return -1; 6912 } 6913 if (a.sortKey > b.sortKey) { 6914 return 1; 6915 } 6916 return 0; 6917 }); 6918 6919 //pull out each row from the table and then append it according to it's order 6920 $.each(rows, function (index, row) { 6921 $(table_body).append(row); 6922 row.sortKey = null; 6923 }); 6924 6925 //Re-check the classes of each row 6926 $(this).find('tr:odd') 6927 .removeClass('even').addClass('odd') 6928 .end() 6929 .find('tr:even') 6930 .removeClass('odd').addClass('even'); 6931 }); 6932}; 6933 6934/** 6935 * Unbind all event handlers before tearing down a page 6936 */ 6937AJAX.registerTeardown('functions.js', function () { 6938 $(document).off('submit', "#create_table_form_minimal.ajax"); 6939 $(document).off('submit', "form.create_table_form.ajax"); 6940 $(document).off('click', "form.create_table_form.ajax input[name=submit_num_fields]"); 6941 $(document).off('keyup', "form.create_table_form.ajax input"); 6942 $(document).off('change', "input[name=partition_count],input[name=subpartition_count],select[name=partition_by]"); 6943}); 6944 6945/** 6946 * jQuery coding for 'Create Table'. Used on db_operations.php, 6947 * db_structure.php and db_tracking.php (i.e., wherever 6948 * libraries/display_create_table.lib.php is used) 6949 * 6950 * Attach Ajax Event handlers for Create Table 6951 */ 6952AJAX.registerOnload('functions.js', function () { 6953 /** 6954 * Attach event handler for submission of create table form (save) 6955 */ 6956 $(document).on('submit', "form.create_table_form.ajax", function (event) { 6957 event.preventDefault(); 6958 6959 /** 6960 * @var the_form object referring to the create table form 6961 */ 6962 var $form = $(this); 6963 6964 /* 6965 * First validate the form; if there is a problem, avoid submitting it 6966 * 6967 * checkTableEditForm() needs a pure element and not a jQuery object, 6968 * this is why we pass $form[0] as a parameter (the jQuery object 6969 * is actually an array of DOM elements) 6970 */ 6971 6972 if (checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) { 6973 PMA_prepareForAjaxRequest($form); 6974 if (PMA_checkReservedWordColumns($form)) { 6975 PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 6976 //User wants to submit the form 6977 $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) { 6978 if (typeof data !== 'undefined' && data.success === true) { 6979 $('#properties_message') 6980 .removeClass('error') 6981 .html(''); 6982 PMA_ajaxShowMessage(data.message); 6983 // Only if the create table dialog (distinct panel) exists 6984 var $createTableDialog = $("#create_table_dialog"); 6985 if ($createTableDialog.length > 0) { 6986 $createTableDialog.dialog("close").remove(); 6987 } 6988 $('#tableslistcontainer').before(data.formatted_sql); 6989 6990 /** 6991 * @var tables_table Object referring to the <tbody> element that holds the list of tables 6992 */ 6993 var tables_table = $("#tablesForm").find("tbody").not("#tbl_summary_row"); 6994 // this is the first table created in this db 6995 if (tables_table.length === 0) { 6996 PMA_commonActions.refreshMain( 6997 PMA_commonParams.get('opendb_url') 6998 ); 6999 } else { 7000 /** 7001 * @var curr_last_row Object referring to the last <tr> element in {@link tables_table} 7002 */
7003 var curr_last_row = $(tables_table).find('tr:last'); 7004 /** 7005 * @var curr_last_row_index_string String containing the index of {@link curr_last_row} 7006 */ 7007 var curr_last_row_index_string = $(curr_last_row).find('input:checkbox').attr('id').match(/\d+/)[0]; 7008 /** 7009 * @var curr_last_row_index Index of {@link curr_last_row} 7010 */ 7011 var curr_last_row_index = parseFloat(curr_last_row_index_string); 7012 /** 7013 * @var new_last_row_index Index of the new row to be appended to {@link tables_table} 7014 */ 7015 var new_last_row_index = curr_last_row_index + 1; 7016 /** 7017 * @var new_last_row_id String containing the id of the row to be appended to {@link tables_table} 7018 */ 7019 var new_last_row_id = 'checkbox_tbl_' + new_last_row_index; 7020 7021 data.new_table_string = data.new_table_string.replace(/checkbox_tbl_/, new_last_row_id); 7022 //append to table 7023 $(data.new_table_string) 7024 .appendTo(tables_table); 7025 7026 //Sort the table 7027 $(tables_table).PMA_sort_table('th'); 7028 7029 // Adjust summary row 7030 PMA_adjustTotals(); 7031 } 7032 7033 //Refresh navigation as a new table has been added 7034 PMA_reloadNavigation(); 7035 // Redirect to table structure page on creation of new table 7036 var params_12 = 'ajax_request=true&ajax_page_request=true'; 7037 if (! (history && history.pushState)) { 7038 params_12 += PMA_MicroHistory.menus.getRequestParam(); 7039 } 7040 tblStruct_url = 'tbl_structure.php?server=' + data._params.server + 7041 '&db='+ data._params.db + '&token=' + data._params.token + 7042 '&goto=db_structure.php&table=' + data._params.table + ''; 7043 $.get(tblStruct_url, params_12, AJAX.responseHandler); 7044 } else { 7045 PMA_ajaxShowMessage( 7046 '<div class="error">' + data.error + '</div>', 7047 false 7048 ); 7049 } 7050 }); // end $.post() 7051 } 7052 } // end if (checkTableEditForm() ) 7053 }); // end create table form (save) 7054 7055 /** 7056 * Submits the intermediate changes in the table creation form 7057 * to refresh the UI accordingly 7058 */ 7059 function submitChangesInCreateTableForm (actionParam) { 7060 7061 /** 7062 * @var the_form object referring to the create table form 7063 */ 7064 var $form = $('form.create_table_form.ajax'); 7065 7066 var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 7067 PMA_prepareForAjaxRequest($form); 7068 7069 //User wants to add more fields to the table 7070 $.post($form.attr('action'), $form.serialize() + "&" + actionParam, function (data) { 7071 if (typeof data !== 'undefined' && data.success) { 7072 var $pageContent = $("#page_content"); 7073 $pageContent.html(data.message); 7074 PMA_highlightSQL($pageContent); 7075 PMA_verifyColumnsProperties(); 7076 PMA_hideShowConnection($('.create_table_form select[name=tbl_storage_engine]')); 7077 PMA_ajaxRemoveMessage($msgbox); 7078 } else { 7079 PMA_ajaxShowMessage(data.error); 7080 } 7081 }); //end $.post() 7082 } 7083 7084 /** 7085 * Attach event handler for create table form (add fields) 7086 */ 7087 $(document).on('click', "form.create_table_form.ajax input[name=submit_num_fields]", function (event) { 7088 event.preventDefault(); 7089 7090 if (!checkFormElementInRange(this.form, 'added_fields', PMA_messages.strLeastColumnError, 1)) { 7091 return; 7092 } 7093 7094 submitChangesInCreateTableForm('submit_num_fields=1'); 7095 }); // end create table form (add fields) 7096 7097 $(document).on('keydown', "form.create_table_form.ajax input[name=added_fields]", function (event) { 7098 if (event.keyCode == 13) { 7099 event.preventDefault(); 7100 event.stopImmediatePropagation(); 7101 $(this) 7102 .closest('form') 7103 .find('input[name=submit_num_fields]') 7104 .click(); 7105 } 7106 }); 7107 7108 /** 7109 * Attach event handler to manage changes in number of partitions and subpartitions 7110 */ 7111 $(document).on('change', "input[name=partition_count],input[name=subpartition_count],select[name=partition_by]", function (event) { 7112 $this = $(this); 7113 $form = $this.parents('form'); 7114 if ($form.is(".create_table_form.ajax")) { 7115 submitChangesInCreateTableForm('submit_partition_change=1'); 7116 } else { 7117 $form.submit(); 7118 } 7119 }); 7120 7121 $("input[value=AUTO_INCREMENT]").change(function(){ 7122 if (this.checked) { 7123 var col = /\d/.exec($(this).attr('name')); 7124 col = col[0]; 7125 var $selectFieldKey = $('select[name="field_key[' + col + ']"]'); 7126 if ($selectFieldKey.val() === 'none_'+col) { 7127 $selectFieldKey.val('primary_'+col).change(); 7128 } 7129 } 7130 }); 7131 $('body') 7132 .off('click', 'input.preview_sql') 7133 .on('click', 'input.preview_sql', function () { 7134 var $form = $(this).closest('form'); 7135 PMA_previewSQL($form); 7136 }); 7137}); 7138 7139 7140/** 7141 * Validates the password field in a form 7142 * 7143 * @see PMA_messages.strPasswordEmpty 7144 * @see PMA_messages.strPasswordNotSame 7145 * @param object $the_form The form to be validated 7146 * @return bool 7147 */ 7148function PMA_checkPassword($the_form) 7149{ 7150 // Did the user select 'no password'?
7151 if ($the_form.find('#nopass_1').is(':checked')) { 7152 return true; 7153 } else { 7154 var $pred = $the_form.find('#select_pred_password'); 7155 if ($pred.length && ($pred.val() == 'none' || $pred.val() == 'keep')) { 7156 return true; 7157 } 7158 } 7159 7160 var $password = $the_form.find('input[name=pma_pw]'); 7161 var $password_repeat = $the_form.find('input[name=pma_pw2]'); 7162 var alert_msg = false; 7163 7164 if ($password.val() === '') { 7165 alert_msg = PMA_messages.strPasswordEmpty; 7166 } else if ($password.val() != $password_repeat.val()) { 7167 alert_msg = PMA_messages.strPasswordNotSame; 7168 } 7169 7170 if (alert_msg) { 7171 alert(alert_msg); 7172 $password.val(''); 7173 $password_repeat.val(''); 7174 $password.focus(); 7175 return false; 7176 } 7177 return true; 7178} 7179 7180/** 7181 * Unbind all event handlers before tearing down a page 7182 */ 7183AJAX.registerTeardown('functions.js', function () { 7184 $(document).off('click', '#change_password_anchor.ajax'); 7185}); 7186/** 7187 * Attach Ajax event handlers for 'Change Password' on index.php 7188 */ 7189AJAX.registerOnload('functions.js', function () { 7190 7191 /* Handler for hostname type */ 7192 $(document).on('change', '#select_pred_hostname', function () { 7193 var hostname = $('#pma_hostname'); 7194 if (this.value == 'any') { 7195 hostname.val('%'); 7196 } else if (this.value == 'localhost') { 7197 hostname.val('localhost'); 7198 } else if (this.value == 'thishost' && $(this).data('thishost')) { 7199 hostname.val($(this).data('thishost')); 7200 } else if (this.value == 'hosttable') { 7201 hostname.val('').prop('required', false); 7202 } else if (this.value == 'userdefined') { 7203 hostname.focus().select().prop('required', true); 7204 } 7205 }); 7206 7207 /* Handler for editing hostname */ 7208 $(document).on('change', '#pma_hostname', function () { 7209 $('#select_pred_hostname').val('userdefined'); 7210 $('#pma_hostname').prop('required', true); 7211 }); 7212 7213 /* Handler for username type */ 7214 $(document).on('change', '#select_pred_username', function () { 7215 if (this.value == 'any') { 7216 $('#pma_username').val('').prop('required', false); 7217 $('#user_exists_warning').css('display', 'none'); 7218 } else if (this.value == 'userdefined') { 7219 $('#pma_username').focus().select().prop('required', true); 7220 } 7221 }); 7222 7223 /* Handler for editing username */ 7224 $(document).on('change', '#pma_username', function () { 7225 $('#select_pred_username').val('userdefined'); 7226 $('#pma_username').prop('required', true); 7227 }); 7228 7229 /* Handler for password type */ 7230 $(document).on('change', '#select_pred_password', function () { 7231 if (this.value == 'none') { 7232 $('#text_pma_pw2').prop('required', false).val(''); 7233 $('#text_pma_pw').prop('required', false).val(''); 7234 } else if (this.value == 'userdefined') { 7235 $('#text_pma_pw2').prop('required', true); 7236 $('#text_pma_pw').prop('required', true).focus().select(); 7237 } else { 7238 $('#text_pma_pw2').prop('required', false); 7239 $('#text_pma_pw').prop('required', false); 7240 } 7241 }); 7242 7243 /* Handler for editing password */ 7244 $(document).on('change', '#text_pma_pw,#text_pma_pw2', function () { 7245 $('#select_pred_password').val('userdefined'); 7246 $('#text_pma_pw2').prop('required', true); 7247 $('#text_pma_pw').prop('required', true); 7248 }); 7249 7250 /** 7251 * Attach Ajax event handler on the change password anchor 7252 */ 7253 $(document).on('click', '#change_password_anchor.ajax', function (event) { 7254 event.preventDefault(); 7255 7256 var $msgbox = PMA_ajaxShowMessage(); 7257 7258 /** 7259 * @var button_options Object containing options to be passed to jQueryUI's dialog 7260 */ 7261 var button_options = {}; 7262 button_options[PMA_messages.strGo] = function () { 7263 7264 event.preventDefault(); 7265 7266 /** 7267 * @var $the_form Object referring to the change password form 7268 */ 7269 var $the_form = $("#change_password_form"); 7270 7271 if (! PMA_checkPassword($the_form)) { 7272 return false;
7273 } 7274 7275 /** 7276 * @var this_value String containing the value of the submit button. 7277 * Need to append this for the change password form on Server Privileges 7278 * page to work 7279 */ 7280 var this_value = $(this).val(); 7281 7282 var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 7283 $the_form.append('<input type="hidden" name="ajax_request" value="true" />'); 7284 7285 $.post($the_form.attr('action'), $the_form.serialize() + '&change_pw=' + this_value, function (data) { 7286 if (typeof data === 'undefined' || data.success !== true) { 7287 PMA_ajaxShowMessage(data.error, false); 7288 return; 7289 } 7290 7291 var $pageContent = $("#page_content"); 7292 $pageContent.prepend(data.message); 7293 PMA_highlightSQL($pageContent); 7294 $("#change_password_dialog").hide().remove(); 7295 $("#edit_user_dialog").dialog("close").remove(); 7296 PMA_ajaxRemoveMessage($msgbox); 7297 }); // end $.post() 7298 }; 7299 7300 button_options[PMA_messages.strCancel] = function () { 7301 $(this).dialog('close'); 7302 }; 7303 $.get($(this).attr('href'), {'ajax_request': true}, function (data) { 7304 if (typeof data === 'undefined' || !data.success) { 7305 PMA_ajaxShowMessage(data.error, false); 7306 return; 7307 } 7308 7309 $('<div id="change_password_dialog"></div>') 7310 .dialog({ 7311 title: PMA_messages.strChangePassword, 7312 width: 600, 7313 close: function (ev, ui) { 7314 $(this).remove(); 7315 }, 7316 buttons: button_options, 7317 modal: true 7318 }) 7319 .append(data.message); 7320 // for this dialog, we remove the fieldset wrapping due to double headings 7321 $("fieldset#fieldset_change_password") 7322 .find("legend").remove().end() 7323 .find("table.noclick").unwrap().addClass("some-margin") 7324 .find("input#text_pma_pw").focus(); 7325 displayPasswordGenerateButton(); 7326 $('#fieldset_change_password_footer').hide(); 7327 PMA_ajaxRemoveMessage($msgbox); 7328 $('#change_password_form').bind('submit', function (e) { 7329 e.preventDefault(); 7330 $(this) 7331 .closest('.ui-dialog') 7332 .find('.ui-dialog-buttonpane .ui-button') 7333 .first() 7334 .click(); 7335 }); 7336 }); // end $.get() 7337 }); // end handler for change password anchor 7338}); // end $() for Change Password 7339 7340/** 7341 * Unbind all event handlers before tearing down a page 7342 */ 7343AJAX.registerTeardown('functions.js', function () { 7344 $(document).off('change', "select.column_type"); 7345 $(document).off('change', "select.default_type"); 7346 $(document).off('change', "select.virtuality"); 7347 $(document).off('change', 'input.allow_null'); 7348 $(document).off('change', '.create_table_form select[name=tbl_storage_engine]'); 7349}); 7350/** 7351 * Toggle the hiding/showing of the "Open in ENUM/SET editor" message when 7352 * the page loads and when the selected data type changes 7353 */ 7354AJAX.registerOnload('functions.js', function () { 7355 // is called here for normal page loads and also when opening 7356 // the Create table dialog 7357 PMA_verifyColumnsProperties(); 7358 // 7359 // needs on() to work also in the Create Table dialog 7360 $(document).on('change', "select.column_type", function () { 7361 PMA_showNoticeForEnum($(this)); 7362 }); 7363 $(document).on('change', "select.default_type", function () { 7364 PMA_hideShowDefaultValue($(this)); 7365 }); 7366 $(document).on('change', "select.virtuality", function () { 7367 PMA_hideShowExpression($(this)); 7368 }); 7369 $(document).on('change', 'input.allow_null', function () { 7370 PMA_validateDefaultValue($(this)); 7371 }); 7372 $(document).on('change', '.create_table_form select[name=tbl_storage_engine]', function () { 7373 PMA_hideShowConnection($(this)); 7374 }); 7375}); 7376 7377/** 7378 * If the chosen storage engine is FEDERATED show connection field. Hide otherwise 7379 * 7380 * @param $engine_selector storage engine selector 7381 */ 7382function PMA_hideShowConnection($engine_selector) 7383{ 7384 var $connection = $('.create_table_form input[name=connection]'); 7385 var index = $connection.parent('td').index() + 1; 7386 var $labelTh = $connection.parents('tr').prev('tr').children('th:nth-child(' + index + ')'); 7387 if ($engine_selector.val() != 'FEDERATED') { 7388 $connection 7389 .prop('disabled', true) 7390 .parent('td').hide(); 7391 $labelTh.hide(); 7392 } else { 7393 $connection 7394 .prop('disabled', false) 7395 .parent('td').show(); 7396 $labelTh.show(); 7397 } 7398} 7399 7400/** 7401 * If the column does not allow NULL values, makes sure that default is not NULL 7402 */ 7403function PMA_validateDefaultValue($null_checkbox) 7404{ 7405 if (! $null_checkbox.prop('checked')) { 7406 var $default = $null_checkbox.closest('tr').find('.default_t
7406ype'); 7407 if ($default.val() == 'NULL') { 7408 $default.val('NONE'); 7409 } 7410 } 7411} 7412 7413/** 7414 * function to populate the input fields on picking a column from central list 7415 * 7416 * @param string input_id input id of the name field for the column to be populated 7417 * @param integer offset of the selected column in central list of columns 7418 */ 7419function autoPopulate(input_id, offset) 7420{ 7421 var db = PMA_commonParams.get('db'); 7422 var table = PMA_commonParams.get('table'); 7423 input_id = input_id.substring(0, input_id.length - 1); 7424 $('#' + input_id + '1').val(central_column_list[db + '_' + table][offset].col_name); 7425 var col_type = central_column_list[db + '_' + table][offset].col_type.toUpperCase(); 7426 $('#' + input_id + '2').val(col_type); 7427 var $input3 = $('#' + input_id + '3'); 7428 $input3.val(central_column_list[db + '_' + table][offset].col_length); 7429 if(col_type === 'ENUM' || col_type === 'SET') { 7430 $input3.next().show(); 7431 } else { 7432 $input3.next().hide(); 7433 } 7434 var col_default = central_column_list[db + '_' + table][offset].col_default.toUpperCase(); 7435 var $input4 = $('#' + input_id + '4'); 7436 if (col_default !== '' && col_default !== 'NULL' && col_default !== 'CURRENT_TIMESTAMP') { 7437 $input4.val("USER_DEFINED"); 7438 $input4.next().next().show(); 7439 $input4.next().next().val(central_column_list[db + '_' + table][offset].col_default); 7440 } else { 7441 $input4.val(central_column_list[db + '_' + table][offset].col_default); 7442 $input4.next().next().hide(); 7443 } 7444 $('#' + input_id + '5').val(central_column_list[db + '_' + table][offset].col_collation); 7445 var $input6 = $('#' + input_id + '6'); 7446 $input6.val(central_column_list[db + '_' + table][offset].col_attribute); 7447 if(central_column_list[db + '_' + table][offset].col_extra === 'on update CURRENT_TIMESTAMP') { 7448 $input6.val(central_column_list[db + '_' + table][offset].col_extra); 7449 } 7450 if(central_column_list[db + '_' + table][offset].col_extra.toUpperCase() === 'AUTO_INCREMENT') { 7451 $('#' + input_id + '9').prop("checked",true).change(); 7452 } else { 7453 $('#' + input_id + '9').prop("checked",false); 7454 } 7455 if(central_column_list[db + '_' + table][offset].col_isNull !== '0') { 7456 $('#' + input_id + '7').prop("checked",true); 7457 } else { 7458 $('#' + input_id + '7').prop("checked",false); 7459 } 7460} 7461 7462/** 7463 * Unbind all event handlers before tearing down a page 7464 */ 7465AJAX.registerTeardown('functions.js', function () { 7466 $(document).off('click', "a.open_enum_editor"); 7467 $(document).off('click', "input.add_value"); 7468 $(document).off('click', "#enum_editor td.drop"); 7469 $(document).off('click', 'a.central_columns_dialog'); 7470}); 7471/** 7472 * @var $enum_editor_dialog An object that points to the jQuery 7473 * dialog of the ENUM/SET editor 7474 */ 7475var $enum_editor_dialog = null; 7476/** 7477 * Opens the ENUM/SET editor and controls its functions 7478 */ 7479AJAX.registerOnload('functions.js', function () { 7480 $(document).on('click', "a.open_enum_editor", function () { 7481 // Get the name of the column that is being edited 7482 var colname = $(this).closest('tr').find('input:first').val(); 7483 var title; 7484 var i; 7485 // And use it to make up a title for the page 7486 if (colname.length < 1) { 7487 title = PMA_messages.enum_newColumnVals; 7488 } else { 7489 title = PMA_messages.enum_columnVals.replace( 7490 /%s/, 7491 '"' + escapeHtml(decodeURIComponent(colname)) + '"' 7492 ); 7493 } 7494 // Get the values as a string 7495 var inputstring = $(this) 7496 .closest('td') 7497 .find("input") 7498 .val(); 7499 // Escape html entities 7500 inputstring = $('<div/>') 7501 .text(inputstring) 7502 .html(); 7503 // Parse the values, escaping quotes and 7504 // slashes on the fly, into an array 7505 var values = []; 7506 var in_string = false; 7507 var curr, next, buffer = ''; 7508 for (i = 0; i < inputstring.length; i++) { 7509 curr = inputstring.charAt(i); 7510 next = i == inputstring.length ? '' : inputstring.charAt(i + 1); 7511 if (! in_string && curr == "'") { 7512 in_string = true; 7513 } else if (in_string && curr == "\\" && next == "\\") { 7514 buffer += "\"; 7515 i++; 7516 } else if (in_string && next == "'" && (curr == "'" || curr == "\\")) { 7517 buffer += "'"; 7518 i++; 7519 } else if (in_string && curr == "'") { 7520 in_string = false;
7521 values.push(buffer); 7522 buffer = ''; 7523 } else if (in_string) { 7524 buffer += curr; 7525 } 7526 } 7527 if (buffer.length > 0) { 7528 // The leftovers in the buffer are the last value (if any) 7529 values.push(buffer); 7530 } 7531 var fields = ''; 7532 // If there are no values, maybe the user is about to make a 7533 // new list so we add a few for him/her to get started with. 7534 if (values.length === 0) { 7535 values.push('', '', '', ''); 7536 } 7537 // Add the parsed values to the editor 7538 var drop_icon = PMA_getImage('b_drop.png'); 7539 for (i = 0; i < values.length; i++) { 7540 fields += "<tr><td>" + 7541 "<input type='text' value='" + values[i] + "'/>" + 7542 "</td><td class='drop'>" + 7543 drop_icon + 7544 "</td></tr>"; 7545 } 7546 /** 7547 * @var dialog HTML code for the ENUM/SET dialog 7548 */ 7549 var dialog = "<div id='enum_editor'>" + 7550 "<fieldset>" + 7551 "<legend>" + title + "</legend>" + 7552 "<p>" + PMA_getImage('s_notice.png') + 7553 PMA_messages.enum_hint + "</p>" + 7554 "<table class='values'>" + fields + "</table>" + 7555 "</fieldset><fieldset class='tblFooters'>" + 7556 "<table class='add'><tr><td>" + 7557 "<div class='slider'></div>" + 7558 "</td><td>" + 7559 "<form><div><input type='submit' class='add_value' value='" + 7560 PMA_sprintf(PMA_messages.enum_addValue, 1) + 7561 "'/></div></form>" + 7562 "</td></tr></table>" + 7563 "<input type='hidden' value='" + // So we know which column's data is being edited 7564 $(this).closest('td').find("input").attr("id") + 7565 "' />" + 7566 "</fieldset>" + 7567 "</div>"; 7568 /** 7569 * @var Defines functions to be called when the buttons in 7570 * the buttonOptions jQuery dialog bar are pressed 7571 */ 7572 var buttonOptions = {}; 7573 buttonOptions[PMA_messages.strGo] = function () { 7574 // When the submit button is clicked, 7575 // put the data back into the original form 7576 var value_array = []; 7577 $(this).find(".values input").each(function (index, elm) { 7578 var val = elm.value.replace(/\\/g, '\\\\').replace(/'/g, "''"); 7579 value_array.push("'" + val + "'"); 7580 }); 7581 // get the Length/Values text field where this value belongs 7582 var values_id = $(this).find("input[type='hidden']").val(); 7583 $("input#" + values_id).val(value_array.join(",")); 7584 $(this).dialog("close"); 7585 }; 7586 buttonOptions[PMA_messages.strClose] = function () { 7587 $(this).dialog("close"); 7588 }; 7589 // Show the dialog 7590 var width = parseInt( 7591 (parseInt($('html').css('font-size'), 10) / 13) * 340, 7592 10 7593 ); 7594 if (! width) { 7595 width = 340; 7596 } 7597 $enum_editor_dialog = $(dialog).dialog({ 7598 minWidth: width, 7599 maxHeight: 450, 7600 modal: true, 7601 title: PMA_messages.enum_editor, 7602 buttons: buttonOptions, 7603 open: function () { 7604 // Focus the "Go" button after opening the dialog 7605 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus(); 7606 }, 7607 close: function () { 7608 $(this).remove(); 7609 } 7610 }); 7611 // slider for choosing how many fields to add 7612 $enum_editor_dialog.find(".slider").slider({ 7613 animate: true, 7614 range: "min", 7615 value: 1, 7616 min: 1, 7617 max: 9, 7618 slide: function (event, ui) { 7619 $(this).closest('table').find('input[type=submit]').val( 7620 PMA_sprintf(PMA_messages.enum_addValue, ui.value) 7621 ); 7622 } 7623 }); 7624 // Focus the slider, otherwise it looks nearly transparent 7625 $('a.ui-slider-handle').addClass('ui-state-focus'); 7626 return false;
7627 }); 7628 7629 $(document).on('click', 'a.central_columns_dialog', function (e) { 7630 var href = "db_central_columns.php"; 7631 var db = PMA_commonParams.get('db'); 7632 var table = PMA_commonParams.get('table'); 7633 var maxRows = $(this).data('maxrows'); 7634 var pick = $(this).data('pick'); 7635 if (pick !== false) { 7636 pick = true; 7637 } 7638 var params = { 7639 'ajax_request' : true, 7640 'token' : PMA_commonParams.get('token'), 7641 'server' : PMA_commonParams.get('server'), 7642 'db' : PMA_commonParams.get('db'), 7643 'cur_table' : PMA_commonParams.get('table'), 7644 'getColumnList':true 7645 }; 7646 var colid = $(this).closest('td').find("input").attr("id"); 7647 var fields = ''; 7648 if (! (db + '_' + table in central_column_list)) { 7649 central_column_list.push(db + '_' + table); 7650 $.ajax({ 7651 type: 'POST', 7652 url: href, 7653 data: params, 7654 success: function (data) { 7655 central_column_list[db + '_' + table] = $.parseJSON(data.message); 7656 }, 7657 async:false 7658 }); 7659 } 7660 var i = 0; 7661 var list_size = central_column_list[db + '_' + table].length; 7662 var min = (list_size <= maxRows) ? list_size : maxRows; 7663 for (i = 0; i < min; i++) { 7664 7665 fields += '<tr><td><div><span style="font-weight:bold">' + 7666 escapeHtml(central_column_list[db + '_' + table][i].col_name) + 7667 '</span><br><span style="color:gray">' + central_column_list[db + '_' + table][i].col_type; 7668 7669 if (central_column_list[db + '_' + table][i].col_attribute !== '') { 7670 fields += '(' + escapeHtml(central_column_list[db + '_' + table][i].col_attribute) + ') '; 7671 } 7672 if (central_column_list[db + '_' + table][i].col_length !== '') { 7673 fields += '(' + escapeHtml(central_column_list[db + '_' + table][i].col_length) +') '; 7674 } 7675 fields += escapeHtml(central_column_list[db + '_' + table][i].col_extra) + '</span>' + 7676 '</div></td>'; 7677 if (pick) { 7678 fields += '<td><input class="pick" style="width:100%" type="submit" value="' + 7679 PMA_messages.pickColumn + '" onclick="autoPopulate(\'' + colid + '\',' + i + ')"/></td>'; 7680 } 7681 fields += '</tr>'; 7682 } 7683 var result_pointer = i; 7684 var search_in = '<input type="text" class="filter_rows" placeholder="' + PMA_messages.searchList + '">'; 7685 if (fields === '') { 7686 fields = PMA_sprintf(PMA_messages.strEmptyCentralList, "'" + escapeHtml(db) + "'"); 7687 search_in = ''; 7688 } 7689 var seeMore = ''; 7690 if (list_size > maxRows) { 7691 seeMore = "<fieldset class='tblFooters' style='text-align:center;font-weight:bold'>" + 7692 "<a href='#' id='seeMore'>" + PMA_messages.seeMore + "</a></fieldset>"; 7693 } 7694 var central_columns_dialog = "<div style='max-height:400px'>" + 7695 "<fieldset>" + 7696 search_in + 7697 "<table id='col_list' style='width:100%' class='values'>" + fields + "</table>" + 7698 "</fieldset>" + 7699 seeMore + 7700 "</div>"; 7701 7702 var width = parseInt( 7703 (parseInt($('html').css('font-size'), 10) / 13) * 500, 7704 10 7705 ); 7706 if (! width) { 7707 width = 500; 7708 } 7709 var buttonOptions = {}; 7710 var $central_columns_dialog = $(central_columns_dialog).dialog({ 7711 minWidth: width, 7712 maxHeight: 450, 7713 modal: true, 7714 title: PMA_messages.pickColumnTitle, 7715 buttons: buttonOptions, 7716 open: function () { 7717 $('#col_list').on("click", ".pick", function (){ 7718 $central_columns_dialog.remove(); 7719 }); 7720 $(".filter_rows").on("keyup", function () { 7721 $.uiTableFilter($("#col_list"), $(this).val()); 7722 }); 7723 $("#seeMore").click(function() { 7724 fields = ''; 7725 min = (list_size <= maxRows + result_pointer) ? list_size : maxR
7725ows + result_pointer; 7726 for (i = result_pointer; i < min; i++) { 7727 7728 fields += '<tr><td><div><span style="font-weight:bold">' + 7729 central_column_list[db + '_' + table][i].col_name + 7730 '</span><br><span style="color:gray">' + 7731 central_column_list[db + '_' + table][i].col_type; 7732 7733 if (central_column_list[db + '_' + table][i].col_attribute !== '') { 7734 fields += '(' + central_column_list[db + '_' + table][i].col_attribute + ') '; 7735 } 7736 if (central_column_list[db + '_' + table][i].col_length !== '') { 7737 fields += '(' + central_column_list[db + '_' + table][i].col_length + ') '; 7738 } 7739 fields += central_column_list[db + '_' + table][i].col_extra + '</span>' + 7740 '</div></td>'; 7741 if (pick) { 7742 fields += '<td><input class="pick" style="width:100%" type="submit" value="' + 7743 PMA_messages.pickColumn + '" onclick="autoPopulate(\'' + colid + '\',' + i + ')"/></td>'; 7744 } 7745 fields += '</tr>'; 7746 } 7747 $("#col_list").append(fields); 7748 result_pointer = i; 7749 if (result_pointer === list_size) { 7750 $('.tblFooters').hide(); 7751 } 7752 return false; 7753 }); 7754 $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button:first').focus(); 7755 }, 7756 close: function () { 7757 $('#col_list').off("click", ".pick"); 7758 $(".filter_rows").off("keyup"); 7759 $(this).remove(); 7760 } 7761 }); 7762 return false; 7763 }); 7764 7765 // $(document).on('click', 'a.show_central_list',function(e) { 7766 7767 // }); 7768 // When "add a new value" is clicked, append an empty text field 7769 $(document).on('click', "input.add_value", function (e) { 7770 e.preventDefault(); 7771 var num_new_rows = $enum_editor_dialog.find("div.slider").slider('value'); 7772 while (num_new_rows--) { 7773 $enum_editor_dialog.find('.values') 7774 .append( 7775 "<tr style='display: none;'><td>" + 7776 "<input type='text' />" + 7777 "</td><td class='drop'>" + 7778 PMA_getImage('b_drop.png') + 7779 "</td></tr>" 7780 ) 7781 .find('tr:last') 7782 .show('fast'); 7783 } 7784 }); 7785 7786 // Removes the specified row from the enum editor 7787 $(document).on('click', "#enum_editor td.drop", function () { 7788 $(this).closest('tr').hide('fast', function () { 7789 $(this).remove(); 7790 }); 7791 }); 7792}); 7793 7794/** 7795 * Ensures indexes names are valid according to their type and, for a primary 7796 * key, lock index name to 'PRIMARY' 7797 * @param string form_id Variable which parses the form name as 7798 * the input 7799 * @return boolean false if there is no index form, true else 7800 */ 7801function checkIndexName(form_id) 7802{ 7803 if ($("#" + form_id).length === 0) { 7804 return false; 7805 } 7806 7807 // Gets the elements pointers 7808 var $the_idx_name = $("#input_index_name"); 7809 var $the_idx_choice = $("#select_index_choice"); 7810 7811 // Index is a primary key 7812 if ($the_idx_choice.find("option:selected").val() == 'PRIMARY') { 7813 $the_idx_name.val('PRIMARY'); 7814 $the_idx_name.prop("disabled", true); 7815 } 7816 7817 // Other cases 7818 else { 7819 if ($the_idx_name.val() == 'PRIMARY') { 7820 $the_idx_name.val(""); 7821 } 7822 $the_idx_name.prop("disabled", false); 7823 } 7824 7825 return true; 7826} // end of the 'checkIndexName()' function 7827 7828AJAX.registerTeardown('functions.js', function () { 7829 $(document).off('click', '#index_frm input[type=submit]'); 7830}); 7831AJAX.registerOnload('functions.js', function () { 7832 /** 7833 * Handler for adding more columns to an index in the editor
7834 */ 7835 $(document).on('click', '#index_frm input[type=submit]', function (event) { 7836 event.preventDefault(); 7837 var rows_to_add = $(this) 7838 .closest('fieldset') 7839 .find('.slider') 7840 .slider('value'); 7841 7842 var tempEmptyVal = function () { 7843 $(this).val(''); 7844 }; 7845 7846 var tempSetFocus = function () { 7847 if ($(this).find("option:selected").val() === '') { 7848 return true; 7849 } 7850 $(this).closest("tr").find("input").focus(); 7851 }; 7852 7853 while (rows_to_add--) { 7854 var $indexColumns = $('#index_columns'); 7855 var $newrow = $indexColumns 7856 .find('tbody > tr:first') 7857 .clone() 7858 .appendTo( 7859 $indexColumns.find('tbody') 7860 ); 7861 $newrow.find(':input').each(tempEmptyVal); 7862 // focus index size input on column picked 7863 $newrow.find('select').change(tempSetFocus); 7864 } 7865 }); 7866}); 7867 7868function indexEditorDialog(url, title, callback_success, callback_failure) 7869{ 7870 /*Remove the hidden dialogs if there are*/ 7871 var $editIndexDialog = $('#edit_index_dialog'); 7872 if ($editIndexDialog.length !== 0) { 7873 $editIndexDialog.remove(); 7874 } 7875 var $div = $('<div id="edit_index_dialog"></div>'); 7876 7877 /** 7878 * @var button_options Object that stores the options 7879 * passed to jQueryUI dialog 7880 */ 7881 var button_options = {}; 7882 button_options[PMA_messages.strGo] = function () { 7883 /** 7884 * @var the_form object referring to the export form 7885 */ 7886 var $form = $("#index_frm"); 7887 var $msgbox = PMA_ajaxShowMessage(PMA_messages.strProcessingRequest); 7888 PMA_prepareForAjaxRequest($form); 7889 //User wants to submit the form 7890 $.post($form.attr('action'), $form.serialize() + "&do_save_data=1", function (data) { 7891 var $sqlqueryresults = $(".sqlqueryresults"); 7892 if ($sqlqueryresults.length !== 0) { 7893 $sqlqueryresults.remove(); 7894 } 7895 if (typeof data !== 'undefined' && data.success === true) { 7896 PMA_ajaxShowMessage(data.message); 7897 var $resultQuery = $('.result_query'); 7898 if ($resultQuery.length) { 7899 $resultQuery.remove(); 7900 } 7901 if (data.sql_query) { 7902 $('<div class="result_query"></div>') 7903 .html(data.sql_query) 7904 .prependTo('#page_content'); 7905 PMA_highlightSQL($('#page_content')); 7906 } 7907 $(".result_query .notice").remove(); 7908 $resultQuery.prepend(data.message); 7909 /*Reload the field form*/ 7910 $("#table_index").remove(); 7911 $("<div id='temp_div'><div>") 7912 .append(data.index_table) 7913 .find("#table_index") 7914 .insertAfter("#index_header"); 7915 var $editIndexDialog = $("#edit_index_dialog"); 7916 if ($editIndexDialog.length > 0) { 7917 $editIndexDialog.dialog("close"); 7918 } 7919 $('div.no_indexes_defined').hide(); 7920 if (callback_success) { 7921 callback_success(); 7922 } 7923 PMA_reloadNavigation(); 7924 } else { 7925 var $temp_div = $("<div id='temp_div'><div>").append(data.error); 7926 var $error; 7927 if ($temp_div.find(".error code").length !== 0) { 7928 $error = $temp_div.find(".error code").addClass("error"); 7929 } else { 7930 $error = $temp_div; 7931 } 7932 if (callback_failure) { 7933 callback_failure(); 7934 } 7935 PMA_ajaxShowMessage($error, false); 7936 } 7937 }); // end $.post() 7938 }; 7939 button_options[PMA_messages.strPreviewSQL] = function () { 7940 // Function for Previewing SQL 7941 var $form = $('#index_frm'); 7942 PMA_previewSQL($form); 7943 }; 7944 button_options[PMA_messages.strCancel] = function () { 7945 $(this).dialog('close'); 7946 };
7947 var $msgbox = PMA_ajaxShowMessage(); 7948 $.get("tbl_indexes.php", url, function (data) { 7949 if (typeof data !== 'undefined' && data.success === false) { 7950 //in the case of an error, show the error message returned. 7951 PMA_ajaxShowMessage(data.error, false); 7952 } else { 7953 PMA_ajaxRemoveMessage($msgbox); 7954 // Show dialog if the request was successful 7955 $div 7956 .append(data.message) 7957 .dialog({ 7958 title: title, 7959 width: 450, 7960 height: 350, 7961 open: PMA_verifyColumnsProperties, 7962 modal: true, 7963 buttons: button_options, 7964 close: function () { 7965 $(this).remove(); 7966 } 7967 }); 7968 $div.find('.tblFooters').remove(); 7969 showIndexEditDialog($div); 7970 } 7971 }); // end $.get() 7972} 7973 7974function showIndexEditDialog($outer) 7975{ 7976 checkIndexType(); 7977 checkIndexName("index_frm"); 7978 var $indexColumns = $('#index_columns'); 7979 $indexColumns.find('td').each(function () { 7980 $(this).css("width", $(this).width() + 'px'); 7981 }); 7982 $indexColumns.find('tbody').sortable({ 7983 axis: 'y', 7984 containment: $indexColumns.find("tbody"), 7985 tolerance: 'pointer' 7986 }); 7987 PMA_showHints($outer); 7988 PMA_init_slider(); 7989 // Add a slider for selecting how many columns to add to the index 7990 $outer.find('.slider').slider({ 7991 animate: true, 7992 value: 1, 7993 min: 1, 7994 max: 16, 7995 slide: function (event, ui) { 7996 $(this).closest('fieldset').find('input[type=submit]').val( 7997 PMA_sprintf(PMA_messages.strAddToIndex, ui.value) 7998 ); 7999 } 8000 }); 8001 $('div.add_fields').removeClass('hide'); 8002 // focus index size input on column picked 8003 $outer.find('table#index_columns select').change(function () { 8004 if ($(this).find("option:selected").val() === '') { 8005 return true; 8006 } 8007 $(this).closest("tr").find("input").focus(); 8008 }); 8009 // Focus the slider, otherwise it looks nearly transparent 8010 $('a.ui-slider-handle').addClass('ui-state-focus'); 8011 // set focus on index name input, if empty 8012 var input = $outer.find('input#input_index_name'); 8013 if (! input.val()) { 8014 input.focus(); 8015 } 8016} 8017 8018/** 8019 * Function to display tooltips that were 8020 * generated on the PHP side by PMA\libraries\Util::showHint() 8021 * 8022 * @param object $div a div jquery object which specifies the 8023 * domain for searching for tooltips. If we 8024 * omit this parameter the function searches 8025 * in the whole body 8026 **/ 8027function PMA_showHints($div) 8028{ 8029 if ($div === undefined || ! $div instanceof jQuery || $div.length === 0) { 8030 $div = $("body"); 8031 } 8032 $div.find('.pma_hint').each(function () { 8033 PMA_tooltip( 8034 $(this).children('img'), 8035 'img', 8036 $(this).children('span').html() 8037 ); 8038 }); 8039} 8040 8041AJAX.registerOnload('functions.js', function () { 8042 PMA_showHints(); 8043}); 8044 8045function PMA_mainMenuResizerCallback() { 8046 // 5 px margin for jumping menu in Chrome 8047 return $(document.body).width() - 5; 8048} 8049// This must be fired only once after the initial page load 8050$(function () { 8051 // Initialise the menu resize plugin 8052 $('#topmenu').menuResizer(PMA_mainMenuResizerCallback); 8053 // register resize event 8054 $(window).resize(function () { 8055 $('#topmenu').menuResizer('resize'); 8056 }); 8057}); 8058 8059/** 8060 * Get the row number from the classlist (for example, row_1) 8061 */ 8062function PMA_getRowNumber(classlist) 8063{ 8064 return parseInt(classlist.split(/\s+row_/)[1], 10); 8065} 8066 8067/** 8068 * Changes status of slider 8069 */ 8070function PMA_set_status_label($element) 8071{ 8072 var text; 8073 if ($element.css('display') == 'none') { 8074 text = '+ '; 8075 } else { 8076 text = '- '; 8077 } 8078 $element.closest('.slide-wrapper').prev().find('span').text(text); 8079} 8080 8081/** 8082 * var toggleButton This is a function that creates a toggle 8083 * sliding button given a jQuery reference 8084 * to the correct DOM element 8085 */ 8086var toggleButton = function ($obj) { 8087 // In rtl mode the toggle switch is flipped horizontally 8088 // so we need to take that into account 8089 var right; 8090 if ($('span.text_direction', $obj).text() == 'ltr') { 8091 right = 'right'; 8092 } else { 8093 right = 'left'; 8094 } 8095 /** 8096 * var h Height of the button, used to scale the 8097 * background image and position the layers 8098 */ 8099 var h = $obj.height(); 8100 $('img', $obj).height(h); 8101 $('table', $obj).css('bottom', h - 1); 8102 /** 8103 * var on Width of the "ON" part of the toggle switch 8104 * var off Width of the "OFF" part of the toggle switch 8105 */ 8106 var on = $('td.toggleOn', $obj).width(); 8107 var off = $('td.toggleOff', $obj).width(); 8108 // Make the "ON" and "OFF" parts of the switch the same size 8109 // + 2 pixels to avoid overflowed 8110 $('td.toggleOn > div', $obj).width(Math.max(on, off) + 2); 8111 $('td.toggleOff > div', $obj).width(Math.max(on, off) + 2); 8112 /** 8113 * var w Width of the central part of the switch 8114 */ 8115 var w = parseInt(($('img', $obj).height() / 16) * 22, 10); 8116 // Resize the central part of the switch on the top 8117 // layer to match the background 8118 $('table td:nth-child(2) > div', $obj).width(w); 8119 /** 8120 * var imgw Width of the background image 8121 * var tblw Width of the foreground layer 8122 * var offset By how many pixels to move the background 8123 * image, so that it matches the top layer 8124 */ 8125 var imgw = $('img', $obj).width(); 8126 var tblw = $('table', $obj).width(); 8127 var offset = parseInt(((imgw - tblw) / 2), 10); 8128 // Move the background to match the layout of the top layer 8129 $obj.find('img').css(right, offset); 8130 /** 8131 * var offw Outer width of the "ON" part of the toggle switch 8132 * var btnw Outer width of the central part of the switch 8133 */ 8134 var offw = $('td.toggleOff', $obj).outerWidth(); 8135 var btnw = $('table td:nth-child(2)', $obj).outerWidth(); 8136 // Resize the main div so that exactly one side of
8137 // the switch plus the central part fit into it. 8138 $obj.width(offw + btnw + 2); 8139 /** 8140 * var move How many pixels to move the 8141 * switch by when toggling 8142 */ 8143 var move = $('td.toggleOff', $obj).outerWidth(); 8144 // If the switch is initialized to the 8145 // OFF state we need to move it now. 8146 if ($('div.container', $obj).hasClass('off')) { 8147 if (right == 'right') { 8148 $('div.container', $obj).animate({'left': '-=' + move + 'px'}, 0); 8149 } else { 8150 $('div.container', $obj).animate({'left': '+=' + move + 'px'}, 0); 8151 } 8152 } 8153 // Attach an 'onclick' event to the switch 8154 $('div.container', $obj).click(function () { 8155 if ($(this).hasClass('isActive')) { 8156 return false; 8157 } else { 8158 $(this).addClass('isActive'); 8159 } 8160 var $msg = PMA_ajaxShowMessage(); 8161 var $container = $(this); 8162 var callback = $('span.callback', this).text(); 8163 var operator, url, removeClass, addClass; 8164 // Perform the actual toggle 8165 if ($(this).hasClass('on')) { 8166 if (right == 'right') { 8167 operator = '-='; 8168 } else { 8169 operator = '+='; 8170 } 8171 url = $(this).find('td.toggleOff > span').text(); 8172 removeClass = 'on'; 8173 addClass = 'off'; 8174 } else { 8175 if (right == 'right') { 8176 operator = '+='; 8177 } else { 8178 operator = '-='; 8179 } 8180 url = $(this).find('td.toggleOn > span').text(); 8181 removeClass = 'off'; 8182 addClass = 'on'; 8183 } 8184 $.post(url, {'ajax_request': true}, function (data) { 8185 if (typeof data !== 'undefined' && data.success === true) { 8186 PMA_ajaxRemoveMessage($msg); 8187 $container 8188 .removeClass(removeClass) 8189 .addClass(addClass) 8190 .animate({'left': operator + move + 'px'}, function () { 8191 $container.removeClass('isActive'); 8192 }); 8193 eval(callback); 8194 } else { 8195 PMA_ajaxShowMessage(data.error, false); 8196 $container.removeClass('isActive'); 8197 } 8198 }); 8199 }); 8200}; 8201 8202/** 8203 * Unbind all event handlers before tearing down a page 8204 */ 8205AJAX.registerTeardown('functions.js', function () { 8206 $('div.container').unbind('click'); 8207}); 8208/** 8209 * Initialise all toggle buttons 8210 */ 8211AJAX.registerOnload('functions.js', function () { 8212 $('div.toggleAjax').each(function () { 8213 var $button = $(this).show(); 8214 $button.find('img').each(function () { 8215 if (this.complete) { 8216 toggleButton($button); 8217 } else { 8218 $(this).load(function () { 8219 toggleButton($button); 8220 }); 8221 } 8222 }); 8223 }); 8224}); 8225 8226/** 8227 * Unbind all event handlers before tearing down a page 8228 */ 8229AJAX.registerTeardown('functions.js', function () { 8230 $(document).off('change', 'select.pageselector'); 8231 $(document).off('click', 'a.formLinkSubmit'); 8232 $('#update_recent_tables').unbind('ready'); 8233 $('#sync_favorite_tables').unbind('ready'); 8234}); 8235 8236AJAX.registerOnload('functions.js', function () { 8237 8238 /** 8239 * Autosubmit page selector 8240 */ 8241 $(document).on('change', 'select.pageselector', function (event) { 8242 event.stopPropagation(); 8243 // Check where to load the new content 8244 if ($(this).closest("#pma_navigation").length === 0) { 8245 // For the main page we don't need to do anything, 8246 $(this).closest("form").submit(); 8247 } else { 8248 // but for the navigation we need to manually replace the content 8249 PMA_navigationTreePagination($(this)); 8250 } 8251 }); 8252 8253 /** 8254 * Load version information asynchronously. 8255 */ 8256 if ($('li.jsversioncheck').length > 0) { 8257 $.ajax({ 8258 dataType: "json", 8259 url: 'version_check.php', 8260 method: "POST", 8261 data: { 8262 "server": PMA_commonParams.get('server'), 8263 "token": PMA_commonParams.get('token'), 8264 }, 8265 success: PMA_current_version 8266 }); 8267 } 8268 8269 if ($('#is_git_revision').length > 0) { 8270 setTimeout(PMA_display_git_revision, 10); 8271 } 8272 8273 /** 8274 * Slider effect. 8275 */ 8276 PMA_init_slider(); 8277 8278 /** 8279 * Enables the text generated by PMA\libraries\Util::linkOrButton() to be clickable 8280 */ 8281 $(document).on('click', 'a.formLinkSubmit', function (e) { 8282 if (! $(this).hasClass('requireConfirm')) { 8283 submitFormLink($(this)); 8284 return false;
8285 } 8286 }); 8287 8288 var $updateRecentTables = $('#update_recent_tables'); 8289 if ($updateRecentTables.length) { 8290 $.get( 8291 $updateRecentTables.attr('href'), 8292 {no_debug: true}, 8293 function (data) { 8294 if (typeof data !== 'undefined' && data.success === true) { 8295 $('#pma_recent_list').html(data.list); 8296 } 8297 } 8298 ); 8299 } 8300 8301 // Sync favorite tables from localStorage to pmadb. 8302 if ($('#sync_favorite_tables').length) { 8303 $.ajax({ 8304 url: $('#sync_favorite_tables').attr("href"), 8305 cache: false, 8306 type: 'POST', 8307 data: { 8308 favorite_tables: (isStorageSupported('localStorage') && typeof window.localStorage.favorite_tables !== 'undefined') 8309 ? window.localStorage.favorite_tables 8310 : '', 8311 no_debug: true 8312 }, 8313 success: function (data) { 8314 // Update localStorage. 8315 if (isStorageSupported('localStorage')) { 8316 window.localStorage.favorite_tables = data.favorite_tables; 8317 } 8318 $('#pma_favorite_list').html(data.list); 8319 } 8320 }); 8321 } 8322}); // end of $() 8323 8324/** 8325 * Submits the form placed in place of a link due to the excessive url length 8326 * 8327 * @param $link anchor 8328 * @returns {Boolean} 8329 */ 8330function submitFormLink($link) 8331{ 8332 if ($link.attr('href').indexOf('=') != -1) { 8333 var data = $link.attr('href').substr($link.attr('href').indexOf('#') + 1).split('=', 2); 8334 $link.parents('form').append('<input type="hidden" name="' + data[0] + '" value="' + data[1] + '"/>'); 8335 } 8336 $link.parents('form').submit(); 8337} 8338 8339/** 8340 * Initializes slider effect. 8341 */ 8342function PMA_init_slider() 8343{ 8344 $('div.pma_auto_slider').each(function () { 8345 var $this = $(this); 8346 if ($this.data('slider_init_done')) { 8347 return; 8348 } 8349 var $wrapper = $('<div>', {'class': 'slide-wrapper'}); 8350 $wrapper.toggle($this.is(':visible')); 8351 $('<a>', {href: '#' + this.id, "class": 'ajax'}) 8352 .text($this.attr('title')) 8353 .prepend($('<span>')) 8354 .insertBefore($this) 8355 .click(function () { 8356 var $wrapper = $this.closest('.slide-wrapper'); 8357 var visible = $this.is(':visible'); 8358 if (!visible) { 8359 $wrapper.show(); 8360 } 8361 $this[visible ? 'hide' : 'show']('blind', function () { 8362 $wrapper.toggle(!visible); 8363 $wrapper.parent().toggleClass("print_ignore", visible); 8364 PMA_set_status_label($this); 8365 }); 8366 return false; 8367 }); 8368 $this.wrap($wrapper); 8369 $this.removeAttr('title'); 8370 PMA_set_status_label($this); 8371 $this.data('slider_init_done', 1); 8372 }); 8373} 8374 8375/** 8376 * Initializes slider effect. 8377 */ 8378AJAX.registerOnload('functions.js', function () { 8379 PMA_init_slider(); 8380}); 8381 8382/** 8383 * Restores sliders to the state they were in before initialisation. 8384 */ 8385AJAX.registerTeardown('functions.js', function () { 8386 $('div.pma_auto_slider').each(function () { 8387 var $this = $(this); 8388 $this.removeData(); 8389 $this.parent().replaceWith($this); 8390 $this.parent().children('a').remove(); 8391 }); 8392}); 8393 8394/** 8395 * Creates a message inside an object with a sliding effect 8396 * 8397 * @param msg A string containing the text to display 8398 * @param $obj a jQuery object containing the reference 8399 * to the element where to put the message 8400 * This is optional, if no element is 8401 * provided, one will be created below the 8402 * navigation links at the top of the page 8403 * 8404 * @return bool True on success, false on failure 8405 */ 8406function PMA_slidingMessage(msg, $obj) 8407{ 8408 if (msg === undefined || msg.length === 0) { 8409 // Don't show an empty message 8410 return false; 8411 } 8412 if ($obj === undefined || ! $obj instanceof jQuery || $obj.length === 0) { 8413 // If the second argument was not supplied, 8414 // we might have to create a new DOM node. 8415 if ($('#PMA_slidingMessage').length === 0) { 8416 $('#page_content').prepend( 8417 '<span id="PMA_slidingMessage" ' + 8418 'style="display: inline-block;"></span>' 8419 ); 8420 } 8421 $obj = $('#PMA_slidingMessage'); 8422 } 8423 if ($obj.has('div').length > 0) { 8424 // If there already is a message inside the 8425 // target object, we must get rid of it 8426 $obj 8427 .find('div') 8428 .first() 8429 .fadeOut(function () { 8430 $obj 8431 .children() 8432 .remove(); 8433 $obj 8434 .append('<div>' + msg + '</div>'); 8435 // highlight any sql before taking height; 8436 PMA_highlightSQL($obj); 8437 $obj.find('div') 8438 .first() 8439 .hide(); 8440 $obj 8441 .animate({ 8442 height: $obj.find('div').first().height() 8443 }) 8444 .find('div') 8445 .first() 8446 .fadeIn(); 8447 }); 8448 } else { 8449 // Object does not already have a message 8450 // inside it, so we simply slide it down 8451 $obj.width('100%') 8452 .html('<div>' + msg + '</div>'); 8453 // highlight any sql before taking height; 8454 PMA_highlightSQL($obj); 8455 var h = $obj 8456 .find('div') 8457 .first() 8458 .hide() 8459 .height(); 8460 $obj 8461 .find('div') 8462 .first() 8463 .css('height', 0) 8464 .show() 8465 .animate({ 8466 height: h 8467 }, function () { 8468 // Set the height of the parent 8469 // to the height of the child 8470 $obj 8471 .height( 8472 $obj 8473 .find('div') 8474 .first() 8475 .height() 8476 ); 8477 }); 8478 } 8479 return true; 8480} // end PMA_slidingMessage() 8481 8482/** 8483 * Attach CodeMirror2 editor to SQL edit area. 8484 */ 8485AJAX.registerOnload('functions.js', function () { 8486 var $elm = $('#sqlquery'); 8487 if ($elm.length > 0) { 8488 if (typeof CodeMirror != 'undefined') { 8489 codemirror_editor = PMA_getSQLEditor($elm); 8490 codemirror_editor.focus(); 8491 codemirror_editor.on("blur", updateQueryParameters); 8492 } else { 8493 // without codemirror 8494 $elm.focus() 8495 .bind('blur', updateQueryParameters); 8496 } 8497 } 8498 PMA_highlightSQL($('body')); 8499}); 8500AJAX.registerTeardown('functions.js', function () { 8501 if (codemirror_editor) { 8502 $('#sqlquery').text(codemirror_editor.getValue()); 8503 codemirror_editor.toTextArea(); 8504 codemirror_editor = false;
8505 } 8506}); 8507AJAX.registerOnload('functions.js', function () { 8508 // initializes all lock-page elements lock-id and 8509 // val-hash data property 8510 $('#page_content form.lock-page textarea, ' + 8511 '#page_content form.lock-page input[type="text"], '+ 8512 '#page_content form.lock-page input[type="number"], '+ 8513 '#page_content form.lock-page select').each(function (i) { 8514 $(this).data('lock-id', i); 8515 // val-hash is the hash of default value of the field 8516 // so that it can be compared with new value hash 8517 // to check whether field was modified or not. 8518 $(this).data('val-hash', AJAX.hash($(this).val())); 8519 }); 8520 8521 // initializes lock-page elements (input types checkbox and radio buttons) 8522 // lock-id and val-hash data property 8523 $('#page_content form.lock-page input[type="checkbox"], ' + 8524 '#page_content form.lock-page input[type="radio"]').each(function (i) { 8525 $(this).data('lock-id', i); 8526 $(this).data('val-hash', AJAX.hash($(this).is(":checked"))); 8527 }); 8528}); 8529 8530/** 8531 * jQuery plugin to correctly filter input fields by value, needed 8532 * because some nasty values may break selector syntax 8533 */ 8534(function ($) { 8535 $.fn.filterByValue = function (value) { 8536 return this.filter(function () { 8537 return $(this).val() === value; 8538 }); 8539 }; 8540})(jQuery); 8541 8542/** 8543 * Return value of a cell in a table. 8544 */ 8545function PMA_getCellValue(td) { 8546 var $td = $(td); 8547 if ($td.is('.null')) { 8548 return ''; 8549 } else if ((! $td.is('.to_be_saved') 8550 || $td.is('.set')) 8551 && $td.data('original_data') 8552 ) { 8553 return $td.data('original_data'); 8554 } else { 8555 return $td.text(); 8556 } 8557} 8558 8559$(window).on('popstate', function (event, data) { 8560 $('#printcss').attr('media','print'); 8561 return true; 8562}); 8563 8564/** 8565 * Unbind all event handlers before tearing down a page 8566 */ 8567AJAX.registerTeardown('functions.js', function () { 8568 $(document).off('click', 'a.themeselect'); 8569 $(document).off('change', '.autosubmit'); 8570 $('a.take_theme').unbind('click'); 8571}); 8572 8573AJAX.registerOnload('functions.js', function () { 8574 /** 8575 * Theme selector. 8576 */ 8577 $(document).on('click', 'a.themeselect', function (e) { 8578 window.open( 8579 e.target, 8580 'themes', 8581 'left=10,top=20,width=510,height=350,scrollbars=yes,status=yes,resizable=yes' 8582 ); 8583 return false; 8584 }); 8585 8586 /** 8587 * Automatic form submission on change. 8588 */ 8589 $(document).on('change', '.autosubmit', function (e) { 8590 $(this).closest('form').submit(); 8591 }); 8592 8593 /** 8594 * Theme changer. 8595 */ 8596 $('a.take_theme').click(function (e) { 8597 var what = this.name; 8598 if (window.opener && window.opener.document.forms.setTheme.elements.set_theme) { 8599 window.opener.document.forms.setTheme.elements.set_theme.value = what; 8600 window.opener.document.forms.setTheme.submit(); 8601 window.close(); 8602 return false; 8603 } 8604 return true; 8605 }); 8606}); 8607 8608/** 8609 * Produce print preview 8610 */ 8611function printPreview() 8612{ 8613 $('#printcss').attr('media','all'); 8614 createPrintAndBackButtons(); 8615} 8616 8617/** 8618 * Create print and back buttons in preview page 8619 */ 8620function createPrintAndBackButtons() { 8621 8622 var back_button = $("<input/>",{ 8623 type: 'button', 8624 value: PMA_messages.back, 8625 id: 'back_button_print_view' 8626 }); 8627 back_button.click(removePrintAndBackButton); 8628 back_button.appendTo('#page_content'); 8629 var print_button = $("<input/>",{ 8630 type: 'button', 8631 value: PMA_messages.print, 8632 id: 'print_button_print_view' 8633 }); 8634 print_button.click(printPage); 8635 print_button.appendTo('#page_content'); 8636} 8637 8638/** 8639 * Remove print and back buttons and revert to normal view 8640 */ 8641function removePrintAndBackButton(){ 8642 $('#printcss').attr('media','print'); 8643 $('#back_button_print_view').remove(); 8644 $('#print_button_print_view').remove(); 8645} 8646 8647/** 8648 * Print page 8649 */ 8650function printPage(){ 8651 if (typeof(window.print) != 'undefined') { 8652 window.print(); 8653 } 8654} 8655 8656/** 8657 * Print button 8658 */ 8659function copyToClipboard() 8660{ 8661 var textArea = document.createElement("textarea"); 8662 8663 // 8664 // *** This styling is an extra step which is likely not required. *** 8665 // 8666 // Why is it here? To ensure: 8667 // 1. the element is able to have focus and selection. 8668 // 2. if element was to flash render it has minimal visual impact. 8669 // 3. less flakyness with selection and copying which **might** occur if 8670 // the textarea element is not visible. 8671 // 8672 // The likelihood is the element won't even render, not even a flash, 8673 // so some of these are just precautions. However in IE the element 8674 // is visible whilst the popup box asking the user for permission for 8675 // the web page to copy to the clipboard. 8676 // 8677 8678 // Place in top-left corner of screen regardless of scroll position. 8679 textArea.style.position = 'fixed'; 8680 textArea.style.top = 0; 8681 textArea.style.left = 0; 8682 8683 // Ensure it has a small width and height. Setting to 1px / 1em 8684 // doesn't work as this gives a negative w/h on some browsers. 8685 textArea.style.width = '2em'; 8686 textArea.style.height = '2em'; 8687 8688 // We don't need padding, reducing the size if it does flash render. 8689 textArea.style.padding = 0; 8690 8691 // Clean up any borders. 8692 textArea.style.border = 'none'; 8693 textArea.style.outline = 'none';
8694 textArea.style.boxShadow = 'none'; 8695 8696 // Avoid flash of white box if rendered for any reason. 8697 textArea.style.background = 'transparent'; 8698 8699 textArea.value = ''; 8700 8701 var elementList = $('#serverinfo a'); 8702 8703 elementList.each(function(){ 8704 textArea.value += $(this).text().split(':')[1].trim() + '/'; 8705 }); 8706 textArea.value += '\t\t' + window.location.href; 8707 textArea.value += '\n'; 8708 8709 elementList = $('.notice,.success'); 8710 8711 elementList.each(function(){ 8712 textArea.value += $(this).clone().children().remove().end().text() + '\n\n'; 8713 }); 8714 8715 elementList = $('.sql pre'); 8716 8717 elementList.each(function() { 8718 textArea.value += $(this).text() + '\n\n'; 8719 }); 8720 8721 elementList = $('.table_results .column_heading a'); 8722 8723 elementList.each(function() { 8724 textArea.value += $(this).clone().children().remove().end().text() + '\t'; 8725 }); 8726 8727 textArea.value += '\n'; 8728 elementList = $('tbody .odd,tbody .even'); 8729 elementList.each(function() { 8730 var childElementList = $(this).find('.data span'); 8731 childElementList.each(function(){ 8732 textArea.value += $(this).clone().children().remove().end().text() + '\t'; 8733 }); 8734 textArea.value += '\n'; 8735 }); 8736 8737 document.body.appendChild(textArea); 8738 8739 textArea.select(); 8740 8741 try { 8742 document.execCommand('copy'); 8743 } catch (err) { 8744 alert('Sorry! Unable to copy'); 8745 } 8746 8747 document.body.removeChild(textArea); 8748} 8749 8750/** 8751 * Unbind all event handlers before tearing down a page 8752 */ 8753AJAX.registerTeardown('functions.js', function () { 8754 $('input#print').unbind('click'); 8755 $(document).off('click', 'a.create_view.ajax'); 8756 $(document).off('keydown', '#createViewDialog input, #createViewDialog select'); 8757 $(document).off('change', '#fkc_checkbox'); 8758}); 8759 8760AJAX.registerOnload('functions.js', function () { 8761 $('input#print').click(printPage); 8762 /** 8763 * Ajaxification for the "Create View" action 8764 */ 8765 $(document).on('click', 'a.create_view.ajax', function (e) { 8766 e.preventDefault(); 8767 PMA_createViewDialog($(this)); 8768 }); 8769 /** 8770 * Attach Ajax event handlers for input fields in the editor 8771 * and used to submit the Ajax request when the ENTER key is pressed. 8772 */ 8773 if ($('#createViewDialog').length !== 0) { 8774 $(document).on('keydown', '#createViewDialog input, #createViewDialog select', function (e) { 8775 if (e.which === 13) { // 13 is the ENTER key 8776 e.preventDefault(); 8777 8778 // with preventing default, selection by <select> tag 8779 // was also prevented in IE 8780 $(this).blur(); 8781 8782 $(this).closest('.ui-dialog').find('.ui-button:first').click(); 8783 } 8784 }); // end $(document).on() 8785 } 8786 8787 syntaxHighlighter = PMA_getSQLEditor($('textarea[name="view[as]"]')); 8788 8789}); 8790 8791function PMA_createViewDialog($this) 8792{ 8793 var $msg = PMA_ajaxShowMessage(); 8794 var syntaxHighlighter = null; 8795 $.get($this.attr('href') + '&ajax_request=1&ajax_dialog=1', function (data) { 8796 if (typeof data !== 'undefined' && data.success === true) { 8797 PMA_ajaxRemoveMessage($msg); 8798 var buttonOptions = {}; 8799 buttonOptions[PMA_messages.strGo] = function () { 8800 if (typeof CodeMirror !== 'undefined') { 8801 syntaxHighlighter.save(); 8802 } 8803 $msg = PMA_ajaxShowMessage(); 8804 $.post('view_create.php', $('#createViewDialog').find('form').serialize(), function (data) { 8805 PMA_ajaxRemoveMessage($msg); 8806 if (typeof data !== 'undefined' && data.success === true) { 8807 $('#createViewDialog').dialog("close"); 8808 $('.result_query').html(data.message); 8809 PMA_reloadNavigation(); 8810 } else { 8811 PMA_ajaxShowMessage(data.error, false); 8812 } 8813 }); 8814 }; 8815 buttonOptions[PMA_messages.strClose] = function () { 8816 $(this).dialog("close"); 8817 }; 8818 var $dialog = $('<div/>').attr('id', 'createViewDialog').append(data.message).dialog({ 8819 width: 600, 8820 minWidth: 400, 8821 modal: true, 8822 buttons: buttonOptions, 8823 title: PMA_messages.strCreateView, 8824 close: function () { 8825 $(this).remove(); 8826 } 8827 }); 8828 // Attach syntax highlighted editor 8829 syntaxHighlighter = PMA_getSQLEditor($dialog.find('textarea')); 8830 $('input:visible[type=text]', $dialog).first().focus(); 8831 } else { 8832 PMA_ajaxShowMessage(data.error); 8833 } 8834 }); 8835} 8836 8837/** 8838 * Makes the breadcrumbs and the menu bar float at the top of the viewport 8839 */ 8840$(function () { 8841 if ($("#floating_menubar").length && $('#PMA_disable_floating_menubar').length === 0) { 8842 var left = $('html').attr('dir') == 'ltr' ? 'left' : 'right'; 8843 $("#floating_menubar") 8844 .css('margin-' + left, $('#pma_navigation').width() + $('#pma_navigation_resizer').width()) 8845 .css(left, 0) 8846 .css({ 8847 'position': 'fixed', 8848 'top': 0, 8849 'width': '100%', 8850 'z-index': 99 8851 }) 8852 .append($('#serverinfo')) 8853 .append($('#topmenucontainer')); 8854 // Allow the DOM to render, then adjust the padding on the body 8855 setTimeout(function () { 8856 $('body').css( 8857 'padding-top', 8858 $('#floating_menubar').outerHeight(true) 8859 ); 8860 $('#topmenu').menuResizer('resize'); 8861 }, 4); 8862 } 8863}); 8864 8865/** 8866 * Scrolls the page to the top if clicking the serverinfo bar 8867 */ 8868$(function () { 8869 $(document).delegate("#serverinfo, #goto_pagetop", "click", function (event) { 8870 event.preventDefault(); 8871 $('html, body').animate({scrollTop: 0}, 'fast'); 8872 }); 8873}); 8874 8875var checkboxes_sel = "input.checkall:checkbox:enabled"; 8876/** 8877 * Watches checkboxes in a form to set the checkall box accordingly 8878 */ 8879var checkboxes_changed = function () { 8880 var $form = $(this.form); 8881 // total number of checkboxes in current form 8882 var total_boxes = $form.find(checkboxes_sel).length; 8883 // number of checkboxes checked in current form 8884 var checked_boxes = $form.find(checkboxes_sel + ":checked").length; 8885 var $checkall = $form.find("input.checkall_box"); 8886 if (total_boxes == checked_boxes) { 8887 $checkall.prop({checked: true, indeterminate: false}); 8888 } 8889 else if (checked_boxes > 0) { 8890 $checkall.prop({checked: true, indeterminate: true}); 8891 } 8892 else {
8893 $checkall.prop({checked: false, indeterminate: false}); 8894 } 8895}; 8896$(document).on("change", checkboxes_sel, checkboxes_changed); 8897 8898$(document).on("change", "input.checkall_box", function () { 8899 var is_checked = $(this).is(":checked"); 8900 $(this.form).find(checkboxes_sel).prop("checked", is_checked) 8901 .parents("tr").toggleClass("marked", is_checked); 8902}); 8903 8904/** 8905 * Watches checkboxes in a sub form to set the sub checkall box accordingly 8906 */ 8907var sub_checkboxes_changed = function () { 8908 var $form = $(this).parent().parent(); 8909 // total number of checkboxes in current sub form 8910 var total_boxes = $form.find(checkboxes_sel).length; 8911 // number of checkboxes checked in current sub form 8912 var checked_boxes = $form.find(checkboxes_sel + ":checked").length; 8913 var $checkall = $form.find("input.sub_checkall_box"); 8914 if (total_boxes == checked_boxes) { 8915 $checkall.prop({checked: true, indeterminate: false}); 8916 } 8917 else if (checked_boxes > 0) { 8918 $checkall.prop({checked: true, indeterminate: true}); 8919 } 8920 else { 8921 $checkall.prop({checked: false, indeterminate: false}); 8922 } 8923}; 8924$(document).on("change", checkboxes_sel + ", input.checkall_box:checkbox:enabled", sub_checkboxes_changed); 8925 8926$(document).on("change", "input.sub_checkall_box", function () { 8927 var is_checked = $(this).is(":checked"); 8928 var $form = $(this).parent().parent(); 8929 $form.find(checkboxes_sel).prop("checked", is_checked) 8930 .parents("tr").toggleClass("marked", is_checked); 8931}); 8932 8933/** 8934 * Toggles row colors of a set of 'tr' elements starting from a given element 8935 * 8936 * @param $start Starting element 8937 */ 8938function toggleRowColors($start) 8939{ 8940 for (var $curr_row = $start; $curr_row.length > 0; $curr_row = $curr_row.next()) { 8941 if ($curr_row.hasClass('odd')) { 8942 $curr_row.removeClass('odd').addClass('even'); 8943 } else if ($curr_row.hasClass('even')) { 8944 $curr_row.removeClass('even').addClass('odd'); 8945 } 8946 } 8947} 8948 8949/** 8950 * Formats a byte number to human-readable form 8951 * 8952 * @param bytes the bytes to format 8953 * @param optional subdecimals the number of digits after the point 8954 * @param optional pointchar the char to use as decimal point 8955 */ 8956function formatBytes(bytes, subdecimals, pointchar) { 8957 if (!subdecimals) { 8958 subdecimals = 0; 8959 } 8960 if (!pointchar) { 8961 pointchar = '.'; 8962 } 8963 var units = ['B', 'KiB', 'MiB', 'GiB']; 8964 for (var i = 0; bytes > 1024 && i < units.length; i++) { 8965 bytes /= 1024; 8966 } 8967 var factor = Math.pow(10, subdecimals); 8968 bytes = Math.round(bytes * factor) / factor; 8969 bytes = bytes.toString().split('.').join(pointchar); 8970 return bytes + ' ' + units[i]; 8971} 8972 8973AJAX.registerOnload('functions.js', function () { 8974 /** 8975 * Reveal the login form to users with JS enabled 8976 * and focus the appropriate input field 8977 */ 8978 var $loginform = $('#loginform'); 8979 if ($loginform.length) { 8980 $loginform.find('.js-show').show(); 8981 if ($('#input_username').val()) { 8982 $('#input_password').focus(); 8983 } else { 8984 $('#input_username').focus(); 8985 } 8986 } 8987}); 8988 8989/** 8990 * Dynamically adjust the width of the boxes 8991 * on the table and db operations pages 8992 */ 8993(function () { 8994 function DynamicBoxes() { 8995 var $boxContainer = $('#boxContainer'); 8996 if ($boxContainer.length) { 8997 var minWidth = $boxContainer.data('box-width'); 8998 var viewport = $(window).width() - $('#pma_navigation').width(); 8999 var slots = Math.floor(viewport / minWidth); 9000 $boxContainer.children() 9001 .each(function () { 9002 if (viewport < minWidth) { 9003 $(this).width(minWidth); 9004 } else { 9005 $(this).css('width', ((1 / slots) * 100) + "%"); 9006 } 9007 }) 9008 .removeClass('clearfloat') 9009 .filter(':nth-child(' + slots + 'n+1)') 9010 .addClass('clearfloat'); 9011 } 9012 } 9013 AJAX.registerOnload('functions.js', function () { 9014 DynamicBoxes(); 9015 }); 9016 $(function () { 9017 $(window).resize(DynamicBoxes); 9018 }); 9019})(); 9020 9021/** 9022 * Formats timestamp for display 9023 */ 9024function PMA_formatDateTime(date, seconds) { 9025 var result = $.datepicker.formatDate('yy-mm-dd', date); 9026 var timefmt = 'HH:mm'; 9027 if (seconds) { 9028 timefmt = 'HH:mm:ss'; 9029 } 9030 return result + ' ' + $.datepicker.formatTime( 9031 timefmt, { 9032 hour: date.getHours(), 9033 minute: date.getMinutes(), 9034 second: date.getSeconds() 9035 } 9036 ); 9037} 9038 9039/** 9040 * Check than forms have less fields than max allowed by PHP. 9041 */ 9042function checkNumberOfFields() { 9043 if (typeof maxInputVars === 'undefined') { 9044 return false;
9045 } 9046 if (false === maxInputVars) { 9047 return false; 9048 } 9049 $('form').each(function() { 9050 var nbInputs = $(this).find(':input').length; 9051 if (nbInputs > maxInputVars) { 9052 var warning = PMA_sprintf(PMA_messages.strTooManyInputs, maxInputVars); 9053 PMA_ajaxShowMessage(warning); 9054 return false; 9055 } 9056 return true; 9057 }); 9058 9059 return true; 9060} 9061 9062/** 9063 * Ignore the displayed php errors. 9064 * Simply removes the displayed errors. 9065 * 9066 * @param clearPrevErrors whether to clear errors stored 9067 * in $_SESSION['prev_errors'] at server 9068 * 9069 */ 9070function PMA_ignorePhpErrors(clearPrevErrors){ 9071 if (typeof(clearPrevErrors) === "undefined" || 9072 clearPrevErrors === null 9073 ) { 9074 str = false; 9075 } 9076 // send AJAX request to error_report.php with send_error_report=0, exception_type=php & token. 9077 // It clears the prev_errors stored in session. 9078 if(clearPrevErrors){ 9079 var $pmaReportErrorsForm = $('#pma_report_errors_form'); 9080 $pmaReportErrorsForm.find('input[name="send_error_report"]').val(0); // change send_error_report to '0' 9081 $pmaReportErrorsForm.submit(); 9082 } 9083 9084 // remove displayed errors 9085 var $pmaErrors = $('#pma_errors'); 9086 $pmaErrors.fadeOut( "slow"); 9087 $pmaErrors.remove(); 9088} 9089 9090/** 9091 * Toggle the Datetimepicker UI if the date value entered 9092 * by the user in the 'text box' is not going to be accepted 9093 * by the Datetimepicker plugin (but is accepted by MySQL) 9094 */ 9095function toggleDatepickerIfInvalid($td, $input_field) { 9096 // Regex allowed by the Datetimepicker UI 9097 var dtexpDate = new RegExp(['^([0-9]{4})',
9098 '-(((01|03|05|07|08|10|12)-((0[1-9])|([1-2][0-9])|(3[0-1])))|((02|04|06|09|11)', 9099 '-((0[1-9])|([1-2][0-9])|30)))$'].join('')); 9100 var dtexpTime = new RegExp(['^(([0-1][0-9])|(2[0-3]))', 9101 ':((0[0-9])|([1-5][0-9]))', 9102 ':((0[0-9])|([1-5][0-9]))(\.[0-9]{1,6}){0,1}$'].join('')); 9103 9104 // If key-ed in Time or Date values are unsupported by the UI, close it 9105 if ($td.attr('data-type') === 'date' && ! dtexpDate.test($input_field.val())) { 9106 $input_field.datepicker('hide'); 9107 } else if ($td.attr('data-type') === 'time' && ! dtexpTime.test($input_field.val())) { 9108 $input_field.datepicker('hide'); 9109 } else { 9110 $input_field.datepicker('show'); 9111 } 9112} 9113 9114/** 9115 * Unbind all event handlers before tearing down a page 9116 */ 9117AJAX.registerTeardown('functions.js', function(){ 9118 $(document).off('keydown', 'form input, form textarea, form select'); 9119}); 9120 9121AJAX.registerOnload('functions.js', function () { 9122 /** 9123 * Handle 'Ctrl/Alt + Enter' form submits 9124 */ 9125 $('form input, form textarea, form select').on('keydown', function(e){ 9126 if((e.ctrlKey && e.which == 13) || (e.altKey && e.which == 13)) { 9127 $form = $(this).closest('form'); 9128 if (! $form.find('input[type="submit"]') || 9129 ! $form.find('input[type="submit"]').click() 9130 ) { 9131 $form.submit(); 9132 } 9133 } 9134 }); 9135}); 9136 9137/** 9138 * Unbind all event handlers before tearing down a page 9139 */ 9140AJAX.registerTeardown('functions.js', function(){ 9141 $(document).off('change', 'input[type=radio][name="pw_hash"]'); 9142}); 9143 9144AJAX.registerOnload('functions.js', function(){ 9145 /* 9146 * Display warning regarding SSL when sha256_password 9147 * method is selected 9148 * Used in user_password.php (Change Password link on index.php) 9149 */ 9150 $(document).on("change", 'select#select_authentication_plugin_cp', function() { 9151 if (this.value === 'sha256_password') { 9152 $('#ssl_reqd_warning_cp').show(); 9153 } else { 9154 $('#ssl_reqd_warning_cp').hide(); 9155 } 9156 }); 9157}); 9158; 9159 9160/* vim: set expandtab sw=4 ts=4 sts=4: */ 9161/** 9162 * function used in or for navigation panel 9163 * 9164 * @package phpMyAdmin-Navigation 9165 */ 9166 9167/** 9168 * updates the tree state in sessionStorage 9169 * 9170 * @returns void 9171 */ 9172function navTreeStateUpdate() { 9173 // update if session storage is supported 9174 if (isStorageSupported('sessionStorage')) { 9175 var storage = window.sessionStorage; 9176 // try catch necessary here to detect whether 9177 // content to be stored exceeds storage capacity 9178 try { 9179 storage.setItem('navTreePaths', JSON.stringify(traverseNavigationForPaths())); 9180 storage.setItem('server', PMA_commonParams.get('server')); 9181 storage.setItem('token', PMA_commonParams.get('token')); 9182 } catch(error) { 9183 // storage capacity exceeded & old navigation tree 9184 // state is no more valid, so remove it 9185 storage.removeItem('navTreePaths'); 9186 storage.removeItem('server'); 9187 storage.removeItem('token'); 9188 } 9189 } 9190} 9191 9192/** 9193 * Loads child items of a node and executes a given callback 9194 * 9195 * @param isNode 9196 * @param $expandElem expander 9197 * @param callback callback function 9198 * 9199 * @returns void 9200 */ 9201function loadChildNodes(isNode, $expandElem, callback) { 9202 9203 var $destination = null; 9204 var params = null; 9205 9206 if (isNode) { 9207 if (!$expandElem.hasClass('expander')) { 9208 return; 9209 } 9210 $destination = $expandElem.closest('li'); 9211 params = { 9212 aPath: $expandElem.find('span.aPath').text(), 9213 vPath: $expandElem.find('span.vPath').text(), 9214 pos: $expandElem.find('span.pos').text(), 9215 pos2_name: $expandElem.find('span.pos2_name').text(), 9216 pos2_value: $expandElem.find('span.pos2_value').text(), 9217 searchClause: '', 9218 searchClause2: '' 9219 }; 9220 if ($expandElem.closest('ul').hasClass('search_results')) { 9221 params.searchClause = PMA_fastFilter.getSearchClause(); 9222 params.searchClause2 = PMA_fastFilter.getSearchClause2($expandElem); 9223 } 9224 } else { 9225 $destination = $('#pma_navigation_tree_content'); 9226 params = { 9227 aPath: $expandElem.attr('aPath'),
9228 vPath: $expandElem.attr('vPath'), 9229 pos: $expandElem.attr('pos'), 9230 pos2_name: '', 9231 pos2_value: '', 9232 searchClause: '', 9233 searchClause2: '' 9234 }; 9235 } 9236 9237 var url = $('#pma_navigation').find('a.navigation_url').attr('href'); 9238 $.get(url, params, function (data) { 9239 if (typeof data !== 'undefined' && data.success === true) { 9240 $destination.find('div.list_container').remove(); // FIXME: Hack, there shouldn't be a list container there 9241 if (isNode) { 9242 $destination.append(data.message); 9243 $expandElem.addClass('loaded'); 9244 } else { 9245 $destination.html(data.message); 9246 $destination.children() 9247 .first() 9248 .css({ 9249 border: '0px', 9250 margin: '0em', 9251 padding : '0em' 9252 }) 9253 .slideDown('slow'); 9254 } 9255 if (data._errors) { 9256 var $errors = $(data._errors); 9257 if ($errors.children().length > 0) { 9258 $('#pma_errors').replaceWith(data._errors); 9259 } 9260 } 9261 if (callback && typeof callback == 'function') { 9262 callback(data); 9263 } 9264 } else if(data.redirect_flag == "1") { 9265 if (window.location.href.indexOf('?') === -1) { 9266 window.location.href += '?session_expired=1'; 9267 } else { 9268 window.location.href += '&session_expired=1'; 9269 } 9270 window.location.reload(); 9271 } else { 9272 var $throbber = $expandElem.find('img.throbber'); 9273 $throbber.hide(); 9274 var $icon = $expandElem.find('img.ic_b_plus'); 9275 $icon.show(); 9276 PMA_ajaxShowMessage(data.error, false); 9277 } 9278 }); 9279} 9280 9281/** 9282 * Collapses a node in navigation tree. 9283 * 9284 * @param $expandElem expander 9285 * 9286 * @returns void 9287 */ 9288function collapseTreeNode($expandElem) { 9289 var $children = $expandElem.closest('li').children('div.list_container'); 9290 var $icon = $expandElem.find('img'); 9291 if ($expandElem.hasClass('loaded')) { 9292 if ($icon.is('.ic_b_minus')) { 9293 $icon.removeClass('ic_b_minus').addClass('ic_b_plus'); 9294 $children.slideUp('fast'); 9295 } 9296 } 9297 $expandElem.blur(); 9298 $children.promise().done(navTreeStateUpdate); 9299} 9300 9301/** 9302 * Traverse the navigation tree backwards to generate all the actual 9303 * and virtual paths, as well as the positions in the pagination at 9304 * various levels, if necessary. 9305 * 9306 * @return Object 9307 */ 9308function traverseNavigationForPaths() { 9309 var params = { 9310 pos: $('#pma_navigation_tree').find('div.dbselector select').val() 9311 }; 9312 if ($('#navi_db_select').length) { 9313 return params; 9314 } 9315 var count = 0; 9316 $('#pma_navigation_tree').find('a.expander:visible').each(function () { 9317 if ($(this).find('img').is('.ic_b_minus') && 9318 $(this).closest('li').find('div.list_container .ic_b_minus').length === 0 9319 ) { 9320 params['n' + count + '_aPath'] = $(this).find('span.aPath').text(); 9321 params['n' + count + '_vPath'] = $(this).find('span.vPath').text(); 9322 9323 var pos2_name = $(this).find('span.pos2_name').text(); 9324 if (! pos2_name) { 9325 pos2_name = $(this) 9326 .parent() 9327 .parent() 9328 .find('span.pos2_name:last') 9329 .text(); 9330 } 9331 var pos2_value = $(this).find('span.pos2_value').text(); 9332 if (! pos2_value) { 9333 pos2_value = $(this) 9334 .parent() 9335 .parent() 9336 .find('span.pos2_value:last') 9337 .text(); 9338 } 9339 9340 params['n' + count + '_pos2_name'] = pos2_name; 9341 params['n' + count + '_pos2_value'] = pos2_value; 9342 9343 params['n' + count + '_pos3_name'] = $(this).find('span.pos3_name').text(); 9344 params['n' + count + '_pos3_value'] = $(this).find('span.pos3_value').text(); 9345 count++; 9346 } 9347 }); 9348 return params; 9349} 9350 9351/** 9352 * Executed on page load 9353 */ 9354$(function () { 9355 if (! $('#pma_navigation').length) { 9356 // Don't bother running any code if the navigation is not even on the page 9357 return; 9358 } 9359
9360 // Do not let the page reload on submitting the fast filter 9361 $(document).on('submit', '.fast_filter', function (event) { 9362 event.preventDefault(); 9363 }); 9364 9365 // Fire up the resize handlers 9366 new ResizeHandler(); 9367 9368 /** 9369 * opens/closes (hides/shows) tree elements 9370 * loads data via ajax 9371 */ 9372 $(document).on('click', '#pma_navigation_tree a.expander', function (event) { 9373 event.preventDefault(); 9374 event.stopImmediatePropagation(); 9375 var $icon = $(this).find('img'); 9376 if ($icon.is('.ic_b_plus')) { 9377 expandTreeNode($(this)); 9378 } else { 9379 collapseTreeNode($(this)); 9380 } 9381 }); 9382 9383 /** 9384 * Register event handler for click on the reload 9385 * navigation icon at the top of the panel 9386 */ 9387 $(document).on('click', '#pma_navigation_reload', function (event) { 9388 event.preventDefault(); 9389 // reload icon object 9390 var $icon = $(this).find('img'); 9391 // source of the hidden throbber icon 9392 var icon_throbber_src = $('#pma_navigation').find('.throbber').attr('src'); 9393 // source of the reload icon 9394 var icon_reload_src = $icon.attr('src'); 9395 // replace the source of the reload icon with the one for throbber 9396 $icon.attr('src', icon_throbber_src); 9397 PMA_reloadNavigation(); 9398 // after one second, put back the reload icon 9399 setTimeout(function () { 9400 $icon.attr('src', icon_reload_src); 9401 }, 1000); 9402 }); 9403 9404 $(document).on("change", '#navi_db_select', function (event) { 9405 if (! $(this).val()) { 9406 PMA_commonParams.set('db', ''); 9407 PMA_reloadNavigation(); 9408 } 9409 $(this).closest('form').trigger('submit'); 9410 }); 9411 9412 /** 9413 * Register event handler for click on the collapse all 9414 * navigation icon at the top of the navigation tree 9415 */ 9416 $(document).on('click', '#pma_navigation_collapse', function (event) { 9417 event.preventDefault(); 9418 $('#pma_navigation_tree').find('a.expander').each(function() { 9419 var $icon = $(this).find('img'); 9420 if ($icon.is('.ic_b_minus')) { 9421 $(this).click(); 9422 } 9423 }); 9424 }); 9425 9426 /** 9427 * Register event handler to toggle 9428 * the 'link with main panel' icon on mouseenter. 9429 */ 9430 $(document).on('mouseenter', '#pma_navigation_sync', function (event) { 9431 event.preventDefault(); 9432 var synced = $('#pma_navigation_tree').hasClass('synced'); 9433 var $img = $('#pma_navigation_sync').children('img'); 9434 if (synced) { 9435 $img.removeClass('ic_s_link').addClass('ic_s_unlink'); 9436 } else { 9437 $img.removeClass('ic_s_unlink').addClass('ic_s_link'); 9438 } 9439 }); 9440 9441 /** 9442 * Register event handler to toggle 9443 * the 'link with main panel' icon on mouseout. 9444 */ 9445 $(document).on('mouseout', '#pma_navigation_sync', function (event) { 9446 event.preventDefault(); 9447 var synced = $('#pma_navigation_tree').hasClass('synced'); 9448 var $img = $('#pma_navigation_sync').children('img'); 9449 if (synced) { 9450 $img.removeClass('ic_s_unlink').addClass('ic_s_link'); 9451 } else { 9452 $img.removeClass('ic_s_link').addClass('ic_s_unlink'); 9453 } 9454 }); 9455 9456 /** 9457 * Register event handler to toggle 9458 * the linking with main panel behavior 9459 */ 9460 $(document).on('click', '#pma_navigation_sync', function (event) { 9461 event.preventDefault(); 9462 var synced = $('#pma_navigation_tree').hasClass('synced'); 9463 var $img = $('#pma_navigation_sync').children('img'); 9464 if (synced) { 9465 $img 9466 .removeClass('ic_s_unlink') 9467 .addClass('ic_s_link') 9468 .attr('alt', PMA_messages.linkWithMain) 9469 .attr('title', PMA_messages.linkWithMain); 9470 $('#pma_navigation_tree') 9471 .removeClass('synced') 9472 .find('li.selected') 9473 .removeClass('selected'); 9474 } else { 9475 $img 9476 .removeClass('ic_s_link') 9477 .addClass('ic_s_unlink') 9478 .attr('alt', PMA_messages.unlinkWithMain) 9479 .attr('title', PMA_messages.unlinkWithMain); 9480 $('#pma_navigation_tree').addClass('synced'); 9481 PMA_showCurrentNavigation(); 9482 } 9483 }); 9484 9485 /** 9486 * Bind all "fast filter" events 9487 */ 9488 $(document).on('click', '#pma_navigation_tree li.fast_filter span', PMA_fastFilter.events.clear); 9489 $(document).on('focus', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.focus); 9490 $(document).on('blur', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.blur); 9491 $(document).on('keyup', '#pma_navigation_tree li.fast_filter input.searchClause', PMA_fastFilter.events.keyup); 9492 9493 /** 9494 * Ajax handler for pagination 9495 */ 9496 $(document).on('click', '#pma_navigation_tree div.pageselector a.ajax', function (event) { 9497 event.preventDefault(); 9498 PMA_navigationTreePagination($(this)); 9499 }); 9500 9501 /** 9502 * Node highlighting 9503 */ 9504 $(document).on( 9505 'mouseover', 9506 '#pma_navigation_tree.highlight li:not(.fast_filter)', 9507 function () { 9508 if ($('li:visible', this).length === 0) { 9509 $(this).addClass('activePointer'); 9510 } 9511 } 9512 ); 9513 $(document).on( 9514 'mouseout', 9515 '#pma_navigation_tree.highlight li:not(.fast_filter)', 9516 function () { 9517 $(this).removeClass('activePointer'); 9518 } 9519 ); 9520 9521 /** Create a Routine, Trigger or Event */ 9522 $(document).on('click', 'li.new_procedure a.ajax, li.new_function a.ajax', function (event) { 9523 event.preventDefault(); 9524 var dialog = new RTE.object('routine'); 9525 dialog.editorDialog(1, $(this)); 9526 }); 9527 $(document).on('click', 'li.new_trigger a.ajax', function (event) { 9528 event.preventDefault(); 9529 var dialog = new RTE.object('trigger'); 9530 dialog.editorDialog(1, $(this)); 9531 }); 9532 $(document).on('click', 'li.new_event a.ajax', function (event) { 9533 event.preventDefault(); 9534 var dialog = new RTE.object('event'); 9535 dialog.editorDialog(1, $(this)); 9536 }); 9537 9538 /** Edit Routines, Triggers or Events */ 9539 $(document).on('click', 'li.procedure > a.ajax, li.function > a.ajax', function (event) { 9540 event.preventDefault(); 9541 var dialog = new RTE.object('routine'); 9542 dialog.editorDialog(0, $(this)); 9543 }); 9544 $(document).on('click', 'li.trigger > a.ajax', function (event) { 9545 event.preventDefault(); 9546 var dialog = new RTE.object('trigger'); 9547 dialog.editorDialog(0, $(this)); 9548 }); 9549 $(document).on('click', 'li.event > a.ajax', function (event) { 9550 event.preventDefault(); 9551 var dialog = new RTE.object('event'); 9552 dialog.editorDialog(0, $(this)); 9553 }); 9554 9555 /** Execute Routines */ 9556 $(document).on('click', 'li.procedure div a.ajax img,' + 9557 ' li.function div a.ajax img', function (event) { 9558 event.preventDefault(); 9559 var dialog = new RTE.object('routine'); 9560 dialog.executeDialog($(this).parent()); 9561 }); 9562 /** Export Triggers and Events */ 9563 $(document).on('click', 'li.trigger div:eq(1) a.ajax img,' + 9564 ' li.event div:eq(1) a.ajax img', function (event) { 9565 event.preventDefault(); 9566 var dialog = new RTE.object(); 9567 dialog.exportDialog($(this).parent()); 9568 }); 9569 9570 /** New index */ 9571 $(document).on('click', '#pma_navigation_tree li.new_index a.ajax', function (event) { 9572 event.preventDefault(); 9573 var url = $(this).attr('href').substr( 9574 $(this).attr('href').indexOf('?') + 1 9575 ) + '&ajax_request=true'; 9576 var title = PMA_messages.strAddIndex; 9577 indexEditorDialog(url, title); 9578 }); 9579 9580 /** Edit index */ 9581 $(document).on('click', 'li.index a.ajax', function (event) { 9582 event.preventDefault(); 9583 var url = $(this).attr('href').substr( 9584 $(this).attr('href').indexOf('?') + 1 9585 ) + '&ajax_request=true'; 9586 var title = PMA_messages.strEditIndex; 9587 indexEditorDialog(url, title); 9588 }); 9589 9590 /** New view */ 9591 $(document).on('click', 'li.new_view a.ajax', function (event) { 9592 event.preventDefault(); 9593 PMA_createViewDialog($(this)); 9594 }); 9595 9596 /** Hide navigation tree item */ 9597 $(document).on('click', 'a.hideNavItem.ajax', function (event) { 9598 event.preventDefault(); 9599 $.ajax({ 9600 type: 'POST', 9601 url: $(this).attr('href') + '&ajax_request=true', 9602 success: function (data) { 9603 if (typeof data !== 'undefined' && data.success === true) { 9604 PMA_reloadNavigation(); 9605 } else { 9606 PMA_ajaxShowMessage(data.error); 9607 } 9608 } 9609 }); 9610 }); 9611
9612 /** Display a dialog to choose hidden navigation items to show */ 9613 $(document).on('click', 'a.showUnhide.ajax', function (event) { 9614 event.preventDefault(); 9615 var $msg = PMA_ajaxShowMessage(); 9616 $.get($(this).attr('href') + '&ajax_request=1', function (data) { 9617 if (typeof data !== 'undefined' && data.success === true) { 9618 PMA_ajaxRemoveMessage($msg); 9619 var buttonOptions = {}; 9620 buttonOptions[PMA_messages.strClose] = function () { 9621 $(this).dialog("close"); 9622 }; 9623 $('<div/>') 9624 .attr('id', 'unhideNavItemDialog') 9625 .append(data.message) 9626 .dialog({ 9627 width: 400, 9628 minWidth: 200, 9629 modal: true, 9630 buttons: buttonOptions, 9631 title: PMA_messages.strUnhideNavItem, 9632 close: function () { 9633 $(this).remove(); 9634 } 9635 }); 9636 } else { 9637 PMA_ajaxShowMessage(data.error); 9638 } 9639 }); 9640 }); 9641 9642 /** Show a hidden navigation tree item */ 9643 $(document).on('click', 'a.unhideNavItem.ajax', function (event) { 9644 event.preventDefault(); 9645 var $tr = $(this).parents('tr'); 9646 var $msg = PMA_ajaxShowMessage(); 9647 $.ajax({ 9648 type: 'POST', 9649 url: $(this).attr('href') + '&ajax_request=true', 9650 success: function (data) { 9651 PMA_ajaxRemoveMessage($msg); 9652 if (typeof data !== 'undefined' && data.success === true) { 9653 $tr.remove(); 9654 PMA_reloadNavigation(); 9655 } else { 9656 PMA_ajaxShowMessage(data.error); 9657 } 9658 } 9659 }); 9660 }); 9661 9662 // Add/Remove favorite table using Ajax. 9663 $(document).on("click", ".favorite_table_anchor", function (event) { 9664 event.preventDefault(); 9665 $self = $(this); 9666 var anchor_id = $self.attr("id"); 9667 if($self.data("favtargetn") !== null) { 9668 if($('a[data-favtargets="' + $self.data("favtargetn") + '"]').length > 0) 9669 { 9670 $('a[data-favtargets="' + $self.data("favtargetn") + '"]').trigger('click'); 9671 return; 9672 } 9673 } 9674 9675 $.ajax({ 9676 url: $self.attr('href'), 9677 cache: false, 9678 type: 'POST', 9679 data: { 9680 favorite_tables: (isStorageSupported('localStorage') && typeof window.localStorage.favorite_tables !== 'undefined') 9681 ? window.localStorage.favorite_tables 9682 : '' 9683 }, 9684 success: function (data) { 9685 if (data.changes) { 9686 $('#pma_favorite_list').html(data.list); 9687 $('#' + anchor_id).parent().html(data.anchor); 9688 PMA_tooltip( 9689 $('#' + anchor_id), 9690 'a', 9691 $('#' + anchor_id).attr("title") 9692 ); 9693 // Update localStorage. 9694 if (isStorageSupported('localStorage')) { 9695 window.localStorage.favorite_tables = data.favorite_tables; 9696 } 9697 } else { 9698 PMA_ajaxShowMessage(data.message); 9699 } 9700 } 9701 }); 9702 }); 9703 // Check if session storage is supported 9704 if (isStorageSupported('sessionStorage')) { 9705 var storage = window.sessionStorage; 9706 // remove tree from storage if Navi_panel config form is submitted 9707 $(document).on('submit', 'form.config-form', function(event) { 9708 storage.removeItem('navTreePaths'); 9709 }); 9710 // Initialize if no previous state is defined 9711 if ($('#pma_navigation_tree_content').length && 9712 typeof storage.navTreePaths === 'undefined' 9713 ) { 9714 PMA_reloadNavigation(); 9715 } else if (PMA_commonParams.get('server') === storage.server && 9716 PMA_commonParams.get('token') === storage.token 9717 ) { 9718 // Reload the tree to the state before page refresh 9719 PMA_reloadNavigation(null, JSON.parse(storage.navTreePaths)); 9720 } else { 9721 // If the user is different 9722 navTreeStateUpdate(); 9723 } 9724 } 9725}); 9726 9727/**
9728 * Expands a node in navigation tree. 9729 * 9730 * @param $expandElem expander 9731 * @param callback callback function 9732 * 9733 * @returns void 9734 */ 9735function expandTreeNode($expandElem, callback) { 9736 var $children = $expandElem.closest('li').children('div.list_container'); 9737 var $icon = $expandElem.find('img'); 9738 if ($expandElem.hasClass('loaded')) { 9739 if ($icon.is('.ic_b_plus')) { 9740 $icon.removeClass('ic_b_plus').addClass('ic_b_minus'); 9741 $children.slideDown('fast'); 9742 } 9743 if (callback && typeof callback == 'function') { 9744 callback.call(); 9745 } 9746 $children.promise().done(navTreeStateUpdate); 9747 } else { 9748 var $throbber = $('#pma_navigation').find('.throbber') 9749 .first() 9750 .clone() 9751 .css({visibility: 'visible', display: 'block'}) 9752 .click(false); 9753 $icon.hide(); 9754 $throbber.insertBefore($icon); 9755 9756 loadChildNodes(true, $expandElem, function (data) { 9757 if (typeof data !== 'undefined' && data.success === true) { 9758 var $destination = $expandElem.closest('li'); 9759 $icon.removeClass('ic_b_plus').addClass('ic_b_minus'); 9760 $children = $destination.children('div.list_container'); 9761 $children.slideDown('fast'); 9762 if ($destination.find('ul > li').length == 1) { 9763 $destination.find('ul > li') 9764 .find('a.expander.container') 9765 .click(); 9766 } 9767 if (callback && typeof callback == 'function') { 9768 callback.call(); 9769 } 9770 PMA_showFullName($destination); 9771 } else { 9772 PMA_ajaxShowMessage(data.error, false); 9773 } 9774 $icon.show(); 9775 $throbber.remove(); 9776 $children.promise().done(navTreeStateUpdate); 9777 }); 9778 } 9779 $expandElem.blur(); 9780} 9781 9782/** 9783 * Auto-scrolls the newly chosen database 9784 * 9785 * @param object $element The element to set to view 9786 * @param boolean $forceToTop Whether to force scroll to top 9787 * 9788 */ 9789function scrollToView($element, $forceToTop) { 9790 var $container = $('#pma_navigation_tree_content'); 9791 var elemTop = $element.offset().top - $container.offset().top; 9792 var textHeight = 20; 9793 var scrollPadding = 20; // extra padding from top of bottom when scrolling to view 9794 if (elemTop < 0 || $forceToTop) { 9795 $container.stop().animate({ 9796 scrollTop: elemTop + $container.scrollTop() - scrollPadding 9797 }); 9798 } else if (elemTop + textHeight > $container.height()) { 9799 $container.stop().animate({ 9800 scrollTop: elemTop + textHeight - $container.height() + $container.scrollTop() + scrollPadding 9801 }); 9802 } 9803} 9804 9805/** 9806 * Expand the navigation and highlight the current database or table/view 9807 * 9808 * @returns void 9809 */ 9810function PMA_showCurrentNavigation() { 9811 var db = PMA_commonParams.get('db'); 9812 var table = PMA_commonParams.get('table'); 9813 $('#pma_navigation_tree') 9814 .find('li.selected') 9815 .removeClass('selected'); 9816 if (db) { 9817 var $dbItem = findLoadedItem( 9818 $('#pma_navigation_tree').find('> div'), db, 'database', !table 9819 ); 9820 if ($('#navi_db_select').length && 9821 $('option:selected', $('#navi_db_select')).length 9822 ) { 9823 if (! PMA_selectCurrentDb()) { 9824 return; 9825 } 9826 // If loaded database in navigation is not same as current one 9827 if ($('#pma_navigation_tree_content').find('span.loaded_db:first').text() 9828 !== $('#navi_db_select').val() 9829 ) { 9830 loadChildNodes(false, $('option:selected', $('#navi_db_select')), function (data) { 9831 handleTableOrDb(table, $('#pma_navigation_tree_content')); 9832 var $children = $('#pma_navigation_tree_content').children('div.list_container'); 9833 $children.promise().done(navTreeStateUpdate); 9834 }); 9835 } else { 9836 handleTableOrDb(table, $('#pma_navigation_tree_content')); 9837 } 9838 } else if ($dbItem) { 9839 var $expander = $dbItem.children('div:first').children('a.expander'); 9840 // if not loaded or loaded but collapsed 9841 if (! $expander.hasClass('loaded') || 9842 $expander.find('img').is('.ic_b_plus') 9843 ) { 9844 expandTreeNode($expander, function () { 9845 handleTableOrDb(table, $dbItem); 9846 }); 9847 } else { 9848 handleTableOrDb(table, $dbItem); 9849 } 9850 } 9851 } else if ($('#navi_db_select').length && $('#navi_db_select').val()) { 9852 $('#navi_db_select').val('').hide().trigger('change'); 9853 } 9854 PMA_showFullName($('#pma_navigation_tree')); 9855 9856 function handleTableOrDb(table, $dbItem) { 9857 if (table) { 9858 loadAndHighlightTableOrView($dbItem, table); 9859 } else { 9860 var $container = $dbItem.children('div.list_container'); 9861 var $tableContainer = $container.children('ul').children('li.tableContainer'); 9862 if ($tableContainer.length > 0) { 9863 var $expander = $tableContainer.children('div:first').children('a.expander'); 9864 $tableContainer.addClass('selected'); 9865 expandTreeNode($expander, function () { 9866 scrollToView($dbItem, true); 9867 }); 9868 } else { 9869 scrollToView($dbItem, true); 9870 } 9871 } 9872 } 9873 9874 function findLoadedItem($container, name, clazz, doSelect) { 9875 var ret = false;
9876 $container.children('ul').children('li').each(function () { 9877 var $li = $(this); 9878 // this is a navigation group, recurse 9879 if ($li.is('.navGroup')) { 9880 var $container = $li.children('div.list_container'); 9881 var $childRet = findLoadedItem( 9882 $container, name, clazz, doSelect 9883 ); 9884 if ($childRet) { 9885 ret = $childRet; 9886 return false; 9887 } 9888 } else { // this is a real navigation item 9889 // name and class matches 9890 if (((clazz && $li.is('.' + clazz)) || ! clazz) && 9891 $li.children('a').text() == name) { 9892 if (doSelect) { 9893 $li.addClass('selected'); 9894 } 9895 // taverse up and expand and parent navigation groups 9896 $li.parents('.navGroup').each(function () { 9897 var $cont = $(this).children('div.list_container'); 9898 if (! $cont.is(':visible')) { 9899 $(this) 9900 .children('div:first') 9901 .children('a.expander') 9902 .click(); 9903 } 9904 }); 9905 ret = $li; 9906 return false; 9907 } 9908 } 9909 }); 9910 return ret; 9911 } 9912 9913 function loadAndHighlightTableOrView($dbItem, itemName) { 9914 var $container = $dbItem.children('div.list_container'); 9915 var $expander; 9916 var $whichItem = isItemInContainer($container, itemName, 'li.table, li.view'); 9917 //If item already there in some container 9918 if ($whichItem) { 9919 //get the relevant container while may also be a subcontainer 9920 var $relatedContainer = $whichItem.closest('li.subContainer').length 9921 ? $whichItem.closest('li.subContainer') 9922 : $dbItem; 9923 $whichItem = findLoadedItem( 9924 $relatedContainer.children('div.list_container'), 9925 itemName, null, true 9926 ); 9927 //Show directly 9928 showTableOrView($whichItem, $relatedContainer.children('div:first').children('a.expander')); 9929 //else if item not there, try loading once 9930 } else { 9931 var $sub_containers = $dbItem.find('.subContainer'); 9932 //If there are subContainers i.e. tableContainer or viewContainer 9933 if($sub_containers.length > 0) { 9934 var $containers = []; 9935 $sub_containers.each(function (index) { 9936 $containers[index] = $(this); 9937 $expander = $containers[index] 9938 .children('div:first') 9939 .children('a.expander'); 9940 if (! $expander.hasClass('loaded')) { 9941 loadAndShowTableOrView($expander, $containers[index], itemName); 9942 } 9943 }); 9944 // else if no subContainers 9945 } else { 9946 $expander = $dbItem 9947 .children('div:first') 9948 .children('a.expander'); 9949 if (! $expander.hasClass('loaded')) { 9950 loadAndShowTableOrView($expander, $dbItem, itemName); 9951 } 9952 } 9953 } 9954 } 9955 9956 function loadAndShowTableOrView($expander, $relatedContainer, itemName) { 9957 loadChildNodes(true, $expander, function (data) { 9958 var $whichItem = findLoadedItem( 9959 $relatedContainer.children('div.list_container'), 9960 itemName, null, true 9961 ); 9962 if ($whichItem) { 9963 showTableOrView($whichItem, $expander); 9964 } 9965 }); 9966 } 9967 9968 function showTableOrView($whichItem, $expander) { 9969 expandTreeNode($expander, function (data) { 9970 if ($whichItem) { 9971 scrollToView($whichItem, false); 9972 } 9973 }); 9974 } 9975 9976 function isItemInContainer($container, name, clazz) 9977 { 9978 var $whichItem = null; 9979 $items = $container.find(clazz); 9980 var found = false;
9981 $items.each(function () { 9982 if ($(this).children('a').text() == name) { 9983 $whichItem = $(this); 9984 return false; 9985 } 9986 }); 9987 return $whichItem; 9988 } 9989} 9990 9991/** 9992 * Disable navigation panel settings 9993 * 9994 * @return void 9995 */ 9996function PMA_disableNaviSettings() { 9997 $('#pma_navigation_settings_icon').addClass('hide'); 9998 $('#pma_navigation_settings').remove(); 9999} 10000 10001/** 10002 * Ensure that navigation panel settings is properly setup. 10003 * If not, set it up 10004 * 10005 * @return void 10006 */ 10007function PMA_ensureNaviSettings(selflink) { 10008 $('#pma_navigation_settings_icon').removeClass('hide'); 10009 10010 if (!$('#pma_navigation_settings').length) { 10011 var params = { 10012 getNaviSettings: true 10013 }; 10014 var url = $('#pma_navigation').find('a.navigation_url').attr('href'); 10015 $.post(url, params, function (data) { 10016 if (typeof data !== 'undefined' && data.success) { 10017 $('#pma_navi_settings_container').html(data.message); 10018 setupRestoreField(); 10019 setupValidation(); 10020 setupConfigTabs(); 10021 $('#pma_navigation_settings').find('form').attr('action', selflink); 10022 } else { 10023 PMA_ajaxShowMessage(data.error); 10024 } 10025 }); 10026 } else { 10027 $('#pma_navigation_settings').find('form').attr('action', selflink); 10028 } 10029} 10030 10031/** 10032 * Reloads the whole navigation tree while preserving its state 10033 * 10034 * @param function the callback function 10035 * @param Object stored navigation paths 10036 * 10037 * @return void 10038 */ 10039function PMA_reloadNavigation(callback, paths) { 10040 var params = { 10041 reload: true, 10042 no_debug: true 10043 }; 10044 paths = paths || traverseNavigationForPaths(); 10045 $.extend(params, paths); 10046 if ($('#navi_db_select').length) { 10047 params.db = PMA_commonParams.get('db'); 10048 requestNaviReload(params); 10049 return; 10050 } 10051 requestNaviReload(params); 10052 10053 function requestNaviReload(params) { 10054 var url = $('#pma_navigation').find('a.navigation_url').attr('href'); 10055 $.post(url, params, function (data) { 10056 if (typeof data !== 'undefined' && data.success) { 10057 $('#pma_navigation_tree').html(data.message).children('div').show(); 10058 if ($('#pma_navigation_tree').hasClass('synced')) { 10059 PMA_selectCurrentDb(); 10060 PMA_showCurrentNavigation(); 10061 } 10062 // Fire the callback, if any 10063 if (typeof callback === 'function') { 10064 callback.call(); 10065 } 10066 navTreeStateUpdate(); 10067 } else { 10068 PMA_ajaxShowMessage(data.error); 10069 } 10070 }); 10071 } 10072} 10073 10074function PMA_selectCurrentDb() { 10075 var $naviDbSelect = $('#navi_db_select'); 10076 10077 if (!$naviDbSelect.length) { 10078 return false; 10079 } 10080 10081 if (PMA_commonParams.get('db')) { // db selected 10082 $naviDbSelect.show(); 10083 } 10084 10085 $naviDbSelect.val(PMA_commonParams.get('db')); 10086 return $naviDbSelect.val() === PMA_commonParams.get('db'); 10087 10088} 10089 10090/** 10091 * Handles any requests to change the page in a branch of a tree 10092 * 10093 * This can be called from link click or select change event handlers 10094 * 10095 * @param object $this A jQuery object that points to the element that 10096 * initiated the action of changing the page 10097 * 10098 * @return void 10099 */ 10100function PMA_navigationTreePagination($this) { 10101 var $msgbox = PMA_ajaxShowMessage(); 10102 var isDbSelector = $this.closest('div.pageselector').is('.dbselector'); 10103 var url, params; 10104 if ($this[0].tagName == 'A') { 10105 url = $this.attr('href'); 10106 params = 'ajax_request=true'; 10107 } else { // tagName == 'SELECT' 10108 url = 'navigation.php'; 10109 params = $this.closest("form").serialize() + '&ajax_request=true'; 10110 } 10111 var searchClause = PMA_fastFilter.getSearchClause(); 10112 if (searchClause) { 10113 params += '&searchClause=' + encodeURIComponent(searchClause); 10114 } 10115 if (isDbSelector) { 10116 params += '&full=true'; 10117 } else { 10118 var searchClause2 = PMA_fastFilter.getSearchClause2($this); 10119 if (searchClause2) { 10120 params += '&searchClause2=' + encodeURIComponent(searchClause2); 10121 } 10122 } 10123 $.post(url, params, function (data) { 10124 if (typeof data !== 'undefined' && data.success) { 10125 PMA_ajaxRemoveMessage($msgbox); 10126 if (isDbSelector) { 10127 var val = PMA_fastFilter.getSearchClause(); 10128 $('#pma_navigation_tree') 10129 .html(data.message) 10130 .children('div') 10131 .show(); 10132 if (val) { 10133 $('#pma_navigation_tree') 10134 .find('li.fast_filter input.searchClause') 10135 .val(val); 10136 } 10137 } else { 10138 var $parent = $this.closest('div.list_container').parent(); 10139 var val = PMA_fastFilter.getSearchClause2($this); 10140 $this.closest('div.list_container').html( 10141 $(data.message).children().show() 10142 ); 10143 if (val) { 10144 $parent.find('li.fast_filter input.searchClause').val(val); 10145 } 10146 $parent.find('span.pos2_value:first').text( 10147 $parent.find('span.pos2_value:last').text() 10148 ); 10149 $parent.find('span.pos3_value:first').text( 10150 $parent.find('span.pos3_value:last').text() 10151 ); 10152 } 10153 } else { 10154 PMA_ajaxShowMessage(data.error); 10155 PMA_handleRedirectAndReload(data); 10156 } 10157 navTreeStateUpdate(); 10158 }); 10159} 10160 10161/** 10162 * @var ResizeHandler Custom object that manages the resizing of the navigation 10163 * 10164 * XXX: Must only be ever instanciated once 10165 * XXX: Inside event handlers the 'this' object is accessed as 'event.data.resize_handler' 10166 */ 10167var ResizeHandler = function () { 10168 /** 10169 * @var int panel_width Used by the collapser to know where to go 10170 * back to when uncollapsing the panel 10171 */ 10172 this.panel_width = 0; 10173 /** 10174 * @var string left Used to provide support for RTL languages 10175 */ 10176 this.left = $('html').attr('dir') == 'ltr' ? 'left' : 'right'; 10177 /** 10178 * Adjusts the width of the navigation panel to the specified value 10179 * 10180 * @param int pos Navigation width in pixels 10181 * 10182 * @return void 10183 */ 10184 this.setWidth = function (pos) { 10185 var $resizer = $('#pma_navigation_resizer'); 10186 var resizer_width = $resizer.width(); 10187 var $collapser = $('#pma_navigation_collapser'); 10188 $('#pma_navigation').width(pos); 10189 $('body').css('margin-' + this.left, pos + 'px'); 10190 $("#floating_menubar, #pma_console") 10191 .css('margin-' + this.left, (pos + resizer_width) + 'px'); 10192 $resizer.css(this.left, pos + 'px'); 10193 if (pos === 0) { 10194 $collapser 10195 .css(this.left, pos + resizer_width) 10196 .html(this.getSymbol(pos)) 10197 .prop('title', PMA_messages.strShowPanel); 10198 } else { 10199 $collapser 10200 .css(this.left, pos) 10201 .html(this.getSymbol(pos)) 10202 .prop('title', PMA_messages.strHidePanel); 10203 } 10204 setTimeout(function () { 10205 $(window).trigger('resize'); 10206 }, 4); 10207 }; 10208 /** 10209 * Returns the horizontal position of the mouse, 10210 * relative to the outer side of the navigation panel 10211 * 10212 * @param int pos Navigation width in pixels 10213 * 10214 * @return void 10215 */ 10216 this.getPos = function (event) { 10217 var pos = event.pageX; 10218 var windowWidth = $(window).width(); 10219 var windowScroll = $(window).scrollLeft(); 10220 pos = pos - windowScroll; 10221 if (this.left != 'left') { 10222 pos = windowWidth - event.pageX; 10223 } 10224 if (pos < 0) { 10225 pos = 0; 10226 } else if (pos + 100 >= windowWidth) { 10227 pos = windowWidth - 100; 10228 } else { 10229 this.panel_width = 0; 10230 } 10231 return pos; 10232 }; 10233 /** 10234 * Returns the HTML code for the arrow symbol used in the collapser 10235 * 10236 * @param int width The width of the panel 10237 * 10238 * @return string 10239 */
10240 this.getSymbol = function (width) { 10241 if (this.left == 'left') { 10242 if (width === 0) { 10243 return '→'; 10244 } else { 10245 return '←'; 10246 } 10247 } else { 10248 if (width === 0) { 10249 return '←'; 10250 } else { 10251 return '→'; 10252 } 10253 } 10254 }; 10255 /** 10256 * Event handler for initiating a resize of the panel 10257 * 10258 * @param object e Event data (contains a reference to resizeHandler) 10259 * 10260 * @return void 10261 */ 10262 this.mousedown = function (event) { 10263 event.preventDefault(); 10264 $(document) 10265 .bind('mousemove', {'resize_handler': event.data.resize_handler}, 10266 $.throttle(event.data.resize_handler.mousemove, 4)) 10267 .bind('mouseup', {'resize_handler': event.data.resize_handler}, 10268 event.data.resize_handler.mouseup); 10269 $('body').css('cursor', 'col-resize'); 10270 }; 10271 /** 10272 * Event handler for terminating a resize of the panel 10273 * 10274 * @param object e Event data (contains a reference to resizeHandler) 10275 * 10276 * @return void 10277 */ 10278 this.mouseup = function (event) { 10279 $('body').css('cursor', ''); 10280 $.cookie('pma_navi_width', event.data.resize_handler.getPos(event)); 10281 $('#topmenu').menuResizer('resize'); 10282 $(document) 10283 .unbind('mousemove') 10284 .unbind('mouseup'); 10285 }; 10286 /** 10287 * Event handler for updating the panel during a resize operation 10288 * 10289 * @param object e Event data (contains a reference to resizeHandler) 10290 * 10291 * @return void 10292 */ 10293 this.mousemove = function (event) { 10294 event.preventDefault(); 10295 var pos = event.data.resize_handler.getPos(event); 10296 event.data.resize_handler.setWidth(pos); 10297 if ($('.sticky_columns').length !== 0) { 10298 handleAllStickyColumns(); 10299 } 10300 }; 10301 /** 10302 * Event handler for collapsing the panel 10303 * 10304 * @param object e Event data (contains a reference to resizeHandler) 10305 * 10306 * @return void 10307 */ 10308 this.collapse = function (event) { 10309 event.preventDefault(); 10310 var panel_width = event.data.resize_handler.panel_width; 10311 var width = $('#pma_navigation').width(); 10312 if (width === 0 && panel_width === 0) { 10313 panel_width = 240; 10314 } 10315 event.data.resize_handler.setWidth(panel_width); 10316 event.data.resize_handler.panel_width = width; 10317 }; 10318 /** 10319 * Event handler for resizing the navigation tree height on window resize 10320 * 10321 * @return void 10322 */ 10323 this.treeResize = function (event) { 10324 var $nav = $("#pma_navigation"), 10325 $nav_tree = $("#pma_navigation_tree"), 10326 $nav_header = $("#pma_navigation_header"), 10327 $nav_tree_content = $("#pma_navigation_tree_content"); 10328 $nav_tree.height($nav.height() - $nav_header.height()); 10329 if ($nav_tree_content.length > 0) { 10330 $nav_tree_content.height($nav_tree.height() - $nav_tree_content.position().top); 10331 } else { 10332 //TODO: in fast filter search response there is no #pma_navigation_tree_content, needs to be added in php 10333 $nav_tree.css({ 10334 'overflow-y': 'auto' 10335 }); 10336 } 10337 // Set content bottom space beacuse of console 10338 $('body').css('margin-bottom', $('#pma_console').height() + 'px'); 10339 }; 10340 /* Initialisation section begins here */ 10341 if ($.cookie('pma_navi_width')) { 10342 // If we have a cookie, set the width of the panel to its value 10343 var pos = Math.abs(parseInt($.cookie('pma_navi_width'), 10) || 0); 10344 this.setWidth(pos); 10345 $('#topmenu').menuResizer('resize'); 10346 } 10347 // Register the events for the resizer and the collapser 10348 $(document).on('mousedown', '#pma_navigation_resizer', {'resize_handler': this}, this.mousedown); 10349 $(document).on('click', '#pma_navigation_collapser', {'resize_handler': this}, this.collapse); 10350 10351 // Add the correct arrow symbol to the collapser 10352 $('#pma_navigation_collapser').html(this.getSymbol($('#pma_navigation').width())); 10353 // Fix navigation tree height 10354 $(window).on('resize', this.treeResize); 10355 // need to call this now and then, browser might decide 10356 // to show/hide horizontal scrollbars depending on page content width 10357 setInterval(this.treeResize, 2000); 10358 this.treeResize(); 10359}; // End of ResizeHandler 10360 10361/** 10362 * @var object PMA_fastFilter Handles the functionality that allows filtering 10363 * of the items in a branch of the navigation tree 10364 */ 10365var PMA_fastFilter = { 10366 /** 10367 * Construct for the asynchronous fast filter functionality 10368 * 10369 * @param object $this A jQuery object pointing to the list container 10370 * which is the nearest parent of the fast filter 10371 * @param string searchClause The query string for the filter 10372 * 10373 * @return new PMA_fastFilter.filter object 10374 */ 10375 filter: function ($this, searchClause) { 10376 /** 10377 * @var object $this A jQuery object pointing to the list container 10378 * which is the nearest parent of the fast filter 10379 */
10380 this.$this = $this; 10381 /** 10382 * @var bool searchClause The query string for the filter 10383 */ 10384 this.searchClause = searchClause; 10385 /** 10386 * @var object $clone A clone of the original contents 10387 * of the navigation branch before 10388 * the fast filter was applied 10389 */ 10390 this.$clone = $this.clone(); 10391 /** 10392 * @var object xhr A reference to the ajax request that is currently running 10393 */ 10394 this.xhr = null; 10395 /** 10396 * @var int timeout Used to delay the request for asynchronous search 10397 */ 10398 this.timeout = null; 10399 10400 var $filterInput = $this.find('li.fast_filter input.searchClause'); 10401 if ($filterInput.length !== 0 && 10402 $filterInput.val() !== '' && 10403 $filterInput.val() != $filterInput[0].defaultValue 10404 ) { 10405 this.request(); 10406 } 10407 }, 10408 /** 10409 * Gets the query string from the database fast filter form 10410 * 10411 * @return string 10412 */ 10413 getSearchClause: function () { 10414 var retval = ''; 10415 var $input = $('#pma_navigation_tree') 10416 .find('li.fast_filter.db_fast_filter input.searchClause'); 10417 if ($input.length && $input.val() != $input[0].defaultValue) { 10418 retval = $input.val(); 10419 } 10420 return retval; 10421 }, 10422 /** 10423 * Gets the query string from a second level item's fast filter form 10424 * The retrieval is done by trasversing the navigation tree backwards 10425 * 10426 * @return string 10427 */ 10428 getSearchClause2: function ($this) { 10429 var $filterContainer = $this.closest('div.list_container'); 10430 var $filterInput = $([]); 10431 if ($filterContainer 10432 .find('li.fast_filter:not(.db_fast_filter) input.searchClause') 10433 .length !== 0) { 10434 $filterInput = $filterContainer 10435 .find('li.fast_filter:not(.db_fast_filter) input.searchClause'); 10436 } 10437 var searchClause2 = ''; 10438 if ($filterInput.length !== 0 && 10439 $filterInput.first().val() != $filterInput[0].defaultValue 10440 ) { 10441 searchClause2 = $filterInput.val(); 10442 } 10443 return searchClause2; 10444 }, 10445 /** 10446 * @var hash events A list of functions that are bound to DOM events 10447 * at the top of this file 10448 */ 10449 events: { 10450 focus: function (event) { 10451 var $obj = $(this).closest('div.list_container'); 10452 if (! $obj.data('fastFilter')) { 10453 $obj.data( 10454 'fastFilter', 10455 new PMA_fastFilter.filter($obj, $(this).val()) 10456 ); 10457 } 10458 if ($(this).val() == this.defaultValue) { 10459 $(this).val(''); 10460 } else { 10461 $(this).select(); 10462 } 10463 }, 10464 blur: function (event) { 10465 if ($(this).val() === '') { 10466 $(this).val(this.defaultValue); 10467 } 10468 var $obj = $(this).closest('div.list_container'); 10469 if ($(this).val() == this.defaultValue && $obj.data('fastFilter')) { 10470 $obj.data('fastFilter').restore(); 10471 } 10472 }, 10473 keyup: function (event) { 10474 var $obj = $(this).closest('div.list_container'); 10475 var str = ''; 10476 if ($(this).val() != this.defaultValue && $(this).val() !== '') { 10477 $obj.find('div.pageselector').hide(); 10478 str = $(this).val(); 10479 } 10480 10481 /** 10482 * FIXME at the server level a value match is done while on 10483 * the client side it is a regex match. These two should be aligned 10484 */ 10485 10486 // regex used for filtering. 10487 var regex; 10488 try { 10489 regex = new RegExp(str, 'i'); 10490 } catch (err) { 10491 return; 10492 } 10493 10494 // this is the div that houses the items to be filtered by this filter. 10495 var outerContainer; 10496 if ($(this).closest('li.fast_filter').is('.db_fast_filter')) { 10497 outerContainer = $('#pma_navigation_tree_content'); 10498 } else { 10499 outerContainer = $obj; 10500 } 10501 10502 // filters items that are directly under the div as well as grouped in 10503 // groups. Does not filter child items (i.e. a database search does 10504 // not filter tables) 10505 var item_filter = function($curr) { 10506 $curr.children('ul').children('li.navGroup').each(function() { 10507 $(this).children('div.list_container').each(function() { 10508 item_filter($(this)); // recursive 10509 }); 10510 }); 10511 $curr.children('ul').children('li').children('a').not('.container').each(function() { 10512 if (regex.test($(this).text())) { 10513 $(this).parent().show().removeClass('hidden'); 10514 } else { 10515 $(this).parent().hide().addClass('hidden'); 10516 } 10517 }); 10518 }; 10519 item_filter(outerContainer); 10520 10521 // hides containers that does not have any visible children 10522 var container_filter = function ($curr) { 10523 $curr.children('ul').children('li.navGroup').each(function() { 10524 var $group = $(this); 10525 $group.children('div.list_container').each(function() { 10526 container_filter($(this)); // recursive 10527 }); 10528 $group.show().removeClass('hidden'); 10529 if ($group.children('div.list_container').children('ul') 10530 .children('li').not('.hidden').length === 0) { 10531 $group.hide().addClass('hidden'); 10532 } 10533 }); 10534 }; 10535 container_filter(outerContainer); 10536 10537 if ($(this).val() != this.defaultValue && $(this).val() !== '') { 10538 if (! $obj.data('fastFilter')) { 10539 $obj.data( 10540 'fastFilter', 10541 new PMA_fastFilter.filter($obj, $(this).val()) 10542 ); 10543 } else { 10544 if (event.keyCode == 13) { 10545 $obj.data('fastFilter').update($(this).val()); 10546 } 10547 } 10548 } else if ($obj.data('fastFilter')) { 10549 $obj.data('fastFilter').restore(true); 10550 } 10551 }, 10552 clear: function (event) { 10553 event.stopPropagation(); 10554 // Clear the input and apply the fast filter with empty input 10555 var filter = $(this).closest('div.list_container').data('fastFilter'); 10556 if (filter) {
10557 filter.restore(); 10558 } 10559 var value = $(this).prev()[0].defaultValue; 10560 $(this).prev().val(value).trigger('keyup'); 10561 } 10562 } 10563}; 10564/** 10565 * Handles a change in the search clause 10566 * 10567 * @param string searchClause The query string for the filter 10568 * 10569 * @return void 10570 */ 10571PMA_fastFilter.filter.prototype.update = function (searchClause) { 10572 if (this.searchClause != searchClause) { 10573 this.searchClause = searchClause; 10574 this.request(); 10575 } 10576}; 10577/** 10578 * After a delay of 250mS, initiates a request to retrieve search results 10579 * Multiple calls to this function will always abort the previous request 10580 * 10581 * @return void 10582 */ 10583PMA_fastFilter.filter.prototype.request = function () { 10584 var self = this; 10585 if (self.$this.find('li.fast_filter').find('img.throbber').length === 0) { 10586 self.$this.find('li.fast_filter').append( 10587 $('<div class="throbber"></div>').append( 10588 $('#pma_navigation_content') 10589 .find('img.throbber') 10590 .clone() 10591 .css({visibility: 'visible', display: 'block'}) 10592 ) 10593 ); 10594 } 10595 if (self.xhr) { 10596 self.xhr.abort(); 10597 } 10598 var url = $('#pma_navigation').find('a.navigation_url').attr('href'); 10599 var params = self.$this.find('> ul > li > form.fast_filter').first().serialize(); 10600 if (self.$this.find('> ul > li > form.fast_filter:first input[name=searchClause]').length === 0) { 10601 var $input = $('#pma_navigation_tree').find('li.fast_filter.db_fast_filter input.searchClause'); 10602 if ($input.length && $input.val() != $input[0].defaultValue) { 10603 params += '&searchClause=' + encodeURIComponent($input.val()); 10604 } 10605 } 10606 self.xhr = $.ajax({ 10607 url: url, 10608 type: 'post', 10609 dataType: 'json', 10610 data: params, 10611 complete: function (jqXHR, status) { 10612 if (status != 'abort') { 10613 var data = $.parseJSON(jqXHR.responseText); 10614 self.$this.find('li.fast_filter').find('div.throbber').remove(); 10615 if (data && data.results) { 10616 self.swap.apply(self, [data.message]); 10617 } 10618 } 10619 } 10620 }); 10621}; 10622/** 10623 * Replaces the contents of the navigation branch with the search results 10624 * 10625 * @param string list The search results 10626 * 10627 * @return void 10628 */ 10629PMA_fastFilter.filter.prototype.swap = function (list) { 10630 this.$this 10631 .html($(list).html()) 10632 .children() 10633 .show() 10634 .end() 10635 .find('li.fast_filter input.searchClause') 10636 .val(this.searchClause); 10637 this.$this.data('fastFilter', this); 10638}; 10639/** 10640 * Restores the navigation to the original state after the fast filter is cleared 10641 * 10642 * @param bool focus Whether to also focus the input box of the fast filter 10643 * 10644 * @return void 10645 */ 10646PMA_fastFilter.filter.prototype.restore = function (focus) { 10647 if(this.$this.children('ul').first().hasClass('search_results')) { 10648 this.$this.html(this.$clone.html()).children().show(); 10649 this.$this.data('fastFilter', this); 10650 if (focus) { 10651 this.$this.find('li.fast_filter input.searchClause').focus(); 10652 } 10653 } 10654 this.searchClause = ''; 10655 this.$this.find('div.pageselector').show(); 10656 this.$this.find('div.throbber').remove(); 10657}; 10658 10659/** 10660 * Show full name when cursor hover and name not shown completely 10661 * 10662 * @param object $containerELem Container element 10663 * 10664 * @return void 10665 */ 10666function PMA_showFullName($containerELem) { 10667 10668 $containerELem.find('.hover_show_full').mouseenter(function() { 10669 /** mouseenter */ 10670 var $this = $(this); 10671 var thisOffset = $this.offset(); 10672 if($this.text() === '') { 10673 return; 10674 } 10675 var $parent = $this.parent(); 10676 if( ($parent.offset().left + $parent.outerWidth()) 10677 < (thisOffset.left + $this.outerWidth())) 10678 { 10679 var $fullNameLayer = $('#full_name_layer'); 10680 if($fullNameLayer.length === 0) 10681 { 10682 $('body').append('<div id="full_name_layer" class="hide"></div>'); 10683 $('#full_name_layer').mouseleave(function() { 10684 /** mouseleave */ 10685 $(this).addClass('hide') 10686 .removeClass('hovering'); 10687 }).mouseenter(function() { 10688 /** mouseenter */ 10689 $(this).addClass('hovering'); 10690 }); 10691 $fullNameLayer = $('#full_name_layer'); 10692 } 10693 $fullNameLayer.removeClass('hide'); 10694 $fullNameLayer.css({left: thisOffset.left, top: thisOffset.top}); 10695 $fullNameLayer.html($this.clone()); 10696 setTimeout(function() { 10697 if(! $fullNameLayer.hasClass('hovering')) 10698 { 10699 $fullNameLayer.trigger('mouseleave'); 10700 } 10701 }, 200); 10702 } 10703 }); 10704} 10705; 10706
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.