[36a30d1] | 1 | /*! |
---|
| 2 | * jQuery Form Plugin |
---|
| 3 | * version: 3.51.0-2014.06.20 |
---|
| 4 | * Requires jQuery v1.5 or later |
---|
| 5 | * Copyright (c) 2014 M. Alsup |
---|
| 6 | * Examples and documentation at: http://malsup.com/jquery/form/ |
---|
| 7 | * Project repository: https://github.com/malsup/form |
---|
| 8 | * Dual licensed under the MIT and GPL licenses. |
---|
| 9 | * https://github.com/malsup/form#copyright-and-license |
---|
| 10 | */ |
---|
| 11 | /*global ActiveXObject */ |
---|
| 12 | |
---|
| 13 | // AMD support |
---|
| 14 | (function (factory) { |
---|
| 15 | "use strict"; |
---|
| 16 | if (typeof define === 'function' && define.amd) { |
---|
| 17 | // using AMD; register as anon module |
---|
| 18 | define(['jquery'], factory); |
---|
| 19 | } else { |
---|
| 20 | // no AMD; invoke directly |
---|
| 21 | factory( (typeof(jQuery) != 'undefined') ? jQuery : window.Zepto ); |
---|
| 22 | } |
---|
| 23 | } |
---|
| 24 | |
---|
| 25 | (function($) { |
---|
| 26 | "use strict"; |
---|
| 27 | |
---|
| 28 | /* |
---|
| 29 | Usage Note: |
---|
| 30 | ----------- |
---|
| 31 | Do not use both ajaxSubmit and ajaxForm on the same form. These |
---|
| 32 | functions are mutually exclusive. Use ajaxSubmit if you want |
---|
| 33 | to bind your own submit handler to the form. For example, |
---|
| 34 | |
---|
| 35 | $(document).ready(function() { |
---|
| 36 | $('#myForm').on('submit', function(e) { |
---|
| 37 | e.preventDefault(); // <-- important |
---|
| 38 | $(this).ajaxSubmit({ |
---|
| 39 | target: '#output' |
---|
| 40 | }); |
---|
| 41 | }); |
---|
| 42 | }); |
---|
| 43 | |
---|
| 44 | Use ajaxForm when you want the plugin to manage all the event binding |
---|
| 45 | for you. For example, |
---|
| 46 | |
---|
| 47 | $(document).ready(function() { |
---|
| 48 | $('#myForm').ajaxForm({ |
---|
| 49 | target: '#output' |
---|
| 50 | }); |
---|
| 51 | }); |
---|
| 52 | |
---|
| 53 | You can also use ajaxForm with delegation (requires jQuery v1.7+), so the |
---|
| 54 | form does not have to exist when you invoke ajaxForm: |
---|
| 55 | |
---|
| 56 | $('#myForm').ajaxForm({ |
---|
| 57 | delegation: true, |
---|
| 58 | target: '#output' |
---|
| 59 | }); |
---|
| 60 | |
---|
| 61 | When using ajaxForm, the ajaxSubmit function will be invoked for you |
---|
| 62 | at the appropriate time. |
---|
| 63 | */ |
---|
| 64 | |
---|
| 65 | /** |
---|
| 66 | * Feature detection |
---|
| 67 | */ |
---|
| 68 | var feature = {}; |
---|
| 69 | feature.fileapi = $("<input type='file'/>").get(0).files !== undefined; |
---|
| 70 | feature.formdata = window.FormData !== undefined; |
---|
| 71 | |
---|
| 72 | var hasProp = !!$.fn.prop; |
---|
| 73 | |
---|
| 74 | // attr2 uses prop when it can but checks the return type for |
---|
| 75 | // an expected string. this accounts for the case where a form |
---|
| 76 | // contains inputs with names like "action" or "method"; in those |
---|
| 77 | // cases "prop" returns the element |
---|
| 78 | $.fn.attr2 = function() { |
---|
| 79 | if ( ! hasProp ) { |
---|
| 80 | return this.attr.apply(this, arguments); |
---|
| 81 | } |
---|
| 82 | var val = this.prop.apply(this, arguments); |
---|
| 83 | if ( ( val && val.jquery ) || typeof val === 'string' ) { |
---|
| 84 | return val; |
---|
| 85 | } |
---|
| 86 | return this.attr.apply(this, arguments); |
---|
| 87 | }; |
---|
| 88 | |
---|
| 89 | /** |
---|
| 90 | * ajaxSubmit() provides a mechanism for immediately submitting |
---|
| 91 | * an HTML form using AJAX. |
---|
| 92 | */ |
---|
| 93 | $.fn.ajaxSubmit = function(options) { |
---|
| 94 | /*jshint scripturl:true */ |
---|
| 95 | |
---|
| 96 | // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) |
---|
| 97 | if (!this.length) { |
---|
| 98 | log('ajaxSubmit: skipping submit process - no element selected'); |
---|
| 99 | return this; |
---|
| 100 | } |
---|
| 101 | |
---|
| 102 | var method, action, url, $form = this; |
---|
| 103 | |
---|
| 104 | if (typeof options == 'function') { |
---|
| 105 | options = { success: options }; |
---|
| 106 | } |
---|
| 107 | else if ( options === undefined ) { |
---|
| 108 | options = {}; |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | method = options.type || this.attr2('method'); |
---|
| 112 | action = options.url || this.attr2('action'); |
---|
| 113 | |
---|
| 114 | url = (typeof action === 'string') ? $.trim(action) : ''; |
---|
| 115 | url = url || window.location.href || ''; |
---|
| 116 | if (url) { |
---|
| 117 | // clean url (don't include hash vaue) |
---|
| 118 | url = (url.match(/^([^#]+)/)||[])[1]; |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | options = $.extend(true, { |
---|
| 122 | url: url, |
---|
| 123 | success: $.ajaxSettings.success, |
---|
| 124 | type: method || $.ajaxSettings.type, |
---|
| 125 | iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' |
---|
| 126 | }, options); |
---|
| 127 | |
---|
| 128 | // hook for manipulating the form data before it is extracted; |
---|
| 129 | // convenient for use with rich editors like tinyMCE or FCKEditor |
---|
| 130 | var veto = {}; |
---|
| 131 | this.trigger('form-pre-serialize', [this, options, veto]); |
---|
| 132 | if (veto.veto) { |
---|
| 133 | log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); |
---|
| 134 | return this; |
---|
| 135 | } |
---|
| 136 | |
---|
| 137 | // provide opportunity to alter form data before it is serialized |
---|
| 138 | if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { |
---|
| 139 | log('ajaxSubmit: submit aborted via beforeSerialize callback'); |
---|
| 140 | return this; |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | var traditional = options.traditional; |
---|
| 144 | if ( traditional === undefined ) { |
---|
| 145 | traditional = $.ajaxSettings.traditional; |
---|
| 146 | } |
---|
| 147 | |
---|
| 148 | var elements = []; |
---|
| 149 | var qx, a = this.formToArray(options.semantic, elements); |
---|
| 150 | if (options.data) { |
---|
| 151 | options.extraData = options.data; |
---|
| 152 | qx = $.param(options.data, traditional); |
---|
| 153 | } |
---|
| 154 | |
---|
| 155 | // give pre-submit callback an opportunity to abort the submit |
---|
| 156 | if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { |
---|
| 157 | log('ajaxSubmit: submit aborted via beforeSubmit callback'); |
---|
| 158 | return this; |
---|
| 159 | } |
---|
| 160 | |
---|
| 161 | // fire vetoable 'validate' event |
---|
| 162 | this.trigger('form-submit-validate', [a, this, options, veto]); |
---|
| 163 | if (veto.veto) { |
---|
| 164 | log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); |
---|
| 165 | return this; |
---|
| 166 | } |
---|
| 167 | |
---|
| 168 | var q = $.param(a, traditional); |
---|
| 169 | if (qx) { |
---|
| 170 | q = ( q ? (q + '&' + qx) : qx ); |
---|
| 171 | } |
---|
| 172 | if (options.type.toUpperCase() == 'GET') { |
---|
| 173 | options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; |
---|
| 174 | options.data = null; // data is null for 'get' |
---|
| 175 | } |
---|
| 176 | else { |
---|
| 177 | options.data = q; // data is the query string for 'post' |
---|
| 178 | } |
---|
| 179 | |
---|
| 180 | var callbacks = []; |
---|
| 181 | if (options.resetForm) { |
---|
| 182 | callbacks.push(function() { $form.resetForm(); }); |
---|
| 183 | } |
---|
| 184 | if (options.clearForm) { |
---|
| 185 | callbacks.push(function() { $form.clearForm(options.includeHidden); }); |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | // perform a load on the target only if dataType is not provided |
---|
| 189 | if (!options.dataType && options.target) { |
---|
| 190 | var oldSuccess = options.success || function(){}; |
---|
| 191 | callbacks.push(function(data) { |
---|
| 192 | var fn = options.replaceTarget ? 'replaceWith' : 'html'; |
---|
| 193 | $(options.target)[fn](data).each(oldSuccess, arguments); |
---|
| 194 | }); |
---|
| 195 | } |
---|
| 196 | else if (options.success) { |
---|
| 197 | callbacks.push(options.success); |
---|
| 198 | } |
---|
| 199 | |
---|
| 200 | options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg |
---|
| 201 | var context = options.context || this ; // jQuery 1.4+ supports scope context |
---|
| 202 | for (var i=0, max=callbacks.length; i < max; i++) { |
---|
| 203 | callbacks[i].apply(context, [data, status, xhr || $form, $form]); |
---|
| 204 | } |
---|
| 205 | }; |
---|
| 206 | |
---|
| 207 | if (options.error) { |
---|
| 208 | var oldError = options.error; |
---|
| 209 | options.error = function(xhr, status, error) { |
---|
| 210 | var context = options.context || this; |
---|
| 211 | oldError.apply(context, [xhr, status, error, $form]); |
---|
| 212 | }; |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | if (options.complete) { |
---|
| 216 | var oldComplete = options.complete; |
---|
| 217 | options.complete = function(xhr, status) { |
---|
| 218 | var context = options.context || this; |
---|
| 219 | oldComplete.apply(context, [xhr, status, $form]); |
---|
| 220 | }; |
---|
| 221 | } |
---|
| 222 | |
---|
| 223 | // are there files to upload? |
---|
| 224 | |
---|
| 225 | // [value] (issue #113), also see comment: |
---|
| 226 | // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 |
---|
| 227 | var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; }); |
---|
| 228 | |
---|
| 229 | var hasFileInputs = fileInputs.length > 0; |
---|
| 230 | var mp = 'multipart/form-data'; |
---|
| 231 | var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); |
---|
| 232 | |
---|
| 233 | var fileAPI = feature.fileapi && feature.formdata; |
---|
| 234 | log("fileAPI :" + fileAPI); |
---|
| 235 | var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; |
---|
| 236 | |
---|
| 237 | var jqxhr; |
---|
| 238 | |
---|
| 239 | // options.iframe allows user to force iframe mode |
---|
| 240 | // 06-NOV-09: now defaulting to iframe mode if file input is detected |
---|
| 241 | if (options.iframe !== false && (options.iframe || shouldUseFrame)) { |
---|
| 242 | // hack to fix Safari hang (thanks to Tim Molendijk for this) |
---|
| 243 | // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d |
---|
| 244 | if (options.closeKeepAlive) { |
---|
| 245 | $.get(options.closeKeepAlive, function() { |
---|
| 246 | jqxhr = fileUploadIframe(a); |
---|
| 247 | }); |
---|
| 248 | } |
---|
| 249 | else { |
---|
| 250 | jqxhr = fileUploadIframe(a); |
---|
| 251 | } |
---|
| 252 | } |
---|
| 253 | else if ((hasFileInputs || multipart) && fileAPI) { |
---|
| 254 | jqxhr = fileUploadXhr(a); |
---|
| 255 | } |
---|
| 256 | else { |
---|
| 257 | jqxhr = $.ajax(options); |
---|
| 258 | } |
---|
| 259 | |
---|
| 260 | $form.removeData('jqxhr').data('jqxhr', jqxhr); |
---|
| 261 | |
---|
| 262 | // clear element array |
---|
| 263 | for (var k=0; k < elements.length; k++) { |
---|
| 264 | elements[k] = null; |
---|
| 265 | } |
---|
| 266 | |
---|
| 267 | // fire 'notify' event |
---|
| 268 | this.trigger('form-submit-notify', [this, options]); |
---|
| 269 | return this; |
---|
| 270 | |
---|
| 271 | // utility fn for deep serialization |
---|
| 272 | function deepSerialize(extraData){ |
---|
| 273 | var serialized = $.param(extraData, options.traditional).split('&'); |
---|
| 274 | var len = serialized.length; |
---|
| 275 | var result = []; |
---|
| 276 | var i, part; |
---|
| 277 | for (i=0; i < len; i++) { |
---|
| 278 | // #252; undo param space replacement |
---|
| 279 | serialized[i] = serialized[i].replace(/\+/g,' '); |
---|
| 280 | part = serialized[i].split('='); |
---|
| 281 | // #278; use array instead of object storage, favoring array serializations |
---|
| 282 | result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); |
---|
| 283 | } |
---|
| 284 | return result; |
---|
| 285 | } |
---|
| 286 | |
---|
| 287 | // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) |
---|
| 288 | function fileUploadXhr(a) { |
---|
| 289 | var formdata = new FormData(); |
---|
| 290 | |
---|
| 291 | for (var i=0; i < a.length; i++) { |
---|
| 292 | formdata.append(a[i].name, a[i].value); |
---|
| 293 | } |
---|
| 294 | |
---|
| 295 | if (options.extraData) { |
---|
| 296 | var serializedData = deepSerialize(options.extraData); |
---|
| 297 | for (i=0; i < serializedData.length; i++) { |
---|
| 298 | if (serializedData[i]) { |
---|
| 299 | formdata.append(serializedData[i][0], serializedData[i][1]); |
---|
| 300 | } |
---|
| 301 | } |
---|
| 302 | } |
---|
| 303 | |
---|
| 304 | options.data = null; |
---|
| 305 | |
---|
| 306 | var s = $.extend(true, {}, $.ajaxSettings, options, { |
---|
| 307 | contentType: false, |
---|
| 308 | processData: false, |
---|
| 309 | cache: false, |
---|
| 310 | type: method || 'POST' |
---|
| 311 | }); |
---|
| 312 | |
---|
| 313 | if (options.uploadProgress) { |
---|
| 314 | // workaround because jqXHR does not expose upload property |
---|
| 315 | s.xhr = function() { |
---|
| 316 | var xhr = $.ajaxSettings.xhr(); |
---|
| 317 | if (xhr.upload) { |
---|
| 318 | xhr.upload.addEventListener('progress', function(event) { |
---|
| 319 | var percent = 0; |
---|
| 320 | var position = event.loaded || event.position; /*event.position is deprecated*/ |
---|
| 321 | var total = event.total; |
---|
| 322 | if (event.lengthComputable) { |
---|
| 323 | percent = Math.ceil(position / total * 100); |
---|
| 324 | } |
---|
| 325 | options.uploadProgress(event, position, total, percent); |
---|
| 326 | }, false); |
---|
| 327 | } |
---|
| 328 | return xhr; |
---|
| 329 | }; |
---|
| 330 | } |
---|
| 331 | |
---|
| 332 | s.data = null; |
---|
| 333 | var beforeSend = s.beforeSend; |
---|
| 334 | s.beforeSend = function(xhr, o) { |
---|
| 335 | //Send FormData() provided by user |
---|
| 336 | if (options.formData) { |
---|
| 337 | o.data = options.formData; |
---|
| 338 | } |
---|
| 339 | else { |
---|
| 340 | o.data = formdata; |
---|
| 341 | } |
---|
| 342 | if(beforeSend) { |
---|
| 343 | beforeSend.call(this, xhr, o); |
---|
| 344 | } |
---|
| 345 | }; |
---|
| 346 | return $.ajax(s); |
---|
| 347 | } |
---|
| 348 | |
---|
| 349 | // private function for handling file uploads (hat tip to YAHOO!) |
---|
| 350 | function fileUploadIframe(a) { |
---|
| 351 | var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; |
---|
| 352 | var deferred = $.Deferred(); |
---|
| 353 | |
---|
| 354 | // #341 |
---|
| 355 | deferred.abort = function(status) { |
---|
| 356 | xhr.abort(status); |
---|
| 357 | }; |
---|
| 358 | |
---|
| 359 | if (a) { |
---|
| 360 | // ensure that every serialized input is still enabled |
---|
| 361 | for (i=0; i < elements.length; i++) { |
---|
| 362 | el = $(elements[i]); |
---|
| 363 | if ( hasProp ) { |
---|
| 364 | el.prop('disabled', false); |
---|
| 365 | } |
---|
| 366 | else { |
---|
| 367 | el.removeAttr('disabled'); |
---|
| 368 | } |
---|
| 369 | } |
---|
| 370 | } |
---|
| 371 | |
---|
| 372 | s = $.extend(true, {}, $.ajaxSettings, options); |
---|
| 373 | s.context = s.context || s; |
---|
| 374 | id = 'jqFormIO' + (new Date().getTime()); |
---|
| 375 | if (s.iframeTarget) { |
---|
| 376 | $io = $(s.iframeTarget); |
---|
| 377 | n = $io.attr2('name'); |
---|
| 378 | if (!n) { |
---|
| 379 | $io.attr2('name', id); |
---|
| 380 | } |
---|
| 381 | else { |
---|
| 382 | id = n; |
---|
| 383 | } |
---|
| 384 | } |
---|
| 385 | else { |
---|
| 386 | $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); |
---|
| 387 | $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); |
---|
| 388 | } |
---|
| 389 | io = $io[0]; |
---|
| 390 | |
---|
| 391 | |
---|
| 392 | xhr = { // mock object |
---|
| 393 | aborted: 0, |
---|
| 394 | responseText: null, |
---|
| 395 | responseXML: null, |
---|
| 396 | status: 0, |
---|
| 397 | statusText: 'n/a', |
---|
| 398 | getAllResponseHeaders: function() {}, |
---|
| 399 | getResponseHeader: function() {}, |
---|
| 400 | setRequestHeader: function() {}, |
---|
| 401 | abort: function(status) { |
---|
| 402 | var e = (status === 'timeout' ? 'timeout' : 'aborted'); |
---|
| 403 | log('aborting upload... ' + e); |
---|
| 404 | this.aborted = 1; |
---|
| 405 | |
---|
| 406 | try { // #214, #257 |
---|
| 407 | if (io.contentWindow.document.execCommand) { |
---|
| 408 | io.contentWindow.document.execCommand('Stop'); |
---|
| 409 | } |
---|
| 410 | } |
---|
| 411 | catch(ignore) {} |
---|
| 412 | |
---|
| 413 | $io.attr('src', s.iframeSrc); // abort op in progress |
---|
| 414 | xhr.error = e; |
---|
| 415 | if (s.error) { |
---|
| 416 | s.error.call(s.context, xhr, e, status); |
---|
| 417 | } |
---|
| 418 | if (g) { |
---|
| 419 | $.event.trigger("ajaxError", [xhr, s, e]); |
---|
| 420 | } |
---|
| 421 | if (s.complete) { |
---|
| 422 | s.complete.call(s.context, xhr, e); |
---|
| 423 | } |
---|
| 424 | } |
---|
| 425 | }; |
---|
| 426 | |
---|
| 427 | g = s.global; |
---|
| 428 | // trigger ajax global events so that activity/block indicators work like normal |
---|
| 429 | if (g && 0 === $.active++) { |
---|
| 430 | $.event.trigger("ajaxStart"); |
---|
| 431 | } |
---|
| 432 | if (g) { |
---|
| 433 | $.event.trigger("ajaxSend", [xhr, s]); |
---|
| 434 | } |
---|
| 435 | |
---|
| 436 | if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { |
---|
| 437 | if (s.global) { |
---|
| 438 | $.active--; |
---|
| 439 | } |
---|
| 440 | deferred.reject(); |
---|
| 441 | return deferred; |
---|
| 442 | } |
---|
| 443 | if (xhr.aborted) { |
---|
| 444 | deferred.reject(); |
---|
| 445 | return deferred; |
---|
| 446 | } |
---|
| 447 | |
---|
| 448 | // add submitting element to data if we know it |
---|
| 449 | sub = form.clk; |
---|
| 450 | if (sub) { |
---|
| 451 | n = sub.name; |
---|
| 452 | if (n && !sub.disabled) { |
---|
| 453 | s.extraData = s.extraData || {}; |
---|
| 454 | s.extraData[n] = sub.value; |
---|
| 455 | if (sub.type == "image") { |
---|
| 456 | s.extraData[n+'.x'] = form.clk_x; |
---|
| 457 | s.extraData[n+'.y'] = form.clk_y; |
---|
| 458 | } |
---|
| 459 | } |
---|
| 460 | } |
---|
| 461 | |
---|
| 462 | var CLIENT_TIMEOUT_ABORT = 1; |
---|
| 463 | var SERVER_ABORT = 2; |
---|
| 464 | |
---|
| 465 | function getDoc(frame) { |
---|
| 466 | /* it looks like contentWindow or contentDocument do not |
---|
| 467 | * carry the protocol property in ie8, when running under ssl |
---|
| 468 | * frame.document is the only valid response document, since |
---|
| 469 | * the protocol is know but not on the other two objects. strange? |
---|
| 470 | * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy |
---|
| 471 | */ |
---|
| 472 | |
---|
| 473 | var doc = null; |
---|
| 474 | |
---|
| 475 | // IE8 cascading access check |
---|
| 476 | try { |
---|
| 477 | if (frame.contentWindow) { |
---|
| 478 | doc = frame.contentWindow.document; |
---|
| 479 | } |
---|
| 480 | } catch(err) { |
---|
| 481 | // IE8 access denied under ssl & missing protocol |
---|
| 482 | log('cannot get iframe.contentWindow document: ' + err); |
---|
| 483 | } |
---|
| 484 | |
---|
| 485 | if (doc) { // successful getting content |
---|
| 486 | return doc; |
---|
| 487 | } |
---|
| 488 | |
---|
| 489 | try { // simply checking may throw in ie8 under ssl or mismatched protocol |
---|
| 490 | doc = frame.contentDocument ? frame.contentDocument : frame.document; |
---|
| 491 | } catch(err) { |
---|
| 492 | // last attempt |
---|
| 493 | log('cannot get iframe.contentDocument: ' + err); |
---|
| 494 | doc = frame.document; |
---|
| 495 | } |
---|
| 496 | return doc; |
---|
| 497 | } |
---|
| 498 | |
---|
| 499 | // Rails CSRF hack (thanks to Yvan Barthelemy) |
---|
| 500 | var csrf_token = $('meta[name=csrf-token]').attr('content'); |
---|
| 501 | var csrf_param = $('meta[name=csrf-param]').attr('content'); |
---|
| 502 | if (csrf_param && csrf_token) { |
---|
| 503 | s.extraData = s.extraData || {}; |
---|
| 504 | s.extraData[csrf_param] = csrf_token; |
---|
| 505 | } |
---|
| 506 | |
---|
| 507 | // take a breath so that pending repaints get some cpu time before the upload starts |
---|
| 508 | function doSubmit() { |
---|
| 509 | // make sure form attrs are set |
---|
| 510 | var t = $form.attr2('target'), |
---|
| 511 | a = $form.attr2('action'), |
---|
| 512 | mp = 'multipart/form-data', |
---|
| 513 | et = $form.attr('enctype') || $form.attr('encoding') || mp; |
---|
| 514 | |
---|
| 515 | // update form attrs in IE friendly way |
---|
| 516 | form.setAttribute('target',id); |
---|
| 517 | if (!method || /post/i.test(method) ) { |
---|
| 518 | form.setAttribute('method', 'POST'); |
---|
| 519 | } |
---|
| 520 | if (a != s.url) { |
---|
| 521 | form.setAttribute('action', s.url); |
---|
| 522 | } |
---|
| 523 | |
---|
| 524 | // ie borks in some cases when setting encoding |
---|
| 525 | if (! s.skipEncodingOverride && (!method || /post/i.test(method))) { |
---|
| 526 | $form.attr({ |
---|
| 527 | encoding: 'multipart/form-data', |
---|
| 528 | enctype: 'multipart/form-data' |
---|
| 529 | }); |
---|
| 530 | } |
---|
| 531 | |
---|
| 532 | // support timout |
---|
| 533 | if (s.timeout) { |
---|
| 534 | timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); |
---|
| 535 | } |
---|
| 536 | |
---|
| 537 | // look for server aborts |
---|
| 538 | function checkState() { |
---|
| 539 | try { |
---|
| 540 | var state = getDoc(io).readyState; |
---|
| 541 | log('state = ' + state); |
---|
| 542 | if (state && state.toLowerCase() == 'uninitialized') { |
---|
| 543 | setTimeout(checkState,50); |
---|
| 544 | } |
---|
| 545 | } |
---|
| 546 | catch(e) { |
---|
| 547 | log('Server abort: ' , e, ' (', e.name, ')'); |
---|
| 548 | cb(SERVER_ABORT); |
---|
| 549 | if (timeoutHandle) { |
---|
| 550 | clearTimeout(timeoutHandle); |
---|
| 551 | } |
---|
| 552 | timeoutHandle = undefined; |
---|
| 553 | } |
---|
| 554 | } |
---|
| 555 | |
---|
| 556 | // add "extra" data to form if provided in options |
---|
| 557 | var extraInputs = []; |
---|
| 558 | try { |
---|
| 559 | if (s.extraData) { |
---|
| 560 | for (var n in s.extraData) { |
---|
| 561 | if (s.extraData.hasOwnProperty(n)) { |
---|
| 562 | // if using the $.param format that allows for multiple values with the same name |
---|
| 563 | if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { |
---|
| 564 | extraInputs.push( |
---|
| 565 | $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value) |
---|
| 566 | .appendTo(form)[0]); |
---|
| 567 | } else { |
---|
| 568 | extraInputs.push( |
---|
| 569 | $('<input type="hidden" name="'+n+'">').val(s.extraData[n]) |
---|
| 570 | .appendTo(form)[0]); |
---|
| 571 | } |
---|
| 572 | } |
---|
| 573 | } |
---|
| 574 | } |
---|
| 575 | |
---|
| 576 | if (!s.iframeTarget) { |
---|
| 577 | // add iframe to doc and submit the form |
---|
| 578 | $io.appendTo('body'); |
---|
| 579 | } |
---|
| 580 | if (io.attachEvent) { |
---|
| 581 | io.attachEvent('onload', cb); |
---|
| 582 | } |
---|
| 583 | else { |
---|
| 584 | io.addEventListener('load', cb, false); |
---|
| 585 | } |
---|
| 586 | setTimeout(checkState,15); |
---|
| 587 | |
---|
| 588 | try { |
---|
| 589 | form.submit(); |
---|
| 590 | } catch(err) { |
---|
| 591 | // just in case form has element with name/id of 'submit' |
---|
| 592 | var submitFn = document.createElement('form').submit; |
---|
| 593 | submitFn.apply(form); |
---|
| 594 | } |
---|
| 595 | } |
---|
| 596 | finally { |
---|
| 597 | // reset attrs and remove "extra" input elements |
---|
| 598 | form.setAttribute('action',a); |
---|
| 599 | form.setAttribute('enctype', et); // #380 |
---|
| 600 | if(t) { |
---|
| 601 | form.setAttribute('target', t); |
---|
| 602 | } else { |
---|
| 603 | $form.removeAttr('target'); |
---|
| 604 | } |
---|
| 605 | $(extraInputs).remove(); |
---|
| 606 | } |
---|
| 607 | } |
---|
| 608 | |
---|
| 609 | if (s.forceSync) { |
---|
| 610 | doSubmit(); |
---|
| 611 | } |
---|
| 612 | else { |
---|
| 613 | setTimeout(doSubmit, 10); // this lets dom updates render |
---|
| 614 | } |
---|
| 615 | |
---|
| 616 | var data, doc, domCheckCount = 50, callbackProcessed; |
---|
| 617 | |
---|
| 618 | function cb(e) { |
---|
| 619 | if (xhr.aborted || callbackProcessed) { |
---|
| 620 | return; |
---|
| 621 | } |
---|
| 622 | |
---|
| 623 | doc = getDoc(io); |
---|
| 624 | if(!doc) { |
---|
| 625 | log('cannot access response document'); |
---|
| 626 | e = SERVER_ABORT; |
---|
| 627 | } |
---|
| 628 | if (e === CLIENT_TIMEOUT_ABORT && xhr) { |
---|
| 629 | xhr.abort('timeout'); |
---|
| 630 | deferred.reject(xhr, 'timeout'); |
---|
| 631 | return; |
---|
| 632 | } |
---|
| 633 | else if (e == SERVER_ABORT && xhr) { |
---|
| 634 | xhr.abort('server abort'); |
---|
| 635 | deferred.reject(xhr, 'error', 'server abort'); |
---|
| 636 | return; |
---|
| 637 | } |
---|
| 638 | |
---|
| 639 | if (!doc || doc.location.href == s.iframeSrc) { |
---|
| 640 | // response not received yet |
---|
| 641 | if (!timedOut) { |
---|
| 642 | return; |
---|
| 643 | } |
---|
| 644 | } |
---|
| 645 | if (io.detachEvent) { |
---|
| 646 | io.detachEvent('onload', cb); |
---|
| 647 | } |
---|
| 648 | else { |
---|
| 649 | io.removeEventListener('load', cb, false); |
---|
| 650 | } |
---|
| 651 | |
---|
| 652 | var status = 'success', errMsg; |
---|
| 653 | try { |
---|
| 654 | if (timedOut) { |
---|
| 655 | throw 'timeout'; |
---|
| 656 | } |
---|
| 657 | |
---|
| 658 | var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); |
---|
| 659 | log('isXml='+isXml); |
---|
| 660 | if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { |
---|
| 661 | if (--domCheckCount) { |
---|
| 662 | // in some browsers (Opera) the iframe DOM is not always traversable when |
---|
| 663 | // the onload callback fires, so we loop a bit to accommodate |
---|
| 664 | log('requeing onLoad callback, DOM not available'); |
---|
| 665 | setTimeout(cb, 250); |
---|
| 666 | return; |
---|
| 667 | } |
---|
| 668 | // let this fall through because server response could be an empty document |
---|
| 669 | //log('Could not access iframe DOM after mutiple tries.'); |
---|
| 670 | //throw 'DOMException: not available'; |
---|
| 671 | } |
---|
| 672 | |
---|
| 673 | //log('response detected'); |
---|
| 674 | var docRoot = doc.body ? doc.body : doc.documentElement; |
---|
| 675 | xhr.responseText = docRoot ? docRoot.innerHTML : null; |
---|
| 676 | xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; |
---|
| 677 | if (isXml) { |
---|
| 678 | s.dataType = 'xml'; |
---|
| 679 | } |
---|
| 680 | xhr.getResponseHeader = function(header){ |
---|
| 681 | var headers = {'content-type': s.dataType}; |
---|
| 682 | return headers[header.toLowerCase()]; |
---|
| 683 | }; |
---|
| 684 | // support for XHR 'status' & 'statusText' emulation : |
---|
| 685 | if (docRoot) { |
---|
| 686 | xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status; |
---|
| 687 | xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; |
---|
| 688 | } |
---|
| 689 | |
---|
| 690 | var dt = (s.dataType || '').toLowerCase(); |
---|
| 691 | var scr = /(json|script|text)/.test(dt); |
---|
| 692 | if (scr || s.textarea) { |
---|
| 693 | // see if user embedded response in textarea |
---|
| 694 | var ta = doc.getElementsByTagName('textarea')[0]; |
---|
| 695 | if (ta) { |
---|
| 696 | xhr.responseText = ta.value; |
---|
| 697 | // support for XHR 'status' & 'statusText' emulation : |
---|
| 698 | xhr.status = Number( ta.getAttribute('status') ) || xhr.status; |
---|
| 699 | xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; |
---|
| 700 | } |
---|
| 701 | else if (scr) { |
---|
| 702 | // account for browsers injecting pre around json response |
---|
| 703 | var pre = doc.getElementsByTagName('pre')[0]; |
---|
| 704 | var b = doc.getElementsByTagName('body')[0]; |
---|
| 705 | if (pre) { |
---|
| 706 | xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; |
---|
| 707 | } |
---|
| 708 | else if (b) { |
---|
| 709 | xhr.responseText = b.textContent ? b.textContent : b.innerText; |
---|
| 710 | } |
---|
| 711 | } |
---|
| 712 | } |
---|
| 713 | else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { |
---|
| 714 | xhr.responseXML = toXml(xhr.responseText); |
---|
| 715 | } |
---|
| 716 | |
---|
| 717 | try { |
---|
| 718 | data = httpData(xhr, dt, s); |
---|
| 719 | } |
---|
| 720 | catch (err) { |
---|
| 721 | status = 'parsererror'; |
---|
| 722 | xhr.error = errMsg = (err || status); |
---|
| 723 | } |
---|
| 724 | } |
---|
| 725 | catch (err) { |
---|
| 726 | log('error caught: ',err); |
---|
| 727 | status = 'error'; |
---|
| 728 | xhr.error = errMsg = (err || status); |
---|
| 729 | } |
---|
| 730 | |
---|
| 731 | if (xhr.aborted) { |
---|
| 732 | log('upload aborted'); |
---|
| 733 | status = null; |
---|
| 734 | } |
---|
| 735 | |
---|
| 736 | if (xhr.status) { // we've set xhr.status |
---|
| 737 | status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; |
---|
| 738 | } |
---|
| 739 | |
---|
| 740 | // ordering of these callbacks/triggers is odd, but that's how $.ajax does it |
---|
| 741 | if (status === 'success') { |
---|
| 742 | if (s.success) { |
---|
| 743 | s.success.call(s.context, data, 'success', xhr); |
---|
| 744 | } |
---|
| 745 | deferred.resolve(xhr.responseText, 'success', xhr); |
---|
| 746 | if (g) { |
---|
| 747 | $.event.trigger("ajaxSuccess", [xhr, s]); |
---|
| 748 | } |
---|
| 749 | } |
---|
| 750 | else if (status) { |
---|
| 751 | if (errMsg === undefined) { |
---|
| 752 | errMsg = xhr.statusText; |
---|
| 753 | } |
---|
| 754 | if (s.error) { |
---|
| 755 | s.error.call(s.context, xhr, status, errMsg); |
---|
| 756 | } |
---|
| 757 | deferred.reject(xhr, 'error', errMsg); |
---|
| 758 | if (g) { |
---|
| 759 | $.event.trigger("ajaxError", [xhr, s, errMsg]); |
---|
| 760 | } |
---|
| 761 | } |
---|
| 762 | |
---|
| 763 | if (g) { |
---|
| 764 | $.event.trigger("ajaxComplete", [xhr, s]); |
---|
| 765 | } |
---|
| 766 | |
---|
| 767 | if (g && ! --$.active) { |
---|
| 768 | $.event.trigger("ajaxStop"); |
---|
| 769 | } |
---|
| 770 | |
---|
| 771 | if (s.complete) { |
---|
| 772 | s.complete.call(s.context, xhr, status); |
---|
| 773 | } |
---|
| 774 | |
---|
| 775 | callbackProcessed = true; |
---|
| 776 | if (s.timeout) { |
---|
| 777 | clearTimeout(timeoutHandle); |
---|
| 778 | } |
---|
| 779 | |
---|
| 780 | // clean up |
---|
| 781 | setTimeout(function() { |
---|
| 782 | if (!s.iframeTarget) { |
---|
| 783 | $io.remove(); |
---|
| 784 | } |
---|
| 785 | else { //adding else to clean up existing iframe response. |
---|
| 786 | $io.attr('src', s.iframeSrc); |
---|
| 787 | } |
---|
| 788 | xhr.responseXML = null; |
---|
| 789 | }, 100); |
---|
| 790 | } |
---|
| 791 | |
---|
| 792 | var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) |
---|
| 793 | if (window.ActiveXObject) { |
---|
| 794 | doc = new ActiveXObject('Microsoft.XMLDOM'); |
---|
| 795 | doc.async = 'false'; |
---|
| 796 | doc.loadXML(s); |
---|
| 797 | } |
---|
| 798 | else { |
---|
| 799 | doc = (new DOMParser()).parseFromString(s, 'text/xml'); |
---|
| 800 | } |
---|
| 801 | return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; |
---|
| 802 | }; |
---|
| 803 | var parseJSON = $.parseJSON || function(s) { |
---|
| 804 | /*jslint evil:true */ |
---|
| 805 | return window['eval']('(' + s + ')'); |
---|
| 806 | }; |
---|
| 807 | |
---|
| 808 | var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 |
---|
| 809 | |
---|
| 810 | var ct = xhr.getResponseHeader('content-type') || '', |
---|
| 811 | xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, |
---|
| 812 | data = xml ? xhr.responseXML : xhr.responseText; |
---|
| 813 | |
---|
| 814 | if (xml && data.documentElement.nodeName === 'parsererror') { |
---|
| 815 | if ($.error) { |
---|
| 816 | $.error('parsererror'); |
---|
| 817 | } |
---|
| 818 | } |
---|
| 819 | if (s && s.dataFilter) { |
---|
| 820 | data = s.dataFilter(data, type); |
---|
| 821 | } |
---|
| 822 | if (typeof data === 'string') { |
---|
| 823 | if (type === 'json' || !type && ct.indexOf('json') >= 0) { |
---|
| 824 | data = parseJSON(data); |
---|
| 825 | } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { |
---|
| 826 | $.globalEval(data); |
---|
| 827 | } |
---|
| 828 | } |
---|
| 829 | return data; |
---|
| 830 | }; |
---|
| 831 | |
---|
| 832 | return deferred; |
---|
| 833 | } |
---|
| 834 | }; |
---|
| 835 | |
---|
| 836 | /** |
---|
| 837 | * ajaxForm() provides a mechanism for fully automating form submission. |
---|
| 838 | * |
---|
| 839 | * The advantages of using this method instead of ajaxSubmit() are: |
---|
| 840 | * |
---|
| 841 | * 1: This method will include coordinates for <input type="image" /> elements (if the element |
---|
| 842 | * is used to submit the form). |
---|
| 843 | * 2. This method will include the submit element's name/value data (for the element that was |
---|
| 844 | * used to submit the form). |
---|
| 845 | * 3. This method binds the submit() method to the form for you. |
---|
| 846 | * |
---|
| 847 | * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely |
---|
| 848 | * passes the options argument along after properly binding events for submit elements and |
---|
| 849 | * the form itself. |
---|
| 850 | */ |
---|
| 851 | $.fn.ajaxForm = function(options) { |
---|
| 852 | options = options || {}; |
---|
| 853 | options.delegation = options.delegation && $.isFunction($.fn.on); |
---|
| 854 | |
---|
| 855 | // in jQuery 1.3+ we can fix mistakes with the ready state |
---|
| 856 | if (!options.delegation && this.length === 0) { |
---|
| 857 | var o = { s: this.selector, c: this.context }; |
---|
| 858 | if (!$.isReady && o.s) { |
---|
| 859 | log('DOM not ready, queuing ajaxForm'); |
---|
| 860 | $(function() { |
---|
| 861 | $(o.s,o.c).ajaxForm(options); |
---|
| 862 | }); |
---|
| 863 | return this; |
---|
| 864 | } |
---|
| 865 | // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() |
---|
| 866 | log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); |
---|
| 867 | return this; |
---|
| 868 | } |
---|
| 869 | |
---|
| 870 | if ( options.delegation ) { |
---|
| 871 | $(document) |
---|
| 872 | .off('submit.form-plugin', this.selector, doAjaxSubmit) |
---|
| 873 | .off('click.form-plugin', this.selector, captureSubmittingElement) |
---|
| 874 | .on('submit.form-plugin', this.selector, options, doAjaxSubmit) |
---|
| 875 | .on('click.form-plugin', this.selector, options, captureSubmittingElement); |
---|
| 876 | return this; |
---|
| 877 | } |
---|
| 878 | |
---|
| 879 | return this.ajaxFormUnbind() |
---|
| 880 | .bind('submit.form-plugin', options, doAjaxSubmit) |
---|
| 881 | .bind('click.form-plugin', options, captureSubmittingElement); |
---|
| 882 | }; |
---|
| 883 | |
---|
| 884 | // private event handlers |
---|
| 885 | function doAjaxSubmit(e) { |
---|
| 886 | /*jshint validthis:true */ |
---|
| 887 | var options = e.data; |
---|
| 888 | if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed |
---|
| 889 | e.preventDefault(); |
---|
| 890 | $(e.target).ajaxSubmit(options); // #365 |
---|
| 891 | } |
---|
| 892 | } |
---|
| 893 | |
---|
| 894 | function captureSubmittingElement(e) { |
---|
| 895 | /*jshint validthis:true */ |
---|
| 896 | var target = e.target; |
---|
| 897 | var $el = $(target); |
---|
| 898 | if (!($el.is("[type=submit],[type=image]"))) { |
---|
| 899 | // is this a child element of the submit el? (ex: a span within a button) |
---|
| 900 | var t = $el.closest('[type=submit]'); |
---|
| 901 | if (t.length === 0) { |
---|
| 902 | return; |
---|
| 903 | } |
---|
| 904 | target = t[0]; |
---|
| 905 | } |
---|
| 906 | var form = this; |
---|
| 907 | form.clk = target; |
---|
| 908 | if (target.type == 'image') { |
---|
| 909 | if (e.offsetX !== undefined) { |
---|
| 910 | form.clk_x = e.offsetX; |
---|
| 911 | form.clk_y = e.offsetY; |
---|
| 912 | } else if (typeof $.fn.offset == 'function') { |
---|
| 913 | var offset = $el.offset(); |
---|
| 914 | form.clk_x = e.pageX - offset.left; |
---|
| 915 | form.clk_y = e.pageY - offset.top; |
---|
| 916 | } else { |
---|
| 917 | form.clk_x = e.pageX - target.offsetLeft; |
---|
| 918 | form.clk_y = e.pageY - target.offsetTop; |
---|
| 919 | } |
---|
| 920 | } |
---|
| 921 | // clear form vars |
---|
| 922 | setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); |
---|
| 923 | } |
---|
| 924 | |
---|
| 925 | |
---|
| 926 | // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm |
---|
| 927 | $.fn.ajaxFormUnbind = function() { |
---|
| 928 | return this.unbind('submit.form-plugin click.form-plugin'); |
---|
| 929 | }; |
---|
| 930 | |
---|
| 931 | /** |
---|
| 932 | * formToArray() gathers form element data into an array of objects that can |
---|
| 933 | * be passed to any of the following ajax functions: $.get, $.post, or load. |
---|
| 934 | * Each object in the array has both a 'name' and 'value' property. An example of |
---|
| 935 | * an array for a simple login form might be: |
---|
| 936 | * |
---|
| 937 | * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] |
---|
| 938 | * |
---|
| 939 | * It is this array that is passed to pre-submit callback functions provided to the |
---|
| 940 | * ajaxSubmit() and ajaxForm() methods. |
---|
| 941 | */ |
---|
| 942 | $.fn.formToArray = function(semantic, elements) { |
---|
| 943 | var a = []; |
---|
| 944 | if (this.length === 0) { |
---|
| 945 | return a; |
---|
| 946 | } |
---|
| 947 | |
---|
| 948 | var form = this[0]; |
---|
| 949 | var formId = this.attr('id'); |
---|
| 950 | var els = semantic ? form.getElementsByTagName('*') : form.elements; |
---|
| 951 | var els2; |
---|
| 952 | |
---|
| 953 | if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390 |
---|
| 954 | els = $(els).get(); // convert to standard array |
---|
| 955 | } |
---|
| 956 | |
---|
| 957 | // #386; account for inputs outside the form which use the 'form' attribute |
---|
| 958 | if ( formId ) { |
---|
| 959 | els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet |
---|
| 960 | if ( els2.length ) { |
---|
| 961 | els = (els || []).concat(els2); |
---|
| 962 | } |
---|
| 963 | } |
---|
| 964 | |
---|
| 965 | if (!els || !els.length) { |
---|
| 966 | return a; |
---|
| 967 | } |
---|
| 968 | |
---|
| 969 | var i,j,n,v,el,max,jmax; |
---|
| 970 | for(i=0, max=els.length; i < max; i++) { |
---|
| 971 | el = els[i]; |
---|
| 972 | n = el.name; |
---|
| 973 | if (!n || el.disabled) { |
---|
| 974 | continue; |
---|
| 975 | } |
---|
| 976 | |
---|
| 977 | if (semantic && form.clk && el.type == "image") { |
---|
| 978 | // handle image inputs on the fly when semantic == true |
---|
| 979 | if(form.clk == el) { |
---|
| 980 | a.push({name: n, value: $(el).val(), type: el.type }); |
---|
| 981 | a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
---|
| 982 | } |
---|
| 983 | continue; |
---|
| 984 | } |
---|
| 985 | |
---|
| 986 | v = $.fieldValue(el, true); |
---|
| 987 | if (v && v.constructor == Array) { |
---|
| 988 | if (elements) { |
---|
| 989 | elements.push(el); |
---|
| 990 | } |
---|
| 991 | for(j=0, jmax=v.length; j < jmax; j++) { |
---|
| 992 | a.push({name: n, value: v[j]}); |
---|
| 993 | } |
---|
| 994 | } |
---|
| 995 | else if (feature.fileapi && el.type == 'file') { |
---|
| 996 | if (elements) { |
---|
| 997 | elements.push(el); |
---|
| 998 | } |
---|
| 999 | var files = el.files; |
---|
| 1000 | if (files.length) { |
---|
| 1001 | for (j=0; j < files.length; j++) { |
---|
| 1002 | a.push({name: n, value: files[j], type: el.type}); |
---|
| 1003 | } |
---|
| 1004 | } |
---|
| 1005 | else { |
---|
| 1006 | // #180 |
---|
| 1007 | a.push({ name: n, value: '', type: el.type }); |
---|
| 1008 | } |
---|
| 1009 | } |
---|
| 1010 | else if (v !== null && typeof v != 'undefined') { |
---|
| 1011 | if (elements) { |
---|
| 1012 | elements.push(el); |
---|
| 1013 | } |
---|
| 1014 | a.push({name: n, value: v, type: el.type, required: el.required}); |
---|
| 1015 | } |
---|
| 1016 | } |
---|
| 1017 | |
---|
| 1018 | if (!semantic && form.clk) { |
---|
| 1019 | // input type=='image' are not found in elements array! handle it here |
---|
| 1020 | var $input = $(form.clk), input = $input[0]; |
---|
| 1021 | n = input.name; |
---|
| 1022 | if (n && !input.disabled && input.type == 'image') { |
---|
| 1023 | a.push({name: n, value: $input.val()}); |
---|
| 1024 | a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
---|
| 1025 | } |
---|
| 1026 | } |
---|
| 1027 | return a; |
---|
| 1028 | }; |
---|
| 1029 | |
---|
| 1030 | /** |
---|
| 1031 | * Serializes form data into a 'submittable' string. This method will return a string |
---|
| 1032 | * in the format: name1=value1&name2=value2 |
---|
| 1033 | */ |
---|
| 1034 | $.fn.formSerialize = function(semantic) { |
---|
| 1035 | //hand off to jQuery.param for proper encoding |
---|
| 1036 | return $.param(this.formToArray(semantic)); |
---|
| 1037 | }; |
---|
| 1038 | |
---|
| 1039 | /** |
---|
| 1040 | * Serializes all field elements in the jQuery object into a query string. |
---|
| 1041 | * This method will return a string in the format: name1=value1&name2=value2 |
---|
| 1042 | */ |
---|
| 1043 | $.fn.fieldSerialize = function(successful) { |
---|
| 1044 | var a = []; |
---|
| 1045 | this.each(function() { |
---|
| 1046 | var n = this.name; |
---|
| 1047 | if (!n) { |
---|
| 1048 | return; |
---|
| 1049 | } |
---|
| 1050 | var v = $.fieldValue(this, successful); |
---|
| 1051 | if (v && v.constructor == Array) { |
---|
| 1052 | for (var i=0,max=v.length; i < max; i++) { |
---|
| 1053 | a.push({name: n, value: v[i]}); |
---|
| 1054 | } |
---|
| 1055 | } |
---|
| 1056 | else if (v !== null && typeof v != 'undefined') { |
---|
| 1057 | a.push({name: this.name, value: v}); |
---|
| 1058 | } |
---|
| 1059 | }); |
---|
| 1060 | //hand off to jQuery.param for proper encoding |
---|
| 1061 | return $.param(a); |
---|
| 1062 | }; |
---|
| 1063 | |
---|
| 1064 | /** |
---|
| 1065 | * Returns the value(s) of the element in the matched set. For example, consider the following form: |
---|
| 1066 | * |
---|
| 1067 | * <form><fieldset> |
---|
| 1068 | * <input name="A" type="text" /> |
---|
| 1069 | * <input name="A" type="text" /> |
---|
| 1070 | * <input name="B" type="checkbox" value="B1" /> |
---|
| 1071 | * <input name="B" type="checkbox" value="B2"/> |
---|
| 1072 | * <input name="C" type="radio" value="C1" /> |
---|
| 1073 | * <input name="C" type="radio" value="C2" /> |
---|
| 1074 | * </fieldset></form> |
---|
| 1075 | * |
---|
| 1076 | * var v = $('input[type=text]').fieldValue(); |
---|
| 1077 | * // if no values are entered into the text inputs |
---|
| 1078 | * v == ['',''] |
---|
| 1079 | * // if values entered into the text inputs are 'foo' and 'bar' |
---|
| 1080 | * v == ['foo','bar'] |
---|
| 1081 | * |
---|
| 1082 | * var v = $('input[type=checkbox]').fieldValue(); |
---|
| 1083 | * // if neither checkbox is checked |
---|
| 1084 | * v === undefined |
---|
| 1085 | * // if both checkboxes are checked |
---|
| 1086 | * v == ['B1', 'B2'] |
---|
| 1087 | * |
---|
| 1088 | * var v = $('input[type=radio]').fieldValue(); |
---|
| 1089 | * // if neither radio is checked |
---|
| 1090 | * v === undefined |
---|
| 1091 | * // if first radio is checked |
---|
| 1092 | * v == ['C1'] |
---|
| 1093 | * |
---|
| 1094 | * The successful argument controls whether or not the field element must be 'successful' |
---|
| 1095 | * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). |
---|
| 1096 | * The default value of the successful argument is true. If this value is false the value(s) |
---|
| 1097 | * for each element is returned. |
---|
| 1098 | * |
---|
| 1099 | * Note: This method *always* returns an array. If no valid value can be determined the |
---|
| 1100 | * array will be empty, otherwise it will contain one or more values. |
---|
| 1101 | */ |
---|
| 1102 | $.fn.fieldValue = function(successful) { |
---|
| 1103 | for (var val=[], i=0, max=this.length; i < max; i++) { |
---|
| 1104 | var el = this[i]; |
---|
| 1105 | var v = $.fieldValue(el, successful); |
---|
| 1106 | if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { |
---|
| 1107 | continue; |
---|
| 1108 | } |
---|
| 1109 | if (v.constructor == Array) { |
---|
| 1110 | $.merge(val, v); |
---|
| 1111 | } |
---|
| 1112 | else { |
---|
| 1113 | val.push(v); |
---|
| 1114 | } |
---|
| 1115 | } |
---|
| 1116 | return val; |
---|
| 1117 | }; |
---|
| 1118 | |
---|
| 1119 | /** |
---|
| 1120 | * Returns the value of the field element. |
---|
| 1121 | */ |
---|
| 1122 | $.fieldValue = function(el, successful) { |
---|
| 1123 | var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); |
---|
| 1124 | if (successful === undefined) { |
---|
| 1125 | successful = true; |
---|
| 1126 | } |
---|
| 1127 | |
---|
| 1128 | if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || |
---|
| 1129 | (t == 'checkbox' || t == 'radio') && !el.checked || |
---|
| 1130 | (t == 'submit' || t == 'image') && el.form && el.form.clk != el || |
---|
| 1131 | tag == 'select' && el.selectedIndex == -1)) { |
---|
| 1132 | return null; |
---|
| 1133 | } |
---|
| 1134 | |
---|
| 1135 | if (tag == 'select') { |
---|
| 1136 | var index = el.selectedIndex; |
---|
| 1137 | if (index < 0) { |
---|
| 1138 | return null; |
---|
| 1139 | } |
---|
| 1140 | var a = [], ops = el.options; |
---|
| 1141 | var one = (t == 'select-one'); |
---|
| 1142 | var max = (one ? index+1 : ops.length); |
---|
| 1143 | for(var i=(one ? index : 0); i < max; i++) { |
---|
| 1144 | var op = ops[i]; |
---|
| 1145 | if (op.selected) { |
---|
| 1146 | var v = op.value; |
---|
| 1147 | if (!v) { // extra pain for IE... |
---|
| 1148 | v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; |
---|
| 1149 | } |
---|
| 1150 | if (one) { |
---|
| 1151 | return v; |
---|
| 1152 | } |
---|
| 1153 | a.push(v); |
---|
| 1154 | } |
---|
| 1155 | } |
---|
| 1156 | return a; |
---|
| 1157 | } |
---|
| 1158 | return $(el).val(); |
---|
| 1159 | }; |
---|
| 1160 | |
---|
| 1161 | /** |
---|
| 1162 | * Clears the form data. Takes the following actions on the form's input fields: |
---|
| 1163 | * - input text fields will have their 'value' property set to the empty string |
---|
| 1164 | * - select elements will have their 'selectedIndex' property set to -1 |
---|
| 1165 | * - checkbox and radio inputs will have their 'checked' property set to false |
---|
| 1166 | * - inputs of type submit, button, reset, and hidden will *not* be effected |
---|
| 1167 | * - button elements will *not* be effected |
---|
| 1168 | */ |
---|
| 1169 | $.fn.clearForm = function(includeHidden) { |
---|
| 1170 | return this.each(function() { |
---|
| 1171 | $('input,select,textarea', this).clearFields(includeHidden); |
---|
| 1172 | }); |
---|
| 1173 | }; |
---|
| 1174 | |
---|
| 1175 | /** |
---|
| 1176 | * Clears the selected form elements. |
---|
| 1177 | */ |
---|
| 1178 | $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { |
---|
| 1179 | var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list |
---|
| 1180 | return this.each(function() { |
---|
| 1181 | var t = this.type, tag = this.tagName.toLowerCase(); |
---|
| 1182 | if (re.test(t) || tag == 'textarea') { |
---|
| 1183 | this.value = ''; |
---|
| 1184 | } |
---|
| 1185 | else if (t == 'checkbox' || t == 'radio') { |
---|
| 1186 | this.checked = false; |
---|
| 1187 | } |
---|
| 1188 | else if (tag == 'select') { |
---|
| 1189 | this.selectedIndex = -1; |
---|
| 1190 | } |
---|
| 1191 | else if (t == "file") { |
---|
| 1192 | if (/MSIE/.test(navigator.userAgent)) { |
---|
| 1193 | $(this).replaceWith($(this).clone(true)); |
---|
| 1194 | } else { |
---|
| 1195 | $(this).val(''); |
---|
| 1196 | } |
---|
| 1197 | } |
---|
| 1198 | else if (includeHidden) { |
---|
| 1199 | // includeHidden can be the value true, or it can be a selector string |
---|
| 1200 | // indicating a special test; for example: |
---|
| 1201 | // $('#myForm').clearForm('.special:hidden') |
---|
| 1202 | // the above would clean hidden inputs that have the class of 'special' |
---|
| 1203 | if ( (includeHidden === true && /hidden/.test(t)) || |
---|
| 1204 | (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) { |
---|
| 1205 | this.value = ''; |
---|
| 1206 | } |
---|
| 1207 | } |
---|
| 1208 | }); |
---|
| 1209 | }; |
---|
| 1210 | |
---|
| 1211 | /** |
---|
| 1212 | * Resets the form data. Causes all form elements to be reset to their original value. |
---|
| 1213 | */ |
---|
| 1214 | $.fn.resetForm = function() { |
---|
| 1215 | return this.each(function() { |
---|
| 1216 | // guard against an input with the name of 'reset' |
---|
| 1217 | // note that IE reports the reset function as an 'object' |
---|
| 1218 | if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { |
---|
| 1219 | this.reset(); |
---|
| 1220 | } |
---|
| 1221 | }); |
---|
| 1222 | }; |
---|
| 1223 | |
---|
| 1224 | /** |
---|
| 1225 | * Enables or disables any matching elements. |
---|
| 1226 | */ |
---|
| 1227 | $.fn.enable = function(b) { |
---|
| 1228 | if (b === undefined) { |
---|
| 1229 | b = true; |
---|
| 1230 | } |
---|
| 1231 | return this.each(function() { |
---|
| 1232 | this.disabled = !b; |
---|
| 1233 | }); |
---|
| 1234 | }; |
---|
| 1235 | |
---|
| 1236 | /** |
---|
| 1237 | * Checks/unchecks any matching checkboxes or radio buttons and |
---|
| 1238 | * selects/deselects and matching option elements. |
---|
| 1239 | */ |
---|
| 1240 | $.fn.selected = function(select) { |
---|
| 1241 | if (select === undefined) { |
---|
| 1242 | select = true; |
---|
| 1243 | } |
---|
| 1244 | return this.each(function() { |
---|
| 1245 | var t = this.type; |
---|
| 1246 | if (t == 'checkbox' || t == 'radio') { |
---|
| 1247 | this.checked = select; |
---|
| 1248 | } |
---|
| 1249 | else if (this.tagName.toLowerCase() == 'option') { |
---|
| 1250 | var $sel = $(this).parent('select'); |
---|
| 1251 | if (select && $sel[0] && $sel[0].type == 'select-one') { |
---|
| 1252 | // deselect all other options |
---|
| 1253 | $sel.find('option').selected(false); |
---|
| 1254 | } |
---|
| 1255 | this.selected = select; |
---|
| 1256 | } |
---|
| 1257 | }); |
---|
| 1258 | }; |
---|
| 1259 | |
---|
| 1260 | // expose debug var |
---|
| 1261 | $.fn.ajaxSubmit.debug = false; |
---|
| 1262 | |
---|
| 1263 | // helper fn for console logging |
---|
| 1264 | function log() { |
---|
| 1265 | if (!$.fn.ajaxSubmit.debug) { |
---|
| 1266 | return; |
---|
| 1267 | } |
---|
| 1268 | var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); |
---|
| 1269 | if (window.console && window.console.log) { |
---|
| 1270 | window.console.log(msg); |
---|
| 1271 | } |
---|
| 1272 | else if (window.opera && window.opera.postError) { |
---|
| 1273 | window.opera.postError(msg); |
---|
| 1274 | } |
---|
| 1275 | } |
---|
| 1276 | |
---|
| 1277 | })); |
---|