1(function($, undefined) { 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 // Cut down on the number of issues from people inadvertently including jquery_ujs twice 14 // by detecting and raising an error when it happens. 15 if ( $.rails !== undefined ) { 16 $.error('jquery-ujs has already been loaded!'); 17 } 18 19 // Shorthand to make it a little easier to call public rails functions from within rails.js 20 var rails; 21 var $document = $(document); 22 23 $.rails = rails = { 24 // Link elements bound by jquery-ujs 25 linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with], a[data-disable]', 26 27 // Button elements bound by jquery-ujs 28 buttonClickSelector: 'button[data-remote]:not(form button), button[data-confirm]:not(form button)', 29 30 // Select elements bound by jquery-ujs 31 inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', 32 33 // Form elements bound by jquery-ujs 34 formSubmitSelector: 'form', 35 36 // Form input elements bound by jquery-ujs 37 formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])', 38 39 // Form input elements disabled during form submission 40 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', 41 42 // Form input elements re-enabled after form submission 43 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', 44 45 // Form required input elements 46 requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', 47 48 // Form file input elements 49 fileInputSelector: 'input[type=file]', 50 51 // Link onClick disable selector with possible reenable after remote submission 52 linkDisableSelector: 'a[data-disable-with], a[data-disable]', 53 54 // Button onClick disable selector with possible reenable after remote submission 55 buttonDisableSelector: 'button[data-remote][data-disable-with], button[data-remote][data-disable]', 56 57 // Make sure that every Ajax request sends the CSRF token 58 CSRFProtection: function(xhr) { 59 var token = $('meta[name="csrf-token"]').attr('content'); 60 if (token) xhr.setRequestHeader('X-CSRF-Token', token); 61 }, 62 63 // making sure that all forms have actual up-to-date token(cached forms contain old one) 64 refreshCSRFTokens: function(){ 65 var csrfToken = $('meta[name=csrf-token]').attr('content'); 66 var csrfParam = $('meta[name=csrf-param]').attr('content'); 67 $('form input[name="' + csrfParam + '"]').val(csrfToken); 68 }, 69 70 // Triggers an event on an element and returns false if the event result is false 71 fire: function(obj, name, data) { 72 var event = $.Event(name); 73 obj.trigger(event, data); 74 return event.result !== false; 75 }, 76 77 // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm 78 confirm: function(message) { 79 return confirm(message); 80 }, 81 82 // Default ajax function, may be overridden with custom function in $.rails.ajax 83 ajax: function(options) { 84 return $.ajax(options); 85 }, 86 87 // Default way to get an element's href. May be overridden at $.rails.href. 88 href: function(element) { 89 return element.attr('href'); 90 }, 91 92 // Submits "remote" forms and links with ajax 93 handleRemote: function(element) { 94 var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options; 95 96 if (rails.fire(element, 'ajax:before')) { 97 elCrossDomain = element.data('cross-domain');
98 crossDomain = elCrossDomain === undefined ? null : elCrossDomain; 99 withCredentials = element.data('with-credentials') || null; 100 dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); 101 102 if (element.is('form')) { 103 method = element.attr('method'); 104 url = element.attr('action'); 105 data = element.serializeArray(); 106 // memoized value from clicked submit button 107 var button = element.data('ujs:submit-button'); 108 if (button) { 109 data.push(button); 110 element.data('ujs:submit-button', null); 111 } 112 } else if (element.is(rails.inputChangeSelector)) { 113 method = element.data('method'); 114 url = element.data('url'); 115 data = element.serialize(); 116 if (element.data('params')) data = data + "&" + element.data('params'); 117 } else if (element.is(rails.buttonClickSelector)) { 118 method = element.data('method') || 'get'; 119 url = element.data('url'); 120 data = element.serialize(); 121 if (element.data('params')) data = data + "&" + element.data('params'); 122 } else { 123 method = element.data('method'); 124 url = rails.href(element); 125 data = element.data('params') || null; 126 } 127 128 options = { 129 type: method || 'GET', data: data, dataType: dataType, 130 // stopping the "ajax:beforeSend" event will cancel the ajax request 131 beforeSend: function(xhr, settings) { 132 if (settings.dataType === undefined) { 133 xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); 134 } 135 if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) { 136 element.trigger('ajax:send', xhr); 137 } else { 138 return false; 139 } 140 }, 141 success: function(data, status, xhr) { 142 element.trigger('ajax:success', [data, status, xhr]); 143 }, 144 complete: function(xhr, status) { 145 element.trigger('ajax:complete', [xhr, status]); 146 }, 147 error: function(xhr, status, error) { 148 element.trigger('ajax:error', [xhr, status, error]); 149 }, 150 crossDomain: crossDomain 151 }; 152 153 // There is no withCredentials for IE6-8 when 154 // "Enable native XMLHTTP support" is disabled 155 if (withCredentials) { 156 options.xhrFields = { 157 withCredentials: withCredentials 158 }; 159 } 160 161 // Only pass url to `ajax` options if not blank 162 if (url) { options.url = url; } 163 164 return rails.ajax(options); 165 } else { 166 return false; 167 } 168 }, 169 170 // Handles "data-method" on links such as: 171 // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> 172 handleMethod: function(link) { 173 var href = rails.href(link), 174 method = link.data('method'), 175 target = link.attr('target'), 176 csrfToken = $('meta[name=csrf-token]').attr('content'), 177 csrfParam = $('meta[name=csrf-param]').attr('content'), 178 form = $('<form method="post" action="' + href + '"></form>'), 179 metadataInput = '<input name="_method" value="' + method + '" type="hidden" />'; 180 181 if (csrfParam !== undefined && csrfToken !== undefined) { 182 metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />'; 183 } 184 185 if (target) { form.attr('target', target); } 186 187 form.hide().append(metadataInput).appendTo('body'); 188 form.submit(); 189 }, 190 191 // Helper function that returns form elements that match the specified CSS selector 192 // If form is actually a "form" element this will return associated elements outside the from that have 193 // the html form attribute set 194 formElements: function(form, selector) { 195 return form.is('form') ? $(form[0].elements).filter(selector) : form.find(selector); 196 }, 197 198 /* Disables form elements: 199 - Caches element value in 'ujs:enable-with' data store 200 - Replaces element text with value of 'data-disable-with' attribute 201 - Sets disabled property to true 202 */ 203 disableFormElements: function(form) { 204 rails.formElements(form, rails.disableSelector).each(function() { 205 rails.disableFormElement($(this)); 206 }); 207 }, 208 209 disableFormElement: function(element) { 210 var method, replacement; 211 212 method = element.is('button') ? 'html' : 'val'; 213 replacement = element.data('disable-with'); 214 215 element.data('ujs:enable-with', element[method]()); 216 if (replacement !== undefined) { 217 element[method](replacement); 218 } 219 220 element.prop('disabled', true); 221 }, 222 223 /* Re-enables disabled form elements: 224 - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) 225 - Sets disabled property to false 226 */ 227 enableFormElements: function(form) { 228 rails.formElements(form, rails.enableSelector).each(function() { 229 rails.enableFormElement($(this)); 230 }); 231 }, 232 233 enableFormElement: function(element) { 234 var method = element.is('button') ? 'html' : 'val'; 235 if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); 236 element.prop('disabled', false); 237 }, 238 239 /* For 'data-confirm' attribute: 240 - Fires `confirm` event 241 - Shows the confirmation dialog 242 - Fires the `confirm:complete` event 243 244 Returns `true` if no function stops the chain and user chose yes; `false` otherwise. 245 Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog. 246 Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
247 return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog. 248 */ 249 allowAction: function(element) { 250 var message = element.data('confirm'), 251 answer = false, callback; 252 if (!message) { return true; } 253 254 if (rails.fire(element, 'confirm')) { 255 answer = rails.confirm(message); 256 callback = rails.fire(element, 'confirm:complete', [answer]); 257 } 258 return answer && callback; 259 }, 260 261 // Helper function which checks for blank inputs in a form that match the specified CSS selector 262 blankInputs: function(form, specifiedSelector, nonBlank) { 263 var inputs = $(), input, valueToCheck, 264 selector = specifiedSelector || 'input,textarea', 265 allInputs = form.find(selector); 266 267 allInputs.each(function() { 268 input = $(this); 269 valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val(); 270 // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey 271 if (!valueToCheck === !nonBlank) { 272 273 // Don't count unchecked required radio if other radio with same name is checked 274 if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) { 275 return true; // Skip to next input 276 } 277 278 inputs = inputs.add(input); 279 } 280 }); 281 return inputs.length ? inputs : false; 282 }, 283 284 // Helper function which checks for non-blank inputs in a form that match the specified CSS selector 285 nonBlankInputs: function(form, specifiedSelector) { 286 return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank 287 }, 288 289 // Helper function, needed to provide consistent behavior in IE 290 stopEverything: function(e) { 291 $(e.target).trigger('ujs:everythingStopped'); 292 e.stopImmediatePropagation(); 293 return false; 294 }, 295 296 // replace element's html with the 'data-disable-with' after storing original html 297 // and prevent clicking on it 298 disableElement: function(element) { 299 var replacement = element.data('disable-with'); 300 301 element.data('ujs:enable-with', element.html()); // store enabled state 302 if (replacement !== undefined) { 303 element.html(replacement); 304 } 305 306 element.bind('click.railsDisable', function(e) { // prevent further clicking 307 return rails.stopEverything(e); 308 }); 309 }, 310 311 // restore element to its original state which was disabled by 'disableElement' above 312 enableElement: function(element) { 313 if (element.data('ujs:enable-with') !== undefined) { 314 element.html(element.data('ujs:enable-with')); // set to old enabled state 315 element.removeData('ujs:enable-with'); // clean up cache 316 } 317 element.unbind('click.railsDisable'); // enable element 318 } 319 }; 320 321 if (rails.fire($document, 'rails:attachBindings')) { 322 323 $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); 324 325 $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() { 326 rails.enableElement($(this)); 327 }); 328 329 $document.delegate(rails.buttonDisableSelector, 'ajax:complete', function() { 330 rails.enableFormElement($(this)); 331 }); 332 333 $document.delegate(rails.linkClickSelector, 'click.rails', function(e) { 334 var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey; 335 if (!rails.allowAction(link)) return rails.stopEverything(e); 336 337 if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link); 338 339 if (link.data('remote') !== undefined) { 340 if (metaClick && (!method || method === 'GET') && !data) { return true; } 341 342 var handleRemote = rails.handleRemote(link); 343 // response from rails.handleRemote() will either be false or a deferred object promise. 344 if (handleRemote === false) { 345 rails.enableElement(link); 346 } else { 347 handleRemote.error( function() { rails.enableElement(link); } ); 348 } 349 return false;
350 351 } else if (link.data('method')) { 352 rails.handleMethod(link); 353 return false; 354 } 355 }); 356 357 $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) { 358 var button = $(this); 359 360 if (!rails.allowAction(button)) return rails.stopEverything(e); 361 362 if (button.is(rails.buttonDisableSelector)) rails.disableFormElement(button); 363 364 var handleRemote = rails.handleRemote(button); 365 // response from rails.handleRemote() will either be false or a deferred object promise. 366 if (handleRemote === false) { 367 rails.enableFormElement(button); 368 } else { 369 handleRemote.error( function() { rails.enableFormElement(button); } ); 370 } 371 return false; 372 }); 373 374 $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) { 375 var link = $(this); 376 if (!rails.allowAction(link)) return rails.stopEverything(e); 377 378 rails.handleRemote(link); 379 return false; 380 }); 381 382 $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) { 383 var form = $(this), 384 remote = form.data('remote') !== undefined, 385 blankRequiredInputs, 386 nonBlankFileInputs; 387 388 if (!rails.allowAction(form)) return rails.stopEverything(e); 389 390 // skip other logic when required values are missing or file upload is present 391 if (form.attr('novalidate') == undefined) { 392 blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector); 393 if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { 394 return rails.stopEverything(e); 395 } 396 } 397 398 if (remote) { 399 nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); 400 if (nonBlankFileInputs) { 401 // slight timeout so that the submit button gets properly serialized 402 // (make it easy for event handler to serialize form without disabled values) 403 setTimeout(function(){ rails.disableFormElements(form); }, 13); 404 var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]); 405 406 // re-enable form elements if event bindings return false (canceling normal form submission) 407 if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); } 408 409 return aborted; 410 } 411 412 rails.handleRemote(form); 413 return false; 414 415 } else { 416 // slight timeout so that the submit button gets properly serialized 417 setTimeout(function(){ rails.disableFormElements(form); }, 13); 418 } 419 }); 420 421 $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) { 422 var button = $(this); 423 424 if (!rails.allowAction(button)) return rails.stopEverything(event); 425 426 // register the pressed submit button 427 var name = button.attr('name'), 428 data = name ? {name:name, value:button.val()} : null; 429 430 button.closest('form').data('ujs:submit-button', data); 431 }); 432 433 $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) { 434 if (this == event.target) rails.disableFormElements($(this)); 435 }); 436 437 $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { 438 if (this == event.target) rails.enableFormElements($(this)); 439 }); 440 441 $(function(){ 442 rails.refreshCSRFTokens(); 443 }); 444 } 445 446})( 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.