diff options
Diffstat (limited to 'srcs/phpmyadmin/js/functions.js')
| -rw-r--r-- | srcs/phpmyadmin/js/functions.js | 5148 |
1 files changed, 5148 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/js/functions.js b/srcs/phpmyadmin/js/functions.js new file mode 100644 index 0000000..cf973b5 --- /dev/null +++ b/srcs/phpmyadmin/js/functions.js @@ -0,0 +1,5148 @@ +/* vim: set expandtab sw=4 ts=4 sts=4: */ + +/* global isStorageSupported */ // js/config.js +/* global ChartType, ColumnType, DataTable, JQPlotChartFactory */ // js/chart.js +/* global DatabaseStructure */ // js/database/structure.js +/* global mysqlDocBuiltin, mysqlDocKeyword */ // js/doclinks.js +/* global Indexes */ // js/indexes.js +/* global maxInputVars, mysqlDocTemplate, pmaThemeImage */ // js/messages.php +/* global MicroHistory */ // js/microhistory.js +/* global checkPasswordStrength */ // js/server/privileges.js +/* global sprintf */ // js/vendor/sprintf.js +/* global Int32Array */ // ES6 + +/** + * general function, usually for data manipulation pages + * + */ +var Functions = {}; + +/** + * @var sqlBoxLocked lock for the sqlbox textarea in the querybox + */ +// eslint-disable-next-line no-unused-vars +var sqlBoxLocked = false; + +/** + * @var {array} holds elements which content should only selected once + */ +var onlyOnceElements = []; + +/** + * @var {int} ajaxMessageCount Number of AJAX messages shown since page load + */ +var ajaxMessageCount = 0; + +/** + * @var codeMirrorEditor object containing CodeMirror editor of the query editor in SQL tab + */ +var codeMirrorEditor = false; + +/** + * @var codeMirrorInlineEditor object containing CodeMirror editor of the inline query editor + */ +var codeMirrorInlineEditor = false; + +/** + * @var {boolean} sqlAutoCompleteInProgress shows if Table/Column name autocomplete AJAX is in progress + */ +var sqlAutoCompleteInProgress = false; + +/** + * @var sqlAutoComplete object containing list of columns in each table + */ +var sqlAutoComplete = false; + +/** + * @var {string} sqlAutoCompleteDefaultTable string containing default table to autocomplete columns + */ +var sqlAutoCompleteDefaultTable = ''; + +/** + * @var {array} centralColumnList array to hold the columns in central list per db. + */ +var centralColumnList = []; + +/** + * @var {array} primaryIndexes array to hold 'Primary' index columns. + */ +// eslint-disable-next-line no-unused-vars +var primaryIndexes = []; + +/** + * @var {array} uniqueIndexes array to hold 'Unique' index columns. + */ +// eslint-disable-next-line no-unused-vars +var uniqueIndexes = []; + +/** + * @var {array} indexes array to hold 'Index' columns. + */ +// eslint-disable-next-line no-unused-vars +var indexes = []; + +/** + * @var {array} fulltextIndexes array to hold 'Fulltext' columns. + */ +// eslint-disable-next-line no-unused-vars +var fulltextIndexes = []; + +/** + * @var {array} spatialIndexes array to hold 'Spatial' columns. + */ +// eslint-disable-next-line no-unused-vars +var spatialIndexes = []; + +/** + * Make sure that ajax requests will not be cached + * by appending a random variable to their parameters + */ +$.ajaxPrefilter(function (options, originalOptions) { + var nocache = new Date().getTime() + '' + Math.floor(Math.random() * 1000000); + if (typeof options.data === 'string') { + options.data += '&_nocache=' + nocache + '&token=' + encodeURIComponent(CommonParams.get('token')); + } else if (typeof options.data === 'object') { + options.data = $.extend(originalOptions.data, { '_nocache' : nocache, 'token': CommonParams.get('token') }); + } +}); + +/** + * Adds a date/time picker to an element + * + * @param {object} $thisElement a jQuery object pointing to the element + */ +Functions.addDatepicker = function ($thisElement, type, options) { + if (type !== 'date' && type !== 'time' && type !== 'datetime' && type !== 'timestamp') { + return; + } + + var showTimepicker = true; + if (type === 'date') { + showTimepicker = false; + } + + // Getting the current Date and time + var currentDateTime = new Date(); + + var defaultOptions = { + timeInput : true, + hour: currentDateTime.getHours(), + minute: currentDateTime.getMinutes(), + second: currentDateTime.getSeconds(), + showOn: 'button', + buttonImage: pmaThemeImage + 'b_calendar.png', + buttonImageOnly: true, + stepMinutes: 1, + stepHours: 1, + showSecond: true, + showMillisec: true, + showMicrosec: true, + showTimepicker: showTimepicker, + showButtonPanel: false, + dateFormat: 'yy-mm-dd', // yy means year with four digits + timeFormat: 'HH:mm:ss.lc', + constrainInput: false, + altFieldTimeOnly: false, + showAnim: '', + beforeShow: function (input, inst) { + // Remember that we came from the datepicker; this is used + // in table/change.js by verificationsAfterFieldChange() + $thisElement.data('comes_from', 'datepicker'); + if ($(input).closest('.cEdit').length > 0) { + setTimeout(function () { + inst.dpDiv.css({ + top: 0, + left: 0, + position: 'relative' + }); + }, 0); + } + setTimeout(function () { + // Fix wrong timepicker z-index, doesn't work without timeout + $('#ui-timepicker-div').css('z-index', $('#ui-datepicker-div').css('z-index')); + // Integrate tooltip text into dialog + var tooltip = $thisElement.tooltip('instance'); + if (typeof tooltip !== 'undefined') { + tooltip.disable(); + var $note = $('<p class="note"></div>'); + $note.text(tooltip.option('content')); + $('div.ui-datepicker').append($note); + } + }, 0); + }, + onSelect: function () { + $thisElement.data('datepicker').inline = true; + }, + onClose: function () { + // The value is no more from the date picker + $thisElement.data('comes_from', ''); + if (typeof $thisElement.data('datepicker') !== 'undefined') { + $thisElement.data('datepicker').inline = false; + } + var tooltip = $thisElement.tooltip('instance'); + if (typeof tooltip !== 'undefined') { + tooltip.enable(); + } + } + }; + if (type === 'time') { + $thisElement.timepicker($.extend(defaultOptions, options)); + // Add a tip regarding entering MySQL allowed-values for TIME data-type + Functions.tooltip($thisElement, 'input', Messages.strMysqlAllowedValuesTipTime); + } else { + $thisElement.datetimepicker($.extend(defaultOptions, options)); + } +}; + +/** + * Add a date/time picker to each element that needs it + * (only when jquery-ui-timepicker-addon.js is loaded) + */ +Functions.addDateTimePicker = function () { + if ($.timepicker !== undefined) { + $('input.timefield, input.datefield, input.datetimefield').each(function () { + var decimals = $(this).parent().attr('data-decimals'); + var type = $(this).parent().attr('data-type'); + + var showMillisec = false; + var showMicrosec = false; + var timeFormat = 'HH:mm:ss'; + var hourMax = 23; + // check for decimal places of seconds + if (decimals > 0 && type.indexOf('time') !== -1) { + if (decimals > 3) { + showMillisec = true; + showMicrosec = true; + timeFormat = 'HH:mm:ss.lc'; + } else { + showMillisec = true; + timeFormat = 'HH:mm:ss.l'; + } + } + if (type === 'time') { + hourMax = 99; + } + Functions.addDatepicker($(this), type, { + showMillisec: showMillisec, + showMicrosec: showMicrosec, + timeFormat: timeFormat, + hourMax: hourMax + }); + // Add a tip regarding entering MySQL allowed-values + // for TIME and DATE data-type + if ($(this).hasClass('timefield')) { + Functions.tooltip($(this), 'input', Messages.strMysqlAllowedValuesTipTime); + } else if ($(this).hasClass('datefield')) { + Functions.tooltip($(this), 'input', Messages.strMysqlAllowedValuesTipDate); + } + }); + } +}; + +/** + * Handle redirect and reload flags sent as part of AJAX requests + * + * @param data ajax response data + */ +Functions.handleRedirectAndReload = function (data) { + if (parseInt(data.redirect_flag) === 1) { + // add one more GET param to display session expiry msg + if (window.location.href.indexOf('?') === -1) { + window.location.href += '?session_expired=1'; + } else { + window.location.href += CommonParams.get('arg_separator') + 'session_expired=1'; + } + window.location.reload(); + } else if (parseInt(data.reload_flag) === 1) { + window.location.reload(); + } +}; + +/** + * Creates an SQL editor which supports auto completing etc. + * + * @param $textarea jQuery object wrapping the textarea to be made the editor + * @param options optional options for CodeMirror + * @param resize optional resizing ('vertical', 'horizontal', 'both') + * @param lintOptions additional options for lint + */ +Functions.getSqlEditor = function ($textarea, options, resize, lintOptions) { + var resizeType = resize; + if ($textarea.length > 0 && typeof CodeMirror !== 'undefined') { + // merge options for CodeMirror + var defaults = { + lineNumbers: true, + matchBrackets: true, + extraKeys: { 'Ctrl-Space': 'autocomplete' }, + hintOptions: { 'completeSingle': false, 'completeOnSingleClick': true }, + indentUnit: 4, + mode: 'text/x-mysql', + lineWrapping: true + }; + + if (CodeMirror.sqlLint) { + $.extend(defaults, { + gutters: ['CodeMirror-lint-markers'], + lint: { + 'getAnnotations': CodeMirror.sqlLint, + 'async': true, + 'lintOptions': lintOptions + } + }); + } + + $.extend(true, defaults, options); + + // create CodeMirror editor + var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults); + // allow resizing + if (! resizeType) { + resizeType = 'vertical'; + } + var handles = ''; + if (resizeType === 'vertical') { + handles = 's'; + } + if (resizeType === 'both') { + handles = 'all'; + } + if (resizeType === 'horizontal') { + handles = 'e, w'; + } + $(codemirrorEditor.getWrapperElement()) + .css('resize', resizeType) + .resizable({ + handles: handles, + resize: function () { + codemirrorEditor.setSize($(this).width(), $(this).height()); + } + }); + // enable autocomplete + codemirrorEditor.on('inputRead', Functions.codeMirrorAutoCompleteOnInputRead); + + // page locking + codemirrorEditor.on('change', function (e) { + e.data = { + value: 3, + content: codemirrorEditor.isClean(), + }; + AJAX.lockPageHandler(e); + }); + + return codemirrorEditor; + } + return null; +}; + +/** + * Clear text selection + */ +Functions.clearSelection = function () { + if (document.selection && document.selection.empty) { + document.selection.empty(); + } else if (window.getSelection) { + var sel = window.getSelection(); + if (sel.empty) { + sel.empty(); + } + if (sel.removeAllRanges) { + sel.removeAllRanges(); + } + } +}; + +/** + * Create a jQuery UI tooltip + * + * @param $elements jQuery object representing the elements + * @param item the item + * (see https://api.jqueryui.com/tooltip/#option-items) + * @param myContent content of the tooltip + * @param additionalOptions to override the default options + * + */ +Functions.tooltip = function ($elements, item, myContent, additionalOptions) { + if ($('#no_hint').length > 0) { + return; + } + + var defaultOptions = { + content: myContent, + items: item, + tooltipClass: 'tooltip', + track: true, + show: false, + hide: false + }; + + $elements.tooltip($.extend(true, defaultOptions, additionalOptions)); +}; + +/** + * HTML escaping + */ +Functions.escapeHtml = function (unsafe) { + if (typeof(unsafe) !== 'undefined') { + return unsafe + .toString() + .replace(/&/g, '&') + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } else { + return false; + } +}; + +Functions.escapeJsString = function (unsafe) { + if (typeof(unsafe) !== 'undefined') { + return unsafe + .toString() + .replace('\x00', '') + .replace('\\', '\\\\') + .replace('\'', '\\\'') + .replace(''', '\\'') + .replace('"', '\\"') + .replace('"', '\\"') + .replace('\n', '\n') + .replace('\r', '\r') + .replace(/<\/script/gi, '</\' + \'script'); + } else { + return false; + } +}; + +Functions.escapeBacktick = function (s) { + return s.replace('`', '``'); +}; + +Functions.escapeSingleQuote = function (s) { + return s.replace('\\', '\\\\').replace('\'', '\\\''); +}; + +Functions.sprintf = function () { + return sprintf.apply(this, arguments); +}; + +/** + * Hides/shows the default value input field, depending on the default type + * Ticks the NULL checkbox if NULL is chosen as default value. + */ +Functions.hideShowDefaultValue = function ($defaultType) { + if ($defaultType.val() === 'USER_DEFINED') { + $defaultType.siblings('.default_value').show().focus(); + } else { + $defaultType.siblings('.default_value').hide(); + if ($defaultType.val() === 'NULL') { + var $nullCheckbox = $defaultType.closest('tr').find('.allow_null'); + $nullCheckbox.prop('checked', true); + } + } +}; + +/** + * Hides/shows the input field for column expression based on whether + * VIRTUAL/PERSISTENT is selected + * + * @param $virtuality virtuality dropdown + */ +Functions.hideShowExpression = function ($virtuality) { + if ($virtuality.val() === '') { + $virtuality.siblings('.expression').hide(); + } else { + $virtuality.siblings('.expression').show(); + } +}; + +/** + * Show notices for ENUM columns; add/hide the default value + * + */ +Functions.verifyColumnsProperties = function () { + $('select.column_type').each(function () { + Functions.showNoticeForEnum($(this)); + }); + $('select.default_type').each(function () { + Functions.hideShowDefaultValue($(this)); + }); + $('select.virtuality').each(function () { + Functions.hideShowExpression($(this)); + }); +}; + +/** + * Add a hidden field to the form to indicate that this will be an + * Ajax request (only if this hidden field does not exist) + * + * @param $form object the form + */ +Functions.prepareForAjaxRequest = function ($form) { + if (! $form.find('input:hidden').is('#ajax_request_hidden')) { + $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true">'); + } +}; + +/** + * Generate a new password and copy it to the password input areas + * + * @param {object} passwordForm the form that holds the password fields + * + * @return {boolean} always true + */ +Functions.suggestPassword = function (passwordForm) { + // restrict the password to just letters and numbers to avoid problems: + // "editors and viewers regard the password as multiple words and + // things like double click no longer work" + var pwchars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZ'; + var passwordlength = 16; // do we want that to be dynamic? no, keep it simple :) + var passwd = passwordForm.generated_pw; + var randomWords = new Int32Array(passwordlength); + + passwd.value = ''; + + var i; + + // First we're going to try to use a built-in CSPRNG + if (window.crypto && window.crypto.getRandomValues) { + window.crypto.getRandomValues(randomWords); + } else if (window.msCrypto && window.msCrypto.getRandomValues) { + // Because of course IE calls it msCrypto instead of being standard + window.msCrypto.getRandomValues(randomWords); + } else { + // Fallback to Math.random + for (i = 0; i < passwordlength; i++) { + randomWords[i] = Math.floor(Math.random() * pwchars.length); + } + } + + for (i = 0; i < passwordlength; i++) { + passwd.value += pwchars.charAt(Math.abs(randomWords[i]) % pwchars.length); + } + + var $jQueryPasswordForm = $(passwordForm); + + passwordForm.elements.pma_pw.value = passwd.value; + passwordForm.elements.pma_pw2.value = passwd.value; + var meterObj = $jQueryPasswordForm.find('meter[name="pw_meter"]').first(); + var meterObjLabel = $jQueryPasswordForm.find('span[name="pw_strength"]').first(); + checkPasswordStrength(passwd.value, meterObj, meterObjLabel); + return true; +}; + +/** + * Version string to integer conversion. + */ +Functions.parseVersionString = function (str) { + if (typeof(str) !== 'string') { + return false; + } + var add = 0; + // Parse possible alpha/beta/rc/ + var state = str.split('-'); + if (state.length >= 2) { + if (state[1].substr(0, 2) === 'rc') { + add = - 20 - parseInt(state[1].substr(2), 10); + } else if (state[1].substr(0, 4) === 'beta') { + add = - 40 - parseInt(state[1].substr(4), 10); + } else if (state[1].substr(0, 5) === 'alpha') { + add = - 60 - parseInt(state[1].substr(5), 10); + } else if (state[1].substr(0, 3) === 'dev') { + /* We don't handle dev, it's git snapshot */ + add = 0; + } + } + // Parse version + var x = str.split('.'); + // Use 0 for non existing parts + var maj = parseInt(x[0], 10) || 0; + var min = parseInt(x[1], 10) || 0; + var pat = parseInt(x[2], 10) || 0; + var hotfix = parseInt(x[3], 10) || 0; + return maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add; +}; + +/** + * Indicates current available version on main page. + */ +Functions.currentVersion = function (data) { + if (data && data.version && data.date) { + var current = Functions.parseVersionString($('span.version').text()); + var latest = Functions.parseVersionString(data.version); + var url = 'https://www.phpmyadmin.net/files/' + Functions.escapeHtml(encodeURIComponent(data.version)) + '/'; + var versionInformationMessage = document.createElement('span'); + versionInformationMessage.className = 'latest'; + var versionInformationMessageLink = document.createElement('a'); + versionInformationMessageLink.href = url; + versionInformationMessageLink.className = 'disableAjax'; + var versionInformationMessageLinkText = document.createTextNode(data.version); + versionInformationMessageLink.appendChild(versionInformationMessageLinkText); + var prefixMessage = document.createTextNode(Messages.strLatestAvailable + ' '); + versionInformationMessage.appendChild(prefixMessage); + versionInformationMessage.appendChild(versionInformationMessageLink); + if (latest > current) { + var message = Functions.sprintf( + Messages.strNewerVersion, + Functions.escapeHtml(data.version), + Functions.escapeHtml(data.date) + ); + var htmlClass = 'notice'; + if (Math.floor(latest / 10000) === Math.floor(current / 10000)) { + /* Security update */ + htmlClass = 'error'; + } + $('#newer_version_notice').remove(); + var mainContainerDiv = document.createElement('div'); + mainContainerDiv.id = 'newer_version_notice'; + mainContainerDiv.className = htmlClass; + var mainContainerDivLink = document.createElement('a'); + mainContainerDivLink.href = url; + mainContainerDivLink.className = 'disableAjax'; + var mainContainerDivLinkText = document.createTextNode(message); + mainContainerDivLink.appendChild(mainContainerDivLinkText); + mainContainerDiv.appendChild(mainContainerDivLink); + $('#maincontainer').append($(mainContainerDiv)); + } + if (latest === current) { + versionInformationMessage = document.createTextNode(' (' + Messages.strUpToDate + ')'); + } + /* Remove extra whitespace */ + var versionInfo = $('#li_pma_version').contents().get(2); + if (typeof versionInfo !== 'undefined') { + versionInfo.textContent = $.trim(versionInfo.textContent); + } + var $liPmaVersion = $('#li_pma_version'); + $liPmaVersion.find('span.latest').remove(); + $liPmaVersion.append($(versionInformationMessage)); + } +}; + +/** + * Loads Git revision data from ajax for index.php + */ +Functions.displayGitRevision = function () { + $('#is_git_revision').remove(); + $('#li_pma_version_git').remove(); + $.get( + 'index.php', + { + 'server': CommonParams.get('server'), + 'git_revision': true, + 'ajax_request': true, + 'no_debug': true + }, + function (data) { + if (typeof data !== 'undefined' && data.success === true) { + $(data.message).insertAfter('#li_pma_version'); + } + } + ); +}; + +/** + * for PhpMyAdmin\Display\ChangePassword + * libraries/user_password.php + * + */ +Functions.displayPasswordGenerateButton = function () { + var generatePwdRow = $('<tr></tr>').addClass('vmiddle'); + $('<td></td>').html(Messages.strGeneratePassword).appendTo(generatePwdRow); + var pwdCell = $('<td></td>').appendTo(generatePwdRow); + var pwdButton = $('<input>') + .attr({ type: 'button', id: 'button_generate_password', value: Messages.strGenerate }) + .addClass('btn btn-secondary button') + .on('click', function () { + Functions.suggestPassword(this.form); + }); + var pwdTextbox = $('<input>') + .attr({ type: 'text', name: 'generated_pw', id: 'generated_pw' }); + pwdCell.append(pwdButton).append(pwdTextbox); + + if (document.getElementById('button_generate_password') === null) { + $('#tr_element_before_generate_password').parent().append(generatePwdRow); + } + + var generatePwdDiv = $('<div></div>').addClass('item'); + $('<label></label>').attr({ for: 'button_generate_password' }) + .html(Messages.strGeneratePassword + ':') + .appendTo(generatePwdDiv); + var optionsSpan = $('<span></span>').addClass('options') + .appendTo(generatePwdDiv); + pwdButton.clone(true).appendTo(optionsSpan); + pwdTextbox.clone(true).appendTo(generatePwdDiv); + + if (document.getElementById('button_generate_password') === null) { + $('#div_element_before_generate_password').parent().append(generatePwdDiv); + } +}; + +/** + * selects the content of a given object, f.e. a textarea + * + * @param {object} element element of which the content will be selected + * @param {var} lock variable which holds the lock for this element or true, if no lock exists + * @param {boolean} onlyOnce boolean if true this is only done once f.e. only on first focus + */ +Functions.selectContent = function (element, lock, onlyOnce) { + if (onlyOnce && onlyOnceElements[element.name]) { + return; + } + + onlyOnceElements[element.name] = true; + + if (lock) { + return; + } + + element.select(); +}; + +/** + * Displays a confirmation box before submitting a "DROP/DELETE/ALTER" query. + * This function is called while clicking links + * + * @param theLink object the link + * @param theSqlQuery object the sql query to submit + * + * @return boolean whether to run the query or not + */ +Functions.confirmLink = function (theLink, theSqlQuery) { + // Confirmation is not required in the configuration file + // or browser is Opera (crappy js implementation) + if (Messages.strDoYouReally === '' || typeof(window.opera) !== 'undefined') { + return true; + } + + var isConfirmed = confirm(Functions.sprintf(Messages.strDoYouReally, theSqlQuery)); + if (isConfirmed) { + if (typeof(theLink.href) !== 'undefined') { + theLink.href += CommonParams.get('arg_separator') + 'is_js_confirmed=1'; + } else if (typeof(theLink.form) !== 'undefined') { + theLink.form.action += '?is_js_confirmed=1'; + } + } + + return isConfirmed; +}; + +/** + * Confirms a "DROP/DELETE/ALTER" query before + * submitting it if required. + * This function is called by the 'Functions.checkSqlQuery()' js function. + * + * @param theForm1 object the form + * @param sqlQuery1 string the sql query string + * + * @return boolean whether to run the query or not + * + * @see Functions.checkSqlQuery() + */ +Functions.confirmQuery = function (theForm1, sqlQuery1) { + // Confirmation is not required in the configuration file + if (Messages.strDoYouReally === '') { + return true; + } + + // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement + // + // TODO: find a way (if possible) to use the parser-analyser + // for this kind of verification + // For now, I just added a ^ to check for the statement at + // beginning of expression + + var doConfirmRegExp0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|PROCEDURE)\\s', 'i'); + var doConfirmRegExp1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i'); + var doConfirmRegExp2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i'); + var doConfirmRegExp3 = new RegExp('^\\s*TRUNCATE\\s', 'i'); + var doConfirmRegExp4 = new RegExp('^(?=.*UPDATE\\b)^((?!WHERE).)*$', 'i'); + + if (doConfirmRegExp0.test(sqlQuery1) || + doConfirmRegExp1.test(sqlQuery1) || + doConfirmRegExp2.test(sqlQuery1) || + doConfirmRegExp3.test(sqlQuery1) || + doConfirmRegExp4.test(sqlQuery1)) { + var message; + if (sqlQuery1.length > 100) { + message = sqlQuery1.substr(0, 100) + '\n ...'; + } else { + message = sqlQuery1; + } + var isConfirmed = confirm(Functions.sprintf(Messages.strDoYouReally, message)); + // statement is confirmed -> update the + // "is_js_confirmed" form field so the confirm test won't be + // run on the server side and allows to submit the form + if (isConfirmed) { + theForm1.elements.is_js_confirmed.value = 1; + return true; + } else { + // statement is rejected -> do not submit the form + window.focus(); + return false; + } // end if (handle confirm box result) + } // end if (display confirm box) + + return true; +}; + +/** + * Displays an error message if the user submitted the sql query form with no + * sql query, else checks for "DROP/DELETE/ALTER" statements + * + * @param theForm object the form + * + * @return boolean always false + * + * @see Functions.confirmQuery() + */ +Functions.checkSqlQuery = function (theForm) { + // get the textarea element containing the query + var sqlQuery; + if (codeMirrorEditor) { + codeMirrorEditor.save(); + sqlQuery = codeMirrorEditor.getValue(); + } else { + sqlQuery = theForm.elements.sql_query.value; + } + var spaceRegExp = new RegExp('\\s+'); + if (typeof(theForm.elements.sql_file) !== 'undefined' && + theForm.elements.sql_file.value.replace(spaceRegExp, '') !== '') { + return true; + } + if (typeof(theForm.elements.id_bookmark) !== 'undefined' && + (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') && + theForm.elements.id_bookmark.selectedIndex !== 0) { + return true; + } + var result = false; + // Checks for "DROP/DELETE/ALTER" statements + if (sqlQuery.replace(spaceRegExp, '') !== '') { + result = Functions.confirmQuery(theForm, sqlQuery); + } else { + alert(Messages.strFormEmpty); + } + + if (codeMirrorEditor) { + codeMirrorEditor.focus(); + } else if (codeMirrorInlineEditor) { + codeMirrorInlineEditor.focus(); + } + return result; +}; + +/** + * Check if a form's element is empty. + * An element containing only spaces is also considered empty + * + * @param {object} theForm the form + * @param {string} theFieldName the name of the form field to put the focus on + * + * @return {boolean} whether the form field is empty or not + */ +Functions.emptyCheckTheField = function (theForm, theFieldName) { + var theField = theForm.elements[theFieldName]; + var spaceRegExp = new RegExp('\\s+'); + return theField.value.replace(spaceRegExp, '') === ''; +}; + +/** + * Ensures a value submitted in a form is numeric and is in a range + * + * @param object the form + * @param string the name of the form field to check + * @param integer the minimum authorized value + * @param integer the maximum authorized value + * + * @return boolean whether a valid number has been submitted or not + */ +Functions.checkFormElementInRange = function (theForm, theFieldName, message, minimum, maximum) { + var theField = theForm.elements[theFieldName]; + var val = parseInt(theField.value, 10); + var min = minimum; + var max = maximum; + + if (typeof(min) === 'undefined') { + min = 0; + } + if (typeof(max) === 'undefined') { + max = Number.MAX_VALUE; + } + + if (isNaN(val)) { + theField.select(); + alert(Messages.strEnterValidNumber); + theField.focus(); + return false; + } else if (val < min || val > max) { + theField.select(); + alert(Functions.sprintf(message, val)); + theField.focus(); + return false; + } else { + theField.value = val; + } + return true; +}; + +Functions.checkTableEditForm = function (theForm, fieldsCnt) { + // TODO: avoid sending a message if user just wants to add a line + // on the form but has not completed at least one field name + + var atLeastOneField = 0; + var i; + var elm; + var elm2; + var elm3; + var val; + var id; + + for (i = 0; i < fieldsCnt; i++) { + id = '#field_' + i + '_2'; + elm = $(id); + val = elm.val(); + if (val === 'VARCHAR' || val === 'CHAR' || val === 'BIT' || val === 'VARBINARY' || val === 'BINARY') { + elm2 = $('#field_' + i + '_3'); + val = parseInt(elm2.val(), 10); + elm3 = $('#field_' + i + '_1'); + if (isNaN(val) && elm3.val() !== '') { + elm2.select(); + alert(Messages.strEnterValidLength); + elm2.focus(); + return false; + } + } + + if (atLeastOneField === 0) { + id = 'field_' + i + '_1'; + if (!Functions.emptyCheckTheField(theForm, id)) { + atLeastOneField = 1; + } + } + } + if (atLeastOneField === 0) { + var theField = theForm.elements.field_0_1; + alert(Messages.strFormEmpty); + theField.focus(); + return false; + } + + // at least this section is under jQuery + var $input = $('input.textfield[name=\'table\']'); + if ($input.val() === '') { + alert(Messages.strFormEmpty); + $input.focus(); + return false; + } + + return true; +}; + +/** + * True if last click is to check a row. + */ +var lastClickChecked = false; + +/** + * Zero-based index of last clicked row. + * Used to handle the shift + click event in the code above. + */ +var lastClickedRow = -1; + +/** + * Zero-based index of last shift clicked row. + */ +var lastShiftClickedRow = -1; + +var idleSecondsCounter = 0; +var incInterval; +var updateTimeout; +AJAX.registerTeardown('functions.js', function () { + clearTimeout(updateTimeout); + clearInterval(incInterval); + $(document).off('mousemove'); +}); + +AJAX.registerOnload('functions.js', function () { + document.onclick = function () { + idleSecondsCounter = 0; + }; + $(document).on('mousemove',function () { + idleSecondsCounter = 0; + }); + document.onkeypress = function () { + idleSecondsCounter = 0; + }; + function guid () { + function s4 () { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + + s4() + '-' + s4() + s4() + s4(); + } + + function SetIdleTime () { + idleSecondsCounter++; + } + function UpdateIdleTime () { + var href = 'index.php'; + var guid = 'default'; + if (isStorageSupported('sessionStorage')) { + guid = window.sessionStorage.guid; + } + var params = { + 'ajax_request' : true, + 'server' : CommonParams.get('server'), + 'db' : CommonParams.get('db'), + 'guid': guid, + 'access_time': idleSecondsCounter, + 'check_timeout': 1 + }; + $.ajax({ + type: 'POST', + url: href, + data: params, + success: function (data) { + if (data.success) { + if (CommonParams.get('LoginCookieValidity') - idleSecondsCounter < 0) { + /* There is other active window, let's reset counter */ + idleSecondsCounter = 0; + } + var remaining = Math.min( + /* Remaining login validity */ + CommonParams.get('LoginCookieValidity') - idleSecondsCounter, + /* Remaining time till session GC */ + CommonParams.get('session_gc_maxlifetime') + ); + var interval = 1000; + if (remaining > 5) { + // max value for setInterval() function + interval = Math.min((remaining - 1) * 1000, Math.pow(2, 31) - 1); + } + updateTimeout = window.setTimeout(UpdateIdleTime, interval); + } else { // timeout occurred + clearInterval(incInterval); + if (isStorageSupported('sessionStorage')) { + window.sessionStorage.clear(); + } + // append the login form on the page, disable all the forms which were not disabled already, close all the open jqueryui modal boxes + if (!$('#modalOverlay').length) { + $('fieldset').not(':disabled').attr('disabled', 'disabled').addClass('disabled_for_expiration'); + $('body').append(data.error); + $('.ui-dialog').each(function () { + $('#' + $(this).attr('aria-describedby')).dialog('close'); + }); + $('#input_username').t |
