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 | })); |
---|