1(function ($, undefined) { 2 /* 3 * Unobtrusive scripting adapter for jQuery, largely taken from 4 * the wonderful https://github.com/rails/jquery-ujs 5 * 6 * 7 * Released under the MIT license 8 * 9 */ 10 'use strict'; 11 if ($.web2py !== undefined) { 12 $.error('web2py.js has already been loaded!'); 13 } 14 15 var FORMDATA_IS_SUPPORTED = typeof(FormData) !== 'undefined'; 16 var animateIn = 'fadeIn'; 17 // var animateIn = 'slideDown'; 18 19 String.prototype.reverse = function () { 20 return this.split('').reverse().join(''); 21 }; 22 var web2py; 23 24 $.web2py = web2py = { 25 26 isUndefined: function (obj) { 27 /* grabbed from underscore.js */ 28 return obj === void 0; 29 }, 30 popup: function (url) { 31 /* popup a window */ 32 var newwindow = window.open(url, 'name', 'height=400,width=600'); 33 if (window.focus) newwindow.focus(); 34 return false; 35 }, 36 collapse: function (id) { 37 /* toggle an element */ 38 $('#' + id).slideToggle(); 39 }, 40 fade: function (id, value) { 41 /*fade something*/ 42 if (value > 0) $('#' + id).hide().fadeIn('slow'); 43 else $('#' + id).show().fadeOut('slow'); 44 }, 45 ajax: function (u, s, t, options) { 46 /*simple ajax function*/ 47 48 // set options default value 49 options = typeof options !== 'undefined' ? options : {}; 50 51 var query = ''; 52 if (typeof s == 'string') { 53 var d = $(s).serialize(); 54 if (d) { 55 query = d; 56 } 57 } else { 58 var pcs = []; 59 if (s !== null && !web2py.isUndefined(s)) 60 for (var i = 0; i < s.length; i++) { 61 var q = $('[name=' + s[i] + ']').serialize(); 62 if (q) { 63 pcs.push(q); 64 } 65 } 66 if (pcs.length > 0) { 67 query = pcs.join('&'); 68 } 69 } 70 71 // default success action 72 var success_function = function (msg) { 73 if (t) { 74 if (t == ':eval') eval(msg); 75 else if (typeof t == 'string') $('#' + t).html(msg); 76 else t(msg); 77 } 78 }; 79 80 // declare success actions as array 81 var success = [success_function]; 82 83 // add user success actions 84 if ($.isArray(options.done)){ 85 success = $.merge(success, options.done); 86 } else { 87 success.push(options.done); 88 } 89 90 // default jquery ajax options 91 var ajax_options = { 92 type: 'POST', 93 url: u, 94 data: query, 95 success: success 96 }; 97 98 //remove custom "done" option if exists 99 delete options.done; 100 101 // merge default ajax options with user custom options 102 for (var attrname in options) { 103 ajax_options[attrname] = options[attrname]; 104 } 105 106 // call ajax function 107 $.ajax(ajax_options); 108 }, 109 ajax_fields: function (target) { 110 /* 111 *this attaches something to a newly loaded fragment/page 112 * Ideally all events should be bound to the document, so we can avoid calling 113 * this over and over... all will be bound to the document 114 */ 115 /*adds btn class to buttons*/ 116 $('button:not([class^="btn"])', target).addClass('btn'); 117 $( 118 'form input[type="submit"]:not([class^="btn"]), form input[type="button"]:not([class^="btn"])', 119 target).addClass('btn'); 120 /* javascript for PasswordWidget*/ 121 $('input[type=password][data-w2p_entropy]', target).each(function () { 122 web2py.validate_entropy($(this)); 123 }); 124 /* javascript for ListWidget*/ 125 $('ul.w2p_list', target).each(function () { 126 function pe(ul, e) { 127 var new_line = ml(ul); 128 rel(ul); 129 if ($(e.target).parent().is(':visible')) { 130 /* make sure we didn't delete the element before we insert after */ 131 new_line.insertAfter($(e.target).parent()); 132 } else { 133 /* the line we clicked on was deleted, just add to end of list */ 134 new_line.appendTo(ul); 135 } 136 new_line.find(':text').focus(); 137 return false;
138 } 139 140 function rl(ul, e) { 141 if ($(ul).children().length > 1) { 142 /* only remove if we have more than 1 item so the list is never empty */ 143 $(e.target).parent().remove(); 144 } 145 } 146 147 function ml(ul) { 148 /* clone the first field */ 149 var line = $(ul).find('li:first').clone(true); 150 line.find(':text').val(''); 151 return line; 152 } 153 154 function rel(ul) { 155 /* keep only as many as needed*/ 156 $(ul).find('li').each(function () { 157 var trimmed = $.trim($(this.firstChild).val()); 158 if (trimmed === '') $(this).remove(); 159 else $(this.firstChild).val(trimmed); 160 }); 161 } 162 var ul = this; 163 $(ul).find(':text').after('<a href="#">+</a> <a href="#">-</a>').keypress( 164 function (e) { 165 return (e.which == 13) ? pe(ul, e) : true; 166 }).next().click(function (e) { 167 pe(ul, e); 168 e.preventDefault(); 169 }).next().click(function (e) { 170 rl(ul, e); 171 e.preventDefault(); 172 }); 173 }); 174 }, 175 ajax_init: function (target) { 176 /*called whenever a fragment gets loaded */ 177 $('.w2p_hidden', target).hide(); 178 web2py.manage_errors(target); 179 web2py.ajax_fields(target); 180 web2py.show_if_handler(target); 181 web2py.component_handler(target); 182 }, 183 /* manage errors in forms */ 184 manage_errors: function (target) { 185 $('div.error', target).hide()[animateIn]('slow'); 186 }, 187 after_ajax: function (xhr) { 188 /* called whenever an ajax request completes */ 189 var command = xhr.getResponseHeader('web2py-component-command'); 190 var flash = xhr.getResponseHeader('web2py-component-flash'); 191 if (command !== null) { 192 eval(decodeURIComponent(command)); 193 } 194 if (flash) { 195 web2py.flash(decodeURIComponent(flash)); 196 } 197 }, 198 event_handlers: function () { 199 /* 200 * This is called once for page 201 * Ideally it should bound all the things that are needed 202 * and require no dom manipulations 203 */ 204 var doc = $(document); 205 doc.on('click', '.w2p_flash', function (event) { 206 event.preventDefault(); 207 var t = $(this); 208 if (t.css('top') == '0px') t.slideUp('slow'); 209 else t.fadeOut(); 210 }); 211 doc.on('keyup', 'input.integer', function () { 212 var nvalue = this.value.reverse().replace(/[^0-9\-]|\-(?=.)/g, '').reverse(); 213 if (this.value != nvalue) this.value = nvalue; 214 }); 215 doc.on('keyup', 'input.double, input.decimal', function () { 216 var nvalue = this.value.reverse().replace( 217 /[^0-9\-\.,]|[\-](?=.)|[\.,](?=[0-9]*[\.,])/g, '').reverse(); 218 if (this.value != nvalue) this.value = nvalue; 219 }); 220 var confirm_message = !web2py.isUndefined(w2p_ajax_confirm_message) ? w2p_ajax_confirm_message : 221 'Are you sure you want to delete this object?'; 222 doc.on('click', 'input[type="checkbox"].delete', function () { 223 if (this.checked) 224 if (!web2py.confirm(confirm_message)) this.checked = false; 225 }); 226 var datetime_format = !web2py.isUndefined(w2p_ajax_datetime_format) ? w2p_ajax_datetime_format : 227 '%Y-%m-%d %H:%M:%S'; 228 doc.on('click', 'input.datetime', function () { 229 var tformat = $(this).data('w2p_datetime_format'); 230 var active = $(this).data('w2p_datetime'); 231 var format = !web2py.isUndefined(tformat) ? tformat : datetime_format; 232 if (active === undefined) { 233 Calendar.setup({ 234 inputField: this, 235 ifFormat: format, 236 showsTime: true, 237 timeFormat: '24' 238 }); 239 $(this).attr('autocomplete', 'off'); 240 $(this).data('w2p_datetime', 1); 241 $(this).trigger('click'); 242 } 243 }); 244 var date_format = !web2py.isUndefined(w2p_ajax_date_format) ? w2p_ajax_date_format : '%Y-%m-%d'; 245 doc.on('click', 'input.date', function () { 246 var tformat = $(this).data('w2p_date_format'); 247 var active = $(this).data('w2p_date'); 248 var format = !web2py.isUndefined(tformat) ? tformat : date_format; 249 if (active === undefined) { 250 Calendar.setup({ 251 inputField: this, 252 ifFormat: format, 253 showsTime: false 254 }); 255 $(this).data('w2p_date', 1); 256 $(this).attr('autocomplete', 'off'); 257 $(this).trigger('click'); 258 } 259 }); 260 doc.on('focus', 'input.time', function () { 261 var active = $(this).data('w2p_time'); 262 if (web2py.isUndefined(active)) { 263 $(this).timeEntry({ 264 spinnerImage: '' 265 }).attr('autocomplete', 'off'); 266 $(this).data('w2p_time', 1); 267 } 268 }); 269 /* help preventing double form submission for normal form (not LOADed) */ 270 $(doc).on('submit', 'form', function (e) { 271 var submit_buttons = $(this).find(web2py.formInputClickSelector); 272 submit_buttons.each(function() { 273 web2py.disableElement($(this)); 274 }) 275 /* safeguard in case the form doesn't trigger a refresh, 276 see https://github.com/web2py/web2py/issues/1100 */ 277 setTimeout(function () { 278 submit_buttons.each(function() { 279 web2py.enableElement($(this)); 280 }); 281 }, 5000); 282 }); 283 doc.ajaxSuccess(function (e, xhr) { 284 var redirect = xhr.getResponseHeader('web2py-redirect-location'); 285 if (redirect !== null) { 286 window.location = redirect; 287 } 288 /* run this here only if this Ajax request is NOT for a web2py component. */ 289 if (xhr.getResponseHeader('web2py-component-content') === null) { 290 web2py.after_ajax(xhr); 291 } 292 }); 293 294 doc.ajaxError(function (e, xhr, settings, exception) { 295 /*personally I don't like it. 296 *if there's an error it it flashed and can be removed 297 *as any other message 298 *doc.off('click', '.w2p_flash') 299 */ 300 switch (xhr.status) { 301 case 500: 302 web2py.flash(ajax_error_500); 303 } 304 }); 305 306 }, 307 trap_form: function (action, target) { 308 /* traps any LOADed form */ 309 $('#' + target + ' form').each(function () { 310 var form = $(this); 311 if (form.hasClass('no_trap')) { 312 return; 313 } 314 315 var w2p_target = $(this).attr('data-w2p_target'); 316 if (web2py.isUndefined(w2p_target) || w2p_target === false) { 317 form.attr('data-w2p_target', target); 318 } else { 319 target = w2p_target; 320 } 321 322 var url = form.attr('action'); 323 if ((url === '') || (url === '#') || web2py.isUndefined(url)) { 324 /* form has no action. Use component url. */ 325 url = action; 326 } 327 328 form.submit(function (e) { 329 web2py.disableElement(form.find(web2py.formInputClickSelector)); 330 web2py.hide_flash(); 331 332 var formData; 333 if (FORMDATA_IS_SUPPORTED) { 334 formData = new FormData(form[0]); // Allows file uploads. 335 } else { 336 formData = form.serialize(); // Fallback for older browsers. 337 } 338 web2py.ajax_page('post', url, formData, target, form); 339 340 e.preventDefault(); 341 }); 342 form.on('click', web2py.formInputClickSelector, function (e) { 343 e.preventDefault(); 344 var input_name = $(this).attr('name'); 345 if (!web2py.isUndefined(input_name)) { 346 $('<input type="hidden" />').attr('name', input_name) 347 .attr('value', $(this).val()).appendTo(form); 348 } 349 form.trigger('submit'); 350 }); 351 }); 352 }, 353 ajax_page: function (method, action, data, target, element) { 354 /* element is a new parameter, but should be put be put in front */ 355 if (web2py.isUndefined(element)) element = $(document); 356 /* if target is not there, fill it with something that there isn't in the page*/ 357 if (web2py.isUndefined(target) || target === '') target = 'w2p_none'; 358 359 /* processData and contentType must be set to false when passing a FormData 360 object to jQuery.ajax. */ 361 var isFormData = Object.prototype.toString.call(data) === '[object FormData]'; 362 var contentType = isFormData ? false : 'application/x-www-form-urlencoded; charset=UTF-8'; 363 if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */ 364 $.ajax({ 365 'type': method, 366 'url': action, 367 'data': data, 368 'processData': !isFormData, 369 'contentType': contentType, 370 'beforeSend': function (xhr, settings) { 371 xhr.setRequestHeader('web2py-component-location', document.location); 372 xhr.setRequestHeader('web2py-component-element', target); 373 web2py.fire(element, 'w2p:componentBegin', [xhr, settings], target); 374 return web2py.fire(element, 'ajax:beforeSend', [xhr, settings], target); //test a usecase, should stop here if returns false 375 }, 376 'success': function (data, status, xhr) { 377 /*bummer for form submissions....the element is not there after complete 378 *because it gets replaced by the new response.... 379 */ 380 web2py.fire(element, 'ajax:success', [data, status, xhr], target); 381 }, 382 'error': function (xhr, status, error) { 383 /*bummer for form submissions....in addition to the element being not there after 384 *complete because it gets replaced by the new response, standard form 385 *handling just returns the same status code for good and bad 386 *form submissions (i.e. that triggered a validator error) 387 */ 388 web2py.fire(element, 'ajax:error', [xhr, status, error], target); 389 }, 390 'complete': function (xhr, status) { 391 web2py.fire(element, 'ajax:complete', [xhr, status], target); 392 web2py.updatePage(xhr, target); /* Parse and load the html received */ 393 web2py.trap_form(action, target); 394 web2py.ajax_init('#' + target); 395 web2py.after_ajax(xhr); 396 web2py.fire(element, 'w2p:componentComplete', [xhr, status], target); // Let us know the component is finished loading 397 } 398 }); 399 } 400 }, 401 component: function (action, target, timeout, times, el) { 402 /* element is a new parameter, but should be put in front */ 403 $(function () { 404 var jelement = $('#' + target); 405 var element = jelement.get(0); 406 var statement = 'jQuery("#' + target + '").get(0).reload();'; 407 element.reload = function () { 408 /* Continue if times is Infinity or 409 * the times limit is not reached 410 */ 411 if (element.reload_check()) { 412 web2py.ajax_page('get', action, null, target, el); 413 } 414 }; 415 /* Method to check timing limit */
416 element.reload_check = function () { 417 if (jelement.hasClass('w2p_component_stop')) { 418 clearInterval(this.timing); 419 return false; 420 } 421 if (this.reload_counter == Infinity) { 422 return true; 423 } else { 424 if (!isNaN(this.reload_counter)) { 425 this.reload_counter -= 1; 426 if (this.reload_counter < 0) { 427 if (!this.run_once) { 428 clearInterval(this.timing); 429 return false; 430 } 431 } else { 432 return true; 433 } 434 } 435 } 436 return false; 437 }; 438 if (!isNaN(timeout)) { 439 element.timeout = timeout; 440 element.reload_counter = times; 441 if (times > 1) { 442 /* Multiple or infinite reload 443 * Run first iteration 444 */ 445 web2py.ajax_page('get', action, null, target, el); 446 element.run_once = false; 447 element.timing = setInterval(statement, timeout); 448 element.reload_counter -= 1; 449 } else if (times == 1) { 450 /* Run once with timeout */ 451 element.run_once = true; 452 element.setTimeout = setTimeout; 453 element.timing = setTimeout(statement, timeout); 454 } 455 } else { 456 /* run once (no timeout specified) */ 457 element.reload_counter = Infinity; 458 web2py.ajax_page('get', action, null, target, el); 459 } 460 }); 461 }, 462 updatePage: function (xhr, target) { 463 var t = $('#' + target); 464 var html = $.parseHTML(xhr.responseText, document, true); 465 var title_elements = $(html).filter('title').add($(html).find('title')); 466 var title = title_elements.last().text(); 467 if (title) { 468 title_elements.remove(); /* Remove any title elements from the response */ 469 document.title = $.trim(title); /* Set the new document title */ 470 } 471 var content = xhr.getResponseHeader('web2py-component-content'); 472 if (content == 'prepend') t.prepend(xhr.responseText); 473 else if (content == 'append') t.append(xhr.responseText); 474 else if (content != 'hide') t.html(html); 475 }, 476 calc_entropy: function (mystring) { 477 /* calculate a simple entropy for a given string */ 478 var csets = new Array( 479 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 480 '0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/', 481 '0123456789abcdefghijklmnopqrstuvwxyz'); 482 var score = 0, 483 other = {}, 484 seen = {}, 485 lastset = null, 486 mystringlist = mystring.split(''); 487 for (var i = 0; i < mystringlist.length; i++) { /* classify this character */ 488 var c = mystringlist[i], 489 inset = 5; 490 for (var j = 0; j < csets.length; j++) 491 if (csets[j].indexOf(c) != -1) { 492 inset = j; 493 break; 494 } 495 /*calculate effect of character on alphabet size */ 496 if (!(inset in seen)) { 497 seen[inset] = 1; 498 score += csets[inset].length; 499 } else if (!(c in other)) { 500 score += 1; 501 other[c] = 1; 502 } 503 if (inset != lastset) { 504 score += 1; 505 lastset = inset; 506 } 507 } 508 var entropy = mystring.length * Math.log(score) / 0.6931471805599453; 509 return Math.round(entropy * 100) / 100; 510 }, 511 validate_entropy: function (myfield, req_entropy) { 512 if (!web2py.isUndefined(myfield.data('w2p_entropy'))) req_entropy = myfield.data('w2p_entropy');
513 var validator = function () { 514 var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy; 515 var r = 0, 516 g = 0, 517 b = 0, 518 rs = function (x) { 519 return Math.round(x * 15).toString(16); 520 }; 521 if (v <= 0.5) { 522 r = 1.0; 523 g = 2.0 * v; 524 } else { 525 r = (1.0 - 2.0 * (Math.max(v, 0) - 0.5)); 526 g = 1.0; 527 } 528 var color = '#' + rs(r) + rs(g) + rs(b); 529 myfield.css('background-color', color); 530 var entropy_callback = myfield.data('entropy_callback'); 531 if (entropy_callback) entropy_callback(v); 532 }; 533 if (!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator) 534 .addClass('entropy_check'); 535 }, 536 web2py_websocket: function (url, onmessage, onopen, onclose) { 537 if ('WebSocket' in window) { 538 var ws = new WebSocket(url); 539 ws.onopen = onopen ? onopen : (function () {}); 540 ws.onmessage = onmessage; 541 ws.onclose = onclose ? onclose : (function () {}); 542 return true; /* supported */ 543 } else return false; /* not supported */ 544 }, 545 /* new from here */ 546 /* Form input elements bound by web2py.js */ 547 formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])', 548 /* Form input elements disabled during form submission */ 549 disableSelector: 'input, button, textarea, select', 550 /* Form input elements re-enabled after form submission */ 551 enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled', 552 /* Triggers an event on an element and returns false if the event result is false */ 553 fire: function (obj, type, data, target) { 554 var event = $.Event(type, { 555 'containerTarget': $('#' + target)[0] 556 }); 557 obj.trigger(event, data); 558 return event.result !== false; 559 }, 560 /* Helper function, needed to provide consistent behavior in IE */ 561 stopEverything: function (e) { 562 $(e.target).trigger('w2p:everythingStopped'); 563 e.stopImmediatePropagation(); 564 return false; 565 }, 566 confirm: function (message) { 567 return confirm(message); 568 }, 569 /* replace element's html with the 'data-disable-with' after storing original html 570 * and prevent clicking on it */ 571 disableElement: function (el) { 572 if (!web2py.isUndefined(el.data('w2p_disable'))) { 573 return false; 574 } 575 el.addClass('disabled'); 576 var method = el.is('input') ? 'val' : 'html'; 577 //method = el.attr('name') ? 'html' : 'val'; 578 var disable_with_message = (!web2py.isUndefined(w2p_ajax_disable_with_message)) ? 579 w2p_ajax_disable_with_message : 'Working...'; 580 /*store enabled state if not already disabled */ 581 if (web2py.isUndefined(el.data('w2p_enable_with'))) { 582 el.data('w2p_enable_with', el[method]()); 583 } 584 /*if you don't want to see "working..." on buttons, replace the following 585 * two lines with this one 586 * el.data('w2p_disable_with', el[method]()); 587 */ 588 if ((el.data('w2p_disable_with') == 'default') || (web2py.isUndefined(el.data( 589 'w2p_disable_with')))) { 590 el.data('w2p_disable_with', disable_with_message); 591 } 592 593 /* set to disabled state*/ 594 el[method](el.data('w2p_disable_with')); 595 596 el.bind('click.w2pDisable', function (e) { /* prevent further clicking*/ 597 return web2py.stopEverything(e); 598 }); 599 }, 600 601 /* restore element to its original state which was disabled by 'disableElement' above*/ 602 enableElement: function (el) { 603 var method = el.is('input') ? 'val' : 'html'; 604 if (!web2py.isUndefined(el.data('w2p_enable_with'))) { 605 /* set to old enabled state */ 606 el[method](el.data('w2p_enable_with')); 607 el.removeData('w2p_enable_with'); 608 } 609 el.removeClass('disabled'); 610 el.unbind('click.w2pDisable'); 611 }, 612 /*convenience wrapper, internal use only */ 613 simple_component: function (action, target, element) { 614 web2py.component(action, target, 0, 1, element); 615 }, 616 /*helper for flash messages*/ 617 flash: function (message, status) { 618 var flash = $('.w2p_flash'); 619 web2py.hide_flash(); 620 flash.text(message).addClass(status); 621 if (flash.html()) flash.append('<span id="closeflash"> × </span>')[animateIn](); 622 }, 623 hide_flash: function () { 624 $('.w2p_flash').fadeOut(0).html(''); 625 }, 626 show_if_handler: function (target) { 627 var triggers = {}; 628 var show_if = function () { 629 var t = $(this); 630 var id = t.attr('id'); 631 t.attr('value', t.val()); 632 for (var k = 0; k < triggers[id].length; k++) { 633 var dep = $('#' + triggers[id][k], target); 634 var tr = $('#' + triggers[id][k] + '__row', target); 635 if (t.is(dep.attr('data-show-if'))) tr[animateIn](); 636 else tr.hide(); 637 } 638 }; 639 $('[data-show-trigger]', target).each(function () { 640 var name = $(this).attr('data-show-trigger'); 641 // The field exists only when creating/editing a row
642 if ($('#' + name).length) { 643 if (!triggers[name]) triggers[name] = []; 644 triggers[name].push($(this).attr('id')); 645 } 646 }); 647 for (var name in triggers) { 648 $('#' + name, target).change(show_if).keyup(show_if); 649 show_if.call($('#' + name, target)); 650 } 651 }, 652 component_handler: function (target) { 653 $('div[data-w2p_remote]', target).each(function () { 654 var remote, times, timeout, target; 655 var el = $(this); 656 remote = el.data('w2p_remote'); 657 times = el.data('w2p_times'); 658 timeout = el.data('w2p_timeout'); 659 target = el.attr('id'); 660 web2py.component(remote, target, timeout, times, $(this)); 661 }); 662 }, 663 a_handler: function (el, e) { 664 e.preventDefault(); 665 var method = el.data('w2p_method'); 666 var action = el.attr('href'); 667 var target = el.data('w2p_target'); 668 var confirm_message = el.data('w2p_confirm'); 669 670 var pre_call = el.data('w2p_pre_call'); 671 if (!web2py.isUndefined(pre_call)) { 672 eval(pre_call); 673 } 674 if (confirm_message) { 675 if (confirm_message == 'default') { 676 confirm_message = !web2py.isUndefined(w2p_ajax_confirm_message) ? 677 w2p_ajax_confirm_message : 'Are you sure you want to delete this object?'; 678 } 679 if (!web2py.confirm(confirm_message)) { 680 web2py.stopEverything(e); 681 return; 682 } 683 } 684 if (web2py.isUndefined(target)) { 685 if (method == 'GET') { 686 web2py.ajax_page('get', action, [], '', el); 687 } else if (method == 'POST') { 688 web2py.ajax_page('post', action, [], '', el); 689 } 690 } else { 691 if (method == 'GET') { 692 web2py.ajax_page('get', action, [], target, el); 693 } else if (method == 'POST') { 694 web2py.ajax_page('post', action, [], target, el); 695 } 696 } 697 }, 698 a_handlers: function () { 699 var el = $(document); 700 el.on('click', 'a[data-w2p_method]', function (e) { 701 web2py.a_handler($(this), e); 702 }); 703 /* removal of element should happen only on success */ 704 el.on('ajax:success', 'a[data-w2p_method][data-w2p_remove]', function () { 705 var el = $(this); 706 var toremove = el.data('w2p_remove'); 707 if (!web2py.isUndefined(toremove)) { 708 toremove = el.closest(toremove); 709 if (!toremove.length) { 710 /*this enables removal of whatever selector if a closest is not found */ 711 toremove = $(toremove); 712 } 713 toremove.remove(); 714 } 715 }); 716 el.on('ajax:beforeSend', 'a[data-w2p_method][data-w2p_disable_with]', function () { 717 web2py.disableElement($(this)); 718 }); 719 /*re-enable click on completion*/ 720 el.on('ajax:complete', 'a[data-w2p_method][data-w2p_disable_with]', function () { 721 web2py.enableElement($(this)); 722 }); 723 }, 724 /* Disables form elements: 725 - Does not disable elements with 'data-w2p_disable' attribute 726 - Caches element value in 'w2p_enable_with' data store 727 - Replaces element text with value of 'data-w2p_disable_with' attribute 728 - Sets disabled property to true 729 */ 730 disableFormElements: function (form) { 731 form.find(web2py.disableSelector).each(function () { 732 var element = $(this), 733 method = element.is('button') ? 'html' : 'val'; 734 var disable_with = element.data('w2p_disable_with'); 735 var disable = element.data('w2p_disable'); 736 if (!web2py.isUndefined(disable)) { 737 return false;
738 } 739 if (!element.is(':file')) { // Altering file input values is not allowed. 740 if (web2py.isUndefined(disable_with)) { 741 element.data('w2p_disable_with', element[method]()); 742 } 743 if (web2py.isUndefined(element.data('w2p_enable_with'))) { 744 element.data('w2p_enable_with', element[method]()); 745 } 746 element[method](element.data('w2p_disable_with')); 747 } 748 element.prop('disabled', true); 749 }); 750 }, 751 752 /* Re-enables disabled form elements: 753 - Replaces element text with cached value from 'w2p_enable_with' data store (created in `disableFormElements`) 754 - Sets disabled property to false 755 */ 756 enableFormElements: function (form) { 757 form.find(web2py.enableSelector).each(function () { 758 var element = $(this), 759 method = element.is('button') ? 'html' : 'val'; 760 if (element.data('w2p_enable_with')) { 761 element[method](element.data('w2p_enable_with')); 762 element.removeData('w2p_enable_with'); 763 } 764 element.prop('disabled', false); 765 }); 766 }, 767 form_handlers: function () { 768 var el = $(document); 769 el.on('ajax:beforeSend', 'form[data-w2p_target]', function () { 770 web2py.disableFormElements($(this)); 771 }); 772 el.on('ajax:complete', 'form[data-w2p_target]', function () { 773 web2py.enableFormElements($(this)); 774 }); 775 }, 776 /* Invalidate and force reload of a web2py component 777 */ 778 invalidate: function (target) { 779 $('div[data-w2p_remote]', target).each(function () { 780 var el = $('#' + $(this).attr('id')).get(0); 781 if (!web2py.isUndefined(el.timing)) { // Block triggering regular routines 782 clearInterval(el.timing); 783 } 784 }); 785 $.web2py.component_handler(target); 786 }, 787 main_hook: function () { 788 var flash = $('.w2p_flash'); 789 flash.hide(); 790 if (flash.html()) web2py.flash(flash.html()); 791 web2py.ajax_init(document); 792 web2py.event_handlers(); 793 web2py.a_handlers(); 794 web2py.form_handlers(); 795 } 796 }; 797 /*end of functions */ 798 /*main hook*/ 799 $(function () { 800 web2py.main_hook(); 801 }); 802 803})(jQuery); 804 805/* compatibility code - start */ 806ajax = jQuery.web2py.ajax; 807web2py_component = jQuery.web2py.component; 808web2py_websocket = jQuery.web2py.web2py_websocket; 809web2py_ajax_page = jQuery.web2py.ajax_page; 810/*needed for IS_STRONG(entropy)*/ 811web2py_validate_entropy = jQuery.web2py.validate_entropy; 812/*needed for crud.search and SQLFORM.grid's search*/ 813web2py_ajax_fields = jQuery.web2py.ajax_fields; 814/*used for LOAD(ajax=False)*/ 815web2py_trap_form = jQuery.web2py.trap_form; 816 817/*undocumented - rare*/ 818popup = jQuery.web2py.popup; 819collapse = jQuery.web2py.collapse; 820fade = jQuery.web2py.fade; 821 822/* internals - shouldn't be needed 823web2py_ajax_init = jQuery.web2py.ajax_init; 824web2py_event_handlers = jQuery.web2py.event_handlers; 825web2py_trap_link = jQuery.web2py.trap_link; 826web2py_calc_entropy = jQuery.web2py.calc_entropy; 827*/ 828/* compatibility code - end*/
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.