/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
* general function, usally for data manipulation pages
*
*/
/**
* @var sql_box_locked lock for the sqlbox textarea in the querybox/querywindow
*/
var sql_box_locked = false;
/**
* @var array holds elements which content should only selected once
*/
var only_once_elements = new Array();
/**
* @var ajax_message_init boolean boolean that stores status of
* notification for PMA_ajaxShowNotification
*/
var ajax_message_init = false;
/**
* 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 object the form
*/
function PMA_prepareForAjaxRequest($form) {
if (! $form.find('input:hidden').is('#ajax_request_hidden')) {
$form.append('');
}
}
/**
* Generate a new password and copy it to the password input areas
*
* @param object the form that holds the password fields
*
* @return boolean always true
*/
function suggestPassword(passwd_form) {
// 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 = "abcdefhjmnpqrstuvwxyz23456789ABCDEFGHJKLMNPQRSTUVWYXZ";
var passwordlength = 16; // do we want that to be dynamic? no, keep it simple :)
var passwd = passwd_form.generated_pw;
passwd.value = '';
for ( i = 0; i < passwordlength; i++ ) {
passwd.value += pwchars.charAt( Math.floor( Math.random() * pwchars.length ) )
}
passwd_form.text_pma_pw.value = passwd.value;
passwd_form.text_pma_pw2.value = passwd.value;
return true;
}
/**
* Version string to integer conversion.
*/
function parseVersionString (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));
} else if (state[1].substr(0, 4) == 'beta') {
add = - 40 - parseInt(state[1].substr(4));
} else if (state[1].substr(0, 5) == 'alpha') {
add = - 60 - parseInt(state[1].substr(5));
} 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]) || 0;
var min = parseInt(x[1]) || 0;
var pat = parseInt(x[2]) || 0;
var hotfix = parseInt(x[3]) || 0;
return maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add;
}
/**
* Indicates current available version on main page.
*/
function PMA_current_version() {
var current = parseVersionString(pmaversion);
var latest = parseVersionString(PMA_latest_version);
$('#li_pma_version').append(PMA_messages['strLatestAvailable'] + ' ' + PMA_latest_version);
if (latest > current) {
var message = $.sprintf(PMA_messages['strNewerVersion'], PMA_latest_version, PMA_latest_date);
if (Math.floor(latest / 10000) == Math.floor(current / 10000)) {
/* Security update */
klass = 'error';
} else {
klass = 'notice';
}
$('#maincontainer').after('
' + message + '
');
}
}
/**
* for libraries/display_change_password.lib.php
* libraries/user_password.php
*
*/
function displayPasswordGenerateButton() {
$('#tr_element_before_generate_password').parent().append('
');
}
/*
* Adds a date/time picker to an element
*
* @param object $this_element a jQuery object pointing to the element
*/
function PMA_addDatepicker($this_element) {
var showTimeOption = false;
if ($this_element.is('.datetimefield')) {
showTimeOption = true;
}
$this_element
.datepicker({
showOn: 'button',
buttonImage: themeCalendarImage, // defined in js/messages.php
buttonImageOnly: true,
duration: '',
time24h: true,
stepMinutes: 1,
stepHours: 1,
showTime: showTimeOption,
dateFormat: 'yy-mm-dd', // yy means year with four digits
altTimeField: '',
beforeShow: function(input, inst) {
// Remember that we came from the datepicker; this is used
// in tbl_change.js by verificationsAfterFieldChange()
$this_element.data('comes_from', 'datepicker');
},
constrainInput: false
});
}
/**
* 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 only_once if true this is only done once
* f.e. only on first focus
*/
function selectContent( element, lock, only_once ) {
if ( only_once && only_once_elements[element.name] ) {
return;
}
only_once_elements[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 object the link
* @param object the sql query to submit
*
* @return boolean whether to run the query or not
*/
function confirmLink(theLink, theSqlQuery)
{
// Confirmation is not required in the configuration file
// or browser is Opera (crappy js implementation)
if (PMA_messages['strDoYouReally'] == '' || typeof(window.opera) != 'undefined') {
return true;
}
var is_confirmed = confirm(PMA_messages['strDoYouReally'] + ' :\n' + theSqlQuery);
if (is_confirmed) {
if ( typeof(theLink.href) != 'undefined' ) {
theLink.href += '&is_js_confirmed=1';
} else if ( typeof(theLink.form) != 'undefined' ) {
theLink.form.action += '?is_js_confirmed=1';
}
}
return is_confirmed;
} // end of the 'confirmLink()' function
/**
* Displays a confirmation box before doing some action
*
* @param object the message to display
*
* @return boolean whether to run the query or not
*
* @todo used only by libraries/display_tbl.lib.php. figure out how it is used
* and replace with a jQuery equivalent
*/
function confirmAction(theMessage)
{
// TODO: Confirmation is not required in the configuration file
// or browser is Opera (crappy js implementation)
if (typeof(window.opera) != 'undefined') {
return true;
}
var is_confirmed = confirm(theMessage);
return is_confirmed;
} // end of the 'confirmAction()' function
/**
* Displays an error message if a "DROP DATABASE" statement is submitted
* while it isn't allowed, else confirms a "DROP/DELETE/ALTER" query before
* sumitting it if required.
* This function is called by the 'checkSqlQuery()' js function.
*
* @param object the form
* @param object the sql query textarea
*
* @return boolean whether to run the query or not
*
* @see checkSqlQuery()
*/
function confirmQuery(theForm1, sqlQuery1)
{
// Confirmation is not required in the configuration file
if (PMA_messages['strDoYouReally'] == '') {
return true;
}
// The replace function (js1.2) isn't supported
else if (typeof(sqlQuery1.value.replace) == 'undefined') {
return true;
}
// js1.2+ -> validation with regular expressions
else {
// "DROP DATABASE" statement isn't allowed
if (PMA_messages['strNoDropDatabases'] != '') {
var drop_re = new RegExp('(^|;)\\s*DROP\\s+(IF EXISTS\\s+)?DATABASE\\s', 'i');
if (drop_re.test(sqlQuery1.value)) {
alert(PMA_messages['strNoDropDatabases']);
theForm1.reset();
sqlQuery1.focus();
return false;
} // end if
} // end if
// 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 do_confirm_re_0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|DATABASE|PROCEDURE)\\s', 'i');
var do_confirm_re_1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
var do_confirm_re_2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
var do_confirm_re_3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
if (do_confirm_re_0.test(sqlQuery1.value)
|| do_confirm_re_1.test(sqlQuery1.value)
|| do_confirm_re_2.test(sqlQuery1.value)
|| do_confirm_re_3.test(sqlQuery1.value)) {
var message = (sqlQuery1.value.length > 100)
? sqlQuery1.value.substr(0, 100) + '\n ...'
: sqlQuery1.value;
var is_confirmed = confirm(PMA_messages['strDoYouReally'] + ' :\n' + 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 (is_confirmed) {
theForm1.elements['is_js_confirmed'].value = 1;
return true;
}
// statement is rejected -> do not submit the form
else {
window.focus();
sqlQuery1.focus();
return false;
} // end if (handle confirm box result)
} // end if (display confirm box)
} // end confirmation stuff
return true;
} // end of the 'confirmQuery()' function
/**
* Displays a confirmation box before disabling the BLOB repository for a given database.
* This function is called while clicking links
*
* @param object the database
*
* @return boolean whether to disable the repository or not
*/
function confirmDisableRepository(theDB)
{
// Confirmation is not required in the configuration file
// or browser is Opera (crappy js implementation)
if (PMA_messages['strDoYouReally'] == '' || typeof(window.opera) != 'undefined') {
return true;
}
var is_confirmed = confirm(PMA_messages['strBLOBRepositoryDisableStrongWarning'] + '\n' + PMA_messages['strBLOBRepositoryDisableAreYouSure']);
return is_confirmed;
} // end of the 'confirmDisableBLOBRepository()' function
/**
* Displays an error message if the user submitted the sql query form with no
* sql query, else checks for "DROP/DELETE/ALTER" statements
*
* @param object the form
*
* @return boolean always false
*
* @see confirmQuery()
*/
function checkSqlQuery(theForm)
{
var sqlQuery = theForm.elements['sql_query'];
var isEmpty = 1;
// The replace function (js1.2) isn't supported -> basic tests
if (typeof(sqlQuery.value.replace) == 'undefined') {
isEmpty = (sqlQuery.value == '') ? 1 : 0;
if (isEmpty && typeof(theForm.elements['sql_file']) != 'undefined') {
isEmpty = (theForm.elements['sql_file'].value == '') ? 1 : 0;
}
if (isEmpty && typeof(theForm.elements['sql_localfile']) != 'undefined') {
isEmpty = (theForm.elements['sql_localfile'].value == '') ? 1 : 0;
}
if (isEmpty && typeof(theForm.elements['id_bookmark']) != 'undefined') {
isEmpty = (theForm.elements['id_bookmark'].value == null || theForm.elements['id_bookmark'].value == '');
}
}
// js1.2+ -> validation with regular expressions
else {
var space_re = new RegExp('\\s+');
if (typeof(theForm.elements['sql_file']) != 'undefined' &&
theForm.elements['sql_file'].value.replace(space_re, '') != '') {
return true;
}
if (typeof(theForm.elements['sql_localfile']) != 'undefined' &&
theForm.elements['sql_localfile'].value.replace(space_re, '') != '') {
return true;
}
if (isEmpty && 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;
}
// Checks for "DROP/DELETE/ALTER" statements
if (sqlQuery.value.replace(space_re, '') != '') {
if (confirmQuery(theForm, sqlQuery)) {
return true;
} else {
return false;
}
}
theForm.reset();
isEmpty = 1;
}
if (isEmpty) {
sqlQuery.select();
alert(PMA_messages['strFormEmpty']);
sqlQuery.focus();
return false;
}
return true;
} // end of the 'checkSqlQuery()' function
/**
* Check if a form's element is empty.
* An element containing only spaces is also considered empty
*
* @param object the form
* @param string the name of the form field to put the focus on
*
* @return boolean whether the form field is empty or not
*/
function emptyCheckTheField(theForm, theFieldName)
{
var isEmpty = 1;
var theField = theForm.elements[theFieldName];
// Whether the replace function (js1.2) is supported or not
var isRegExp = (typeof(theField.value.replace) != 'undefined');
if (!isRegExp) {
isEmpty = (theField.value == '') ? 1 : 0;
} else {
var space_re = new RegExp('\\s+');
isEmpty = (theField.value.replace(space_re, '') == '') ? 1 : 0;
}
return isEmpty;
} // end of the 'emptyCheckTheField()' function
/**
* Check whether a form field is empty or not
*
* @param object the form
* @param string the name of the form field to put the focus on
*
* @return boolean whether the form field is empty or not
*/
function emptyFormElements(theForm, theFieldName)
{
var theField = theForm.elements[theFieldName];
var isEmpty = emptyCheckTheField(theForm, theFieldName);
return isEmpty;
} // end of the 'emptyFormElements()' function
/**
* 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
*/
function checkFormElementInRange(theForm, theFieldName, message, min, max)
{
var theField = theForm.elements[theFieldName];
var val = parseInt(theField.value);
if (typeof(min) == 'undefined') {
min = 0;
}
if (typeof(max) == 'undefined') {
max = Number.MAX_VALUE;
}
// It's not a number
if (isNaN(val)) {
theField.select();
alert(PMA_messages['strNotNumber']);
theField.focus();
return false;
}
// It's a number but it is not between min and max
else if (val < min || val > max) {
theField.select();
alert(message.replace('%d', val));
theField.focus();
return false;
}
// It's a valid number
else {
theField.value = val;
}
return true;
} // end of the 'checkFormElementInRange()' function
function checkTableEditForm(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, elm, elm2, elm3, val, id;
for (i=0; i (label and checkbox), so we need to handle this differently
var $tr = $(this);
var $checkbox = $tr.find(':checkbox');
if ($checkbox.length) {
// checkbox in a row, add or remove class depending on checkbox state
var checked = $checkbox.attr('checked');
if (!$(e.target).is(':checkbox, label')) {
checked = !checked;
$checkbox.attr('checked', checked);
}
if (checked) {
$tr.addClass('marked');
} else {
$tr.removeClass('marked');
}
} else {
// normaln data table, just toggle class
$tr.toggleClass('marked');
}
});
/**
* Add a date/time picker to each element that needs it
*/
$('.datefield, .datetimefield').each(function() {
PMA_addDatepicker($(this));
});
})
/**
* Row highlighting in horizontal mode (use "live"
* so that it works also for pages reached via AJAX)
*/
$(document).ready(function() {
$('tr.odd, tr.even').live('hover',function() {
var $tr = $(this);
$tr.toggleClass('hover');
$tr.children().toggleClass('hover');
});
})
/**
* This array is used to remember mark status of rows in browse mode
*/
var marked_row = new Array;
/**
* marks all rows and selects its first checkbox inside the given element
* the given element is usaly a table or a div containing the table or tables
*
* @param container DOM element
*/
function markAllRows( container_id ) {
$("#"+container_id).find("input:checkbox:enabled").attr('checked', 'checked')
.parents("tr").addClass("marked");
return true;
}
/**
* marks all rows and selects its first checkbox inside the given element
* the given element is usaly a table or a div containing the table or tables
*
* @param container DOM element
*/
function unMarkAllRows( container_id ) {
$("#"+container_id).find("input:checkbox:enabled").removeAttr('checked')
.parents("tr").removeClass("marked");
return true;
}
/**
* Checks/unchecks all checkbox in given conainer (f.e. a form, fieldset or div)
*
* @param string container_id the container id
* @param boolean state new value for checkbox (true or false)
* @return boolean always true
*/
function setCheckboxes( container_id, state ) {
if(state) {
$("#"+container_id).find("input:checkbox").attr('checked', 'checked');
}
else {
$("#"+container_id).find("input:checkbox").removeAttr('checked');
}
return true;
} // end of the 'setCheckboxes()' function
/**
* Checks/unchecks all options of a