Merge ssh://github.com/web2py/web2py

This commit is contained in:
Michele Comitini committed 2013-08-30 18:00:59 +02:00
commit 013d9090f4
9 files changed
+510 -375

No files matched your search

+1 -1
View File
@@ -1 +1 @@
Version 2.6.0-development+timestamp.2013.08.28.09.56.28 Version 2.6.0-development+timestamp.2013.08.29.21.31.47
+103 -97
View File
@@ -20,18 +20,22 @@
$.web2py = web2py = { $.web2py = web2py = {
popup: function (url) { popup: function (url) {
/* popup a window */
newwindow = window.open(url, 'name', 'height=400,width=600'); newwindow = window.open(url, 'name', 'height=400,width=600');
if(window.focus) newwindow.focus(); if(window.focus) newwindow.focus();
return false; return false;
}, },
collapse: function (id) { collapse: function (id) {
/* toggle an element */
$('#' + id).slideToggle(); $('#' + id).slideToggle();
}, },
fade: function (id, value) { fade: function (id, value) {
/*fade something*/
if(value > 0) $('#' + id).hide().fadeIn('slow'); if(value > 0) $('#' + id).hide().fadeIn('slow');
else $('#' + id).show().fadeOut('slow'); else $('#' + id).show().fadeOut('slow');
}, },
ajax: function (u, s, t) { ajax: function (u, s, t) {
/*simple ajax function*/
query = ''; query = '';
if(typeof s == "string") { if(typeof s == "string") {
d = $(s).serialize(); d = $(s).serialize();
@@ -65,14 +69,15 @@
}); });
}, },
ajax_fields: function (target) { ajax_fields: function (target) {
/*this attaches something to a newly loaded fragment/page /*
*this attaches something to a newly loaded fragment/page
* Ideally all events should be bound to the document, so we can avoid calling * 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 * this over and over... all will be bound to the document
*/ */
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
$("input.date", target).each(function () { $("input.date", target).each(function () {
$(this).attr('autocomplete','off'); $(this).attr('autocomplete', 'off');
Calendar.setup({ Calendar.setup({
inputField: this, inputField: this,
ifFormat: date_format, ifFormat: date_format,
@@ -80,7 +85,7 @@
}); });
}); });
$("input.datetime", target).each(function () { $("input.datetime", target).each(function () {
$(this).attr('autocomplete','off'); $(this).attr('autocomplete', 'off');
Calendar.setup({ Calendar.setup({
inputField: this, inputField: this,
ifFormat: datetime_format, ifFormat: datetime_format,
@@ -89,25 +94,25 @@
}); });
}); });
$("input.time", target).each(function () { $("input.time", target).each(function () {
$(this).timeEntry().attr('autocomplete','off'); $(this).timeEntry().attr('autocomplete', 'off');
}); });
/*adds btn class to buttons*/ /*adds btn class to buttons*/
$('button', target).addClass('btn'); $('button', target).addClass('btn');
$('form input[type="submit"], form input[type="button"]', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn');
/*no more inline javascript for PasswordWidget*/ /* javascript for PasswordWidget*/
$('input[type=password][data-w2p_entropy]', target).each(function () { $('input[type=password][data-w2p_entropy]', target).each(function () {
web2py.validate_entropy($(this)); web2py.validate_entropy($(this));
}); });
/*no more inline javascript for ListWidget*/ /* javascript for ListWidget*/
$('ul.w2p_list', target).each(function () { $('ul.w2p_list', target).each(function () {
function pe(ul, e) { function pe(ul, e) {
var new_line = ml(ul); var new_line = ml(ul);
rel(ul); rel(ul);
if($(e.target).parent().is(':visible')) { if($(e.target).parent().is(':visible')) {
//make sure we didn't delete the element before we insert after /* make sure we didn't delete the element before we insert after */
new_line.insertAfter($(e.target).parent()); new_line.insertAfter($(e.target).parent());
} else { } else {
//the line we clicked on was deleted, just add to end of list /* the line we clicked on was deleted, just add to end of list */
new_line.appendTo(ul); new_line.appendTo(ul);
} }
new_line.find(":text").focus(); new_line.find(":text").focus();
@@ -116,18 +121,20 @@
function rl(ul, e) { function rl(ul, e) {
if($(ul).children().length > 1) { if($(ul).children().length > 1) {
//only remove if we have more than 1 item so the list is never empty /* only remove if we have more than 1 item so the list is never empty */
$(e.target).parent().remove(); $(e.target).parent().remove();
} }
} }
function ml(ul) { function ml(ul) {
/* clone the first field */
var line = $(ul).find("li:first").clone(true); var line = $(ul).find("li:first").clone(true);
line.find(':text').val(''); line.find(':text').val('');
return line; return line;
} }
function rel(ul) { function rel(ul) {
/* keep only as many as needed*/
$(ul).find("li").each(function () { $(ul).find("li").each(function () {
var trimmed = $.trim($(this.firstChild).val()); var trimmed = $.trim($(this.firstChild).val());
if(trimmed == '') $(this).remove(); if(trimmed == '') $(this).remove();
@@ -147,19 +154,32 @@
}); });
}, },
ajax_init: function (target) { ajax_init: function (target) {
/*called whenever a fragment gets loaded */
$('.hidden', target).hide(); $('.hidden', target).hide();
web2py.manage_errors(target); web2py.manage_errors(target);
web2py.ajax_fields(target); web2py.ajax_fields(target);
web2py.show_if_handler(target); web2py.show_if_handler(target);
web2py.component_handler(target); web2py.component_handler(target);
}, },
//manage errors in forms /* manage errors in forms */
manage_errors: function (target) { manage_errors: function (target) {
$('.error', target).hide().slideDown('slow'); $('.error', target).hide().slideDown('slow');
//jQuery('.error', target).hide().fadeIn('slow'); /* jQuery('.error', target).hide().fadeIn('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 () { event_handlers: function () {
/* This is called once for page /*
* This is called once for page
* Ideally it should bound all the things that are needed * Ideally it should bound all the things that are needed
*/ */
var doc = $(document); var doc = $(document);
@@ -167,9 +187,12 @@
var t = $(this); var t = $(this);
if(t.css('top') == '0px') t.slideUp('slow'); if(t.css('top') == '0px') t.slideUp('slow');
else t.fadeOut(); else t.fadeOut();
//if I want to display a clickable something /* if I want to display a clickable something
//inside flash, I should not be prevented to follow it * inside flash, I should not be prevented to follow it
//e.preventDefault(); *
* e.preventDefault();
*/
}); });
doc.on('keyup', 'input.integer', function () { doc.on('keyup', 'input.integer', function () {
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
@@ -185,24 +208,21 @@
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
var redirect = xhr.getResponseHeader('web2py-redirect-location'); var redirect = xhr.getResponseHeader('web2py-redirect-location');
var command = xhr.getResponseHeader('web2py-component-command');
var flash = xhr.getResponseHeader('web2py-component-flash');
if(redirect !== null) { if(redirect !== null) {
window.location = redirect; window.location = redirect;
}; };
if(command !== null) { /* run this here only if this Ajax request is NOT for a web2py component. */
eval(decodeURIComponent(command)); if(xhr.getResponseHeader('web2py-component-content') == null) {
} web2py.after_ajax(xhr);
if(flash) { };
web2py.flash(decodeURIComponent(flash))
}
}); });
doc.ajaxError(function (e, xhr, settings, exception) { doc.ajaxError(function (e, xhr, settings, exception) {
//personally I don't like it. /*personally I don't like it.
//if there's an error it it flashed and can be removed *if there's an error it it flashed and can be removed
//as any other message *as any other message
//doc.off('click', '.flash') *doc.off('click', '.flash')
*/
switch(xhr.status) { switch(xhr.status) {
case 500: case 500:
web2py.flash(ajax_error_500); web2py.flash(ajax_error_500);
@@ -211,12 +231,14 @@
}, },
trap_form: function (action, target) { trap_form: function (action, target) {
/* traps any LOADed form */
var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working...";
$('#' + target + ' form').each(function (i) { $('#' + target + ' form').each(function (i) {
var form = $(this); var form = $(this);
form.attr('data-w2p_target', target); form.attr('data-w2p_target', target);
if(!form.hasClass('no_trap')) { if(!form.hasClass('no_trap')) {
//should be there by default ? /* should be there by default */
form.find('input[type=submit]').attr('data-w2p_disable_with', 'Working...'); form.find('input[type=submit]').attr('data-w2p_disable_with', disable_with_message);
form.submit(function (e) { form.submit(function (e) {
web2py.hide_flash(); web2py.hide_flash();
web2py.ajax_page('post', action, form.serialize(), target, form); web2py.ajax_page('post', action, form.serialize(), target, form);
@@ -236,31 +258,30 @@
}); });
}, },
ajax_page: function (method, action, data, target, element) { ajax_page: function (method, action, data, target, element) {
//element is a new parameter, but should be put be put in front /* element is a new parameter, but should be put be put in front */
if(element == undefined) element = $(document); if(element == undefined) element = $(document);
if(web2py.fire(element, 'ajax:before')) { //test a usecase, should stop here if returns false if(web2py.fire(element, 'ajax:before')) { /*test a usecase, should stop here if returns false */
$.ajax({ $.ajax({
'type': method, 'type': method,
'url': action, 'url': action,
'data': data, 'data': data,
'beforeSend': function (xhr, settings) { 'beforeSend': function (xhr, settings) {
//added
xhr.setRequestHeader('web2py-component-location', document.location); xhr.setRequestHeader('web2py-component-location', document.location);
xhr.setRequestHeader('web2py-component-element', target); xhr.setRequestHeader('web2py-component-element', target);
return web2py.fire(element, 'ajax:beforeSend', [xhr, settings]); //test a usecase, should stop here if returns false return web2py.fire(element, 'ajax:beforeSend', [xhr, settings]); //test a usecase, should stop here if returns false
}, },
//added
'success': function (data, status, xhr) { 'success': function (data, status, xhr) {
//bummer for form submissions....the element is not there after complete /*bummer for form submissions....the element is not there after complete
//because it gets replaced by the new response.... *because it gets replaced by the new response....
*/
element.trigger('ajax:success', [data, status, xhr]); element.trigger('ajax:success', [data, status, xhr]);
}, },
//added
'error': function (xhr, status, error) { 'error': function (xhr, status, error) {
//bummer for form submissions....in addition to the element being not there after /*bummer for form submissions....in addition to the element being not there after
//complete because it gets replaced by the new response, standard form *complete because it gets replaced by the new response, standard form
//handling just returns the same status code for good and bad *handling just returns the same status code for good and bad
//form submissions (i.e. that triggered a validator error) *form submissions (i.e. that triggered a validator error)
*/
element.trigger('ajax:error', [xhr, status, error]); element.trigger('ajax:error', [xhr, status, error]);
}, },
'complete': function (xhr, status) { 'complete': function (xhr, status) {
@@ -274,24 +295,26 @@
web2py.trap_form(action, target); web2py.trap_form(action, target);
web2py.trap_link(target); web2py.trap_link(target);
web2py.ajax_init('#' + target); web2py.ajax_init('#' + target);
web2py.after_ajax(xhr);
} }
}); });
} }
}, },
component: function (action, target, timeout, times, el) { component: function (action, target, timeout, times, el) {
//element is a new parameter, but should be put in front /* element is a new parameter, but should be put in front */
$(function () { $(function () {
var jelement = $("#" + target); var jelement = $("#" + target);
var element = jelement.get(0); var element = jelement.get(0);
var statement = "jQuery('#" + target + "').get(0).reload();"; var statement = "jQuery('#" + target + "').get(0).reload();";
element.reload = function () { element.reload = function () {
// Continue if times is Infinity or /* Continue if times is Infinity or
// the times limit is not reached * the times limit is not reached
*/
if(element.reload_check()) { if(element.reload_check()) {
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
} }
}; // reload };
// Method to check timing limit /* Method to check timing limit */
element.reload_check = function () { element.reload_check = function () {
if(jelement.hasClass('w2p_component_stop')) { if(jelement.hasClass('w2p_component_stop')) {
clearInterval(this.timing); clearInterval(this.timing);
@@ -313,32 +336,33 @@
} }
} }
return false; return false;
}; // reload check };
if(!isNaN(timeout)) { if(!isNaN(timeout)) {
element.timeout = timeout; element.timeout = timeout;
element.reload_counter = times; element.reload_counter = times;
if(times > 1) { if(times > 1) {
// Multiple or infinite reload /* Multiple or infinite reload
// Run first iteration * Run first iteration
*/
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
element.run_once = false; element.run_once = false;
element.timing = setInterval(statement, timeout); element.timing = setInterval(statement, timeout);
element.reload_counter -= 1; element.reload_counter -= 1;
} else if(times == 1) { } else if(times == 1) {
// Run once with timeout /* Run once with timeout */
element.run_once = true; element.run_once = true;
element.setTimeout = setTimeout; element.setTimeout = setTimeout;
element.timing = setTimeout(statement, timeout); element.timing = setTimeout(statement, timeout);
} }
} else { } else {
// run once (no timeout specified) /* run once (no timeout specified) */
element.reload_counter = Infinity; element.reload_counter = Infinity;
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
} }
}); });
}, },
calc_entropy: function (mystring) { calc_entropy: function (mystring) {
//calculate a simple entropy for a given string /* calculate a simple entropy for a given string */
var csets = new Array( var csets = new Array(
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/', '0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
@@ -346,7 +370,7 @@
var score = 0, var score = 0,
other = {}, seen = {}, lastset = null, other = {}, seen = {}, lastset = null,
mystringlist = mystring.split(''); mystringlist = mystring.split('');
for(var i = 0; i < mystringlist.length; i++) { // classify this character for(var i = 0; i < mystringlist.length; i++) { /* classify this character */
var c = mystringlist[i], var c = mystringlist[i],
inset = 5; inset = 5;
for(var j = 0; j < csets.length; j++) for(var j = 0; j < csets.length; j++)
@@ -354,7 +378,7 @@
inset = j; inset = j;
break; break;
} }
//calculate effect of character on alphabet size /*calculate effect of character on alphabet size */
if(!(inset in seen)) { if(!(inset in seen)) {
seen[inset] = 1; seen[inset] = 1;
score += csets[inset].length; score += csets[inset].length;
@@ -371,7 +395,6 @@
return Math.round(entropy * 100) / 100 return Math.round(entropy * 100) / 100
}, },
validate_entropy: function (myfield, req_entropy) { validate_entropy: function (myfield, req_entropy) {
//added
if(myfield.data('w2p_entropy') != undefined) req_entropy = myfield.data('w2p_entropy'); if(myfield.data('w2p_entropy') != undefined) req_entropy = myfield.data('w2p_entropy');
var validator = function () { var validator = function () {
var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy; var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy;
@@ -401,23 +424,23 @@
ws.onopen = onopen ? onopen : (function () {}); ws.onopen = onopen ? onopen : (function () {});
ws.onmessage = onmessage; ws.onmessage = onmessage;
ws.onclose = onclose ? onclose : (function () {}); ws.onclose = onclose ? onclose : (function () {});
return true; // supported return true; /* supported */
} else return false; // not supported } else return false; /* not supported */
}, },
/* new from here */ /* new from here */
// Form input elements bound by jquery-ujs /* Form input elements bound by jquery-uj */
formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])', formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])',
// Form input elements disabled during form submission /* Form input elements disabled during form submission */
disableSelector: 'input, button, textarea, select', disableSelector: 'input, button, textarea, select',
// Form input elements re-enabled after form submission /* Form input elements re-enabled after form submission */
enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled', enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled',
// Triggers an event on an element and returns false if the event result is false /* Triggers an event on an element and returns false if the event result is false */
fire: function (obj, name, data) { fire: function (obj, name, data) {
var event = $.Event(name); var event = $.Event(name);
obj.trigger(event, data); obj.trigger(event, data);
return event.result !== false; return event.result !== false;
}, },
// Helper function, needed to provide consistent behavior in IE /* Helper function, needed to provide consistent behavior in IE */
stopEverything: function (e) { stopEverything: function (e) {
$(e.target).trigger('w2p:everythingStopped'); $(e.target).trigger('w2p:everythingStopped');
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@@ -426,41 +449,42 @@
confirm: function (message) { confirm: function (message) {
return confirm(message); return confirm(message);
}, },
// replace element's html with the 'data-disable-with' after storing original html /* replace element's html with the 'data-disable-with' after storing original html
// and prevent clicking on it * and prevent clicking on it */
disableElement: function (el) { disableElement: function (el) {
el.addClass('disabled'); el.addClass('disabled');
var method = el.prop('type') == 'submit' ? 'val' : 'html'; var method = el.prop('type') == 'submit' ? 'val' : 'html';
// store enabled state var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working...";
/*store enabled state*/
el.data('w2p:enable-with', el[method]()); el.data('w2p:enable-with', el[method]());
/* little addition by default*/ /* little addition by default*/
if((el.data('w2p_disable_with') == 'default') || (el.data('w2p_disable_with') === undefined)) { if((el.data('w2p_disable_with') == 'default') || (el.data('w2p_disable_with') === undefined)) {
el.data('w2p_disable_with', 'Working...'); el.data('w2p_disable_with', disable_with_message);
} }
// set to disabled state /* set to disabled state*/
el[method](el.data('w2p_disable_with')); el[method](el.data('w2p_disable_with'));
el.bind('click.w2pDisable', function (e) { // prevent further clicking el.bind('click.w2pDisable', function (e) { /* prevent further clicking*/
return web2py.stopEverything(e); return web2py.stopEverything(e);
}); });
}, },
// restore element to its original state which was disabled by 'disableElement' above /* restore element to its original state which was disabled by 'disableElement' above*/
enableElement: function (el) { enableElement: function (el) {
var method = el.prop('type') == 'submit' ? 'val' : 'html'; var method = el.prop('type') == 'submit' ? 'val' : 'html';
if(el.data('w2p:enable-with') !== undefined) { if(el.data('w2p:enable-with') !== undefined) {
// set to old enabled state /* set to old enabled state */
el[method](el.data('w2p:enable-with')); el[method](el.data('w2p:enable-with'));
el.removeData('w2p:enable-with'); // clean up cache el.removeData('w2p:enable-with');
} }
el.removeClass('disabled'); el.removeClass('disabled');
el.unbind('click.w2pDisable'); // enable element el.unbind('click.w2pDisable');
}, },
//convenience wrapper, internal use only /*convenience wrapper, internal use only */
simple_component: function (action, target, element) { simple_component: function (action, target, element) {
web2py.component(action, target, 0, 1, element); web2py.component(action, target, 0, 1, element);
}, },
//helper for flash messages /*helper for flash messages*/
flash: function (message, status) { flash: function (message, status) {
var flash = $('.flash'); var flash = $('.flash');
web2py.hide_flash(); web2py.hide_flash();
@@ -525,34 +549,16 @@
if(target == undefined) { if(target == undefined) {
if(method == 'GET') { if(method == 'GET') {
web2py.ajax_page('get', action, [], 'bogus', el); //fixme? web2py.ajax_page('get', action, [], 'bogus', el); //fixme?
//web2py.simple_component(action, el.attr('id'), el); //not working with original
} else if(method == 'POST') { } else if(method == 'POST') {
//should be web2py.ajax(action, [], ''); but it's too simple
web2py.ajax_page('post', action, [], 'bogus', el); //fixme? web2py.ajax_page('post', action, [], 'bogus', el); //fixme?
} }
} else { } else {
if(method == 'GET') { if(method == 'GET') {
web2py.ajax_page('get', action, [], target, el); web2py.ajax_page('get', action, [], target, el);
//web2py.simple_component(action, target, el);
} else if(method == 'POST') { } else if(method == 'POST') {
//should be web2py.ajax(action, [], target); but it's too simple
web2py.ajax_page('post', action, [], target, el); web2py.ajax_page('post', action, [], target, el);
} }
} }
/*this should happen only on ajaxsuccess
* and should block subsequent clicks until ajaxcomplete
* NB: introduce the first incompatibility because normally
* the element would be removed in either case
/* removal code moved to the ajax:success event - START
if(toremove != undefined) {
toremove = el.closest(toremove);
if(!toremove.length) {
//this enables removal of whatever selector if a closest is not found
toremove = jQuery(toremove);
}
toremove.remove();
}
/* removal code moved to the ajax:success event - END */
}, },
a_handlers: function () { a_handlers: function () {
var el = $(document); var el = $(document);
@@ -566,7 +572,7 @@
if(toremove != undefined) { if(toremove != undefined) {
toremove = el.closest(toremove); toremove = el.closest(toremove);
if(!toremove.length) { if(!toremove.length) {
//this enables removal of whatever selector if a closest is not found /*this enables removal of whatever selector if a closest is not found */
toremove = $(toremove); toremove = $(toremove);
} }
toremove.remove(); toremove.remove();
@@ -622,8 +628,8 @@
} }
} }
//end of functions /*end of functions */
//main hook /*main hook*/
$(function () { $(function () {
var flash = $('.flash'); var flash = $('.flash');
flash.hide(); flash.hide();
@@ -641,11 +647,11 @@ ajax = jQuery.web2py.ajax;
web2py_component = jQuery.web2py.component; web2py_component = jQuery.web2py.component;
web2py_websocket = jQuery.web2py.websocket; web2py_websocket = jQuery.web2py.websocket;
web2py_ajax_page = jQuery.web2py.ajax_page; web2py_ajax_page = jQuery.web2py.ajax_page;
//needed for IS_STRONG(entropy) /*needed for IS_STRONG(entropy)*/
web2py_validate_entropy = jQuery.web2py.validate_entropy; web2py_validate_entropy = jQuery.web2py.validate_entropy;
//needed for crud.search and SQLFORM.grid's search /*needed for crud.search and SQLFORM.grid's search*/
web2py_ajax_fields = jQuery.web2py.ajax_fields; web2py_ajax_fields = jQuery.web2py.ajax_fields;
//used for LOAD(ajax=False) /*used for LOAD(ajax=False)*/
web2py_trap_form = jQuery.web2py.trap_form; web2py_trap_form = jQuery.web2py.trap_form;
/*undocumented - rare*/ /*undocumented - rare*/
+103 -97
View File
@@ -20,18 +20,22 @@
$.web2py = web2py = { $.web2py = web2py = {
popup: function (url) { popup: function (url) {
/* popup a window */
newwindow = window.open(url, 'name', 'height=400,width=600'); newwindow = window.open(url, 'name', 'height=400,width=600');
if(window.focus) newwindow.focus(); if(window.focus) newwindow.focus();
return false; return false;
}, },
collapse: function (id) { collapse: function (id) {
/* toggle an element */
$('#' + id).slideToggle(); $('#' + id).slideToggle();
}, },
fade: function (id, value) { fade: function (id, value) {
/*fade something*/
if(value > 0) $('#' + id).hide().fadeIn('slow'); if(value > 0) $('#' + id).hide().fadeIn('slow');
else $('#' + id).show().fadeOut('slow'); else $('#' + id).show().fadeOut('slow');
}, },
ajax: function (u, s, t) { ajax: function (u, s, t) {
/*simple ajax function*/
query = ''; query = '';
if(typeof s == "string") { if(typeof s == "string") {
d = $(s).serialize(); d = $(s).serialize();
@@ -65,14 +69,15 @@
}); });
}, },
ajax_fields: function (target) { ajax_fields: function (target) {
/*this attaches something to a newly loaded fragment/page /*
*this attaches something to a newly loaded fragment/page
* Ideally all events should be bound to the document, so we can avoid calling * 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 * this over and over... all will be bound to the document
*/ */
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
$("input.date", target).each(function () { $("input.date", target).each(function () {
$(this).attr('autocomplete','off'); $(this).attr('autocomplete', 'off');
Calendar.setup({ Calendar.setup({
inputField: this, inputField: this,
ifFormat: date_format, ifFormat: date_format,
@@ -80,7 +85,7 @@
}); });
}); });
$("input.datetime", target).each(function () { $("input.datetime", target).each(function () {
$(this).attr('autocomplete','off'); $(this).attr('autocomplete', 'off');
Calendar.setup({ Calendar.setup({
inputField: this, inputField: this,
ifFormat: datetime_format, ifFormat: datetime_format,
@@ -89,25 +94,25 @@
}); });
}); });
$("input.time", target).each(function () { $("input.time", target).each(function () {
$(this).timeEntry().attr('autocomplete','off'); $(this).timeEntry().attr('autocomplete', 'off');
}); });
/*adds btn class to buttons*/ /*adds btn class to buttons*/
$('button', target).addClass('btn'); $('button', target).addClass('btn');
$('form input[type="submit"], form input[type="button"]', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn');
/*no more inline javascript for PasswordWidget*/ /* javascript for PasswordWidget*/
$('input[type=password][data-w2p_entropy]', target).each(function () { $('input[type=password][data-w2p_entropy]', target).each(function () {
web2py.validate_entropy($(this)); web2py.validate_entropy($(this));
}); });
/*no more inline javascript for ListWidget*/ /* javascript for ListWidget*/
$('ul.w2p_list', target).each(function () { $('ul.w2p_list', target).each(function () {
function pe(ul, e) { function pe(ul, e) {
var new_line = ml(ul); var new_line = ml(ul);
rel(ul); rel(ul);
if($(e.target).parent().is(':visible')) { if($(e.target).parent().is(':visible')) {
//make sure we didn't delete the element before we insert after /* make sure we didn't delete the element before we insert after */
new_line.insertAfter($(e.target).parent()); new_line.insertAfter($(e.target).parent());
} else { } else {
//the line we clicked on was deleted, just add to end of list /* the line we clicked on was deleted, just add to end of list */
new_line.appendTo(ul); new_line.appendTo(ul);
} }
new_line.find(":text").focus(); new_line.find(":text").focus();
@@ -116,18 +121,20 @@
function rl(ul, e) { function rl(ul, e) {
if($(ul).children().length > 1) { if($(ul).children().length > 1) {
//only remove if we have more than 1 item so the list is never empty /* only remove if we have more than 1 item so the list is never empty */
$(e.target).parent().remove(); $(e.target).parent().remove();
} }
} }
function ml(ul) { function ml(ul) {
/* clone the first field */
var line = $(ul).find("li:first").clone(true); var line = $(ul).find("li:first").clone(true);
line.find(':text').val(''); line.find(':text').val('');
return line; return line;
} }
function rel(ul) { function rel(ul) {
/* keep only as many as needed*/
$(ul).find("li").each(function () { $(ul).find("li").each(function () {
var trimmed = $.trim($(this.firstChild).val()); var trimmed = $.trim($(this.firstChild).val());
if(trimmed == '') $(this).remove(); if(trimmed == '') $(this).remove();
@@ -147,19 +154,32 @@
}); });
}, },
ajax_init: function (target) { ajax_init: function (target) {
/*called whenever a fragment gets loaded */
$('.hidden', target).hide(); $('.hidden', target).hide();
web2py.manage_errors(target); web2py.manage_errors(target);
web2py.ajax_fields(target); web2py.ajax_fields(target);
web2py.show_if_handler(target); web2py.show_if_handler(target);
web2py.component_handler(target); web2py.component_handler(target);
}, },
//manage errors in forms /* manage errors in forms */
manage_errors: function (target) { manage_errors: function (target) {
$('.error', target).hide().slideDown('slow'); $('.error', target).hide().slideDown('slow');
//jQuery('.error', target).hide().fadeIn('slow'); /* jQuery('.error', target).hide().fadeIn('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 () { event_handlers: function () {
/* This is called once for page /*
* This is called once for page
* Ideally it should bound all the things that are needed * Ideally it should bound all the things that are needed
*/ */
var doc = $(document); var doc = $(document);
@@ -167,9 +187,12 @@
var t = $(this); var t = $(this);
if(t.css('top') == '0px') t.slideUp('slow'); if(t.css('top') == '0px') t.slideUp('slow');
else t.fadeOut(); else t.fadeOut();
//if I want to display a clickable something /* if I want to display a clickable something
//inside flash, I should not be prevented to follow it * inside flash, I should not be prevented to follow it
//e.preventDefault(); *
* e.preventDefault();
*/
}); });
doc.on('keyup', 'input.integer', function () { doc.on('keyup', 'input.integer', function () {
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
@@ -185,24 +208,21 @@
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
var redirect = xhr.getResponseHeader('web2py-redirect-location'); var redirect = xhr.getResponseHeader('web2py-redirect-location');
var command = xhr.getResponseHeader('web2py-component-command');
var flash = xhr.getResponseHeader('web2py-component-flash');
if(redirect !== null) { if(redirect !== null) {
window.location = redirect; window.location = redirect;
}; };
if(command !== null) { /* run this here only if this Ajax request is NOT for a web2py component. */
eval(decodeURIComponent(command)); if(xhr.getResponseHeader('web2py-component-content') == null) {
} web2py.after_ajax(xhr);
if(flash) { };
web2py.flash(decodeURIComponent(flash))
}
}); });
doc.ajaxError(function (e, xhr, settings, exception) { doc.ajaxError(function (e, xhr, settings, exception) {
//personally I don't like it. /*personally I don't like it.
//if there's an error it it flashed and can be removed *if there's an error it it flashed and can be removed
//as any other message *as any other message
//doc.off('click', '.flash') *doc.off('click', '.flash')
*/
switch(xhr.status) { switch(xhr.status) {
case 500: case 500:
web2py.flash(ajax_error_500); web2py.flash(ajax_error_500);
@@ -211,12 +231,14 @@
}, },
trap_form: function (action, target) { trap_form: function (action, target) {
/* traps any LOADed form */
var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working...";
$('#' + target + ' form').each(function (i) { $('#' + target + ' form').each(function (i) {
var form = $(this); var form = $(this);
form.attr('data-w2p_target', target); form.attr('data-w2p_target', target);
if(!form.hasClass('no_trap')) { if(!form.hasClass('no_trap')) {
//should be there by default ? /* should be there by default */
form.find('input[type=submit]').attr('data-w2p_disable_with', 'Working...'); form.find('input[type=submit]').attr('data-w2p_disable_with', disable_with_message);
form.submit(function (e) { form.submit(function (e) {
web2py.hide_flash(); web2py.hide_flash();
web2py.ajax_page('post', action, form.serialize(), target, form); web2py.ajax_page('post', action, form.serialize(), target, form);
@@ -236,31 +258,30 @@
}); });
}, },
ajax_page: function (method, action, data, target, element) { ajax_page: function (method, action, data, target, element) {
//element is a new parameter, but should be put be put in front /* element is a new parameter, but should be put be put in front */
if(element == undefined) element = $(document); if(element == undefined) element = $(document);
if(web2py.fire(element, 'ajax:before')) { //test a usecase, should stop here if returns false if(web2py.fire(element, 'ajax:before')) { /*test a usecase, should stop here if returns false */
$.ajax({ $.ajax({
'type': method, 'type': method,
'url': action, 'url': action,
'data': data, 'data': data,
'beforeSend': function (xhr, settings) { 'beforeSend': function (xhr, settings) {
//added
xhr.setRequestHeader('web2py-component-location', document.location); xhr.setRequestHeader('web2py-component-location', document.location);
xhr.setRequestHeader('web2py-component-element', target); xhr.setRequestHeader('web2py-component-element', target);
return web2py.fire(element, 'ajax:beforeSend', [xhr, settings]); //test a usecase, should stop here if returns false return web2py.fire(element, 'ajax:beforeSend', [xhr, settings]); //test a usecase, should stop here if returns false
}, },
//added
'success': function (data, status, xhr) { 'success': function (data, status, xhr) {
//bummer for form submissions....the element is not there after complete /*bummer for form submissions....the element is not there after complete
//because it gets replaced by the new response.... *because it gets replaced by the new response....
*/
element.trigger('ajax:success', [data, status, xhr]); element.trigger('ajax:success', [data, status, xhr]);
}, },
//added
'error': function (xhr, status, error) { 'error': function (xhr, status, error) {
//bummer for form submissions....in addition to the element being not there after /*bummer for form submissions....in addition to the element being not there after
//complete because it gets replaced by the new response, standard form *complete because it gets replaced by the new response, standard form
//handling just returns the same status code for good and bad *handling just returns the same status code for good and bad
//form submissions (i.e. that triggered a validator error) *form submissions (i.e. that triggered a validator error)
*/
element.trigger('ajax:error', [xhr, status, error]); element.trigger('ajax:error', [xhr, status, error]);
}, },
'complete': function (xhr, status) { 'complete': function (xhr, status) {
@@ -274,24 +295,26 @@
web2py.trap_form(action, target); web2py.trap_form(action, target);
web2py.trap_link(target); web2py.trap_link(target);
web2py.ajax_init('#' + target); web2py.ajax_init('#' + target);
web2py.after_ajax(xhr);
} }
}); });
} }
}, },
component: function (action, target, timeout, times, el) { component: function (action, target, timeout, times, el) {
//element is a new parameter, but should be put in front /* element is a new parameter, but should be put in front */
$(function () { $(function () {
var jelement = $("#" + target); var jelement = $("#" + target);
var element = jelement.get(0); var element = jelement.get(0);
var statement = "jQuery('#" + target + "').get(0).reload();"; var statement = "jQuery('#" + target + "').get(0).reload();";
element.reload = function () { element.reload = function () {
// Continue if times is Infinity or /* Continue if times is Infinity or
// the times limit is not reached * the times limit is not reached
*/
if(element.reload_check()) { if(element.reload_check()) {
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
} }
}; // reload };
// Method to check timing limit /* Method to check timing limit */
element.reload_check = function () { element.reload_check = function () {
if(jelement.hasClass('w2p_component_stop')) { if(jelement.hasClass('w2p_component_stop')) {
clearInterval(this.timing); clearInterval(this.timing);
@@ -313,32 +336,33 @@
} }
} }
return false; return false;
}; // reload check };
if(!isNaN(timeout)) { if(!isNaN(timeout)) {
element.timeout = timeout; element.timeout = timeout;
element.reload_counter = times; element.reload_counter = times;
if(times > 1) { if(times > 1) {
// Multiple or infinite reload /* Multiple or infinite reload
// Run first iteration * Run first iteration
*/
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
element.run_once = false; element.run_once = false;
element.timing = setInterval(statement, timeout); element.timing = setInterval(statement, timeout);
element.reload_counter -= 1; element.reload_counter -= 1;
} else if(times == 1) { } else if(times == 1) {
// Run once with timeout /* Run once with timeout */
element.run_once = true; element.run_once = true;
element.setTimeout = setTimeout; element.setTimeout = setTimeout;
element.timing = setTimeout(statement, timeout); element.timing = setTimeout(statement, timeout);
} }
} else { } else {
// run once (no timeout specified) /* run once (no timeout specified) */
element.reload_counter = Infinity; element.reload_counter = Infinity;
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
} }
}); });
}, },
calc_entropy: function (mystring) { calc_entropy: function (mystring) {
//calculate a simple entropy for a given string /* calculate a simple entropy for a given string */
var csets = new Array( var csets = new Array(
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/', '0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
@@ -346,7 +370,7 @@
var score = 0, var score = 0,
other = {}, seen = {}, lastset = null, other = {}, seen = {}, lastset = null,
mystringlist = mystring.split(''); mystringlist = mystring.split('');
for(var i = 0; i < mystringlist.length; i++) { // classify this character for(var i = 0; i < mystringlist.length; i++) { /* classify this character */
var c = mystringlist[i], var c = mystringlist[i],
inset = 5; inset = 5;
for(var j = 0; j < csets.length; j++) for(var j = 0; j < csets.length; j++)
@@ -354,7 +378,7 @@
inset = j; inset = j;
break; break;
} }
//calculate effect of character on alphabet size /*calculate effect of character on alphabet size */
if(!(inset in seen)) { if(!(inset in seen)) {
seen[inset] = 1; seen[inset] = 1;
score += csets[inset].length; score += csets[inset].length;
@@ -371,7 +395,6 @@
return Math.round(entropy * 100) / 100 return Math.round(entropy * 100) / 100
}, },
validate_entropy: function (myfield, req_entropy) { validate_entropy: function (myfield, req_entropy) {
//added
if(myfield.data('w2p_entropy') != undefined) req_entropy = myfield.data('w2p_entropy'); if(myfield.data('w2p_entropy') != undefined) req_entropy = myfield.data('w2p_entropy');
var validator = function () { var validator = function () {
var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy; var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy;
@@ -401,23 +424,23 @@
ws.onopen = onopen ? onopen : (function () {}); ws.onopen = onopen ? onopen : (function () {});
ws.onmessage = onmessage; ws.onmessage = onmessage;
ws.onclose = onclose ? onclose : (function () {}); ws.onclose = onclose ? onclose : (function () {});
return true; // supported return true; /* supported */
} else return false; // not supported } else return false; /* not supported */
}, },
/* new from here */ /* new from here */
// Form input elements bound by jquery-ujs /* Form input elements bound by jquery-uj */
formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])', formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])',
// Form input elements disabled during form submission /* Form input elements disabled during form submission */
disableSelector: 'input, button, textarea, select', disableSelector: 'input, button, textarea, select',
// Form input elements re-enabled after form submission /* Form input elements re-enabled after form submission */
enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled', enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled',
// Triggers an event on an element and returns false if the event result is false /* Triggers an event on an element and returns false if the event result is false */
fire: function (obj, name, data) { fire: function (obj, name, data) {
var event = $.Event(name); var event = $.Event(name);
obj.trigger(event, data); obj.trigger(event, data);
return event.result !== false; return event.result !== false;
}, },
// Helper function, needed to provide consistent behavior in IE /* Helper function, needed to provide consistent behavior in IE */
stopEverything: function (e) { stopEverything: function (e) {
$(e.target).trigger('w2p:everythingStopped'); $(e.target).trigger('w2p:everythingStopped');
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@@ -426,41 +449,42 @@
confirm: function (message) { confirm: function (message) {
return confirm(message); return confirm(message);
}, },
// replace element's html with the 'data-disable-with' after storing original html /* replace element's html with the 'data-disable-with' after storing original html
// and prevent clicking on it * and prevent clicking on it */
disableElement: function (el) { disableElement: function (el) {
el.addClass('disabled'); el.addClass('disabled');
var method = el.prop('type') == 'submit' ? 'val' : 'html'; var method = el.prop('type') == 'submit' ? 'val' : 'html';
// store enabled state var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working...";
/*store enabled state*/
el.data('w2p:enable-with', el[method]()); el.data('w2p:enable-with', el[method]());
/* little addition by default*/ /* little addition by default*/
if((el.data('w2p_disable_with') == 'default') || (el.data('w2p_disable_with') === undefined)) { if((el.data('w2p_disable_with') == 'default') || (el.data('w2p_disable_with') === undefined)) {
el.data('w2p_disable_with', 'Working...'); el.data('w2p_disable_with', disable_with_message);
} }
// set to disabled state /* set to disabled state*/
el[method](el.data('w2p_disable_with')); el[method](el.data('w2p_disable_with'));
el.bind('click.w2pDisable', function (e) { // prevent further clicking el.bind('click.w2pDisable', function (e) { /* prevent further clicking*/
return web2py.stopEverything(e); return web2py.stopEverything(e);
}); });
}, },
// restore element to its original state which was disabled by 'disableElement' above /* restore element to its original state which was disabled by 'disableElement' above*/
enableElement: function (el) { enableElement: function (el) {
var method = el.prop('type') == 'submit' ? 'val' : 'html'; var method = el.prop('type') == 'submit' ? 'val' : 'html';
if(el.data('w2p:enable-with') !== undefined) { if(el.data('w2p:enable-with') !== undefined) {
// set to old enabled state /* set to old enabled state */
el[method](el.data('w2p:enable-with')); el[method](el.data('w2p:enable-with'));
el.removeData('w2p:enable-with'); // clean up cache el.removeData('w2p:enable-with');
} }
el.removeClass('disabled'); el.removeClass('disabled');
el.unbind('click.w2pDisable'); // enable element el.unbind('click.w2pDisable');
}, },
//convenience wrapper, internal use only /*convenience wrapper, internal use only */
simple_component: function (action, target, element) { simple_component: function (action, target, element) {
web2py.component(action, target, 0, 1, element); web2py.component(action, target, 0, 1, element);
}, },
//helper for flash messages /*helper for flash messages*/
flash: function (message, status) { flash: function (message, status) {
var flash = $('.flash'); var flash = $('.flash');
web2py.hide_flash(); web2py.hide_flash();
@@ -525,34 +549,16 @@
if(target == undefined) { if(target == undefined) {
if(method == 'GET') { if(method == 'GET') {
web2py.ajax_page('get', action, [], 'bogus', el); //fixme? web2py.ajax_page('get', action, [], 'bogus', el); //fixme?
//web2py.simple_component(action, el.attr('id'), el); //not working with original
} else if(method == 'POST') { } else if(method == 'POST') {
//should be web2py.ajax(action, [], ''); but it's too simple
web2py.ajax_page('post', action, [], 'bogus', el); //fixme? web2py.ajax_page('post', action, [], 'bogus', el); //fixme?
} }
} else { } else {
if(method == 'GET') { if(method == 'GET') {
web2py.ajax_page('get', action, [], target, el); web2py.ajax_page('get', action, [], target, el);
//web2py.simple_component(action, target, el);
} else if(method == 'POST') { } else if(method == 'POST') {
//should be web2py.ajax(action, [], target); but it's too simple
web2py.ajax_page('post', action, [], target, el); web2py.ajax_page('post', action, [], target, el);
} }
} }
/*this should happen only on ajaxsuccess
* and should block subsequent clicks until ajaxcomplete
* NB: introduce the first incompatibility because normally
* the element would be removed in either case
/* removal code moved to the ajax:success event - START
if(toremove != undefined) {
toremove = el.closest(toremove);
if(!toremove.length) {
//this enables removal of whatever selector if a closest is not found
toremove = jQuery(toremove);
}
toremove.remove();
}
/* removal code moved to the ajax:success event - END */
}, },
a_handlers: function () { a_handlers: function () {
var el = $(document); var el = $(document);
@@ -566,7 +572,7 @@
if(toremove != undefined) { if(toremove != undefined) {
toremove = el.closest(toremove); toremove = el.closest(toremove);
if(!toremove.length) { if(!toremove.length) {
//this enables removal of whatever selector if a closest is not found /*this enables removal of whatever selector if a closest is not found */
toremove = $(toremove); toremove = $(toremove);
} }
toremove.remove(); toremove.remove();
@@ -622,8 +628,8 @@
} }
} }
//end of functions /*end of functions */
//main hook /*main hook*/
$(function () { $(function () {
var flash = $('.flash'); var flash = $('.flash');
flash.hide(); flash.hide();
@@ -641,11 +647,11 @@ ajax = jQuery.web2py.ajax;
web2py_component = jQuery.web2py.component; web2py_component = jQuery.web2py.component;
web2py_websocket = jQuery.web2py.websocket; web2py_websocket = jQuery.web2py.websocket;
web2py_ajax_page = jQuery.web2py.ajax_page; web2py_ajax_page = jQuery.web2py.ajax_page;
//needed for IS_STRONG(entropy) /*needed for IS_STRONG(entropy)*/
web2py_validate_entropy = jQuery.web2py.validate_entropy; web2py_validate_entropy = jQuery.web2py.validate_entropy;
//needed for crud.search and SQLFORM.grid's search /*needed for crud.search and SQLFORM.grid's search*/
web2py_ajax_fields = jQuery.web2py.ajax_fields; web2py_ajax_fields = jQuery.web2py.ajax_fields;
//used for LOAD(ajax=False) /*used for LOAD(ajax=False)*/
web2py_trap_form = jQuery.web2py.trap_form; web2py_trap_form = jQuery.web2py.trap_form;
/*undocumented - rare*/ /*undocumented - rare*/
+99 -95
View File
@@ -20,18 +20,22 @@
$.web2py = web2py = { $.web2py = web2py = {
popup: function (url) { popup: function (url) {
/* popup a window */
newwindow = window.open(url, 'name', 'height=400,width=600'); newwindow = window.open(url, 'name', 'height=400,width=600');
if(window.focus) newwindow.focus(); if(window.focus) newwindow.focus();
return false; return false;
}, },
collapse: function (id) { collapse: function (id) {
/* toggle an element */
$('#' + id).slideToggle(); $('#' + id).slideToggle();
}, },
fade: function (id, value) { fade: function (id, value) {
/*fade something*/
if(value > 0) $('#' + id).hide().fadeIn('slow'); if(value > 0) $('#' + id).hide().fadeIn('slow');
else $('#' + id).show().fadeOut('slow'); else $('#' + id).show().fadeOut('slow');
}, },
ajax: function (u, s, t) { ajax: function (u, s, t) {
/*simple ajax function*/
query = ''; query = '';
if(typeof s == "string") { if(typeof s == "string") {
d = $(s).serialize(); d = $(s).serialize();
@@ -65,14 +69,15 @@
}); });
}, },
ajax_fields: function (target) { ajax_fields: function (target) {
/*this attaches something to a newly loaded fragment/page /*
*this attaches something to a newly loaded fragment/page
* Ideally all events should be bound to the document, so we can avoid calling * 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 * this over and over... all will be bound to the document
*/ */
var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d"; var date_format = (typeof w2p_ajax_date_format != 'undefined') ? w2p_ajax_date_format : "%Y-%m-%d";
var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S"; var datetime_format = (typeof w2p_ajax_datetime_format != 'undefined') ? w2p_ajax_datetime_format : "%Y-%m-%d %H:%M:%S";
$("input.date", target).each(function () { $("input.date", target).each(function () {
$(this).attr('autocomplete','off'); $(this).attr('autocomplete', 'off');
Calendar.setup({ Calendar.setup({
inputField: this, inputField: this,
ifFormat: date_format, ifFormat: date_format,
@@ -80,7 +85,7 @@
}); });
}); });
$("input.datetime", target).each(function () { $("input.datetime", target).each(function () {
$(this).attr('autocomplete','off'); $(this).attr('autocomplete', 'off');
Calendar.setup({ Calendar.setup({
inputField: this, inputField: this,
ifFormat: datetime_format, ifFormat: datetime_format,
@@ -89,25 +94,25 @@
}); });
}); });
$("input.time", target).each(function () { $("input.time", target).each(function () {
$(this).timeEntry().attr('autocomplete','off'); $(this).timeEntry().attr('autocomplete', 'off');
}); });
/*adds btn class to buttons*/ /*adds btn class to buttons*/
$('button', target).addClass('btn'); $('button', target).addClass('btn');
$('form input[type="submit"], form input[type="button"]', target).addClass('btn'); $('form input[type="submit"], form input[type="button"]', target).addClass('btn');
/*no more inline javascript for PasswordWidget*/ /* javascript for PasswordWidget*/
$('input[type=password][data-w2p_entropy]', target).each(function () { $('input[type=password][data-w2p_entropy]', target).each(function () {
web2py.validate_entropy($(this)); web2py.validate_entropy($(this));
}); });
/*no more inline javascript for ListWidget*/ /* javascript for ListWidget*/
$('ul.w2p_list', target).each(function () { $('ul.w2p_list', target).each(function () {
function pe(ul, e) { function pe(ul, e) {
var new_line = ml(ul); var new_line = ml(ul);
rel(ul); rel(ul);
if($(e.target).parent().is(':visible')) { if($(e.target).parent().is(':visible')) {
//make sure we didn't delete the element before we insert after /* make sure we didn't delete the element before we insert after */
new_line.insertAfter($(e.target).parent()); new_line.insertAfter($(e.target).parent());
} else { } else {
//the line we clicked on was deleted, just add to end of list /* the line we clicked on was deleted, just add to end of list */
new_line.appendTo(ul); new_line.appendTo(ul);
} }
new_line.find(":text").focus(); new_line.find(":text").focus();
@@ -116,18 +121,20 @@
function rl(ul, e) { function rl(ul, e) {
if($(ul).children().length > 1) { if($(ul).children().length > 1) {
//only remove if we have more than 1 item so the list is never empty /* only remove if we have more than 1 item so the list is never empty */
$(e.target).parent().remove(); $(e.target).parent().remove();
} }
} }
function ml(ul) { function ml(ul) {
/* clone the first field */
var line = $(ul).find("li:first").clone(true); var line = $(ul).find("li:first").clone(true);
line.find(':text').val(''); line.find(':text').val('');
return line; return line;
} }
function rel(ul) { function rel(ul) {
/* keep only as many as needed*/
$(ul).find("li").each(function () { $(ul).find("li").each(function () {
var trimmed = $.trim($(this.firstChild).val()); var trimmed = $.trim($(this.firstChild).val());
if(trimmed == '') $(this).remove(); if(trimmed == '') $(this).remove();
@@ -147,19 +154,32 @@
}); });
}, },
ajax_init: function (target) { ajax_init: function (target) {
/*called whenever a fragment gets loaded */
$('.hidden', target).hide(); $('.hidden', target).hide();
web2py.manage_errors(target); web2py.manage_errors(target);
web2py.ajax_fields(target); web2py.ajax_fields(target);
web2py.show_if_handler(target); web2py.show_if_handler(target);
web2py.component_handler(target); web2py.component_handler(target);
}, },
//manage errors in forms /* manage errors in forms */
manage_errors: function (target) { manage_errors: function (target) {
$('.error', target).hide().slideDown('slow'); $('.error', target).hide().slideDown('slow');
//jQuery('.error', target).hide().fadeIn('slow'); /* jQuery('.error', target).hide().fadeIn('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 () { event_handlers: function () {
/* This is called once for page /*
* This is called once for page
* Ideally it should bound all the things that are needed * Ideally it should bound all the things that are needed
*/ */
var doc = $(document); var doc = $(document);
@@ -167,9 +187,12 @@
var t = $(this); var t = $(this);
if(t.css('top') == '0px') t.slideUp('slow'); if(t.css('top') == '0px') t.slideUp('slow');
else t.fadeOut(); else t.fadeOut();
//if I want to display a clickable something /* if I want to display a clickable something
//inside flash, I should not be prevented to follow it * inside flash, I should not be prevented to follow it
//e.preventDefault(); *
* e.preventDefault();
*/
}); });
doc.on('keyup', 'input.integer', function () { doc.on('keyup', 'input.integer', function () {
this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); this.value = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse();
@@ -185,24 +208,21 @@
doc.ajaxSuccess(function (e, xhr) { doc.ajaxSuccess(function (e, xhr) {
var redirect = xhr.getResponseHeader('web2py-redirect-location'); var redirect = xhr.getResponseHeader('web2py-redirect-location');
var command = xhr.getResponseHeader('web2py-component-command');
var flash = xhr.getResponseHeader('web2py-component-flash');
if(redirect !== null) { if(redirect !== null) {
window.location = redirect; window.location = redirect;
}; };
if(command !== null) { /* run this here only if this Ajax request is NOT for a web2py component. */
eval(decodeURIComponent(command)); if(xhr.getResponseHeader('web2py-component-content') == null) {
} web2py.after_ajax(xhr);
if(flash) { };
web2py.flash(decodeURIComponent(flash))
}
}); });
doc.ajaxError(function (e, xhr, settings, exception) { doc.ajaxError(function (e, xhr, settings, exception) {
//personally I don't like it. /*personally I don't like it.
//if there's an error it it flashed and can be removed *if there's an error it it flashed and can be removed
//as any other message *as any other message
//doc.off('click', '.flash') *doc.off('click', '.flash')
*/
switch(xhr.status) { switch(xhr.status) {
case 500: case 500:
web2py.flash(ajax_error_500); web2py.flash(ajax_error_500);
@@ -211,12 +231,13 @@
}, },
trap_form: function (action, target) { trap_form: function (action, target) {
/* traps any LOADed form */
var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working..."; var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working...";
$('#' + target + ' form').each(function (i) { $('#' + target + ' form').each(function (i) {
var form = $(this); var form = $(this);
form.attr('data-w2p_target', target); form.attr('data-w2p_target', target);
if(!form.hasClass('no_trap')) { if(!form.hasClass('no_trap')) {
//should be there by default ? /* should be there by default */
form.find('input[type=submit]').attr('data-w2p_disable_with', disable_with_message); form.find('input[type=submit]').attr('data-w2p_disable_with', disable_with_message);
form.submit(function (e) { form.submit(function (e) {
web2py.hide_flash(); web2py.hide_flash();
@@ -237,31 +258,30 @@
}); });
}, },
ajax_page: function (method, action, data, target, element) { ajax_page: function (method, action, data, target, element) {
//element is a new parameter, but should be put be put in front /* element is a new parameter, but should be put be put in front */
if(element == undefined) element = $(document); if(element == undefined) element = $(document);
if(web2py.fire(element, 'ajax:before')) { //test a usecase, should stop here if returns false if(web2py.fire(element, 'ajax:before')) { /*test a usecase, should stop here if returns false */
$.ajax({ $.ajax({
'type': method, 'type': method,
'url': action, 'url': action,
'data': data, 'data': data,
'beforeSend': function (xhr, settings) { 'beforeSend': function (xhr, settings) {
//added
xhr.setRequestHeader('web2py-component-location', document.location); xhr.setRequestHeader('web2py-component-location', document.location);
xhr.setRequestHeader('web2py-component-element', target); xhr.setRequestHeader('web2py-component-element', target);
return web2py.fire(element, 'ajax:beforeSend', [xhr, settings]); //test a usecase, should stop here if returns false return web2py.fire(element, 'ajax:beforeSend', [xhr, settings]); //test a usecase, should stop here if returns false
}, },
//added
'success': function (data, status, xhr) { 'success': function (data, status, xhr) {
//bummer for form submissions....the element is not there after complete /*bummer for form submissions....the element is not there after complete
//because it gets replaced by the new response.... *because it gets replaced by the new response....
*/
element.trigger('ajax:success', [data, status, xhr]); element.trigger('ajax:success', [data, status, xhr]);
}, },
//added
'error': function (xhr, status, error) { 'error': function (xhr, status, error) {
//bummer for form submissions....in addition to the element being not there after /*bummer for form submissions....in addition to the element being not there after
//complete because it gets replaced by the new response, standard form *complete because it gets replaced by the new response, standard form
//handling just returns the same status code for good and bad *handling just returns the same status code for good and bad
//form submissions (i.e. that triggered a validator error) *form submissions (i.e. that triggered a validator error)
*/
element.trigger('ajax:error', [xhr, status, error]); element.trigger('ajax:error', [xhr, status, error]);
}, },
'complete': function (xhr, status) { 'complete': function (xhr, status) {
@@ -275,24 +295,26 @@
web2py.trap_form(action, target); web2py.trap_form(action, target);
web2py.trap_link(target); web2py.trap_link(target);
web2py.ajax_init('#' + target); web2py.ajax_init('#' + target);
web2py.after_ajax(xhr);
} }
}); });
} }
}, },
component: function (action, target, timeout, times, el) { component: function (action, target, timeout, times, el) {
//element is a new parameter, but should be put in front /* element is a new parameter, but should be put in front */
$(function () { $(function () {
var jelement = $("#" + target); var jelement = $("#" + target);
var element = jelement.get(0); var element = jelement.get(0);
var statement = "jQuery('#" + target + "').get(0).reload();"; var statement = "jQuery('#" + target + "').get(0).reload();";
element.reload = function () { element.reload = function () {
// Continue if times is Infinity or /* Continue if times is Infinity or
// the times limit is not reached * the times limit is not reached
*/
if(element.reload_check()) { if(element.reload_check()) {
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
} }
}; // reload };
// Method to check timing limit /* Method to check timing limit */
element.reload_check = function () { element.reload_check = function () {
if(jelement.hasClass('w2p_component_stop')) { if(jelement.hasClass('w2p_component_stop')) {
clearInterval(this.timing); clearInterval(this.timing);
@@ -314,32 +336,33 @@
} }
} }
return false; return false;
}; // reload check };
if(!isNaN(timeout)) { if(!isNaN(timeout)) {
element.timeout = timeout; element.timeout = timeout;
element.reload_counter = times; element.reload_counter = times;
if(times > 1) { if(times > 1) {
// Multiple or infinite reload /* Multiple or infinite reload
// Run first iteration * Run first iteration
*/
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
element.run_once = false; element.run_once = false;
element.timing = setInterval(statement, timeout); element.timing = setInterval(statement, timeout);
element.reload_counter -= 1; element.reload_counter -= 1;
} else if(times == 1) { } else if(times == 1) {
// Run once with timeout /* Run once with timeout */
element.run_once = true; element.run_once = true;
element.setTimeout = setTimeout; element.setTimeout = setTimeout;
element.timing = setTimeout(statement, timeout); element.timing = setTimeout(statement, timeout);
} }
} else { } else {
// run once (no timeout specified) /* run once (no timeout specified) */
element.reload_counter = Infinity; element.reload_counter = Infinity;
web2py.ajax_page('get', action, null, target, el); web2py.ajax_page('get', action, null, target, el);
} }
}); });
}, },
calc_entropy: function (mystring) { calc_entropy: function (mystring) {
//calculate a simple entropy for a given string /* calculate a simple entropy for a given string */
var csets = new Array( var csets = new Array(
'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/', '0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/',
@@ -347,7 +370,7 @@
var score = 0, var score = 0,
other = {}, seen = {}, lastset = null, other = {}, seen = {}, lastset = null,
mystringlist = mystring.split(''); mystringlist = mystring.split('');
for(var i = 0; i < mystringlist.length; i++) { // classify this character for(var i = 0; i < mystringlist.length; i++) { /* classify this character */
var c = mystringlist[i], var c = mystringlist[i],
inset = 5; inset = 5;
for(var j = 0; j < csets.length; j++) for(var j = 0; j < csets.length; j++)
@@ -355,7 +378,7 @@
inset = j; inset = j;
break; break;
} }
//calculate effect of character on alphabet size /*calculate effect of character on alphabet size */
if(!(inset in seen)) { if(!(inset in seen)) {
seen[inset] = 1; seen[inset] = 1;
score += csets[inset].length; score += csets[inset].length;
@@ -372,7 +395,6 @@
return Math.round(entropy * 100) / 100 return Math.round(entropy * 100) / 100
}, },
validate_entropy: function (myfield, req_entropy) { validate_entropy: function (myfield, req_entropy) {
//added
if(myfield.data('w2p_entropy') != undefined) req_entropy = myfield.data('w2p_entropy'); if(myfield.data('w2p_entropy') != undefined) req_entropy = myfield.data('w2p_entropy');
var validator = function () { var validator = function () {
var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy; var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy;
@@ -402,23 +424,23 @@
ws.onopen = onopen ? onopen : (function () {}); ws.onopen = onopen ? onopen : (function () {});
ws.onmessage = onmessage; ws.onmessage = onmessage;
ws.onclose = onclose ? onclose : (function () {}); ws.onclose = onclose ? onclose : (function () {});
return true; // supported return true; /* supported */
} else return false; // not supported } else return false; /* not supported */
}, },
/* new from here */ /* new from here */
// Form input elements bound by jquery-ujs /* Form input elements bound by jquery-uj */
formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])', formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])',
// Form input elements disabled during form submission /* Form input elements disabled during form submission */
disableSelector: 'input, button, textarea, select', disableSelector: 'input, button, textarea, select',
// Form input elements re-enabled after form submission /* Form input elements re-enabled after form submission */
enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled', enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled',
// Triggers an event on an element and returns false if the event result is false /* Triggers an event on an element and returns false if the event result is false */
fire: function (obj, name, data) { fire: function (obj, name, data) {
var event = $.Event(name); var event = $.Event(name);
obj.trigger(event, data); obj.trigger(event, data);
return event.result !== false; return event.result !== false;
}, },
// Helper function, needed to provide consistent behavior in IE /* Helper function, needed to provide consistent behavior in IE */
stopEverything: function (e) { stopEverything: function (e) {
$(e.target).trigger('w2p:everythingStopped'); $(e.target).trigger('w2p:everythingStopped');
e.stopImmediatePropagation(); e.stopImmediatePropagation();
@@ -427,42 +449,42 @@
confirm: function (message) { confirm: function (message) {
return confirm(message); return confirm(message);
}, },
// replace element's html with the 'data-disable-with' after storing original html /* replace element's html with the 'data-disable-with' after storing original html
// and prevent clicking on it * and prevent clicking on it */
disableElement: function (el) { disableElement: function (el) {
el.addClass('disabled'); el.addClass('disabled');
var method = el.prop('type') == 'submit' ? 'val' : 'html'; var method = el.prop('type') == 'submit' ? 'val' : 'html';
var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working..."; var disable_with_message = (typeof w2p_ajax_disable_with_message != 'undefined') ? w2p_ajax_disable_with_message : "Working...";
// store enabled state /*store enabled state*/
el.data('w2p:enable-with', el[method]()); el.data('w2p:enable-with', el[method]());
/* little addition by default*/ /* little addition by default*/
if((el.data('w2p_disable_with') == 'default') || (el.data('w2p_disable_with') === undefined)) { if((el.data('w2p_disable_with') == 'default') || (el.data('w2p_disable_with') === undefined)) {
el.data('w2p_disable_with', disable_with_message); el.data('w2p_disable_with', disable_with_message);
} }
// set to disabled state /* set to disabled state*/
el[method](el.data('w2p_disable_with')); el[method](el.data('w2p_disable_with'));
el.bind('click.w2pDisable', function (e) { // prevent further clicking el.bind('click.w2pDisable', function (e) { /* prevent further clicking*/
return web2py.stopEverything(e); return web2py.stopEverything(e);
}); });
}, },
// restore element to its original state which was disabled by 'disableElement' above /* restore element to its original state which was disabled by 'disableElement' above*/
enableElement: function (el) { enableElement: function (el) {
var method = el.prop('type') == 'submit' ? 'val' : 'html'; var method = el.prop('type') == 'submit' ? 'val' : 'html';
if(el.data('w2p:enable-with') !== undefined) { if(el.data('w2p:enable-with') !== undefined) {
// set to old enabled state /* set to old enabled state */
el[method](el.data('w2p:enable-with')); el[method](el.data('w2p:enable-with'));
el.removeData('w2p:enable-with'); // clean up cache el.removeData('w2p:enable-with');
} }
el.removeClass('disabled'); el.removeClass('disabled');
el.unbind('click.w2pDisable'); // enable element el.unbind('click.w2pDisable');
}, },
//convenience wrapper, internal use only /*convenience wrapper, internal use only */
simple_component: function (action, target, element) { simple_component: function (action, target, element) {
web2py.component(action, target, 0, 1, element); web2py.component(action, target, 0, 1, element);
}, },
//helper for flash messages /*helper for flash messages*/
flash: function (message, status) { flash: function (message, status) {
var flash = $('.flash'); var flash = $('.flash');
web2py.hide_flash(); web2py.hide_flash();
@@ -527,34 +549,16 @@
if(target == undefined) { if(target == undefined) {
if(method == 'GET') { if(method == 'GET') {
web2py.ajax_page('get', action, [], 'bogus', el); //fixme? web2py.ajax_page('get', action, [], 'bogus', el); //fixme?
//web2py.simple_component(action, el.attr('id'), el); //not working with original
} else if(method == 'POST') { } else if(method == 'POST') {
//should be web2py.ajax(action, [], ''); but it's too simple
web2py.ajax_page('post', action, [], 'bogus', el); //fixme? web2py.ajax_page('post', action, [], 'bogus', el); //fixme?
} }
} else { } else {
if(method == 'GET') { if(method == 'GET') {
web2py.ajax_page('get', action, [], target, el); web2py.ajax_page('get', action, [], target, el);
//web2py.simple_component(action, target, el);
} else if(method == 'POST') { } else if(method == 'POST') {
//should be web2py.ajax(action, [], target); but it's too simple
web2py.ajax_page('post', action, [], target, el); web2py.ajax_page('post', action, [], target, el);
} }
} }
/*this should happen only on ajaxsuccess
* and should block subsequent clicks until ajaxcomplete
* NB: introduce the first incompatibility because normally
* the element would be removed in either case
/* removal code moved to the ajax:success event - START
if(toremove != undefined) {
toremove = el.closest(toremove);
if(!toremove.length) {
//this enables removal of whatever selector if a closest is not found
toremove = jQuery(toremove);
}
toremove.remove();
}
/* removal code moved to the ajax:success event - END */
}, },
a_handlers: function () { a_handlers: function () {
var el = $(document); var el = $(document);
@@ -568,7 +572,7 @@
if(toremove != undefined) { if(toremove != undefined) {
toremove = el.closest(toremove); toremove = el.closest(toremove);
if(!toremove.length) { if(!toremove.length) {
//this enables removal of whatever selector if a closest is not found /*this enables removal of whatever selector if a closest is not found */
toremove = $(toremove); toremove = $(toremove);
} }
toremove.remove(); toremove.remove();
@@ -624,8 +628,8 @@
} }
} }
//end of functions /*end of functions */
//main hook /*main hook*/
$(function () { $(function () {
var flash = $('.flash'); var flash = $('.flash');
flash.hide(); flash.hide();
@@ -643,11 +647,11 @@ ajax = jQuery.web2py.ajax;
web2py_component = jQuery.web2py.component; web2py_component = jQuery.web2py.component;
web2py_websocket = jQuery.web2py.websocket; web2py_websocket = jQuery.web2py.websocket;
web2py_ajax_page = jQuery.web2py.ajax_page; web2py_ajax_page = jQuery.web2py.ajax_page;
//needed for IS_STRONG(entropy) /*needed for IS_STRONG(entropy)*/
web2py_validate_entropy = jQuery.web2py.validate_entropy; web2py_validate_entropy = jQuery.web2py.validate_entropy;
//needed for crud.search and SQLFORM.grid's search /*needed for crud.search and SQLFORM.grid's search*/
web2py_ajax_fields = jQuery.web2py.ajax_fields; web2py_ajax_fields = jQuery.web2py.ajax_fields;
//used for LOAD(ajax=False) /*used for LOAD(ajax=False)*/
web2py_trap_form = jQuery.web2py.trap_form; web2py_trap_form = jQuery.web2py.trap_form;
/*undocumented - rare*/ /*undocumented - rare*/
+3
View File
@@ -975,6 +975,7 @@ class Session(Storage):
def _try_store_in_cookie(self, request, response): def _try_store_in_cookie(self, request, response):
if self._forget or self._unchanged(response): if self._forget or self._unchanged(response):
self.save_session_id_cookie()
return False return False
name = response.session_data_name name = response.session_data_name
compression_level = response.session_cookie_compression_level compression_level = response.session_cookie_compression_level
@@ -1006,6 +1007,7 @@ class Session(Storage):
global_settings.db_sessions is not True and global_settings.db_sessions is not True and
response.session_masterapp in global_settings.db_sessions): response.session_masterapp in global_settings.db_sessions):
global_settings.db_sessions.remove(response.session_masterapp) global_settings.db_sessions.remove(response.session_masterapp)
self.save_session_id_cookie()
return False return False
table = response.session_db_table table = response.session_db_table
@@ -1043,6 +1045,7 @@ class Session(Storage):
def _try_store_in_file(self, request, response): def _try_store_in_file(self, request, response):
try: try:
if not response.session_id or self._forget or self._unchanged(response): if not response.session_id or self._forget or self._unchanged(response):
self.save_session_id_cookie()
return False return False
if response.session_new or not response.session_file: if response.session_new or not response.session_file:
# Tests if the session sub-folder exists, if not, create it # Tests if the session sub-folder exists, if not, create it
+4 -12
View File
@@ -423,6 +423,10 @@ class lazyT(object):
return lazyT(self) return lazyT(self)
return lazyT(self.m, symbols, self.T, self.f, self.t, self.M) return lazyT(self.m, symbols, self.T, self.f, self.t, self.M)
def pickle_lazyT(c):
return str, (c.xml(),)
copy_reg.pickle(lazyT, pickle_lazyT)
class translator(object): class translator(object):
""" """
@@ -926,18 +930,6 @@ def findT(path, language=DEFAULT_LANGUAGE):
else sentences['!langcode!']) else sentences['!langcode!'])
write_dict(lang_file, sentences) write_dict(lang_file, sentences)
### important to allow safe session.flash=T(....)
def lazyT_unpickle(data):
return marshal.loads(data)
def lazyT_pickle(data):
return lazyT_unpickle, (marshal.dumps(str(data)),)
copy_reg.pickle(lazyT, lazyT_pickle, lazyT_unpickle)
def update_all_languages(application_path): def update_all_languages(application_path):
path = pjoin(application_path, 'languages/') path = pjoin(application_path, 'languages/')
for language in oslistdir(path): for language in oslistdir(path):
+38 -37
View File
@@ -461,50 +461,51 @@ def wsgibase(environ, responder):
if request.body: if request.body:
request.body.close() request.body.close()
# ################################################## if hasattr(current,'request'):
# on success, try store session in database
# ##################################################
session._try_store_in_db(request, response)
# ################################################## # ##################################################
# on success, commit database # on success, try store session in database
# ################################################## # ##################################################
session._try_store_in_db(request, response)
if response.do_not_commit is True: # ##################################################
BaseAdapter.close_all_instances(None) # on success, commit database
# elif response._custom_commit: # ##################################################
# response._custom_commit()
elif response.custom_commit:
BaseAdapter.close_all_instances(response.custom_commit)
else:
BaseAdapter.close_all_instances('commit')
# ################################################## if response.do_not_commit is True:
# if session not in db try store session on filesystem BaseAdapter.close_all_instances(None)
# this must be done after trying to commit database! elif response.custom_commit:
# ################################################## BaseAdapter.close_all_instances(response.custom_commit)
else:
BaseAdapter.close_all_instances('commit')
session._try_store_in_cookie_or_file(request, response) # ##################################################
# if session not in db try store session on filesystem
# this must be done after trying to commit database!
# ##################################################
if request.cid: session._try_store_in_cookie_or_file(request, response)
if response.flash:
http_response.headers['web2py-component-flash'] = \
urllib2.quote(xmlescape(response.flash)\
.replace('\n',''))
if response.js:
http_response.headers['web2py-component-command'] = \
urllib2.quote(response.js.replace('\n',''))
# ################################################## if request.cid:
# store cookies in headers if response.flash:
# ################################################## http_response.headers['web2py-component-flash'] = \
urllib2.quote(xmlescape(response.flash)\
.replace('\n',''))
if response.js:
http_response.headers['web2py-component-command'] = \
urllib2.quote(response.js.replace('\n',''))
# ##################################################
# store cookies in headers
# ##################################################
rcookies = response.cookies
if session._forget and response.session_id_name in rcookies:
del rcookies[response.session_id_name]
elif session._secure:
rcookies[response.session_id_name]['secure'] = True
http_response.cookies2headers(rcookies)
rcookies = response.cookies
if session._forget and response.session_id_name in rcookies:
del rcookies[response.session_id_name]
elif session._secure:
rcookies[response.session_id_name]['secure'] = True
http_response.cookies2headers(rcookies)
ticket = None ticket = None
except RestrictedError, e: except RestrictedError, e:
+1 -1
View File
@@ -122,7 +122,7 @@ class TestWeb(LiveTest):
client.get('index') client.get('index')
# COMMENTED BECAUSE FAILS BUT WHY? # COMMENTED BECAUSE FAILS BUT WHY?
# self.assertTrue('Welcome Homer' in client.text) self.assertTrue('Welcome Homer' in client.text)
client = WebClient('http://127.0.0.1:8000/admin/default/') client = WebClient('http://127.0.0.1:8000/admin/default/')
client.post('index', data=dict(password='hello')) client.post('index', data=dict(password='hello'))
+158 -35
View File
@@ -1304,16 +1304,17 @@ class Auth(object):
else: else:
raise HTTP(404) raise HTTP(404)
def navbar(self, mode='Default', action=None, prefix='Welcome', referrer_actions=DEFAULT, user_identifier=DEFAULT): def navbar(self, mode='Default', action=None, prefix='Welcome',
"""Navbar with support for more templates referrer_actions=DEFAULT, user_identifier=DEFAULT):
""" Navbar with support for more templates
This uses some code from the old navbar. This uses some code from the old navbar.
Keyword arguments: Keyword arguments:
mode -- see options for list of mode -- see options for list of
""" """
items = [] #Hold all menu items in a list items = [] # Hold all menu items in a list
self.bar = '' #The final self.bar = '' # The final
T = current.T T = current.T
referrer_actions = [] if not referrer_actions else referrer_actions referrer_actions = [] if not referrer_actions else referrer_actions
if not action: if not action:
@@ -1325,23 +1326,33 @@ class Auth(object):
else: else:
next = '?_next=' + urllib.quote(URL(args=request.args, next = '?_next=' + urllib.quote(URL(args=request.args,
vars=request.get_vars)) vars=request.get_vars))
href = lambda function: '%s/%s%s' % (action, function, href = lambda function: '%s/%s%s' % (action, function, next
next if referrer_actions is DEFAULT or function in referrer_actions else '') if referrer_actions is DEFAULT
or function in referrer_actions
else '')
if isinstance(prefix, str): if isinstance(prefix, str):
prefix = T(prefix) prefix = T(prefix)
if prefix: if prefix:
prefix = prefix.strip() + ' ' prefix = prefix.strip() + ' '
def Anr(*a,**b): def Anr(*a, **b):
b['_rel']='nofollow' b['_rel'] = 'nofollow'
return A(*a,**b) return A(*a, **b)
if self.user_id: #User is logged in if self.user_id: # User is logged in
items.append({'name': T('Logout'), 'href': '%s/logout?_next=%s' % (action, urllib.quote(self.settings.logout_next)), 'icon': 'icon-off'}) logout_next = self.settings.logout_next
items.append({'name': T('Logout'),
'href': '%s/logout?_next=%s' % (action,
urllib.quote(
logout_next)),
'icon': 'icon-off'})
if not 'profile' in self.settings.actions_disabled: if not 'profile' in self.settings.actions_disabled:
items.append({'name': T('Profile'), 'href': href('profile'), 'icon': 'icon-user'}) items.append({'name': T('Profile'), 'href': href('profile'),
'icon': 'icon-user'})
if not 'change_password' in self.settings.actions_disabled: if not 'change_password' in self.settings.actions_disabled:
items.append({'name': T('Password'), 'href': href('change_password'), 'icon': 'icon-lock'}) items.append({'name': T('Password'),
'href': href('change_password'),
'icon': 'icon-lock'})
if user_identifier is DEFAULT: if user_identifier is DEFAULT:
user_identifier = '%(first_name)s' user_identifier = '%(first_name)s'
@@ -1353,42 +1364,144 @@ class Auth(object):
user_identifier = user_identifier % self.user user_identifier = user_identifier % self.user
if not user_identifier: if not user_identifier:
user_identifier = '' user_identifier = ''
else: #User is not logged in else: # User is not logged in
items.append({'name': T('Login'), 'href': href('login'), 'icon': 'icon-off'}) items.append({'name': T('Login'), 'href': href('login'),
'icon': 'icon-off'})
if not 'register' in self.settings.actions_disabled: if not 'register' in self.settings.actions_disabled:
items.append({'name': T('Register'), 'href': href('register'), 'icon': 'icon-user'}) items.append({'name': T('Register'), 'href': href('register'),
'icon': 'icon-user'})
if not 'request_reset_password' in self.settings.actions_disabled: if not 'request_reset_password' in self.settings.actions_disabled:
items.append({'name': T('Lost password?'), 'href': href('request_reset_password'), 'icon': 'icon-lock'}) items.append({'name': T('Lost password?'),
if self.settings.use_username and not 'retrieve_username' in self.settings.actions_disabled: 'href': href('request_reset_password'),
items.append({'name': T('Forgot username?'), 'href': href('retrieve_username'), 'icon': 'icon-edit'}) 'icon': 'icon-lock'})
if (self.settings.use_username and not
'retrieve_username' in self.settings.actions_disabled):
items.append({'name': T('Forgot username?'),
'href': href('retrieve_username'),
'icon': 'icon-edit'})
def menu(): #For inclusion in MENU def menu(): # For inclusion in MENU
self.bar = [(items[0]['name'], False, items[0]['href'], [])] self.bar = [(items[0]['name'], False, items[0]['href'], [])]
del items[0] del items[0]
for item in items: for item in items:
self.bar[0][3].append((item['name'], False, item['href'])) self.bar[0][3].append((item['name'], False, item['href']))
def bootstrap(): #Default web2py scaffolding def bootstrap(): # Default web2py scaffolding
self.bar = UL(LI(Anr(I(_class=items[0]['icon']), ' ' + items[0]['name'], _href=items[0]['href'])), _class='dropdown-menu') self.bar = UL(LI(Anr(I(_class=items[0]['icon']),
' ' + items[0]['name'],
_href=items[0]['href'])),
_class='dropdown-menu')
del items[0] del items[0]
for item in items: for item in items:
self.bar.insert(-1, LI(Anr(I(_class=item['icon']), ' ' + item['name'], _href=item['href']))) self.bar.insert(-1, LI(Anr(I(_class=item['icon']),
' ' + item['name'],
_href=item['href'])))
self.bar.insert(-1, LI('', _class='divider')) self.bar.insert(-1, LI('', _class='divider'))
if self.user_id: if self.user_id:
self.bar = LI(Anr(prefix, user_identifier, _href='#'), self.bar, _class='dropdown') self.bar = LI(Anr(prefix, user_identifier, _href='#'),
self.bar,
_class='dropdown')
else: else:
self.bar = LI(Anr(T('Login'), _href='#'), self.bar, _class='dropdown') self.bar = LI(Anr(T('Login'), _href='#'), self.bar,
_class='dropdown')
def bare():
""" In order to do advanced customization we only need the
prefix, the user_identifier and the href attribute of items
Example:
# in module custom_layout.py
from gluon import *
def navbar(auth_navbar):
bar = auth_navbar
user = bar["user"]
if not user:
btn_login = A(current.T("Login"),
_href=bar["login"],
_class="btn btn-success",
_rel="nofollow")
btn_register = A(current.T("Sign up"),
_href=bar["register"],
_class="btn btn-primary",
_rel="nofollow")
return DIV(btn_register, btn_login, _class="btn-group")
else:
toggletext = "%s back %s" % (bar["prefix"], user)
toggle = A(toggletext,
_href="#",
_class="dropdown-toggle",
_rel="nofollow",
**{"_data-toggle": "dropdown"})
li_profile = LI(A(I(_class="icon-user"), ' ',
current.T("Account details"),
_href=bar["profile"], _rel="nofollow"))
li_custom = LI(A(I(_class="icon-book"), ' ',
current.T("My Agenda"),
_href="#", rel="nofollow"))
li_logout = LI(A(I(_class="icon-off"), ' ',
current.T("logout"),
_href=bar["logout"], _rel="nofollow"))
dropdown = UL(li_profile,
li_custom,
LI('', _class="divider"),
li_logout,
_class="dropdown-menu", _role="menu")
return LI(toggle, dropdown, _class="dropdown")
# in models db.py
import custom_layout as custom
# in layout.html
<ul id="navbar" class="nav pull-right">
{{='auth' in globals() and \
custom.navbar(auth.navbar(mode='bare')) or ''}}</ul>
"""
bare = {}
bare['prefix'] = prefix
bare['user'] = user_identifier if self.user_id else None
for i in items:
if i['name'] == T('Login'):
k = 'login'
elif i['name'] == T('Register'):
k = 'register'
elif i['name'] == T('Lost password?'):
k = 'request_reset_password'
elif i['name'] == T('Forgot username?'):
k = 'retrieve_username'
elif i['name'] == T('Logout'):
k = 'logout'
elif i['name'] == T('Profile'):
k = 'profile'
elif i['name'] == T('Password'):
k = 'change_password'
bare[k] = i['href']
self.bar = bare
options = {'asmenu': menu,
'dropdown': bootstrap,
'bare': bare
} # Define custom modes.
options = {'asmenu' : menu,
'dropdown' : bootstrap
} #Define custom modes.
try: try:
options[mode]() options[mode]()
except KeyError: #KeyError if mode is not in options (do Default) except KeyError: # KeyError if mode is not in options (do Default)
if self.user_id: if self.user_id:
self.bar = SPAN(prefix, user_identifier, '[', Anr(items[0]['name'], _href=items[0]['href']), ']', _class='auth_navbar') self.bar = SPAN(prefix, user_identifier, '[',
Anr(items[0]['name'],
_href=items[0]['href']), ']',
_class='auth_navbar')
else: else:
self.bar = SPAN('[', Anr(items[0]['name'], _href=items[0]['href']), ']', _class='auth_navbar') self.bar = SPAN('[', Anr(items[0]['name'],
_href=items[0]['href']), ']',
_class='auth_navbar')
del items[0] del items[0]
for item in items: for item in items:
self.bar.insert(-1, ']') self.bar.insert(-1, ']')
@@ -1436,9 +1549,11 @@ class Auth(object):
does automatically. does automatically.
""" """
tables = [table for table in tables]
for table in tables: for table in tables:
if '_id' in table.fields() and 'modified_on' in table.fields() and not current_record in table.fields(): fieldnames = table.fields()
if ('id' in fieldnames and
'modified_on' in fieldnames and
not current_record in fieldnames):
table._enable_record_versioning( table._enable_record_versioning(
archive_db=archive_db, archive_db=archive_db,
archive_name=archive_names, archive_name=archive_names,
@@ -5514,7 +5629,7 @@ class Wiki(object):
return dict(content=content) return dict(content=content)
def media(self, id): def media(self, id):
request, db = current.request, self.auth.db request, response, db = current.request, current.response, self.auth.db
media = db.wiki_media(id) media = db.wiki_media(id)
if media: if media:
if self.settings.manage_permissions: if self.settings.manage_permissions:
@@ -5522,7 +5637,15 @@ class Wiki(object):
if not self.can_read(page): if not self.can_read(page):
return self.not_authorized(page) return self.not_authorized(page)
request.args = [media.filename] request.args = [media.filename]
return current.response.download(request, db) m = response.download(request, db)
current.session.forget() # get rid of the cookie
response.headers['Last-Modified'] = \
request.utcnow.strftime("%a, %d %b %Y %H:%M:%S GMT")
if 'Content-Disposition' in response.headers:
del response.headers['Content-Disposition']
response.headers['Pragma'] = 'cache'
response.headers['Cache-Control'] = 'private'
return m
else: else:
raise HTTP(404) raise HTTP(404)