e880da0d0e
When a form includes more than one submit button, after being disabled all buttons are re-enabled with the value of the first button rather than having their original values restored. This change iterates over each button separately to disable and enable.
826 lines
35 KiB
JavaScript
826 lines
35 KiB
JavaScript
(function ($, undefined) {
|
|
/*
|
|
* Unobtrusive scripting adapter for jQuery, largely taken from
|
|
* the wonderful https://github.com/rails/jquery-ujs
|
|
*
|
|
*
|
|
* Released under the MIT license
|
|
*
|
|
*/
|
|
'use strict';
|
|
if ($.web2py !== undefined) {
|
|
$.error('web2py.js has already been loaded!');
|
|
}
|
|
|
|
var FORMDATA_IS_SUPPORTED = typeof(FormData) !== 'undefined';
|
|
|
|
String.prototype.reverse = function () {
|
|
return this.split('').reverse().join('');
|
|
};
|
|
var web2py;
|
|
|
|
$.web2py = web2py = {
|
|
|
|
isUndefined: function (obj) {
|
|
/* grabbed from underscore.js */
|
|
return obj === void 0;
|
|
},
|
|
popup: function (url) {
|
|
/* popup a window */
|
|
var newwindow = window.open(url, 'name', 'height=400,width=600');
|
|
if (window.focus) newwindow.focus();
|
|
return false;
|
|
},
|
|
collapse: function (id) {
|
|
/* toggle an element */
|
|
$('#' + id).slideToggle();
|
|
},
|
|
fade: function (id, value) {
|
|
/*fade something*/
|
|
if (value > 0) $('#' + id).hide().fadeIn('slow');
|
|
else $('#' + id).show().fadeOut('slow');
|
|
},
|
|
ajax: function (u, s, t, options) {
|
|
/*simple ajax function*/
|
|
|
|
// set options default value
|
|
options = typeof options !== 'undefined' ? options : {};
|
|
|
|
var query = '';
|
|
if (typeof s == 'string') {
|
|
var d = $(s).serialize();
|
|
if (d) {
|
|
query = d;
|
|
}
|
|
} else {
|
|
var pcs = [];
|
|
if (s !== null && !web2py.isUndefined(s))
|
|
for (var i = 0; i < s.length; i++) {
|
|
var q = $('[name=' + s[i] + ']').serialize();
|
|
if (q) {
|
|
pcs.push(q);
|
|
}
|
|
}
|
|
if (pcs.length > 0) {
|
|
query = pcs.join('&');
|
|
}
|
|
}
|
|
|
|
// default success action
|
|
var success_function = function (msg) {
|
|
if (t) {
|
|
if (t == ':eval') eval(msg);
|
|
else if (typeof t == 'string') $('#' + t).html(msg);
|
|
else t(msg);
|
|
}
|
|
};
|
|
|
|
// declare success actions as array
|
|
var success = [success_function];
|
|
|
|
// add user success actions
|
|
if ($.isArray(options.done)){
|
|
success = $.merge(success, options.done);
|
|
} else {
|
|
success.push(options.done);
|
|
}
|
|
|
|
// default jquery ajax options
|
|
var ajax_options = {
|
|
type: 'POST',
|
|
url: u,
|
|
data: query,
|
|
success: success
|
|
};
|
|
|
|
//remove custom "done" option if exists
|
|
delete options.done;
|
|
|
|
// merge default ajax options with user custom options
|
|
for (var attrname in options) {
|
|
ajax_options[attrname] = options[attrname];
|
|
}
|
|
|
|
// call ajax function
|
|
$.ajax(ajax_options);
|
|
},
|
|
ajax_fields: function (target) {
|
|
/*
|
|
*this attaches something to a newly loaded fragment/page
|
|
* Ideally all events should be bound to the document, so we can avoid calling
|
|
* this over and over... all will be bound to the document
|
|
*/
|
|
/*adds btn class to buttons*/
|
|
$('button:not([class^="btn"])', target).addClass('btn');
|
|
$(
|
|
'form input[type="submit"]:not([class^="btn"]), form input[type="button"]:not([class^="btn"])',
|
|
target).addClass('btn');
|
|
/* javascript for PasswordWidget*/
|
|
$('input[type=password][data-w2p_entropy]', target).each(function () {
|
|
web2py.validate_entropy($(this));
|
|
});
|
|
/* javascript for ListWidget*/
|
|
$('ul.w2p_list', target).each(function () {
|
|
function pe(ul, e) {
|
|
var new_line = ml(ul);
|
|
rel(ul);
|
|
if ($(e.target).parent().is(':visible')) {
|
|
/* make sure we didn't delete the element before we insert after */
|
|
new_line.insertAfter($(e.target).parent());
|
|
} else {
|
|
/* the line we clicked on was deleted, just add to end of list */
|
|
new_line.appendTo(ul);
|
|
}
|
|
new_line.find(':text').focus();
|
|
return false;
|
|
}
|
|
|
|
function rl(ul, e) {
|
|
if ($(ul).children().length > 1) {
|
|
/* only remove if we have more than 1 item so the list is never empty */
|
|
$(e.target).parent().remove();
|
|
}
|
|
}
|
|
|
|
function ml(ul) {
|
|
/* clone the first field */
|
|
var line = $(ul).find('li:first').clone(true);
|
|
line.find(':text').val('');
|
|
return line;
|
|
}
|
|
|
|
function rel(ul) {
|
|
/* keep only as many as needed*/
|
|
$(ul).find('li').each(function () {
|
|
var trimmed = $.trim($(this.firstChild).val());
|
|
if (trimmed === '') $(this).remove();
|
|
else $(this.firstChild).val(trimmed);
|
|
});
|
|
}
|
|
var ul = this;
|
|
$(ul).find(':text').after('<a href="#">+</a> <a href="#">-</a>').keypress(
|
|
function (e) {
|
|
return (e.which == 13) ? pe(ul, e) : true;
|
|
}).next().click(function (e) {
|
|
pe(ul, e);
|
|
e.preventDefault();
|
|
}).next().click(function (e) {
|
|
rl(ul, e);
|
|
e.preventDefault();
|
|
});
|
|
});
|
|
},
|
|
ajax_init: function (target) {
|
|
/*called whenever a fragment gets loaded */
|
|
$('.w2p_hidden', target).hide();
|
|
web2py.manage_errors(target);
|
|
web2py.ajax_fields(target);
|
|
web2py.show_if_handler(target);
|
|
web2py.component_handler(target);
|
|
},
|
|
/* manage errors in forms */
|
|
manage_errors: function (target) {
|
|
$('div.error', target).hide().slideDown('slow');
|
|
},
|
|
after_ajax: function (xhr) {
|
|
/* called whenever an ajax request completes */
|
|
var command = xhr.getResponseHeader('web2py-component-command');
|
|
var flash = xhr.getResponseHeader('web2py-component-flash');
|
|
if (command !== null) {
|
|
eval(decodeURIComponent(command));
|
|
}
|
|
if (flash) {
|
|
web2py.flash(decodeURIComponent(flash));
|
|
}
|
|
},
|
|
event_handlers: function () {
|
|
/*
|
|
* This is called once for page
|
|
* Ideally it should bound all the things that are needed
|
|
* and require no dom manipulations
|
|
*/
|
|
var doc = $(document);
|
|
doc.on('click', '.w2p_flash', function (event) {
|
|
event.preventDefault();
|
|
var t = $(this);
|
|
if (t.css('top') == '0px') t.slideUp('slow');
|
|
else t.fadeOut();
|
|
});
|
|
doc.on('keyup', 'input.integer', function () {
|
|
var nvalue = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
|
|
if (this.value != nvalue) this.value = nvalue;
|
|
});
|
|
doc.on('keyup', 'input.double, input.decimal', function () {
|
|
var nvalue = this.value.reverse().replace(
|
|
/[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g, '').reverse();
|
|
if (this.value != nvalue) this.value = nvalue;
|
|
});
|
|
var confirm_message = !web2py.isUndefined(w2p_ajax_confirm_message) ? w2p_ajax_confirm_message :
|
|
'Are you sure you want to delete this object?';
|
|
doc.on('click', 'input[type="checkbox"].delete', function () {
|
|
if (this.checked)
|
|
if (!web2py.confirm(confirm_message)) this.checked = false;
|
|
});
|
|
var datetime_format = !web2py.isUndefined(w2p_ajax_datetime_format) ? w2p_ajax_datetime_format :
|
|
'%Y-%m-%d %H:%M:%S';
|
|
doc.on('click', 'input.datetime', function () {
|
|
var tformat = $(this).data('w2p_datetime_format');
|
|
var active = $(this).data('w2p_datetime');
|
|
var format = !web2py.isUndefined(tformat) ? tformat : datetime_format;
|
|
if (active === undefined) {
|
|
Calendar.setup({
|
|
inputField: this,
|
|
ifFormat: format,
|
|
showsTime: true,
|
|
timeFormat: '24'
|
|
});
|
|
$(this).attr('autocomplete', 'off');
|
|
$(this).data('w2p_datetime', 1);
|
|
$(this).trigger('click');
|
|
}
|
|
});
|
|
var date_format = !web2py.isUndefined(w2p_ajax_date_format) ? w2p_ajax_date_format : '%Y-%m-%d';
|
|
doc.on('click', 'input.date', function () {
|
|
var tformat = $(this).data('w2p_date_format');
|
|
var active = $(this).data('w2p_date');
|
|
var format = !web2py.isUndefined(tformat) ? tformat : date_format;
|
|
if (active === undefined) {
|
|
Calendar.setup({
|
|
inputField: this,
|
|
ifFormat: format,
|
|
showsTime: false
|
|
});
|
|
$(this).data('w2p_date', 1);
|
|
$(this).attr('autocomplete', 'off');
|
|
$(this).trigger('click');
|
|
}
|
|
});
|
|
doc.on('focus', 'input.time', function () {
|
|
var active = $(this).data('w2p_time');
|
|
if (web2py.isUndefined(active)) {
|
|
$(this).timeEntry({
|
|
spinnerImage: ''
|
|
}).attr('autocomplete', 'off');
|
|
$(this).data('w2p_time', 1);
|
|
}
|
|
});
|
|
/* help preventing double form submission for normal form (not LOADed) */
|
|
$(doc).on('submit', 'form', function (e) {
|
|
var submit_buttons = $(this).find(web2py.formInputClickSelector);
|
|
submit_buttons.each(function() {
|
|
web2py.disableElement($(this));
|
|
})
|
|
/* safeguard in case the form doesn't trigger a refresh,
|
|
see https://github.com/web2py/web2py/issues/1100 */
|
|
setTimeout(function () {
|
|
submit_buttons.each(function() {
|
|
web2py.enableElement($(this));
|
|
});
|
|
}, 5000);
|
|
});
|
|
doc.ajaxSuccess(function (e, xhr) {
|
|
var redirect = xhr.getResponseHeader('web2py-redirect-location');
|
|
if (redirect !== null) {
|
|
window.location = redirect;
|
|
}
|
|
/* run this here only if this Ajax request is NOT for a web2py component. */
|
|
if (xhr.getResponseHeader('web2py-component-content') === null) {
|
|
web2py.after_ajax(xhr);
|
|
}
|
|
});
|
|
|
|
doc.ajaxError(function (e, xhr, settings, exception) {
|
|
/*personally I don't like it.
|
|
*if there's an error it it flashed and can be removed
|
|
*as any other message
|
|
*doc.off('click', '.w2p_flash')
|
|
*/
|
|
switch (xhr.status) {
|
|
case 500:
|
|
web2py.flash(ajax_error_500);
|
|
}
|
|
});
|
|
|
|
},
|
|
trap_form: function (action, target) {
|
|
/* traps any LOADed form */
|
|
$('#' + target + ' form').each(function () {
|
|
var form = $(this);
|
|
if (form.hasClass('no_trap')) {
|
|
return;
|
|
}
|
|
|
|
var w2p_target = $(this).attr('data-w2p_target');
|
|
if (web2py.isUndefined(w2p_target) || w2p_target === false) {
|
|
form.attr('data-w2p_target', target);
|
|
} else {
|
|
target = w2p_target;
|
|
}
|
|
|
|
var url = form.attr('action');
|
|
if ((url === '') || (url === '#') || web2py.isUndefined(url)) {
|
|
/* form has no action. Use component url. */
|
|
url = action;
|
|
}
|
|
|
|
form.submit(function (e) {
|
|
web2py.disableElement(form.find(web2py.formInputClickSelector));
|
|
web2py.hide_flash();
|
|
|
|
var formData;
|
|
if (FORMDATA_IS_SUPPORTED) {
|
|
formData = new FormData(form[0]); // Allows file uploads.
|
|
} else {
|
|
formData = form.serialize(); // Fallback for older browsers.
|
|
}
|
|
web2py.ajax_page('post', url, formData, target, form);
|
|
|
|
e.preventDefault();
|
|
});
|
|
form.on('click', web2py.formInputClickSelector, function (e) {
|
|
e.preventDefault();
|
|
var input_name = $(this).attr('name');
|
|
if (!web2py.isUndefined(input_name)) {
|
|
$('<input type="hidden" />').attr('name', input_name)
|
|
.attr('value', $(this).val()).appendTo(form);
|
|
}
|
|
form.trigger('submit');
|
|
});
|
|
});
|
|
},
|
|
ajax_page: function (method, action, data, target, element) {
|
|
/* element is a new parameter, but should be put be put in front */
|
|
if (web2py.isUndefined(element)) element = $(document);
|
|
/* if target is not there, fill it with something that there isn't in the page*/
|
|
if (web2py.isUndefined(target) || target === '') target = 'w2p_none';
|
|
|
|
/* processData and contentType must be set to false when passing a FormData
|
|
object to jQuery.ajax. */
|
|
var isFormData = Object.prototype.toString.call(data) === '[object FormData]';
|
|
var contentType = isFormData ? false : 'application/x-www-form-urlencoded; charset=UTF-8';
|
|
if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */
|
|
$.ajax({
|
|
'type': method,
|
|
'url': action,
|
|
'data': data,
|
|
'processData': !isFormData,
|
|
'contentType': contentType,
|
|
'beforeSend': function (xhr, settings) {
|
|
xhr.setRequestHeader('web2py-component-location', document.location);
|
|
xhr.setRequestHeader('web2py-component-element', target);
|
|
web2py.fire(element, 'w2p:componentBegin', [xhr, settings], target);
|
|
return web2py.fire(element, 'ajax:beforeSend', [xhr, settings], target); //test a usecase, should stop here if returns false
|
|
},
|
|
'success': function (data, status, xhr) {
|
|
/*bummer for form submissions....the element is not there after complete
|
|
*because it gets replaced by the new response....
|
|
*/
|
|
web2py.fire(element, 'ajax:success', [data, status, xhr], target);
|
|
},
|
|
'error': function (xhr, status, error) {
|
|
/*bummer for form submissions....in addition to the element being not there after
|
|
*complete because it gets replaced by the new response, standard form
|
|
*handling just returns the same status code for good and bad
|
|
*form submissions (i.e. that triggered a validator error)
|
|
*/
|
|
web2py.fire(element, 'ajax:error', [xhr, status, error], target);
|
|
},
|
|
'complete': function (xhr, status) {
|
|
web2py.fire(element, 'ajax:complete', [xhr, status], target);
|
|
web2py.updatePage(xhr, target); /* Parse and load the html received */
|
|
web2py.trap_form(action, target);
|
|
web2py.ajax_init('#' + target);
|
|
web2py.after_ajax(xhr);
|
|
web2py.fire(element, 'w2p:componentComplete', [xhr, status], target); // Let us know the component is finished loading
|
|
}
|
|
});
|
|
}
|
|
},
|
|
component: function (action, target, timeout, times, el) {
|
|
/* element is a new parameter, but should be put in front */
|
|
$(function () {
|
|
var jelement = $('#' + target);
|
|
var element = jelement.get(0);
|
|
var statement = 'jQuery("#' + target + '").get(0).reload();';
|
|
element.reload = function () {
|
|
/* Continue if times is Infinity or
|
|
* the times limit is not reached
|
|
*/
|
|
if (element.reload_check()) {
|
|
web2py.ajax_page('get', action, null, target, el);
|
|
}
|
|
};
|
|
/* Method to check timing limit */
|
|
element.reload_check = function () {
|
|
if (jelement.hasClass('w2p_component_stop')) {
|
|
clearInterval(this.timing);
|
|
return false;
|
|
}
|
|
if (this.reload_counter == Infinity) {
|
|
return true;
|
|
} else {
|
|
if (!isNaN(this.reload_counter)) {
|
|
this.reload_counter -= 1;
|
|
if (this.reload_counter < 0) {
|
|
if (!this.run_once) {
|
|
clearInterval(this.timing);
|
|
return false;
|
|
}
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
if (!isNaN(timeout)) {
|
|
element.timeout = timeout;
|
|
element.reload_counter = times;
|
|
if (times > 1) {
|
|
/* Multiple or infinite reload
|
|
* Run first iteration
|
|
*/
|
|
web2py.ajax_page('get', action, null, target, el);
|
|
element.run_once = false;
|
|
element.timing = setInterval(statement, timeout);
|
|
element.reload_counter -= 1;
|
|
} else if (times == 1) {
|
|
/* Run once with timeout */
|
|
element.run_once = true;
|
|
element.setTimeout = setTimeout;
|
|
element.timing = setTimeout(statement, timeout);
|
|
}
|
|
} else {
|
|
/* run once (no timeout specified) */
|
|
element.reload_counter = Infinity;
|
|
web2py.ajax_page('get', action, null, target, el);
|
|
}
|
|
});
|
|
},
|
|
updatePage: function (xhr, target) {
|
|
var t = $('#' + target);
|
|
var html = $.parseHTML(xhr.responseText, document, true);
|
|
var title_elements = $(html).filter('title').add($(html).find('title'));
|
|
var title = title_elements.last().text();
|
|
if (title) {
|
|
title_elements.remove(); /* Remove any title elements from the response */
|
|
document.title = $.trim(title); /* Set the new document title */
|
|
}
|
|
var content = xhr.getResponseHeader('web2py-component-content');
|
|
if (content == 'prepend') t.prepend(xhr.responseText);
|
|
else if (content == 'append') t.append(xhr.responseText);
|
|
else if (content != 'hide') t.html(html);
|
|
},
|
|
calc_entropy: function (mystring) {
|
|
/* calculate a simple entropy for a given string */
|
|
var csets = new Array(
|
|
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
|
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
|
|
'0123456789abcdefghijklmnopqrstuvwxyz');
|
|
var score = 0,
|
|
other = {},
|
|
seen = {},
|
|
lastset = null,
|
|
mystringlist = mystring.split('');
|
|
for (var i = 0; i < mystringlist.length; i++) { /* classify this character */
|
|
var c = mystringlist[i],
|
|
inset = 5;
|
|
for (var j = 0; j < csets.length; j++)
|
|
if (csets[j].indexOf(c) != -1) {
|
|
inset = j;
|
|
break;
|
|
}
|
|
/*calculate effect of character on alphabet size */
|
|
if (!(inset in seen)) {
|
|
seen[inset] = 1;
|
|
score += csets[inset].length;
|
|
} else if (!(c in other)) {
|
|
score += 1;
|
|
other[c] = 1;
|
|
}
|
|
if (inset != lastset) {
|
|
score += 1;
|
|
lastset = inset;
|
|
}
|
|
}
|
|
var entropy = mystring.length * Math.log(score) / 0.6931471805599453;
|
|
return Math.round(entropy * 100) / 100;
|
|
},
|
|
validate_entropy: function (myfield, req_entropy) {
|
|
if (!web2py.isUndefined(myfield.data('w2p_entropy'))) req_entropy = myfield.data('w2p_entropy');
|
|
var validator = function () {
|
|
var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy;
|
|
var r = 0,
|
|
g = 0,
|
|
b = 0,
|
|
rs = function (x) {
|
|
return Math.round(x * 15).toString(16);
|
|
};
|
|
if (v <= 0.5) {
|
|
r = 1.0;
|
|
g = 2.0 * v;
|
|
} else {
|
|
r = (1.0 - 2.0 * (Math.max(v, 0) - 0.5));
|
|
g = 1.0;
|
|
}
|
|
var color = '#' + rs(r) + rs(g) + rs(b);
|
|
myfield.css('background-color', color);
|
|
var entropy_callback = myfield.data('entropy_callback');
|
|
if (entropy_callback) entropy_callback(v);
|
|
};
|
|
if (!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator)
|
|
.addClass('entropy_check');
|
|
},
|
|
web2py_websocket: function (url, onmessage, onopen, onclose) {
|
|
if ('WebSocket' in window) {
|
|
var ws = new WebSocket(url);
|
|
ws.onopen = onopen ? onopen : (function () {});
|
|
ws.onmessage = onmessage;
|
|
ws.onclose = onclose ? onclose : (function () {});
|
|
return true; /* supported */
|
|
} else return false; /* not supported */
|
|
},
|
|
/* new from here */
|
|
/* Form input elements bound by web2py.js */
|
|
formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])',
|
|
/* Form input elements disabled during form submission */
|
|
disableSelector: 'input, button, textarea, select',
|
|
/* Form input elements re-enabled after form submission */
|
|
enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled',
|
|
/* Triggers an event on an element and returns false if the event result is false */
|
|
fire: function (obj, type, data, target) {
|
|
var event = $.Event(type, {
|
|
'containerTarget': $('#' + target)[0]
|
|
});
|
|
obj.trigger(event, data);
|
|
return event.result !== false;
|
|
},
|
|
/* Helper function, needed to provide consistent behavior in IE */
|
|
stopEverything: function (e) {
|
|
$(e.target).trigger('w2p:everythingStopped');
|
|
e.stopImmediatePropagation();
|
|
return false;
|
|
},
|
|
confirm: function (message) {
|
|
return confirm(message);
|
|
},
|
|
/* replace element's html with the 'data-disable-with' after storing original html
|
|
* and prevent clicking on it */
|
|
disableElement: function (el) {
|
|
if (!web2py.isUndefined(el.data('w2p_disable'))) {
|
|
return false;
|
|
}
|
|
el.addClass('disabled');
|
|
var method = el.is('input') ? 'val' : 'html';
|
|
//method = el.attr('name') ? 'html' : 'val';
|
|
var disable_with_message = (!web2py.isUndefined(w2p_ajax_disable_with_message)) ?
|
|
w2p_ajax_disable_with_message : 'Working...';
|
|
/*store enabled state if not already disabled */
|
|
if (web2py.isUndefined(el.data('w2p_enable_with'))) {
|
|
el.data('w2p_enable_with', el[method]());
|
|
}
|
|
/*if you don't want to see "working..." on buttons, replace the following
|
|
* two lines with this one
|
|
* el.data('w2p_disable_with', el[method]());
|
|
*/
|
|
if ((el.data('w2p_disable_with') == 'default') || (web2py.isUndefined(el.data(
|
|
'w2p_disable_with')))) {
|
|
el.data('w2p_disable_with', disable_with_message);
|
|
}
|
|
|
|
/* set to disabled state*/
|
|
el[method](el.data('w2p_disable_with'));
|
|
|
|
el.bind('click.w2pDisable', function (e) { /* prevent further clicking*/
|
|
return web2py.stopEverything(e);
|
|
});
|
|
},
|
|
|
|
/* restore element to its original state which was disabled by 'disableElement' above*/
|
|
enableElement: function (el) {
|
|
var method = el.is('input') ? 'val' : 'html';
|
|
if (!web2py.isUndefined(el.data('w2p_enable_with'))) {
|
|
/* set to old enabled state */
|
|
el[method](el.data('w2p_enable_with'));
|
|
el.removeData('w2p_enable_with');
|
|
}
|
|
el.removeClass('disabled');
|
|
el.unbind('click.w2pDisable');
|
|
},
|
|
/*convenience wrapper, internal use only */
|
|
simple_component: function (action, target, element) {
|
|
web2py.component(action, target, 0, 1, element);
|
|
},
|
|
/*helper for flash messages*/
|
|
flash: function (message, status) {
|
|
var flash = $('.w2p_flash');
|
|
web2py.hide_flash();
|
|
flash.html(message).addClass(status);
|
|
if (flash.html()) flash.append('<span id="closeflash"> × </span>').slideDown();
|
|
},
|
|
hide_flash: function () {
|
|
$('.w2p_flash').fadeOut(0).html('');
|
|
},
|
|
show_if_handler: function (target) {
|
|
var triggers = {};
|
|
var show_if = function () {
|
|
var t = $(this);
|
|
var id = t.attr('id');
|
|
t.attr('value', t.val());
|
|
for (var k = 0; k < triggers[id].length; k++) {
|
|
var dep = $('#' + triggers[id][k], target);
|
|
var tr = $('#' + triggers[id][k] + '__row', target);
|
|
if (t.is(dep.attr('data-show-if'))) tr.slideDown();
|
|
else tr.hide();
|
|
}
|
|
};
|
|
$('[data-show-trigger]', target).each(function () {
|
|
var name = $(this).attr('data-show-trigger');
|
|
// The field exists only when creating/editing a row
|
|
if ($('#' + name).length) {
|
|
if (!triggers[name]) triggers[name] = [];
|
|
triggers[name].push($(this).attr('id'));
|
|
}
|
|
});
|
|
for (var name in triggers) {
|
|
$('#' + name, target).change(show_if).keyup(show_if);
|
|
show_if.call($('#' + name, target));
|
|
}
|
|
},
|
|
component_handler: function (target) {
|
|
$('div[data-w2p_remote]', target).each(function () {
|
|
var remote, times, timeout, target;
|
|
var el = $(this);
|
|
remote = el.data('w2p_remote');
|
|
times = el.data('w2p_times');
|
|
timeout = el.data('w2p_timeout');
|
|
target = el.attr('id');
|
|
web2py.component(remote, target, timeout, times, $(this));
|
|
});
|
|
},
|
|
a_handler: function (el, e) {
|
|
e.preventDefault();
|
|
var method = el.data('w2p_method');
|
|
var action = el.attr('href');
|
|
var target = el.data('w2p_target');
|
|
var confirm_message = el.data('w2p_confirm');
|
|
|
|
var pre_call = el.data('w2p_pre_call');
|
|
if (!web2py.isUndefined(pre_call)) {
|
|
eval(pre_call);
|
|
}
|
|
if (confirm_message) {
|
|
if (confirm_message == 'default') {
|
|
confirm_message = !web2py.isUndefined(w2p_ajax_confirm_message) ?
|
|
w2p_ajax_confirm_message : 'Are you sure you want to delete this object?';
|
|
}
|
|
if (!web2py.confirm(confirm_message)) {
|
|
web2py.stopEverything(e);
|
|
return;
|
|
}
|
|
}
|
|
if (web2py.isUndefined(target)) {
|
|
if (method == 'GET') {
|
|
web2py.ajax_page('get', action, [], '', el);
|
|
} else if (method == 'POST') {
|
|
web2py.ajax_page('post', action, [], '', el);
|
|
}
|
|
} else {
|
|
if (method == 'GET') {
|
|
web2py.ajax_page('get', action, [], target, el);
|
|
} else if (method == 'POST') {
|
|
web2py.ajax_page('post', action, [], target, el);
|
|
}
|
|
}
|
|
},
|
|
a_handlers: function () {
|
|
var el = $(document);
|
|
el.on('click', 'a[data-w2p_method]', function (e) {
|
|
web2py.a_handler($(this), e);
|
|
});
|
|
/* removal of element should happen only on success */
|
|
el.on('ajax:success', 'a[data-w2p_method][data-w2p_remove]', function () {
|
|
var el = $(this);
|
|
var toremove = el.data('w2p_remove');
|
|
if (!web2py.isUndefined(toremove)) {
|
|
toremove = el.closest(toremove);
|
|
if (!toremove.length) {
|
|
/*this enables removal of whatever selector if a closest is not found */
|
|
toremove = $(toremove);
|
|
}
|
|
toremove.remove();
|
|
}
|
|
});
|
|
el.on('ajax:beforeSend', 'a[data-w2p_method][data-w2p_disable_with]', function () {
|
|
web2py.disableElement($(this));
|
|
});
|
|
/*re-enable click on completion*/
|
|
el.on('ajax:complete', 'a[data-w2p_method][data-w2p_disable_with]', function () {
|
|
web2py.enableElement($(this));
|
|
});
|
|
},
|
|
/* Disables form elements:
|
|
- Does not disable elements with 'data-w2p_disable' attribute
|
|
- Caches element value in 'w2p_enable_with' data store
|
|
- Replaces element text with value of 'data-w2p_disable_with' attribute
|
|
- Sets disabled property to true
|
|
*/
|
|
disableFormElements: function (form) {
|
|
form.find(web2py.disableSelector).each(function () {
|
|
var element = $(this),
|
|
method = element.is('button') ? 'html' : 'val';
|
|
var disable_with = element.data('w2p_disable_with');
|
|
var disable = element.data('w2p_disable');
|
|
if (!web2py.isUndefined(disable)) {
|
|
return false;
|
|
}
|
|
if (!element.is(':file')) { // Altering file input values is not allowed.
|
|
if (web2py.isUndefined(disable_with)) {
|
|
element.data('w2p_disable_with', element[method]());
|
|
}
|
|
if (web2py.isUndefined(element.data('w2p_enable_with'))) {
|
|
element.data('w2p_enable_with', element[method]());
|
|
}
|
|
element[method](element.data('w2p_disable_with'));
|
|
}
|
|
element.prop('disabled', true);
|
|
});
|
|
},
|
|
|
|
/* Re-enables disabled form elements:
|
|
- Replaces element text with cached value from 'w2p_enable_with' data store (created in `disableFormElements`)
|
|
- Sets disabled property to false
|
|
*/
|
|
enableFormElements: function (form) {
|
|
form.find(web2py.enableSelector).each(function () {
|
|
var element = $(this),
|
|
method = element.is('button') ? 'html' : 'val';
|
|
if (element.data('w2p_enable_with')) {
|
|
element[method](element.data('w2p_enable_with'));
|
|
element.removeData('w2p_enable_with');
|
|
}
|
|
element.prop('disabled', false);
|
|
});
|
|
},
|
|
form_handlers: function () {
|
|
var el = $(document);
|
|
el.on('ajax:beforeSend', 'form[data-w2p_target]', function () {
|
|
web2py.disableFormElements($(this));
|
|
});
|
|
el.on('ajax:complete', 'form[data-w2p_target]', function () {
|
|
web2py.enableFormElements($(this));
|
|
});
|
|
},
|
|
/* Invalidate and force reload of a web2py component
|
|
*/
|
|
invalidate: function (target) {
|
|
$('div[data-w2p_remote]', target).each(function () {
|
|
var el = $('#' + $(this).attr('id')).get(0);
|
|
if (!web2py.isUndefined(el.timing)) { // Block triggering regular routines
|
|
clearInterval(el.timing);
|
|
}
|
|
});
|
|
$.web2py.component_handler(target);
|
|
},
|
|
main_hook: function () {
|
|
var flash = $('.w2p_flash');
|
|
flash.hide();
|
|
if (flash.html()) web2py.flash(flash.html());
|
|
web2py.ajax_init(document);
|
|
web2py.event_handlers();
|
|
web2py.a_handlers();
|
|
web2py.form_handlers();
|
|
}
|
|
};
|
|
/*end of functions */
|
|
/*main hook*/
|
|
$(function () {
|
|
web2py.main_hook();
|
|
});
|
|
|
|
})(jQuery);
|
|
|
|
/* compatibility code - start */
|
|
ajax = jQuery.web2py.ajax;
|
|
web2py_component = jQuery.web2py.component;
|
|
web2py_websocket = jQuery.web2py.web2py_websocket;
|
|
web2py_ajax_page = jQuery.web2py.ajax_page;
|
|
/*needed for IS_STRONG(entropy)*/
|
|
web2py_validate_entropy = jQuery.web2py.validate_entropy;
|
|
/*needed for crud.search and SQLFORM.grid's search*/
|
|
web2py_ajax_fields = jQuery.web2py.ajax_fields;
|
|
/*used for LOAD(ajax=False)*/
|
|
web2py_trap_form = jQuery.web2py.trap_form;
|
|
|
|
/*undocumented - rare*/
|
|
popup = jQuery.web2py.popup;
|
|
collapse = jQuery.web2py.collapse;
|
|
fade = jQuery.web2py.fade;
|
|
|
|
/* internals - shouldn't be needed
|
|
web2py_ajax_init = jQuery.web2py.ajax_init;
|
|
web2py_event_handlers = jQuery.web2py.event_handlers;
|
|
web2py_trap_link = jQuery.web2py.trap_link;
|
|
web2py_calc_entropy = jQuery.web2py.calc_entropy;
|
|
*/
|
|
/* compatibility code - end*/ |