1(function($, undefined) { 2 3/** 4 * Unobtrusive scripting adapter for jQuery 5 * 6 * Requires jQuery 1.6.0 or later. 7 * https://github.com/rails/jquery-ujs 8 9 * Uploading file using rails.js 10 * ============================= 11 * 12 * By default, browsers do not allow files to be uploaded via AJAX. As a result, if there are any non-blank file fields 13 * in the remote form, this adapter aborts the AJAX submission and allows the form to submit through standard means. 14 * 15 * The `ajax:aborted:file` event allows you to bind your own handler to process the form submission however you wish. 16 * 17 * Ex: 18 * $('form').live('ajax:aborted:file', function(event, elements){ 19 * // Implement own remote file-transfer handler here for non-blank file inputs passed in `elements`. 20 * // Returning false in this handler tells rails.js to disallow standard form submission 21 * return false; 22 * }); 23 * 24 * The `ajax:aborted:file` event is fired when a file-type input is detected with a non-blank value. 25 * 26 * Third-party tools can use this hook to detect when an AJAX file upload is attempted, and then use 27 * techniques like the iframe method to upload the file instead. 28 * 29 * Required fields in rails.js 30 * =========================== 31 * 32 * If any blank required inputs (required="required") are detected in the remote form, the whole form submission 33 * is canceled. Note that this is unlike file inputs, which still allow standard (non-AJAX) form submission. 34 * 35 * The `ajax:aborted:required` event allows you to bind your own handler to inform the user of blank required inputs. 36 * 37 * !! Note that Opera does not fire the form's submit event if there are blank required inputs, so this event may never 38 * get fired in Opera. This event is what causes other browsers to exhibit the same submit-aborting behavior. 39 * 40 * Ex: 41 * $('form').live('ajax:aborted:required', function(event, elements){ 42 * // Returning false in this handler tells rails.js to submit the form anyway. 43 * // The blank required inputs are passed to this function in `elements`. 44 * return ! confirm("Would you like to submit the form with missing info?"); 45 * }); 46 */ 47 48 // Shorthand to make it a little easier to call public rails functions from within rails.js 49 var rails; 50 51 $.rails = rails = { 52 // Link elements bound by jquery-ujs 53 linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]', 54 55 // Select elements bound by jquery-ujs 56 inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]', 57 58 // Form elements bound by jquery-ujs 59 formSubmitSelector: 'form', 60 61 // Form input elements bound by jquery-ujs 62 formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not(button[type])', 63 64 // Form input elements disabled during form submission 65 disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]', 66 67 // Form input elements re-enabled after form submission 68 enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled', 69 70 // Form required input elements 71 requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])', 72 73 // Form file input elements 74 fileInputSelector: 'input:file', 75 76 // Link onClick disable selector with possible reenable after remote submission 77 linkDisableSelector: 'a[data-disable-with]', 78 79 // Make sure that every Ajax request sends the CSRF token 80 CSRFProtection: function(xhr) { 81 var token = $('meta[name="csrf-token"]').attr('content'); 82 if (token) xhr.setRequestHeader('X-CSRF-Token', token); 83 }, 84 85 // Triggers an event on an element and returns false if the event result is false 86 fire: function(obj, name, data) { 87 var event = $.Event(name); 88 obj.trigger(event, data); 89 return event.result !== false; 90 }, 91 92 // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm 93 confirm: function(message) { 94 return confirm(message); 95 }, 96 97 // Default ajax function, may be overridden with custom function in $.rails.ajax 98 ajax: function(options) { 99 return $.ajax(options); 100 }, 101 102 // Default way to get an element's href. May be overridden at $.rails.href. 103 href: function(element) { 104 return element.attr('href'); 105 }, 106 107 // Submits "remote" forms and links with ajax 108 handleRemote: function(element) { 109 var method, url, data, crossDomain, dataType, options; 110 111 if (rails.fire(element, 'ajax:before')) { 112 crossDomain = element.data('cross-domain') || null; 113 dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType); 114 115 if (element.is('form')) { 116 method = element.attr('method'); 117 url = element.attr('action'); 118 data = element.serializeArray(); 119 // memoized value from clicked submit button 120 var button = element.data('ujs:submit-button'); 121 if (button) { 122 data.push(button); 123 element.data('ujs:submit-button', null); 124 } 125 } else if (element.is(rails.inputChangeSelector)) { 126 method = element.data('method'); 127 url = element.data('url'); 128 data = element.serialize(); 129 if (element.data('params')) data = data + "&" + element.data('params'); 130 } else { 131 method = element.data('method'); 132 url = rails.href(element); 133 data = element.data('params') || null; 134 } 135 136 options = { 137 type: method || 'GET', data: data, dataType: dataType, crossDomain: crossDomain, 138 // stopping the "ajax:beforeSend" event will cancel the ajax request 139 beforeSend: function(xhr, settings) { 140 if (settings.dataType === undefined) { 141 xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script); 142 } 143 return rails.fire(element, 'ajax:beforeSend', [xhr, settings]); 144 }, 145 success: function(data, status, xhr) {
146 element.trigger('ajax:success', [data, status, xhr]); 147 }, 148 complete: function(xhr, status) { 149 element.trigger('ajax:complete', [xhr, status]); 150 }, 151 error: function(xhr, status, error) { 152 element.trigger('ajax:error', [xhr, status, error]); 153 } 154 }; 155 // Only pass url to `ajax` options if not blank 156 if (url) { options.url = url; } 157 158 return rails.ajax(options); 159 } else { 160 return false; 161 } 162 }, 163 164 // Handles "data-method" on links such as: 165 // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a> 166 handleMethod: function(link) { 167 var href = rails.href(link), 168 method = link.data('method'), 169 target = link.attr('target'), 170 csrf_token = $('meta[name=csrf-token]').attr('content'), 171 csrf_param = $('meta[name=csrf-param]').attr('content'), 172 form = $('<form method="post" action="' + href + '"></form>'), 173 metadata_input = '<input name="_method" value="' + method + '" type="hidden" />'; 174 175 if (csrf_param !== undefined && csrf_token !== undefined) { 176 metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />'; 177 } 178 179 if (target) { form.attr('target', target); } 180 181 form.hide().append(metadata_input).appendTo('body'); 182 form.submit(); 183 }, 184 185 /* Disables form elements: 186 - Caches element value in 'ujs:enable-with' data store 187 - Replaces element text with value of 'data-disable-with' attribute 188 - Sets disabled property to true 189 */ 190 disableFormElements: function(form) { 191 form.find(rails.disableSelector).each(function() { 192 var element = $(this), method = element.is('button') ? 'html' : 'val'; 193 element.data('ujs:enable-with', element[method]()); 194 element[method](element.data('disable-with')); 195 element.prop('disabled', true); 196 }); 197 }, 198 199 /* Re-enables disabled form elements: 200 - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`) 201 - Sets disabled property to false 202 */ 203 enableFormElements: function(form) { 204 form.find(rails.enableSelector).each(function() { 205 var element = $(this), method = element.is('button') ? 'html' : 'val'; 206 if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with')); 207 element.prop('disabled', false); 208 }); 209 }, 210 211 /* For 'data-confirm' attribute: 212 - Fires `confirm` event 213 - Shows the confirmation dialog 214 - Fires the `confirm:complete` event 215 216 Returns `true` if no function stops the chain and user chose yes; `false` otherwise. 217 Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog. 218 Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function 219 return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog. 220 */ 221 allowAction: function(element) { 222 var message = element.data('confirm'), 223 answer = false, callback; 224 if (!message) { return true; } 225 226 if (rails.fire(element, 'confirm')) { 227 answer = rails.confirm(message); 228 callback = rails.fire(element, 'confirm:complete', [answer]); 229 } 230 return answer && callback; 231 }, 232 233 // Helper function which checks for blank inputs in a form that match the specified CSS selector 234 blankInputs: function(form, specifiedSelector, nonBlank) { 235 var inputs = $(), input, 236 selector = specifiedSelector || 'input,textarea'; 237 form.find(selector).each(function() { 238 input = $(this); 239 // Collect non-blank inputs if nonBlank option is true, otherwise, collect blank inputs 240 if (nonBlank ? input.val() : !input.val()) { 241 inputs = inputs.add(input); 242 } 243 }); 244 return inputs.length ? inputs : false; 245 }, 246 247 // Helper function which checks for non-blank inputs in a form that match the specified CSS
247selector 248 nonBlankInputs: function(form, specifiedSelector) { 249 return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank 250 }, 251 252 // Helper function, needed to provide consistent behavior in IE 253 stopEverything: function(e) { 254 $(e.target).trigger('ujs:everythingStopped'); 255 e.stopImmediatePropagation(); 256 return false; 257 }, 258 259 // find all the submit events directly bound to the form and 260 // manually invoke them. If anyone returns false then stop the loop 261 callFormSubmitBindings: function(form, event) { 262 var events = form.data('events'), continuePropagation = true; 263 if (events !== undefined && events['submit'] !== undefined) { 264 $.each(events['submit'], function(i, obj){ 265 if (typeof obj.handler === 'function') return continuePropagation = obj.handler(event); 266 }); 267 } 268 return continuePropagation; 269 }, 270 271 // replace element's html with the 'data-disable-with' after storing original html 272 // and prevent clicking on it 273 disableElement: function(element) { 274 element.data('ujs:enable-with', element.html()); // store enabled state 275 element.html(element.data('disable-with')); // set to disabled state 276 element.bind('click.railsDisable', function(e) { // prevent further clicking 277 return rails.stopEverything(e) 278 }); 279 }, 280 281 // restore element to its original state which was disabled by 'disableElement' above 282 enableElement: function(element) { 283 if (element.data('ujs:enable-with') !== undefined) { 284 element.html(element.data('ujs:enable-with')); // set to old enabled state 285 // this should be element.removeData('ujs:enable-with') 286 // but, there is currently a bug in jquery which makes hyphenated data attributes not get removed 287 element.data('ujs:enable-with', false); // clean up cache 288 } 289 element.unbind('click.railsDisable'); // enable element 290 } 291 292 }; 293 294 $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }}); 295 296 $(document).delegate(rails.linkDisableSelector, 'ajax:complete', function() { 297 rails.enableElement($(this)); 298 }); 299 300 $(document).delegate(rails.linkClickSelector, 'click.rails', function(e) { 301 var link = $(this), method = link.data('method'), data = link.data('params'); 302 if (!rails.allowAction(link)) return rails.stopEverything(e); 303 304 if (link.is(rails.linkDisableSelector)) rails.disableElement(link); 305 306 if (link.data('remote') !== undefined) { 307 if ( (e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data ) { return true; } 308 309 if (rails.handleRemote(link) === false) { rails.enableElement(link); } 310 return false; 311 312 } else if (link.data('method')) { 313 rails.handleMethod(link); 314 return false; 315 } 316 }); 317 318 $(document).delegate(rails.inputChangeSelector, 'change.rails', function(e) { 319 var link = $(this); 320 if (!rails.allowAction(link)) return rails.stopEverything(e); 321 322 rails.handleRemote(link); 323 return false; 324 }); 325 326 $(document).delegate(rails.formSubmitSelector, 'submit.rails', function(e) { 327 var form = $(this), 328 remote = form.data('remote') !== undefined, 329 blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector), 330 nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector); 331 332 if (!rails.allowAction(form)) return rails.stopEverything(e); 333 334 // skip other logic when required values are missing or file upload is present 335 if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) { 336 return rails.stopEverything(e); 337 } 338 339 if (remote) { 340 if (nonBlankFileInputs) { 341 return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]); 342 } 343 344 // If browser does not support submit bubbling, then this live-binding will be called before direct 345 // bindings. Therefore, we should directly call any direct bindings before remotely submitting form. 346 if (!$.support.submitBubbles && $().jquery < '1.7' && rails.callFormSubmitBindings(form, e) === false) return rails.stopEverything(e); 347 348 rails.handleRemote(form); 349 return false;
350 351 } else { 352 // slight timeout so that the submit button gets properly serialized 353 setTimeout(function(){ rails.disableFormElements(form); }, 13); 354 } 355 }); 356 357 $(document).delegate(rails.formInputClickSelector, 'click.rails', function(event) { 358 var button = $(this); 359 360 if (!rails.allowAction(button)) return rails.stopEverything(event); 361 362 // register the pressed submit button 363 var name = button.attr('name'), 364 data = name ? {name:name, value:button.val()} : null; 365 366 button.closest('form').data('ujs:submit-button', data); 367 }); 368 369 $(document).delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) { 370 if (this == event.target) rails.disableFormElements($(this)); 371 }); 372 373 $(document).delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) { 374 if (this == event.target) rails.enableFormElements($(this)); 375 }); 376 377})( 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.