vendor: 21,932 bytes, lines 1-565
1/* jshint node: true */ 2 3/** 4 * Unobtrusive scripting adapter for jQuery 5 * https://github.com/rails/jquery-ujs 6 * 7 * Requires jQuery 1.8.0 or later. 8 * 9 * Released under the MIT license 10 * 11 */ 12 13(function() { 14 'use strict'; 15 16 var jqueryUjsInit = function($, undefined) { 17 18 // Cut down on the number of issues from people inadvertently including jquery_ujs twice 19 // by detecting and raising an error when it happens. 20 if ( $.rails !== undefined ) { 21 $.error('jquery-ujs has already been loaded!'); 22 } 23 24 // Shorthand to make it a little easier to call public rails functions from within rails.js 25 var rails; 26 var $document = $(document); 27 28 $.rails = rails = { 29 // Link elements bound by jquery-ujs 30 linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]', 31 32 // Button elements bound by jquery-ujs 33 buttonClickSelector: 'button[data-remote]:not([form]):not(form button), button[data-confirm]:not([form]):not(form button)', 34 35 // Select elements bound by jquery-ujs 36 inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', 37 38 // Form elements bound by jquery-ujs 39 formSubmitSelector: 'form:not([data-turbo=true])', 40 41 // Form input elements bound by jquery-ujs 42 formInputClickSelector: 'form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])', 43 44 // Form input elements disabled during form submission 45 disableSelector: 'input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled', 46 47 // Form input elements re-enabled after form submission 48 enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled', 49 50 // Form required input elements 51 requiredInputSelector: 'input[name][required]:not([disabled]), textarea[name][required]:not([disabled])', 52 53 // Form file input elements 54 fileInputSelector: 'input[name][type=file]:not([disabled])', 55 56 // Link onClick disable selector with possible reenable after remote submission 57 linkDisableSelector: 'a[data-disable-with], a[data-disable]', 58 59 // Button onClick disable selector with possible reenable after remote submission 60 buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]', 61 62 // Up-to-date Cross-Site Request Forgery token 63 csrfToken: function() { 64 return $('meta[name=csrf-token]').attr('content'); 65 }, 66 67 // URL param that must contain the CSRF token 68 csrfParam: function() { 69 return $('meta[name=csrf-param]').attr('content'); 70 }, 71 72 // Make sure that every Ajax request sends the CSRF token 73 CSRFProtection: function(xhr) { 74 var token = rails.csrfToken(); 75 if (token) xhr.setRequestHeader('X-CSRF-Token', token); 76 }, 77 78 // Make sure that all forms have actual up-to-date tokens (cached forms contain old ones) 79 refreshCSRFTokens: function(){ 80 $('form input[name="' + rails.csrfParam() + '"]').val(rails.csrfToken()); 81 }, 82 83 // Triggers an event on an element and returns false if the event result is false 84 fire: function(obj, name, data) { 85 var event = $.Event(name); 86 obj.trigger(event, data); 87 return event.result !== false; 88 }, 89 90 // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm 91 confirm: function(message) { 92 return confirm(message); 93 }, 94 95 // Default ajax function, may be overridden with custom function in $.rails.ajax 96 ajax: function(options) { 97 return $.ajax(options); 98 }, 99 100 // Default way to get an element's href. May be overridden at $.rails.href. 101 href: function(element) { 102 return element[0].href; 103 }, 104 105 // Checks "data-remote" if true to handle the request through a XHR request. 106 isRemote: function(element) { 107 return element.data('remote') !== undefined && element.data('remote') !== false; 108 }, 109 110 // Submits "remote" forms and links with ajax 111 handleRemote: function(element) { 112 var method, url, data, withCredentials, dataType, options; 113 114 if (rails.fire(element, 'ajax:before')) { 115 withCredentials = element.data('with-credentials') || null; 116 dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); 117 118 if (element.is('form')) { 119 method = element.data('ujs:submit-button-formmethod') || element.attr('method'); 120 url = element.data('ujs:submit-button-formaction') || element.attr('action'); 121 data = $(element[0]).serializeArray(); 122 // memoized value from clicked submit button 123 var button = element.data('ujs:submit-button'); 124 if (button) { 125 data.push(button); 126 element.data('ujs:submit-button', null); 127 } 128 element.data('ujs:submit-button-formmethod', null); 129 element.data('ujs:submit-button-formaction', null); 130 } else if (element.is(rails.inputChangeSelector)) { 131 method = element.data('method'); 132 url = element.data('url'); 133 data = element.serialize(); 134 if (element.data('params')) data = data + '&' + element.data('params'); 135 } else if (element.is(rails.buttonClickSelector)) { 136 method = element.data('method') || 'get'; 137 url = element.data('url'); 138 data = element.serialize(); 139 if (element.data('params')) data = data + '&' + element.data('params'); 140 } else { 141 method = element.data('method'); 142 url = rails.href(element); 143 data = element.data('params') || null; 144 } 145 146 options = { 147 type: method || 'GET', data: data, dataType: dataType, 148 // stopping the "ajax:beforeSend" event will cancel the ajax request 149 beforeSend: function(xhr, settings) { 150 if (settings.dataType === undefined) { 151 xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); 152 } 153 if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) { 154 element.trigger('ajax:send', xhr); 155 } else { 156 return false; 157 } 158 }, 159 success: function(data, status, xhr) { 160 element.trigger('ajax:success', [data, status, xhr]); 161 }, 162 complete: function(xhr, status) { 163 element.trigger('ajax:complete', [xhr, status]); 164 }, 165 error: function(xhr, status, error) { 166 element.trigger('ajax:error', [xhr, status, error]); 167 }, 168 crossDomain: rails.isCrossDomain(url) 169 }; 170 171 // There is no withCredentials for IE6-8 when 172 // "Enable native XMLHTTP support" is disabled 173 if (withCredentials) { 174 options.xhrFields = { 175 withCredentials: withCredentials 176 }; 177 } 178 179 // Only pass url to `ajax` options if not blank 180 if (url) { options.url = url; } 181 182 return rails.ajax(options); 183 } else { 184 return false; 185 } 186 }, 187 188 // Determines if the request is a cross domain request. 189 isCrossDomain: function(url) { 190 var originAnchor = document.createElement('a'); 191 originAnchor.href = location.href; 192 var urlAnchor = document.createElement('a'); 193 194 try { 195 urlAnchor.href = url; 196 // This is a workaround to a IE bug. 197 urlAnchor.href = urlAnchor.href; 198 199 // If URL protocol is false or is a string containing a single colon 200 // *and* host are false, assume it is not a cross-domain request 201 // (should only be the case for IE7 and IE compatibility mode). 202 // Otherwise, evaluate protocol and host of the URL against the origin 203 // protocol and host. 204 return !(((!urlAnchor.protocol || urlAnchor.protocol === ':') && !urlAnchor.host) || 205 (originAnchor.protocol + '//' + originAnchor.host === 206 urlAnchor.protocol + '//' + urlAnchor.host)); 207 } catch (e) { 208 // If there is an error parsing the URL, assume it is crossDomain. 209 return true; 210 } 211 }, 212 213 // Handles "data-method" on links such as: 214 // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> 215 handleMethod: function(link) { 216 var href = rails.href(link), 217 method = link.data('method'), 218 target = link.attr('target'), 219 csrfToken = rails.csrfToken(), 220 csrfParam = rails.csrfParam(), 221 form = $('<form method="post" action="' + href + '"></form>'), 222 metadataInput = '<input name="_method" value="' + method + '" type="hidden" />'; 223 224 if (csrfParam !== undefined && csrfToken !== undefined && !rails.isCrossDomain(href)) { 225 metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />'; 226 } 227 228 if (target) { form.attr('target', target); } 229 230 form.hide().append(metadataInput).appendTo('body'); 231 form.submit(); 232 }, 233 234 // Helper function that returns form elements that match the specified CSS selector 235 // If form is actually a "form" element this will return associated elements outside the from that have 236 // the html form attribute set 237 formElements: function(form, selector) { 238 return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector); 239 }, 240 241 /* Disables form elements: 242 - Caches element value in 'ujs:enable-with' data store 243 - Replaces element text with value of 'data-disable-with' attribute 244 - Sets disabled property to true 245 */ 246 disableFormElements: function(form) { 247 rails.formElements(form, rails.disableSelector).each(function() { 248 rails.disableFormElement($(this)); 249 }); 250 }, 251 252 disableFormElement: function(element) { 253 var method, replacement; 254 255 method = element.is('button') ? 'html' : 'val'; 256 replacement = element.data('disable-with'); 257 258 if (replacement !== undefined) { 259 element.data('ujs:enable-with', element[method]()); 260 element[method](replacement); 261 } 262 263 element.prop('disabled', true); 264 element.data('ujs:disabled', true); 265 }, 266 267 /* Re-enables disabled form elements: 268 - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) 269 - Sets disabled property to false 270 */ 271 enableFormElements: function(form) { 272 rails.formElements(form, rails.enableSelector).each(function() { 273 rails.enableFormElement($(this)); 274 }); 275 }, 276 277 enableFormElement: function(element) { 278 var method = element.is('button') ? 'html' : 'val'; 279 if (element.data('ujs:enable-with') !== undefined) { 280 element[method](element.data('ujs:enable-with')); 281 element.removeData('ujs:enable-with'); // clean up cache 282 } 283 element.prop('disabled', false); 284 element.removeData('ujs:disabled'); 285 }, 286 287 /* For 'data-confirm' attribute: 288 - Fires `confirm` event 289 - Shows the confirmation dialog 290 - Fires the `confirm:complete` event 291 292 Returns `true` if no function stops the chain and user chose yes; `false` otherwise. 293 Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog. 294 Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function 295 return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog. 296 */ 297 allowAction: function(element) { 298 var message = element.data('confirm'), 299 answer = false, callback; 300 if (!message) { return true; } 301 302 if (rails.fire(element, 'confirm')) { 303 try { 304 answer = rails.confirm(message); 305 } catch (e) { 306 (console.error || console.log).call(console, e.stack || e); 307 } 308 callback = rails.fire(element, 'confirm:complete', [answer]); 309 } 310 return answer && callback; 311 }, 312 313 // Helper function which checks for blank inputs in a form that match the specified CSS selector 314 blankInputs: function(form, specifiedSelector, nonBlank) { 315 var foundInputs = $(), 316 input, 317 valueToCheck, 318 radiosForNameWithNoneSelected, 319 radioName, 320 selector = specifiedSelector || 'input,textarea', 321 requiredInputs = form.find(selector), 322 checkedRadioButtonNames = {}; 323 324 requiredInputs.each(function() { 325 input = $(this); 326 if (input.is('input[type=radio]')) { 327 328 // Don't count unchecked required radio as blank if other radio with same name is checked, 329 // regardless of whether same-name radio input has required attribute or not. The spec 330 // states https://www.w3.org/TR/html5/forms.html#the-required-attribute 331 radioName = input.attr('name'); 332 333 // Skip if we've already seen the radio with this name. 334 if (!checkedRadioButtonNames[radioName]) { 335 336 // If none checked 337 if (form.find('input[type=radio]:checked[name="' + radioName + '"]').length === 0) { 338 radiosForNameWithNoneSelected = form.find( 339 'input[type=radio][name="' + radioName + '"]'); 340 foundInputs = foundInputs.add(radiosForNameWithNoneSelected); 341 } 342 343 // We only need to check each name once. 344 checkedRadioButtonNames[radioName] = radioName; 345 } 346 } else { 347 valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : !!input.val(); 348 if (valueToCheck === nonBlank) { 349 foundInputs = foundInputs.add(input); 350 } 351 } 352 }); 353 return foundInputs.length ? foundInputs : false; 354 }, 355 356 // Helper function which checks for non-blank inputs in a form that match the specified CSS selector 357 nonBlankInputs: function(form, specifiedSelector) { 358 return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank 359 }, 360 361 // Helper function, needed to provide consistent behavior in IE 362 stopEverything: function(e) { 363 $(e.target).trigger('ujs:everythingStopped'); 364 e.stopImmediatePropagation(); 365 return false; 366 }, 367 368 // Replace element's html with the 'data-disable-with' after storing original html 369 // and prevent clicking on it 370 disableElement: function(element) { 371 var replacement = element.data('disable-with'); 372 373 if (replacement !== undefined) { 374 element.data('ujs:enable-with', element.html()); // store enabled state 375 element.html(replacement); 376 } 377 378 element.on('click.railsDisable', function(e) { // prevent further clicking 379 return rails.stopEverything(e); 380 }); 381 element.data('ujs:disabled', true); 382 }, 383 384 // Restore element to its original state which was disabled by 'disableElement' above 385 enableElement: function(element) { 386 if (element.data('ujs:enable-with') !== undefined) { 387 element.html(element.data('ujs:enable-with')); // set to old enabled state 388 element.removeData('ujs:enable-with'); // clean up cache 389 } 390 element.off('click.railsDisable'); // enable element 391 element.removeData('ujs:disabled'); 392 } 393 }; 394 395 if (rails.fire($document, 'rails:attachBindings')) { 396 397 $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); 398 399 // This event works the same as the load event, except that it fires every 400 // time the page is loaded. 401 // 402 // See https://github.com/rails/jquery-ujs/issues/357 403 // See https://developer.mozilla.org/en-US/docs/Using_Firefox_1.5_caching 404 $(window).on('pageshow.rails', function () { 405 $($.rails.enableSelector).each(function () { 406 var element = $(this); 407 408 if (element.data('ujs:disabled')) { 409 $.rails.enableFormElement(element); 410 } 411 }); 412 413 $($.rails.linkDisableSelector).each(function () { 414 var element = $(this); 415 416 if (element.data('ujs:disabled')) { 417 $.rails.enableElement(element); 418 } 419 }); 420 }); 421 422 $document.on('ajax:complete', rails.linkDisableSelector, function() { 423 rails.enableElement($(this)); 424 }); 425 426 $document.on('ajax:complete', rails.buttonDisableSelector, function() { 427 rails.enableFormElement($(this)); 428 }); 429 430 $document.on('click.rails', rails.linkClickSelector, function(e) { 431 var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey; 432 if (!rails.allowAction(link)) return rails.stopEverything(e); 433 434 if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link); 435 436 if (rails.isRemote(link)) { 437 if (metaClick && (!method || method === 'GET') && !data) { return true; } 438 439 var handleRemote = rails.handleRemote(link); 440 // Response from rails.handleRemote() will either be false or a deferred object promise. 441 if (handleRemote === false) { 442 rails.enableElement(link); 443 } else { 444 handleRemote.fail( function() { rails.enableElement(link); } ); 445 } 446 return false; 447 448 } else if (method) { 449 rails.handleMethod(link); 450 return false; 451 } 452 }); 453 454 $document.on('click.rails', rails.buttonClickSelector, function(e) { 455 var button = $(this); 456 457 if (!rails.allowAction(button) || !rails.isRemote(button)) return rails.stopEverything(e); 458 459 if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button); 460 461 var handleRemote = rails.handleRemote(button); 462 // Response from rails.handleRemote() will either be false or a deferred object promise. 463 if (handleRemote === false) { 464 rails.enableFormElement(button); 465 } else { 466 handleRemote.fail( function() { rails.enableFormElement(button); } ); 467 } 468 return false; 469 }); 470 471 $document.on('change.rails', rails.inputChangeSelector, function(e) { 472 var link = $(this); 473 if (!rails.allowAction(link) || !rails.isRemote(link)) return rails.stopEverything(e); 474 475 rails.handleRemote(link); 476 return false; 477 }); 478 479 $document.on('submit.rails', rails.formSubmitSelector, function(e) { 480 var form = $(this), 481 remote = rails.isRemote(form), 482 blankRequiredInputs, 483 nonBlankFileInputs; 484 485 if (!rails.allowAction(form)) return rails.stopEverything(e); 486 487 // Skip other logic when required values are missing or file upload is present 488 if (form.attr('novalidate') === undefined) { 489 if (form.data('ujs:formnovalidate-button') === undefined) { 490 blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector, false); 491 if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { 492 return rails.stopEverything(e); 493 } 494 } else { 495 // Clear the formnovalidate in case the next button click is not on a formnovalidate button 496 // Not strictly necessary to do here, since it is also reset on each button click, but just to be certain 497 form.data('ujs:formnovalidate-button', undefined); 498 } 499 } 500 501 if (remote) { 502 nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); 503 if (nonBlankFileInputs) { 504 // Slight timeout so that the submit button gets properly serialized 505 // (make it easy for event handler to serialize form without disabled values) 506 setTimeout(function(){ rails.disableFormElements(form); }, 13); 507 var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]); 508 509 // Re-enable form elements if event bindings return false (canceling normal form submission) 510 if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); } 511 512 return aborted; 513 } 514 515 rails.handleRemote(form); 516 return false; 517 518 } else { 519 // Slight timeout so that the submit button gets properly serialized 520 setTimeout(function(){ rails.disableFormElements(form); }, 13); 521 } 522 }); 523 524 $document.on('click.rails', rails.formInputClickSelector, function(event) { 525 var button = $(this); 526 527 if (!rails.allowAction(button)) return rails.stopEverything(event); 528 529 // Register the pressed submit button 530 var name = button.attr('name'), 531 data = name ? {name:name, value:button.val()} : null; 532 533 var form = button.closest('form'); 534 if (form.length === 0) { 535 form = $('#' + button.attr('form')); 536 } 537 form.data('ujs:submit-button', data); 538 539 // Save attributes from button 540 form.data('ujs:formnovalidate-button', button.attr('formnovalidate')); 541 form.data('ujs:submit-button-formaction', button.attr('formaction')); 542 form.data('ujs:submit-button-formmethod', button.attr('formmethod')); 543 }); 544 545 $document.on('ajax:send.rails', rails.formSubmitSelector, function(event) { 546 if (this === event.target) rails.disableFormElements($(this)); 547 }); 548 549 $document.on('ajax:complete.rails', rails.formSubmitSelector, function(event) { 550 if (this === event.target) rails.enableFormElements($(this)); 551 }); 552 553 $(function(){ 554 rails.refreshCSRFTokens(); 555 }); 556 } 557 558 }; 559 560 if (window.jQuery) { 561 jqueryUjsInit(jQuery); 562 } else if (typeof exports === 'object' && typeof module === 'object') { 563 module.exports = jqueryUjsInit; 564 } 565})();
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.