vendor: 21,574 bytes, lines 1-570
1/** 2 * AJAX Nette Framework plugin for jQuery 3 * 4 * @copyright Copyright (c) 2009, 2010 Jan Marek 5 * @copyright Copyright (c) 2009, 2010 David Grudl 6 * @copyright Copyright (c) 2012-2014 VojtÄch DobeÅ¡ 7 * @license MIT 8 * 9 * @version 2.3.0 10 */ 11 12(function(window, $, undefined) { 13 14 if (typeof $ !== 'function') { 15 return console.error('nette.ajax.js: jQuery is missing, load it please'); 16 } 17 18 var nette = function () { 19 var inner = { 20 self: this, 21 initialized: false, 22 contexts: {}, 23 on: { 24 init: {}, 25 load: {}, 26 prepare: {}, 27 before: {}, 28 start: {}, 29 success: {}, 30 complete: {}, 31 error: {} 32 }, 33 fire: function () { 34 var result = true; 35 var args = Array.prototype.slice.call(arguments); 36 var props = args.shift(); 37 var name = (typeof props === 'string') ? props : props.name; 38 var off = (typeof props === 'object') ? props.off || {} : {}; 39 args.push(inner.self); 40 $.each(inner.on[name], function (index, reaction) { 41 if (reaction === undefined || $.inArray(index, off) !== -1) return true; 42 var temp = reaction.apply(inner.contexts[index], args); 43 return result = (temp === undefined || temp); 44 }); 45 return result; 46 }, 47 requestHandler: function (e) { 48 var xhr = inner.self.ajax({}, this, e); 49 if (xhr && xhr._returnFalse) { // for IE 8 50 return false; 51 } 52 }, 53 ext: function (callbacks, context, name) { 54 while (!name) { 55 name = 'ext_' + Math.random(); 56 if (inner.contexts[name]) { 57 name = undefined; 58 } 59 } 60 61 $.each(callbacks, function (event, callback) { 62 inner.on[event][name] = callback; 63 }); 64 inner.contexts[name] = $.extend(context ? context : {}, { 65 name: function () { 66 return name; 67 }, 68 ext: function (name, force) { 69 var ext = inner.contexts[name]; 70 if (!ext && force) throw "Extension '" + this.name() + "' depends on disabled extension '" + name + "'."; 71 return ext; 72 } 73 }); 74 } 75 }; 76 77 /** 78 * Allows manipulation with extensions. 79 * When called with 1. argument only, it returns extension with given name. 80 * When called with 2. argument equal to false, it removes extension entirely. 81 * When called with 2. argument equal to hash of event callbacks, it adds new extension. 82 * 83 * @param {string} Name of extension 84 * @param {bool|object|null} Set of callbacks for any events OR false for removing extension. 85 * @param {object|null} Context for added extension 86 * @return {$.nette|object} Provides a fluent interface OR returns extensions with given name 87 */ 88 this.ext = function (name, callbacks, context) { 89 if (typeof name === 'object') { 90 inner.ext(name, callbacks); 91 } else if (callbacks === undefined) { 92 return inner.contexts[name]; 93 } else if (!callbacks) { 94 $.each(['init', 'load', 'prepare', 'before', 'start', 'success', 'complete', 'error'], function (index, event) { 95 inner.on[event][name] = undefined; 96 }); 97 inner.contexts[name] = undefined; 98 } else if (typeof name === 'string' && inner.contexts[name] !== undefined) { 99 throw "Cannot override already registered nette-ajax extension '" + name + "'."; 100 } else { 101 inner.ext(callbacks, context, name); 102 } 103 return this; 104 }; 105 106 /** 107 * Initializes the plugin: 108 * - fires 'init' event, then 'load' event 109 * - when called with any arguments, it will override default 'init' extension 110 * with provided callbacks 111 * 112 * @param {function|object|null} Callback for 'load' event or entire set of callbacks for any events 113 * @param {object|null} Context provided for callbacks in first argument 114 * @return {$.nette} Provides a fluent interface 115 */ 116 this.init = function (load, loadContext) { 117 if (inner.initialized) throw 'Cannot initialize nette-ajax twice.'; 118 119 if (typeof load === 'function') { 120 this.ext('init', null); 121 this.ext('init', { 122 load: load 123 }, loadContext); 124 } else if (typeof load === 'object') { 125 this.ext('init', null); 126 this.ext('init', load, loadContext); 127 } else if (load !== undefined) { 128 throw 'Argument of init() can be function or function-hash only.'; 129 } 130 131 inner.initialized = true; 132 133 inner.fire('init'); 134 this.load(); 135 return this; 136 }; 137 138 /** 139 * Fires 'load' event 140 * 141 * @return {$.nette} Provides a fluent interface 142 */ 143 this.load = function () { 144 inner.fire('load', inner.requestHandler); 145 return this; 146 }; 147 148 /** 149 * Executes AJAX request. Attaches listeners and events. 150 * 151 * @param {object|string} settings or URL 152 * @param {Element|null} ussually Anchor or Form 153 * @param {event|null} event causing the request 154 * @return {jqXHR|null} 155 */ 156 this.ajax = function (settings, ui, e) { 157 if ($.type(settings) === 'string') { 158 settings = {url: settings}; 159 } 160 if (!settings.nette && ui && e) { 161 var $el = $(ui), xhr, originalBeforeSend; 162 var analyze = settings.nette = { 163 e: e, 164 ui: ui, 165 el: $el, 166 isForm: $el.is('form'), 167 isSubmit: $el.is('input[type=submit]') || $el.is('button[type=submit]'), 168 isImage: $el.is('input[type=image]'), 169 form: null 170 }; 171 172 if (analyze.isSubmit || analyze.isImage) { 173 analyze.form = analyze.el.closest('form'); 174 } else if (analyze.isForm) { 175 analyze.form = analyze.el; 176 } 177 178 if (!settings.url) { 179 settings.url = analyze.form ? analyze.form.attr('action') || window.location.pathname + window.location.search : ui.href; 180 } 181 if (!settings.type) { 182 settings.type = analyze.form ? analyze.form.attr('method') : 'get'; 183 } 184 185 if ($el.is('[data-ajax-off]')) { 186 var rawOff = $el.attr('data-ajax-off'); 187 if (rawOff.indexOf('[') === 0) { 188 settings.off = $el.data('ajaxOff'); 189 } else if (rawOff.indexOf(',') !== -1) { 190 settings.off = rawOff.split(','); 191 } else if (rawOff.indexOf(' ') !== -1) { 192 settings.off = rawOff.split(' '); 193 } else { 194 settings.off = rawOff; 195 } 196 if (typeof settings.off === 'string') settings.off = [settings.off]; 197 settings.off = $.grep($.each(settings.off, function (off) { 198 return $.trim(off); 199 }), function (off) { 200 return off.length; 201 }); 202 } 203 } 204 205 inner.fire({ 206 name: 'prepare', 207 off: settings.off || {} 208 }, settings); 209 if (settings.prepare) { 210 settings.prepare(settings); 211 } 212 213 originalBeforeSend = settings.beforeSend; 214 settings.beforeSend = function (xhr, settings) { 215 var result = inner.fire({ 216 name: 'before', 217 off: settings.off || {} 218 }, xhr, settings); 219 if ((result || result === undefined) && originalBeforeSend) { 220 result = originalBeforeSend(xhr, settings); 221 } 222 return result; 223 }; 224 225 return this.handleXHR($.ajax(settings), settings); 226 }; 227 228 /** 229 * Binds extension callbacks to existing XHR object 230 * 231 * @param {jqXHR|null} 232 * @param {object} settings 233 * @return {jqXHR|null} 234 */ 235 this.handleXHR = function (xhr, settings) { 236 settings = settings || {}; 237 238 if (xhr && (typeof xhr.statusText === 'undefined' || xhr.statusText !== 'canceled')) { 239 xhr.done(function (payload, status, xhr) { 240 inner.fire({ 241 name: 'success', 242 off: settings.off || {} 243 }, payload, status, xhr, settings); 244 }).fail(function (xhr, status, error) { 245 inner.fire({ 246 name: 'error', 247 off: settings.off || {} 248 }, xhr, status, error, settings); 249 }).always(function (xhr, status) { 250 inner.fire({ 251 name: 'complete', 252 off: settings.off || {} 253 }, xhr, status, settings); 254 }); 255 inner.fire({ 256 name: 'start', 257 off: settings.off || {} 258 }, xhr, settings); 259 if (settings.start) { 260 settings.start(xhr, settings); 261 } 262 } 263 return xhr; 264 }; 265 }; 266 267 $.nette = new ($.extend(nette, $.nette ? $.nette : {})); 268 269 $.fn.netteAjax = function (e, options) { 270 return $.nette.ajax(options || {}, this[0], e); 271 }; 272 273 $.fn.netteAjaxOff = function () { 274 return this.off('.nette'); 275 }; 276 277 $.nette.ext('validation', { 278 before: function (xhr, settings) { 279 if (!settings.nette) return true; 280 else var analyze = settings.nette; 281 var e = analyze.e; 282 283 var validate = $.extend(this.defaults, settings.validate || (function () { 284 if (!analyze.el.is('[data-ajax-validate]')) return; 285 var attr = analyze.el.data('ajaxValidate'); 286 if (attr === false) return { 287 keys: false, 288 url: false, 289 form: false 290 }; else if (typeof attr === 'object') return attr; 291 })() || {}); 292 293 var passEvent = false; 294 if (analyze.el.attr('data-ajax-pass') !== undefined) { 295 passEvent = analyze.el.data('ajaxPass'); 296 passEvent = typeof passEvent === 'bool' ? passEvent : true; 297 } 298 299 if (validate.keys) { 300 // thx to @vrana 301 var explicitNoAjax = e.button || e.ctrlKey || e.shiftKey || e.altKey || e.metaKey; 302 303 if (analyze.form) { 304 if (explicitNoAjax && analyze.isSubmit) { 305 this.explicitNoAjax = true; 306 return false; 307 } else if (analyze.isForm && this.explicitNoAjax) { 308 this.explicitNoAjax = false; 309 return false; 310 } 311 } else if (explicitNoAjax) return false; 312 } 313 314 if (validate.form && analyze.form) { 315 if (analyze.isSubmit || analyze.isImage) { 316 analyze.form.get(0)["nette-submittedBy"] = analyze.el.get(0); 317 } 318 var notValid; 319 if ((typeof Nette.version === 'undefined' || Nette.version == '2.3')) { // Nette 2.3 and older 320 var ie = this.ie(); 321 notValid = (analyze.form.get(0).onsubmit && analyze.form.get(0).onsubmit((typeof ie !== 'undefined' && ie < 9) ? undefined : e) === false); 322 } else { // Nette 2.4 and up 323 notValid = ((analyze.form.get(0).onsubmit ? analyze.form.triggerHandler('submit') : Nette.validateForm(analyze.form.get(0))) === false) 324 } 325 if (notValid) { 326 e.stopImmediatePropagation(); 327 e.preventDefault(); 328 return false; 329 } 330 } 331 332 if (validate.url) { 333 // thx to @vrana 334 var urlToValidate = analyze.form ? settings.url : analyze.el.attr('href'); 335 // Check if URL is absolute 336 if (/(?:^[a-z][a-z0-9+.-]*:|\/\/)/.test(urlToValidate)) { 337 // Parse absolute URL 338 var parsedUrl = new URL(urlToValidate); 339 if (/:|^#/.test(parsedUrl['pathname'] + parsedUrl['search'] + parsedUrl['hash'])) return false; 340 } else { 341 if (/:|^#/.test(urlToValidate)) return false; 342 } 343 } 344 345 if (!passEvent) { 346 e.stopPropagation(); 347 e.preventDefault(); 348 xhr._returnFalse = true; // for IE 8 349 } 350 return true; 351 } 352 }, { 353 defaults: { 354 keys: true, 355 url: true, 356 form: true 357 }, 358 explicitNoAjax: false, 359 ie: function (undefined) { // http://james.padolsey.com/javascript/detect-ie-in-js-using-conditional-comments/ 360 var v = 3; 361 var div = document.createElement('div'); 362 var all = div.getElementsByTagName('i'); 363 while ( 364 div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->', 365 all[0] 366 ); 367 return v > 4 ? v : undefined; 368 } 369 }); 370 371 $.nette.ext('forms', { 372 init: function () { 373 var snippets; 374 if (!window.Nette || !(snippets = this.ext('snippets'))) return; 375 376 snippets.after(function ($el) { 377 $el.find('form').each(function() { 378 window.Nette.initForm(this); 379 }); 380 }); 381 }, 382 prepare: function (settings) { 383 var analyze = settings.nette; 384 if (!analyze || !analyze.form) return; 385 var e = analyze.e; 386 var originalData = settings.data || {}; 387 var data = {}; 388 389 if (analyze.isSubmit) { 390 data[analyze.el.attr('name')] = analyze.el.val() || ''; 391 } else if (analyze.isImage) { 392 var offset = analyze.el.offset(); 393 var name = analyze.el.attr('name'); 394 var dataOffset = [ Math.max(0, e.pageX - offset.left), Math.max(0, e.pageY - offset.top) ]; 395 396 if (name.indexOf('[', 0) !== -1) { // inside a container 397 data[name] = dataOffset; 398 } else { 399 data[name + '.x'] = dataOffset[0]; 400 data[name + '.y'] = dataOffset[1]; 401 } 402 } 403 404 // https://developer.mozilla.org/en-US/docs/Web/Guide/Using_FormData_Objects#Sending_files_using_a_FormData_object 405 var formMethod = analyze.form.attr('method'); 406 if (formMethod && formMethod.toLowerCase() === 'post' && 'FormData' in window) { 407 var formData = new FormData(analyze.form[0]); 408 for (var i in data) { 409 formData.append(i, data[i]); 410 } 411 412 if (typeof originalData !== 'string') { 413 for (var i in originalData) { 414 formData.append(i, originalData[i]); 415 } 416 } 417 418 // remove empty file inputs as these causes Safari 11 to stall 419 // https://stackoverflow.com/questions/49672992/ajax-request-fails-when-sending-formdata-including-empty-file-input-in-safari 420 if (formData.entries && navigator.userAgent.match(/version\/11(\.[0-9]*)? safari/i)) { 421 for (var pair of formData.entries()) { 422 if (pair[1] instanceof File && pair[1].name === '' && pair[1].size === 0) { 423 formData.delete(pair[0]); 424 } 425 } 426 } 427 428 settings.data = formData; 429 settings.processData = false; 430 settings.contentType = false; 431 } else { 432 if (typeof originalData !== 'string') { 433 originalData = $.param(originalData); 434 } 435 data = $.param(data); 436 settings.data = analyze.form.serialize() + (data ? '&' + data : '') + '&' + originalData; 437 } 438 } 439 }); 440 441// default snippet handler 442 $.nette.ext('snippets', { 443 success: function (payload) { 444 if (payload.snippets) { 445 this.updateSnippets(payload.snippets); 446 } 447 } 448 }, { 449 beforeQueue: $.Callbacks(), 450 afterQueue: $.Callbacks(), 451 completeQueue: $.Callbacks(), 452 before: function (callback) { 453 this.beforeQueue.add(callback); 454 }, 455 after: function (callback) { 456 this.afterQueue.add(callback); 457 }, 458 complete: function (callback) { 459 this.completeQueue.add(callback); 460 }, 461 updateSnippets: function (snippets, back) { 462 var that = this; 463 var elements = []; 464 for (var i in snippets) { 465 var $el = this.getElement(i); 466 if ($el.get(0)) { 467 elements.push($el.get(0)); 468 } 469 this.updateSnippet($el, snippets[i], back); 470 } 471 $(elements).promise().done(function () { 472 that.completeQueue.fire(); 473 }); 474 }, 475 updateSnippet: function ($el, html, back) { 476 // Fix for setting document title in IE 477 if ($el.is('title')) { 478 document.title = html; 479 } else { 480 this.beforeQueue.fire($el); 481 this.applySnippet($el, html, back); 482 this.afterQueue.fire($el); 483 } 484 }, 485 getElement: function (id) { 486 return $('#' + this.escapeSelector(id)); 487 }, 488 applySnippet: function ($el, html, back) { 489 if (!back && $el.is('[data-ajax-append]')) { 490 $el.append(html); 491 } else if (!back && $el.is('[data-ajax-prepend]')) { 492 $el.prepend(html); 493 } else if ($el.html() != html || /<[^>]*script/.test(html)) { 494 $el.html(html); 495 } 496 }, 497 escapeSelector: function (selector) { 498 // thx to @uestla (https://github.com/uestla) 499 return selector.replace(/[\!"#\$%&'\(\)\*\+,\.\/:;<=>\?@\[\\\]\^`\{\|\}~]/g, '\\$&'); 500 } 501 }); 502 503// support $this->redirect() 504 $.nette.ext('redirect', { 505 success: function (payload) { 506 if (payload.redirect) { 507 window.location.href = payload.redirect; 508 return false; 509 } 510 } 511 }); 512 513// current page state 514 $.nette.ext('state', { 515 success: function (payload) { 516 if (payload.state) { 517 this.state = payload.state; 518 } 519 } 520 }, {state: null}); 521 522// abort last request if new started 523 $.nette.ext('unique', { 524 start: function (xhr) { 525 if (this.xhr) { 526 this.xhr.abort(); 527 } 528 this.xhr = xhr; 529 }, 530 complete: function () { 531 this.xhr = null; 532 } 533 }, {xhr: null}); 534 535// option to abort by ESC (thx to @vrana) 536 $.nette.ext('abort', { 537 init: function () { 538 $('body').keydown($.proxy(function (e) { 539 if (this.xhr && (e.keyCode.toString() === '27' // Esc 540 && !(e.ctrlKey || e.shiftKey || e.altKey || e.metaKey)) 541 ) { 542 this.xhr.abort(); 543 } 544 }, this)); 545 }, 546 start: function (xhr) { 547 this.xhr = xhr; 548 }, 549 complete: function () { 550 this.xhr = null; 551 } 552 }, {xhr: null}); 553 554 $.nette.ext('load', { 555 success: function () { 556 $.nette.load(); 557 } 558 }); 559 560// default ajaxification (can be overridden in init()) 561 $.nette.ext('init', { 562 load: function (rh) { 563 $(this.linkSelector).off('click.nette', rh).on('click.nette', rh); 564 $(this.formSelector).off('submit.nette', rh).on('submit.nette', rh) 565 .off('click.nette', ':image', rh).on('click.nette', ':image', rh) 566 .off('click.nette', ':submit', rh).on('click.nette', ':submit', rh); 567 $(this.buttonSelector).closest('form') 568 .off('click.nette', this.buttonSelector, rh).on('click.nette', this.buttonSelector, rh); 569 } 570 }, {
571 linkSelector: 'a.ajax', 572 formSelector: 'form.ajax', 573 buttonSelector: 'input.ajax[type="submit"], button.ajax[type="submit"], input.ajax[type="image"]' 574 }); 575 576})(window, window.jQuery);
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.