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 if (!redirect.endsWith('#')) { 287 window.location.href = redirect; 288 } else { 289 window.location.reload(); 290 } 291 } 292 /* run this here only if this Ajax request is NOT for a web2py component. */ 293 if (xhr.getResponseHeader('web2py-component-content') === null) { 294 web2py.after_ajax(xhr); 295 } 296 }); 297 298 doc.ajaxError(function (e, xhr, settings, exception) { 299 /*personally I don't like it. 300 *if there's an error it it flashed and can be removed 301 *as any other message 302 *doc.off('click', '.w2p_flash') 303 */ 304 switch (xhr.status) { 305 case 500: 306 web2py.flash(ajax_error_500); 307 } 308 }); 309 310 }, 311 trap_form: function (action, target) { 312 /* traps any LOADed form */ 313 $('#' + target + ' form').each(function () { 314 var form = $(this); 315 if (form.hasClass('no_trap')) { 316 return; 317 } 318 319 var w2p_target = $(this).attr('data-w2p_target'); 320 if (web2py.isUndefined(w2p_target) || w2p_target === false) { 321 form.attr('data-w2p_target', target); 322 } else { 323 target = w2p_target; 324 } 325 326 var url = form.attr('action'); 327 if ((url === '') || (url === '#') || web2py.isUndefined(url)) { 328 /* form has no action. Use component url. */ 329 url = action; 330 } 331 332 form.submit(function (e) { 333 web2py.disableElement(form.find(web2py.formInputClickSelector)); 334 web2py.hide_flash(); 335 336 var formData; 337 if (FORMDATA_IS_SUPPORTED) { 338 formData = new FormData(form[0]); // Allows file uploads. 339 } else { 340 formData = form.serialize(); // Fallback for older browsers. 341 } 342 web2py.ajax_page('post', url, formData, target); 343 344 e.preventDefault(); 345 }); 346 form.on('click', web2py.formInputClickSelector, function (e) { 347 e.preventDefault(); 348 var input_name = $(this).attr('name'); 349 if (!web2py.isUndefined(input_name)) { 350 $('<input type="hidden" />').attr('name', input_name) 351 .attr('value', $(this).val()).appendTo(form); 352 } 353 form.trigger('submit'); 354 }); 355 }); 356 }, 357 ajax_page: function (method, action, data, target, element) { 358 /* element is a new parameter, but should be put be put in front */ 359 if (web2py.isUndefined(element)) element = $(document); 360 /* if target is not there, fill it with something that there isn't in the page*/ 361 if (web2py.isUndefined(target) || target === '') target = 'w2p_none'; 362 363 /* processData and contentType must be set to false when passing a FormData 364 object to jQuery.ajax. */ 365 var isFormData = Object.prototype.toString.call(data) === '[object FormData]'; 366 var contentType = isFormData ? false : 'application/x-www-form-urlencoded; charset=UTF-8'; 367 if (web2py.fire(element, 'ajax:before', null, target)) { /*test a usecase, should stop here if returns false */ 368 $.ajax({ 369 'type': method, 370 'url': action, 371 'data': data, 372 'processData': !isFormData, 373 'contentType': contentType, 374 'xhr': function() { 375 var xhr = new window.XMLHttpRequest(); 376 377 xhr.upload.addEventListener("progress", function(evt) { 378 if (evt.lengthComputable) { 379 var percentComplete = evt.loaded / evt.total; 380 percentComplete = parseInt(percentComplete * 100); 381 web2py.fire(element, 'w2p:uploadProgress', [percentComplete], target); 382 383 if (percentComplete === 100) { 384 web2py.fire(element, 'w2p:uploadComplete', [], target); 385 } 386 387 } 388 }, false); 389 390 return xhr; 391 }, 392 'beforeSend': function (xhr, settings) { 393 xhr.setRequestHeader('web2py-component-location', document.location); 394 xhr.setRequestHeader('web2py-component-element', target); 395 web2py.fire(element, 'w2p:componentBegin', [xhr, settings], target); 396 return web2py.fire(element, 'ajax:beforeSend', [xhr, settings], target); //test a usecase, should stop here if returns false 397 }, 398 'success': function (data, status, xhr) { 399 /*bummer for form submissions....the element is not there after complete 400 *because it gets replaced by the new response.... 401 */ 402 web2py.fire(element, 'ajax:success', [data, status, xhr], target); 403 }, 404 'error': function (xhr, status, error) { 405 /*bummer for form submissions....in addition to the element being not there after 406 *complete because it gets replaced by the new response, standard form 407 *handling just returns the same status code for good and bad 408 *form submissions (i.e. that triggered a validator error) 409 */ 410 web2py.fire(element, 'ajax:error', [xhr, status, error], target); 411 }, 412 'complete': function (xhr, status) { 413 web2py.fire(element, 'ajax:complete', [xhr, status], target); 414 web2py.updatePage(xhr, target); /* Parse and load the html received */ 415 web2py.trap_form(action, target); 416 web2py.ajax_init('#' + target); 417 web2py.after_ajax(xhr); 418 web2py.fire(element, 'w2p:componentComplete', [xhr, status], target); // Let us know the component is finished loading 419 } 420 }); 421 } 422 }, 423 component: function (action, target, timeout, times, el) { 424 /* element is a new parameter, but should be put in front */ 425 $(function () { 426 var jelement = $('#' + target); 427 var element = jelement.get(0); 428 var statement = 'jQuery("#' + target + '").get(0).reload();'; 429 element.reload = function () { 430 /* Continue if times is Infinity or 431 * the times limit is not reached 432 */ 433 if (element.reload_check()) { 434 web2py.ajax_page('get', action, null, target, el); 435 } 436 }; 437 /* Method to check timing limit */
438 element.reload_check = function () { 439 if (jelement.hasClass('w2p_component_stop')) { 440 clearInterval(this.timing); 441 return false; 442 } 443 if (this.reload_counter == Infinity) { 444 return true; 445 } else { 446 if (!isNaN(this.reload_counter)) { 447 this.reload_counter -= 1; 448 if (this.reload_counter < 0) { 449 if (!this.run_once) { 450 clearInterval(this.timing); 451 return false; 452 } 453 } else { 454 return true; 455 } 456 } 457 } 458 return false; 459 }; 460 if (!isNaN(timeout)) { 461 element.timeout = timeout; 462 element.reload_counter = times; 463 if (times > 1) { 464 /* Multiple or infinite reload 465 * Run first iteration 466 */ 467 web2py.ajax_page('get', action, null, target, el); 468 element.run_once = false; 469 element.timing = setInterval(statement, timeout); 470 element.reload_counter -= 1; 471 } else if (times == 1) { 472 /* Run once with timeout */ 473 element.run_once = true; 474 element.setTimeout = setTimeout; 475 element.timing = setTimeout(statement, timeout); 476 } 477 } else { 478 /* run once (no timeout specified) */ 479 element.reload_counter = Infinity; 480 web2py.ajax_page('get', action, null, target, el); 481 } 482 }); 483 }, 484 updatePage: function (xhr, target) { 485 var t = $('#' + target); 486 var html = $.parseHTML(xhr.responseText, document, true); 487 var title_elements = $(html).filter('title').add($(html).find('title')); 488 var title = title_elements.last().text(); 489 if (title) { 490 title_elements.remove(); /* Remove any title elements from the response */ 491 document.title = $.trim(title); /* Set the new document title */ 492 } 493 var content = xhr.getResponseHeader('web2py-component-content'); 494 if (content == 'prepend') t.prepend(xhr.responseText); 495 else if (content == 'append') t.append(xhr.responseText); 496 else if (content != 'hide') t.html(html); 497 }, 498 calc_entropy: function (mystring) { 499 /* calculate a simple entropy for a given string */ 500 var csets = new Array( 501 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 502 '0123456789', '!@#$\%^&*()', '~`-_=+[]{}\|;:\'",.<>?/', 503 '0123456789abcdefghijklmnopqrstuvwxyz'); 504 var score = 0, 505 other = {}, 506 seen = {}, 507 lastset = null, 508 mystringlist = mystring.split(''); 509 for (var i = 0; i < mystringlist.length; i++) { /* classify this character */ 510 var c = mystringlist[i], 511 inset = 5; 512 for (var j = 0; j < csets.length; j++) 513 if (csets[j].indexOf(c) != -1) { 514 inset = j; 515 break; 516 } 517 /*calculate effect of character on alphabet size */ 518 if (!(inset in seen)) { 519 seen[inset] = 1; 520 score += csets[inset].length; 521 } else if (!(c in other)) { 522 score += 1; 523 other[c] = 1; 524 } 525 if (inset != lastset) { 526 score += 1; 527 lastset = inset; 528 } 529 } 530 var entropy = mystring.length * Math.log(score) / 0.6931471805599453; 531 return Math.round(entropy * 100) / 100; 532 }, 533 validate_entropy: function (myfield, req_entropy) { 534 if (!web2py.isUndefined(myfield.data('w2p_entropy'))) req_entropy = myfield.data('w2p_entropy');
535 var validator = function () { 536 var v = (web2py.calc_entropy(myfield.val()) || 0) / req_entropy; 537 var r = 0, 538 g = 0, 539 b = 0, 540 rs = function (x) { 541 return Math.round(x * 15).toString(16); 542 }; 543 if (v <= 0.5) { 544 r = 1.0; 545 g = 2.0 * v; 546 } else { 547 r = (1.0 - 2.0 * (Math.max(v, 0) - 0.5)); 548 g = 1.0; 549 } 550 var color = '#' + rs(r) + rs(g) + rs(b); 551 myfield.css('background-color', color); 552 var entropy_callback = myfield.data('entropy_callback'); 553 if (entropy_callback) entropy_callback(v); 554 }; 555 if (!myfield.hasClass('entropy_check')) myfield.on('keyup', validator).on('keydown', validator) 556 .addClass('entropy_check'); 557 }, 558 web2py_websocket: function (url, onmessage, onopen, onclose) { 559 if ('WebSocket' in window) { 560 var ws = new WebSocket(url); 561 ws.onopen = onopen ? onopen : (function () {}); 562 ws.onmessage = onmessage; 563 ws.onclose = onclose ? onclose : (function () {}); 564 return true; /* supported */ 565 } else return false; /* not supported */ 566 }, 567 /* new from here */ 568 /* Form input elements bound by web2py.js */ 569 formInputClickSelector: 'input[type=submit], input[type=image], button[type=submit], button:not([type])', 570 /* Form input elements disabled during form submission */ 571 disableSelector: 'input, button, textarea, select', 572 /* Form input elements re-enabled after form submission */ 573 enableSelector: 'input:disabled, button:disabled, textarea:disabled, select:disabled', 574 /* Triggers an event on an element and returns false if the event result is false */ 575 fire: function (obj, type, data, target) { 576 var event = $.Event(type, { 577 'containerTarget': $('#' + target)[0] 578 }); 579 obj.trigger(event, data); 580 return event.result !== false; 581 }, 582 /* Helper function, needed to provide consistent behavior in IE */ 583 stopEverything: function (e) { 584 $(e.target).trigger('w2p:everythingStopped'); 585 e.stopImmediatePropagation(); 586 return false; 587 }, 588 confirm: function (message) { 589 return confirm(message); 590 }, 591 /* replace element's html with the 'data-disable-with' after storing original html 592 * and prevent clicking on it */ 593 disableElement: function (el) { 594 if (!web2py.isUndefined(el.data('w2p_disable'))) { 595 return false; 596 } 597 el.addClass('disabled'); 598 var method = el.is('input') ? 'val' : 'html'; 599 //method = el.attr('name') ? 'html' : 'val'; 600 var disable_with_message = (!web2py.isUndefined(w2p_ajax_disable_with_message)) ? 601 w2p_ajax_disable_with_message : 'Working...'; 602 /*store enabled state if not already disabled */ 603 if (web2py.isUndefined(el.data('w2p_enable_with'))) { 604 el.data('w2p_enable_with', el[method]()); 605 } 606 /*if you don't want to see "working..." on buttons, replace the following 607 * two lines with this one 608 * el.data('w2p_disable_with', el[method]()); 609 */ 610 if ((el.data('w2p_disable_with') == 'default') || (web2py.isUndefined(el.data( 611 'w2p_disable_with')))) { 612 el.data('w2p_disable_with', disable_with_message); 613 } 614 615 /* set to disabled state*/ 616 el[method](el.data('w2p_disable_with')); 617 618 el.bind('click.w2pDisable', function (e) { /* prevent further clicking*/ 619 return web2py.stopEverything(e); 620 }); 621 }, 622 623 /* restore element to its original state which was disabled by 'disableElement' above*/ 624 enableElement: function (el) { 625 var method = el.is('input') ? 'val' : 'html'; 626 if (!web2py.isUndefined(el.data('w2p_enable_with'))) { 627 /* set to old enabled state */ 628 el[method](el.data('w2p_enable_with')); 629 el.removeData('w2p_enable_with'); 630 } 631 el.removeClass('disabled'); 632 el.unbind('click.w2pDisable'); 633 }, 634 /*convenience wrapper, internal use only */ 635 simple_component: function (action, target, element) { 636 web2py.component(action, target, 0, 1, element); 637 }, 638 /*helper for flash messages*/ 639 flash: function (message, status) { 640 var flash = $('.w2p_flash'); 641 web2py.hide_flash(); 642 flash.text(message).addClass(status); 643 if (flash.html()) flash.append('<span id="closeflash"> × </span>')[animateIn](); 644 }, 645 hide_flash: function () { 646 $('.w2p_flash').fadeOut(0).html(''); 647 }, 648 show_if_handler: function (target) { 649 var triggers = {}; 650 var show_if = function () { 651 var t = $(this); 652 var id = t.attr('id'); 653 t.attr('value', t.val()); 654 for (var k = 0; k < triggers[id].length; k++) { 655 var dep = $('#' + triggers[id][k], target); 656 var tr = $('#' + triggers[id][k] + '__row', target); 657 if (t.is(dep.attr('data-show-if'))) tr[animateIn](); 658 else tr.hide(); 659 } 660 }; 661 $('[data-show-trigger]', target).each(function () { 662 var name = $(this).attr('data-show-trigger'); 663 // The field exists only when creating/editing a row
664 if ($('#' + name).length) { 665 if (!triggers[name]) triggers[name] = []; 666 triggers[name].push($(this).attr('id')); 667 } 668 }); 669 for (var name in triggers) { 670 $('#' + name, target).change(show_if).keyup(show_if); 671 show_if.call($('#' + name, target)); 672 } 673 }, 674 component_handler: function (target) { 675 $('div[data-w2p_remote]', target).each(function () { 676 var remote, times, timeout, target; 677 var el = $(this); 678 remote = el.data('w2p_remote'); 679 times = el.data('w2p_times'); 680 timeout = el.data('w2p_timeout'); 681 target = el.attr('id'); 682 web2py.component(remote, target, timeout, times, $(this)); 683 }); 684 }, 685 a_handler: function (el, e) { 686 e.preventDefault(); 687 var method = el.data('w2p_method'); 688 var action = el.attr('href'); 689 var target = el.data('w2p_target'); 690 var confirm_message = el.data('w2p_confirm'); 691 692 var pre_call = el.data('w2p_pre_call'); 693 if (!web2py.isUndefined(pre_call)) { 694 eval(pre_call); 695 } 696 if (confirm_message) { 697 if (confirm_message == 'default') { 698 confirm_message = !web2py.isUndefined(w2p_ajax_confirm_message) ? 699 w2p_ajax_confirm_message : 'Are you sure you want to delete this object?'; 700 } 701 if (!web2py.confirm(confirm_message)) { 702 web2py.stopEverything(e); 703 return; 704 } 705 } 706 if (web2py.isUndefined(target)) { 707 if (method == 'GET') { 708 web2py.ajax_page('get', action, [], '', el); 709 } else if (method == 'POST') { 710 web2py.ajax_page('post', action, [], '', el); 711 } 712 } else { 713 if (method == 'GET') { 714 web2py.ajax_page('get', action, [], target, el); 715 } else if (method == 'POST') { 716 web2py.ajax_page('post', action, [], target, el); 717 } 718 } 719 }, 720 a_handlers: function () { 721 var el = $(document); 722 el.on('click', 'a[data-w2p_method]', function (e) { 723 web2py.a_handler($(this), e); 724 }); 725 /* removal of element should happen only on success */ 726 el.on('ajax:success', 'a[data-w2p_method][data-w2p_remove]', function () { 727 var el = $(this); 728 var toremove = el.data('w2p_remove'); 729 if (!web2py.isUndefined(toremove)) { 730 toremove = el.closest(toremove); 731 if (!toremove.length) { 732 /*this enables removal of whatever selector if a closest is not found */ 733 toremove = $(toremove); 734 } 735 toremove.remove(); 736 } 737 }); 738 el.on('ajax:beforeSend', 'a[data-w2p_method][data-w2p_disable_with]', function () { 739 web2py.disableElement($(this)); 740 }); 741 /*re-enable click on completion*/ 742 el.on('ajax:complete', 'a[data-w2p_method][data-w2p_disable_with]', function () { 743 web2py.enableElement($(this)); 744 }); 745 }, 746 /* Disables form elements: 747 - Does not disable elements with 'data-w2p_disable' attribute 748 - Caches element value in 'w2p_enable_with' data store 749 - Replaces element text with value of 'data-w2p_disable_with' attribute 750 - Sets disabled property to true 751 */ 752 disableFormElements: function (form) { 753 form.find(web2py.disableSelector).each(function () { 754 var element = $(this), 755 method = element.is('button') ? 'html' : 'val'; 756 var disable_with = element.data('w2p_disable_with'); 757 var disable = element.data('w2p_disable'); 758 if (!web2py.isUndefined(disable)) { 759 return false;
760 } 761 if (!element.is(':file')) { // Altering file input values is not allowed. 762 if (web2py.isUndefined(disable_with)) { 763 element.data('w2p_disable_with', element[method]()); 764 } 765 if (web2py.isUndefined(element.data('w2p_enable_with'))) { 766 element.data('w2p_enable_with', element[method]()); 767 } 768 element[method](element.data('w2p_disable_with')); 769 } 770 element.prop('disabled', true); 771 }); 772 }, 773 774 /* Re-enables disabled form elements: 775 - Replaces element text with cached value from 'w2p_enable_with' data store (created in `disableFormElements`) 776 - Sets disabled property to false 777 */ 778 enableFormElements: function (form) { 779 form.find(web2py.enableSelector).each(function () { 780 var element = $(this), 781 method = element.is('button') ? 'html' : 'val'; 782 if (element.data('w2p_enable_with')) { 783 element[method](element.data('w2p_enable_with')); 784 element.removeData('w2p_enable_with'); 785 } 786 element.prop('disabled', false); 787 }); 788 }, 789 form_handlers: function () { 790 var el = $(document); 791 el.on('ajax:beforeSend', 'form[data-w2p_target]', function () { 792 web2py.disableFormElements($(this)); 793 }); 794 el.on('ajax:complete', 'form[data-w2p_target]', function () { 795 web2py.enableFormElements($(this)); 796 }); 797 }, 798 /* Invalidate and force reload of a web2py component 799 */ 800 invalidate: function (target) { 801 $('div[data-w2p_remote]', target).each(function () { 802 var el = $('#' + $(this).attr('id')).get(0); 803 if (!web2py.isUndefined(el.timing)) { // Block triggering regular routines 804 clearInterval(el.timing); 805 } 806 }); 807 $.web2py.component_handler(target); 808 }, 809 main_hook: function () { 810 var flash = $('.w2p_flash'); 811 flash.hide(); 812 if (flash.html()) web2py.flash(flash.html()); 813 web2py.ajax_init(document); 814 web2py.event_handlers(); 815 web2py.a_handlers(); 816 web2py.form_handlers(); 817 } 818 }; 819 /*end of functions */ 820 /*main hook*/ 821 $(function () { 822 web2py.main_hook(); 823 }); 824 825})(jQuery); 826 827/* compatibility code - start */ 828ajax = jQuery.web2py.ajax; 829web2py_component = jQuery.web2py.component; 830web2py_websocket = jQuery.web2py.web2py_websocket; 831web2py_ajax_page = jQuery.web2py.ajax_page; 832/*needed for IS_STRONG(entropy)*/ 833web2py_validate_entropy = jQuery.web2py.validate_entropy; 834/*needed for crud.search and SQLFORM.grid's search*/ 835web2py_ajax_fields = jQuery.web2py.ajax_fields; 836/*used for LOAD(ajax=False)*/ 837web2py_trap_form = jQuery.web2py.trap_form; 838 839/*undocumented - rare*/ 840popup = jQuery.web2py.popup; 841collapse = jQuery.web2py.collapse; 842fade = jQuery.web2py.fade; 843 844/* internals - shouldn't be needed 845web2py_ajax_init = jQuery.web2py.ajax_init; 846web2py_event_handlers = jQuery.web2py.event_handlers; 847web2py_trap_link = jQuery.web2py.trap_link; 848web2py_calc_entropy = jQuery.web2py.calc_entropy; 849*/ 850/* 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.