1 /* |
1 /*! |
2 * jQuery Form Plugin |
2 * jQuery Form Plugin |
3 * version: 2.02 (12/16/2007) |
3 * version: 2.73 (03-MAY-2011) |
4 * @requires jQuery v1.1 or later |
4 * @requires jQuery v1.3.2 or later |
5 * |
5 * |
6 * Examples at: http://malsup.com/jquery/form/ |
6 * Examples and documentation at: http://malsup.com/jquery/form/ |
7 * Dual licensed under the MIT and GPL licenses: |
7 * Dual licensed under the MIT and GPL licenses: |
8 * http://www.opensource.org/licenses/mit-license.php |
8 * http://www.opensource.org/licenses/mit-license.php |
9 * http://www.gnu.org/licenses/gpl.html |
9 * http://www.gnu.org/licenses/gpl.html |
10 * |
10 */ |
11 * Revision: $Id$ |
11 ;(function($) { |
12 */ |
12 |
13 (function($) { |
13 /* |
14 /** |
14 Usage Note: |
15 * ajaxSubmit() provides a mechanism for submitting an HTML form using AJAX. |
15 ----------- |
16 * |
16 Do not use both ajaxSubmit and ajaxForm on the same form. These |
17 * ajaxSubmit accepts a single argument which can be either a success callback function |
17 functions are intended to be exclusive. Use ajaxSubmit if you want |
18 * or an options Object. If a function is provided it will be invoked upon successful |
18 to bind your own submit handler to the form. For example, |
19 * completion of the submit and will be passed the response from the server. |
19 |
20 * If an options Object is provided, the following attributes are supported: |
20 $(document).ready(function() { |
21 * |
21 $('#myForm').bind('submit', function(e) { |
22 * target: Identifies the element(s) in the page to be updated with the server response. |
22 e.preventDefault(); // <-- important |
23 * This value may be specified as a jQuery selection string, a jQuery object, |
23 $(this).ajaxSubmit({ |
24 * or a DOM element. |
24 target: '#output' |
25 * default value: null |
25 }); |
26 * |
26 }); |
27 * url: URL to which the form data will be submitted. |
27 }); |
28 * default value: value of form's 'action' attribute |
28 |
29 * |
29 Use ajaxForm when you want the plugin to manage all the event binding |
30 * type: The method in which the form data should be submitted, 'GET' or 'POST'. |
30 for you. For example, |
31 * default value: value of form's 'method' attribute (or 'GET' if none found) |
31 |
32 * |
32 $(document).ready(function() { |
33 * data: Additional data to add to the request, specified as key/value pairs (see $.ajax). |
33 $('#myForm').ajaxForm({ |
34 * |
34 target: '#output' |
35 * beforeSubmit: Callback method to be invoked before the form is submitted. |
35 }); |
36 * default value: null |
36 }); |
37 * |
37 |
38 * success: Callback method to be invoked after the form has been successfully submitted |
38 When using ajaxForm, the ajaxSubmit function will be invoked for you |
39 * and the response has been returned from the server |
39 at the appropriate time. |
40 * default value: null |
40 */ |
41 * |
41 |
42 * dataType: Expected dataType of the response. One of: null, 'xml', 'script', or 'json' |
42 /** |
43 * default value: null |
43 * ajaxSubmit() provides a mechanism for immediately submitting |
44 * |
44 * an HTML form using AJAX. |
45 * semantic: Boolean flag indicating whether data must be submitted in semantic order (slower). |
|
46 * default value: false |
|
47 * |
|
48 * resetForm: Boolean flag indicating whether the form should be reset if the submit is successful |
|
49 * |
|
50 * clearForm: Boolean flag indicating whether the form should be cleared if the submit is successful |
|
51 * |
|
52 * |
|
53 * The 'beforeSubmit' callback can be provided as a hook for running pre-submit logic or for |
|
54 * validating the form data. If the 'beforeSubmit' callback returns false then the form will |
|
55 * not be submitted. The 'beforeSubmit' callback is invoked with three arguments: the form data |
|
56 * in array format, the jQuery object, and the options object passed into ajaxSubmit. |
|
57 * The form data array takes the following form: |
|
58 * |
|
59 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] |
|
60 * |
|
61 * If a 'success' callback method is provided it is invoked after the response has been returned |
|
62 * from the server. It is passed the responseText or responseXML value (depending on dataType). |
|
63 * See jQuery.ajax for further details. |
|
64 * |
|
65 * |
|
66 * The dataType option provides a means for specifying how the server response should be handled. |
|
67 * This maps directly to the jQuery.httpData method. The following values are supported: |
|
68 * |
|
69 * 'xml': if dataType == 'xml' the server response is treated as XML and the 'success' |
|
70 * callback method, if specified, will be passed the responseXML value |
|
71 * 'json': if dataType == 'json' the server response will be evaluted and passed to |
|
72 * the 'success' callback, if specified |
|
73 * 'script': if dataType == 'script' the server response is evaluated in the global context |
|
74 * |
|
75 * |
|
76 * Note that it does not make sense to use both the 'target' and 'dataType' options. If both |
|
77 * are provided the target will be ignored. |
|
78 * |
|
79 * The semantic argument can be used to force form serialization in semantic order. |
|
80 * This is normally true anyway, unless the form contains input elements of type='image'. |
|
81 * If your form must be submitted with name/value pairs in semantic order and your form |
|
82 * contains an input of type='image" then pass true for this arg, otherwise pass false |
|
83 * (or nothing) to avoid the overhead for this logic. |
|
84 * |
|
85 * |
|
86 * When used on its own, ajaxSubmit() is typically bound to a form's submit event like this: |
|
87 * |
|
88 * $("#form-id").submit(function() { |
|
89 * $(this).ajaxSubmit(options); |
|
90 * return false; // cancel conventional submit |
|
91 * }); |
|
92 * |
|
93 * When using ajaxForm(), however, this is done for you. |
|
94 * |
|
95 * @example |
|
96 * $('#myForm').ajaxSubmit(function(data) { |
|
97 * alert('Form submit succeeded! Server returned: ' + data); |
|
98 * }); |
|
99 * @desc Submit form and alert server response |
|
100 * |
|
101 * |
|
102 * @example |
|
103 * var options = { |
|
104 * target: '#myTargetDiv' |
|
105 * }; |
|
106 * $('#myForm').ajaxSubmit(options); |
|
107 * @desc Submit form and update page element with server response |
|
108 * |
|
109 * |
|
110 * @example |
|
111 * var options = { |
|
112 * success: function(responseText) { |
|
113 * alert(responseText); |
|
114 * } |
|
115 * }; |
|
116 * $('#myForm').ajaxSubmit(options); |
|
117 * @desc Submit form and alert the server response |
|
118 * |
|
119 * |
|
120 * @example |
|
121 * var options = { |
|
122 * beforeSubmit: function(formArray, jqForm) { |
|
123 * if (formArray.length == 0) { |
|
124 * alert('Please enter data.'); |
|
125 * return false; |
|
126 * } |
|
127 * } |
|
128 * }; |
|
129 * $('#myForm').ajaxSubmit(options); |
|
130 * @desc Pre-submit validation which aborts the submit operation if form data is empty |
|
131 * |
|
132 * |
|
133 * @example |
|
134 * var options = { |
|
135 * url: myJsonUrl.php, |
|
136 * dataType: 'json', |
|
137 * success: function(data) { |
|
138 * // 'data' is an object representing the the evaluated json data |
|
139 * } |
|
140 * }; |
|
141 * $('#myForm').ajaxSubmit(options); |
|
142 * @desc json data returned and evaluated |
|
143 * |
|
144 * |
|
145 * @example |
|
146 * var options = { |
|
147 * url: myXmlUrl.php, |
|
148 * dataType: 'xml', |
|
149 * success: function(responseXML) { |
|
150 * // responseXML is XML document object |
|
151 * var data = $('myElement', responseXML).text(); |
|
152 * } |
|
153 * }; |
|
154 * $('#myForm').ajaxSubmit(options); |
|
155 * @desc XML data returned from server |
|
156 * |
|
157 * |
|
158 * @example |
|
159 * var options = { |
|
160 * resetForm: true |
|
161 * }; |
|
162 * $('#myForm').ajaxSubmit(options); |
|
163 * @desc submit form and reset it if successful |
|
164 * |
|
165 * @example |
|
166 * $('#myForm).submit(function() { |
|
167 * $(this).ajaxSubmit(); |
|
168 * return false; |
|
169 * }); |
|
170 * @desc Bind form's submit event to use ajaxSubmit |
|
171 * |
|
172 * |
|
173 * @name ajaxSubmit |
|
174 * @type jQuery |
|
175 * @param options object literal containing options which control the form submission process |
|
176 * @cat Plugins/Form |
|
177 * @return jQuery |
|
178 */ |
45 */ |
179 $.fn.ajaxSubmit = function(options) { |
46 $.fn.ajaxSubmit = function(options) { |
180 if (typeof options == 'function') |
47 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) |
181 options = { success: options }; |
48 if (!this.length) { |
182 |
49 log('ajaxSubmit: skipping submit process - no element selected'); |
183 options = $.extend({ |
50 return this; |
184 url: this.attr('action') || window.location.toString(), |
51 } |
185 type: this.attr('method') || 'GET' |
52 |
186 }, options || {}); |
53 if (typeof options == 'function') { |
187 |
54 options = { success: options }; |
188 // hook for manipulating the form data before it is extracted; |
55 } |
189 // convenient for use with rich editors like tinyMCE or FCKEditor |
56 |
190 var veto = {}; |
57 var action = this.attr('action'); |
191 $.event.trigger('form.pre.serialize', [this, options, veto]); |
58 var url = (typeof action === 'string') ? $.trim(action) : ''; |
192 if (veto.veto) return this; |
59 if (url) { |
193 |
60 // clean url (don't include hash vaue) |
194 var a = this.formToArray(options.semantic); |
61 url = (url.match(/^([^#]+)/)||[])[1]; |
|
62 } |
|
63 url = url || window.location.href || ''; |
|
64 |
|
65 options = $.extend(true, { |
|
66 url: url, |
|
67 success: $.ajaxSettings.success, |
|
68 type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57) |
|
69 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' |
|
70 }, options); |
|
71 |
|
72 // hook for manipulating the form data before it is extracted; |
|
73 // convenient for use with rich editors like tinyMCE or FCKEditor |
|
74 var veto = {}; |
|
75 this.trigger('form-pre-serialize', [this, options, veto]); |
|
76 if (veto.veto) { |
|
77 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); |
|
78 return this; |
|
79 } |
|
80 |
|
81 // provide opportunity to alter form data before it is serialized |
|
82 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { |
|
83 log('ajaxSubmit: submit aborted via beforeSerialize callback'); |
|
84 return this; |
|
85 } |
|
86 |
|
87 var n,v,a = this.formToArray(options.semantic); |
195 if (options.data) { |
88 if (options.data) { |
196 for (var n in options.data) |
89 options.extraData = options.data; |
197 a.push( { name: n, value: options.data[n] } ); |
90 for (n in options.data) { |
198 } |
91 if(options.data[n] instanceof Array) { |
199 |
92 for (var k in options.data[n]) { |
200 // give pre-submit callback an opportunity to abort the submit |
93 a.push( { name: n, value: options.data[n][k] } ); |
201 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) return this; |
94 } |
202 |
95 } |
203 // fire vetoable 'validate' event |
96 else { |
204 $.event.trigger('form.submit.validate', [a, this, options, veto]); |
97 v = options.data[n]; |
205 if (veto.veto) return this; |
98 v = $.isFunction(v) ? v() : v; // if value is fn, invoke it |
206 |
99 a.push( { name: n, value: v } ); |
207 var q = $.param(a);//.replace(/%20/g,'+'); |
100 } |
208 |
101 } |
209 if (options.type.toUpperCase() == 'GET') { |
102 } |
210 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; |
103 |
211 options.data = null; // data is null for 'get' |
104 // give pre-submit callback an opportunity to abort the submit |
212 } |
105 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { |
213 else |
106 log('ajaxSubmit: submit aborted via beforeSubmit callback'); |
214 options.data = q; // data is the query string for 'post' |
107 return this; |
215 |
108 } |
216 var $form = this, callbacks = []; |
109 |
217 if (options.resetForm) callbacks.push(function() { $form.resetForm(); }); |
110 // fire vetoable 'validate' event |
218 if (options.clearForm) callbacks.push(function() { $form.clearForm(); }); |
111 this.trigger('form-submit-validate', [a, this, options, veto]); |
219 |
112 if (veto.veto) { |
220 // perform a load on the target only if dataType is not provided |
113 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); |
221 if (!options.dataType && options.target) { |
114 return this; |
222 var oldSuccess = options.success || function(){}; |
115 } |
223 callbacks.push(function(data) { |
116 |
224 if (this.evalScripts) |
117 var q = $.param(a); |
225 $(options.target).attr("innerHTML", data).evalScripts().each(oldSuccess, arguments); |
118 |
226 else // jQuery v1.1.4 |
119 if (options.type.toUpperCase() == 'GET') { |
227 $(options.target).html(data).each(oldSuccess, arguments); |
120 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; |
228 }); |
121 options.data = null; // data is null for 'get' |
229 } |
122 } |
230 else if (options.success) |
123 else { |
231 callbacks.push(options.success); |
124 options.data = q; // data is the query string for 'post' |
232 |
125 } |
233 options.success = function(data, status) { |
126 |
234 for (var i=0, max=callbacks.length; i < max; i++) |
127 var $form = this, callbacks = []; |
235 callbacks[i](data, status, $form); |
128 if (options.resetForm) { |
236 }; |
129 callbacks.push(function() { $form.resetForm(); }); |
237 |
130 } |
238 // are there files to upload? |
131 if (options.clearForm) { |
239 var files = $('input:file', this).fieldValue(); |
132 callbacks.push(function() { $form.clearForm(); }); |
240 var found = false; |
133 } |
241 for (var j=0; j < files.length; j++) |
134 |
242 if (files[j]) |
135 // perform a load on the target only if dataType is not provided |
243 found = true; |
136 if (!options.dataType && options.target) { |
244 |
137 var oldSuccess = options.success || function(){}; |
245 // options.iframe allows user to force iframe mode |
138 callbacks.push(function(data) { |
246 if (options.iframe || found) { |
139 var fn = options.replaceTarget ? 'replaceWith' : 'html'; |
247 // hack to fix Safari hang (thanks to Tim Molendijk for this) |
140 $(options.target)[fn](data).each(oldSuccess, arguments); |
248 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d |
141 }); |
249 if ($.browser.safari && options.closeKeepAlive) |
142 } |
250 $.get(options.closeKeepAlive, fileUpload); |
143 else if (options.success) { |
251 else |
144 callbacks.push(options.success); |
252 fileUpload(); |
145 } |
253 } |
146 |
254 else |
147 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg |
255 $.ajax(options); |
148 var context = options.context || options; // jQuery 1.4+ supports scope context |
256 |
149 for (var i=0, max=callbacks.length; i < max; i++) { |
257 // fire 'notify' event |
150 callbacks[i].apply(context, [data, status, xhr || $form, $form]); |
258 $.event.trigger('form.submit.notify', [this, options]); |
151 } |
259 return this; |
152 }; |
260 |
153 |
261 |
154 // are there files to upload? |
262 // private function for handling file uploads (hat tip to YAHOO!) |
155 var fileInputs = $('input:file', this).length > 0; |
263 function fileUpload() { |
156 var mp = 'multipart/form-data'; |
264 var form = $form[0]; |
157 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); |
265 var opts = $.extend({}, $.ajaxSettings, options); |
158 |
266 |
159 // options.iframe allows user to force iframe mode |
267 var id = 'jqFormIO' + $.fn.ajaxSubmit.counter++; |
160 // 06-NOV-09: now defaulting to iframe mode if file input is detected |
268 var $io = $('<iframe id="' + id + '" name="' + id + '" />'); |
161 if (options.iframe !== false && (fileInputs || options.iframe || multipart)) { |
269 var io = $io[0]; |
162 // hack to fix Safari hang (thanks to Tim Molendijk for this) |
270 var op8 = $.browser.opera && window.opera.version() < 9; |
163 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d |
271 if ($.browser.msie || op8) io.src = 'javascript:false;document.write("");'; |
164 if (options.closeKeepAlive) { |
272 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); |
165 $.get(options.closeKeepAlive, fileUpload); |
273 |
166 } |
274 var xhr = { // mock object |
167 else { |
275 responseText: null, |
168 fileUpload(); |
276 responseXML: null, |
169 } |
277 status: 0, |
170 } |
278 statusText: 'n/a', |
171 else { |
279 getAllResponseHeaders: function() {}, |
172 $.ajax(options); |
280 getResponseHeader: function() {}, |
173 } |
281 setRequestHeader: function() {} |
174 |
282 }; |
175 // fire 'notify' event |
283 |
176 this.trigger('form-submit-notify', [this, options]); |
284 var g = opts.global; |
177 return this; |
285 // trigger ajax global events so that activity/block indicators work like normal |
178 |
286 if (g && ! $.active++) $.event.trigger("ajaxStart"); |
179 |
287 if (g) $.event.trigger("ajaxSend", [xhr, opts]); |
180 // private function for handling file uploads (hat tip to YAHOO!) |
288 |
181 function fileUpload() { |
289 var cbInvoked = 0; |
182 var form = $form[0]; |
290 var timedOut = 0; |
183 |
291 |
184 if ($(':input[name=submit],:input[id=submit]', form).length) { |
292 // take a breath so that pending repaints get some cpu time before the upload starts |
185 // if there is an input with a name or id of 'submit' then we won't be |
293 setTimeout(function() { |
186 // able to invoke the submit fn on the form (at least not x-browser) |
294 // make sure form attrs are set |
187 alert('Error: Form elements must not have name or id of "submit".'); |
295 var encAttr = form.encoding ? 'encoding' : 'enctype'; |
188 return; |
296 var t = $form.attr('target'); |
189 } |
297 $form.attr({ |
190 |
298 target: id, |
191 var s = $.extend(true, {}, $.ajaxSettings, options); |
299 method: 'POST', |
192 s.context = s.context || s; |
300 action: opts.url |
193 var id = 'jqFormIO' + (new Date().getTime()), fn = '_'+id; |
301 }); |
194 var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ s.iframeSrc +'" />'); |
302 form[encAttr] = 'multipart/form-data'; |
195 var io = $io[0]; |
303 |
196 |
304 // support timout |
197 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); |
305 if (opts.timeout) |
198 |
306 setTimeout(function() { timedOut = true; cb(); }, opts.timeout); |
199 var xhr = { // mock object |
307 |
200 aborted: 0, |
308 // add iframe to doc and submit the form |
201 responseText: null, |
309 $io.appendTo('body'); |
202 responseXML: null, |
310 io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false); |
203 status: 0, |
311 form.submit(); |
204 statusText: 'n/a', |
312 $form.attr('target', t); // reset target |
205 getAllResponseHeaders: function() {}, |
313 }, 10); |
206 getResponseHeader: function() {}, |
314 |
207 setRequestHeader: function() {}, |
315 function cb() { |
208 abort: function(status) { |
316 if (cbInvoked++) return; |
209 var e = (status === 'timeout' ? 'timeout' : 'aborted'); |
317 |
210 log('aborting upload... ' + e); |
|
211 this.aborted = 1; |
|
212 $io.attr('src', s.iframeSrc); // abort op in progress |
|
213 xhr.error = e; |
|
214 s.error && s.error.call(s.context, xhr, e, e); |
|
215 g && $.event.trigger("ajaxError", [xhr, s, e]); |
|
216 s.complete && s.complete.call(s.context, xhr, e); |
|
217 } |
|
218 }; |
|
219 |
|
220 var g = s.global; |
|
221 // trigger ajax global events so that activity/block indicators work like normal |
|
222 if (g && ! $.active++) { |
|
223 $.event.trigger("ajaxStart"); |
|
224 } |
|
225 if (g) { |
|
226 $.event.trigger("ajaxSend", [xhr, s]); |
|
227 } |
|
228 |
|
229 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { |
|
230 if (s.global) { |
|
231 $.active--; |
|
232 } |
|
233 return; |
|
234 } |
|
235 if (xhr.aborted) { |
|
236 return; |
|
237 } |
|
238 |
|
239 var timedOut = 0, timeoutHandle; |
|
240 |
|
241 // add submitting element to data if we know it |
|
242 var sub = form.clk; |
|
243 if (sub) { |
|
244 var n = sub.name; |
|
245 if (n && !sub.disabled) { |
|
246 s.extraData = s.extraData || {}; |
|
247 s.extraData[n] = sub.value; |
|
248 if (sub.type == "image") { |
|
249 s.extraData[n+'.x'] = form.clk_x; |
|
250 s.extraData[n+'.y'] = form.clk_y; |
|
251 } |
|
252 } |
|
253 } |
|
254 |
|
255 // take a breath so that pending repaints get some cpu time before the upload starts |
|
256 function doSubmit() { |
|
257 // make sure form attrs are set |
|
258 var t = $form.attr('target'), a = $form.attr('action'); |
|
259 |
|
260 // update form attrs in IE friendly way |
|
261 form.setAttribute('target',id); |
|
262 if (form.getAttribute('method') != 'POST') { |
|
263 form.setAttribute('method', 'POST'); |
|
264 } |
|
265 if (form.getAttribute('action') != s.url) { |
|
266 form.setAttribute('action', s.url); |
|
267 } |
|
268 |
|
269 // ie borks in some cases when setting encoding |
|
270 if (! s.skipEncodingOverride) { |
|
271 $form.attr({ |
|
272 encoding: 'multipart/form-data', |
|
273 enctype: 'multipart/form-data' |
|
274 }); |
|
275 } |
|
276 |
|
277 // support timout |
|
278 if (s.timeout) { |
|
279 timeoutHandle = setTimeout(function() { timedOut = true; cb(true); }, s.timeout); |
|
280 } |
|
281 |
|
282 // add "extra" data to form if provided in options |
|
283 var extraInputs = []; |
|
284 try { |
|
285 if (s.extraData) { |
|
286 for (var n in s.extraData) { |
|
287 extraInputs.push( |
|
288 $('<input type="hidden" name="'+n+'" value="'+s.extraData[n]+'" />') |
|
289 .appendTo(form)[0]); |
|
290 } |
|
291 } |
|
292 |
|
293 // add iframe to doc and submit the form |
|
294 $io.appendTo('body'); |
|
295 io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false); |
|
296 form.submit(); |
|
297 } |
|
298 finally { |
|
299 // reset attrs and remove "extra" input elements |
|
300 form.setAttribute('action',a); |
|
301 if(t) { |
|
302 form.setAttribute('target', t); |
|
303 } else { |
|
304 $form.removeAttr('target'); |
|
305 } |
|
306 $(extraInputs).remove(); |
|
307 } |
|
308 } |
|
309 |
|
310 if (s.forceSync) { |
|
311 doSubmit(); |
|
312 } |
|
313 else { |
|
314 setTimeout(doSubmit, 10); // this lets dom updates render |
|
315 } |
|
316 |
|
317 var data, doc, domCheckCount = 50, callbackProcessed; |
|
318 |
|
319 function cb(e) { |
|
320 if (xhr.aborted || callbackProcessed) { |
|
321 return; |
|
322 } |
|
323 if (e === true && xhr) { |
|
324 xhr.abort('timeout'); |
|
325 return; |
|
326 } |
|
327 |
|
328 var doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document; |
|
329 if (!doc || doc.location.href == s.iframeSrc) { |
|
330 // response not received yet |
|
331 if (!timedOut) |
|
332 return; |
|
333 } |
318 io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false); |
334 io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false); |
319 |
335 |
320 var ok = true; |
336 var ok = true; |
321 try { |
337 try { |
322 if (timedOut) throw 'timeout'; |
338 if (timedOut) { |
323 // extract the server response from the iframe |
339 throw 'timeout'; |
324 var data, doc; |
340 } |
325 doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document; |
341 |
326 xhr.responseText = doc.body ? doc.body.innerHTML : null; |
342 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); |
327 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; |
343 log('isXml='+isXml); |
328 |
344 if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) { |
329 if (opts.dataType == 'json' || opts.dataType == 'script') { |
345 if (--domCheckCount) { |
330 var ta = doc.getElementsByTagName('textarea')[0]; |
346 // in some browsers (Opera) the iframe DOM is not always traversable when |
331 data = ta ? ta.value : xhr.responseText; |
347 // the onload callback fires, so we loop a bit to accommodate |
332 if (opts.dataType == 'json') |
348 log('requeing onLoad callback, DOM not available'); |
333 eval("data = " + data); |
349 setTimeout(cb, 250); |
334 else |
350 return; |
335 $.globalEval(data); |
351 } |
336 } |
352 // let this fall through because server response could be an empty document |
337 else if (opts.dataType == 'xml') { |
353 //log('Could not access iframe DOM after mutiple tries.'); |
338 data = xhr.responseXML; |
354 //throw 'DOMException: not available'; |
339 if (!data && xhr.responseText != null) |
355 } |
340 data = toXml(xhr.responseText); |
356 |
341 } |
357 //log('response detected'); |
342 else { |
358 xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null; |
343 data = xhr.responseText; |
359 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; |
344 } |
360 if (isXml) |
345 } |
361 s.dataType = 'xml'; |
346 catch(e){ |
362 xhr.getResponseHeader = function(header){ |
347 ok = false; |
363 var headers = {'content-type': s.dataType}; |
348 $.handleError(opts, xhr, 'error', e); |
364 return headers[header]; |
349 } |
365 }; |
350 |
366 |
351 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it |
367 var scr = /(json|script|text)/.test(s.dataType); |
352 if (ok) { |
368 if (scr || s.textarea) { |
353 opts.success(data, 'success'); |
369 // see if user embedded response in textarea |
354 if (g) $.event.trigger("ajaxSuccess", [xhr, opts]); |
370 var ta = doc.getElementsByTagName('textarea')[0]; |
355 } |
371 if (ta) { |
356 if (g) $.event.trigger("ajaxComplete", [xhr, opts]); |
372 xhr.responseText = ta.value; |
357 if (g && ! --$.active) $.event.trigger("ajaxStop"); |
373 } |
358 if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error'); |
374 else if (scr) { |
359 |
375 // account for browsers injecting pre around json response |
360 // clean up |
376 var pre = doc.getElementsByTagName('pre')[0]; |
361 setTimeout(function() { |
377 var b = doc.getElementsByTagName('body')[0]; |
362 $io.remove(); |
378 if (pre) { |
363 xhr.responseXML = null; |
379 xhr.responseText = pre.textContent; |
364 }, 100); |
380 } |
365 }; |
381 else if (b) { |
366 |
382 xhr.responseText = b.innerHTML; |
367 function toXml(s, doc) { |
383 } |
368 if (window.ActiveXObject) { |
384 } |
369 doc = new ActiveXObject('Microsoft.XMLDOM'); |
385 } |
370 doc.async = 'false'; |
386 else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) { |
371 doc.loadXML(s); |
387 xhr.responseXML = toXml(xhr.responseText); |
372 } |
388 } |
373 else |
389 |
374 doc = (new DOMParser()).parseFromString(s, 'text/xml'); |
390 data = httpData(xhr, s.dataType, s); |
375 return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null; |
391 } |
376 }; |
392 catch(e){ |
377 }; |
393 log('error caught:',e); |
378 }; |
394 ok = false; |
379 $.fn.ajaxSubmit.counter = 0; // used to create unique iframe ids |
395 xhr.error = e; |
|
396 s.error && s.error.call(s.context, xhr, 'error', e); |
|
397 g && $.event.trigger("ajaxError", [xhr, s, e]); |
|
398 } |
|
399 |
|
400 if (xhr.aborted) { |
|
401 log('upload aborted'); |
|
402 ok = false; |
|
403 } |
|
404 |
|
405 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it |
|
406 if (ok) { |
|
407 s.success && s.success.call(s.context, data, 'success', xhr); |
|
408 g && $.event.trigger("ajaxSuccess", [xhr, s]); |
|
409 } |
|
410 |
|
411 g && $.event.trigger("ajaxComplete", [xhr, s]); |
|
412 |
|
413 if (g && ! --$.active) { |
|
414 $.event.trigger("ajaxStop"); |
|
415 } |
|
416 |
|
417 s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error'); |
|
418 |
|
419 callbackProcessed = true; |
|
420 if (s.timeout) |
|
421 clearTimeout(timeoutHandle); |
|
422 |
|
423 // clean up |
|
424 setTimeout(function() { |
|
425 $io.removeData('form-plugin-onload'); |
|
426 $io.remove(); |
|
427 xhr.responseXML = null; |
|
428 }, 100); |
|
429 } |
|
430 |
|
431 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) |
|
432 if (window.ActiveXObject) { |
|
433 doc = new ActiveXObject('Microsoft.XMLDOM'); |
|
434 doc.async = 'false'; |
|
435 doc.loadXML(s); |
|
436 } |
|
437 else { |
|
438 doc = (new DOMParser()).parseFromString(s, 'text/xml'); |
|
439 } |
|
440 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; |
|
441 }; |
|
442 var parseJSON = $.parseJSON || function(s) { |
|
443 return window['eval']('(' + s + ')'); |
|
444 }; |
|
445 |
|
446 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 |
|
447 var ct = xhr.getResponseHeader('content-type') || '', |
|
448 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, |
|
449 data = xml ? xhr.responseXML : xhr.responseText; |
|
450 |
|
451 if (xml && data.documentElement.nodeName === 'parsererror') { |
|
452 $.error && $.error('parsererror'); |
|
453 } |
|
454 if (s && s.dataFilter) { |
|
455 data = s.dataFilter(data, type); |
|
456 } |
|
457 if (typeof data === 'string') { |
|
458 if (type === 'json' || !type && ct.indexOf('json') >= 0) { |
|
459 data = parseJSON(data); |
|
460 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { |
|
461 $.globalEval(data); |
|
462 } |
|
463 } |
|
464 return data; |
|
465 }; |
|
466 } |
|
467 }; |
380 |
468 |
381 /** |
469 /** |
382 * ajaxForm() provides a mechanism for fully automating form submission. |
470 * ajaxForm() provides a mechanism for fully automating form submission. |
383 * |
471 * |
384 * The advantages of using this method instead of ajaxSubmit() are: |
472 * The advantages of using this method instead of ajaxSubmit() are: |
385 * |
473 * |
386 * 1: This method will include coordinates for <input type="image" /> elements (if the element |
474 * 1: This method will include coordinates for <input type="image" /> elements (if the element |
387 * is used to submit the form). |
475 * is used to submit the form). |
388 * 2. This method will include the submit element's name/value data (for the element that was |
476 * 2. This method will include the submit element's name/value data (for the element that was |
389 * used to submit the form). |
477 * used to submit the form). |
390 * 3. This method binds the submit() method to the form for you. |
478 * 3. This method binds the submit() method to the form for you. |
391 * |
|
392 * Note that for accurate x/y coordinates of image submit elements in all browsers |
|
393 * you need to also use the "dimensions" plugin (this method will auto-detect its presence). |
|
394 * |
479 * |
395 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely |
480 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely |
396 * passes the options argument along after properly binding events for submit elements and |
481 * passes the options argument along after properly binding events for submit elements and |
397 * the form itself. See ajaxSubmit for a full description of the options argument. |
482 * the form itself. |
398 * |
|
399 * |
|
400 * @example |
|
401 * var options = { |
|
402 * target: '#myTargetDiv' |
|
403 * }; |
|
404 * $('#myForm').ajaxSForm(options); |
|
405 * @desc Bind form's submit event so that 'myTargetDiv' is updated with the server response |
|
406 * when the form is submitted. |
|
407 * |
|
408 * |
|
409 * @example |
|
410 * var options = { |
|
411 * success: function(responseText) { |
|
412 * alert(responseText); |
|
413 * } |
|
414 * }; |
|
415 * $('#myForm').ajaxSubmit(options); |
|
416 * @desc Bind form's submit event so that server response is alerted after the form is submitted. |
|
417 * |
|
418 * |
|
419 * @example |
|
420 * var options = { |
|
421 * beforeSubmit: function(formArray, jqForm) { |
|
422 * if (formArray.length == 0) { |
|
423 * alert('Please enter data.'); |
|
424 * return false; |
|
425 * } |
|
426 * } |
|
427 * }; |
|
428 * $('#myForm').ajaxSubmit(options); |
|
429 * @desc Bind form's submit event so that pre-submit callback is invoked before the form |
|
430 * is submitted. |
|
431 * |
|
432 * |
|
433 * @name ajaxForm |
|
434 * @param options object literal containing options which control the form submission process |
|
435 * @return jQuery |
|
436 * @cat Plugins/Form |
|
437 * @type jQuery |
|
438 */ |
483 */ |
439 $.fn.ajaxForm = function(options) { |
484 $.fn.ajaxForm = function(options) { |
440 return this.ajaxFormUnbind().submit(submitHandler).each(function() { |
485 // in jQuery 1.3+ we can fix mistakes with the ready state |
441 // store options in hash |
486 if (this.length === 0) { |
442 this.formPluginId = $.fn.ajaxForm.counter++; |
487 var o = { s: this.selector, c: this.context }; |
443 $.fn.ajaxForm.optionHash[this.formPluginId] = options; |
488 if (!$.isReady && o.s) { |
444 $(":submit,input:image", this).click(clickHandler); |
489 log('DOM not ready, queuing ajaxForm'); |
445 }); |
490 $(function() { |
446 }; |
491 $(o.s,o.c).ajaxForm(options); |
447 |
492 }); |
448 $.fn.ajaxForm.counter = 1; |
493 return this; |
449 $.fn.ajaxForm.optionHash = {}; |
494 } |
450 |
495 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() |
451 function clickHandler(e) { |
496 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); |
452 var $form = this.form; |
497 return this; |
453 $form.clk = this; |
498 } |
454 if (this.type == 'image') { |
499 |
455 if (e.offsetX != undefined) { |
500 return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) { |
456 $form.clk_x = e.offsetX; |
501 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed |
457 $form.clk_y = e.offsetY; |
502 e.preventDefault(); |
458 } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin |
503 $(this).ajaxSubmit(options); |
459 var offset = $(this).offset(); |
504 } |
460 $form.clk_x = e.pageX - offset.left; |
505 }).bind('click.form-plugin', function(e) { |
461 $form.clk_y = e.pageY - offset.top; |
506 var target = e.target; |
462 } else { |
507 var $el = $(target); |
463 $form.clk_x = e.pageX - this.offsetLeft; |
508 if (!($el.is(":submit,input:image"))) { |
464 $form.clk_y = e.pageY - this.offsetTop; |
509 // is this a child element of the submit el? (ex: a span within a button) |
465 } |
510 var t = $el.closest(':submit'); |
466 } |
511 if (t.length == 0) { |
467 // clear form vars |
512 return; |
468 setTimeout(function() { $form.clk = $form.clk_x = $form.clk_y = null; }, 10); |
513 } |
469 }; |
514 target = t[0]; |
470 |
515 } |
471 function submitHandler() { |
516 var form = this; |
472 // retrieve options from hash |
517 form.clk = target; |
473 var id = this.formPluginId; |
518 if (target.type == 'image') { |
474 var options = $.fn.ajaxForm.optionHash[id]; |
519 if (e.offsetX != undefined) { |
475 $(this).ajaxSubmit(options); |
520 form.clk_x = e.offsetX; |
476 return false; |
521 form.clk_y = e.offsetY; |
477 }; |
522 } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin |
478 |
523 var offset = $el.offset(); |
479 /** |
524 form.clk_x = e.pageX - offset.left; |
480 * ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm |
525 form.clk_y = e.pageY - offset.top; |
481 * |
526 } else { |
482 * @name ajaxFormUnbind |
527 form.clk_x = e.pageX - target.offsetLeft; |
483 * @return jQuery |
528 form.clk_y = e.pageY - target.offsetTop; |
484 * @cat Plugins/Form |
529 } |
485 * @type jQuery |
530 } |
486 */ |
531 // clear form vars |
|
532 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); |
|
533 }); |
|
534 }; |
|
535 |
|
536 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm |
487 $.fn.ajaxFormUnbind = function() { |
537 $.fn.ajaxFormUnbind = function() { |
488 this.unbind('submit', submitHandler); |
538 return this.unbind('submit.form-plugin click.form-plugin'); |
489 return this.each(function() { |
|
490 $(":submit,input:image", this).unbind('click', clickHandler); |
|
491 }); |
|
492 |
|
493 }; |
539 }; |
494 |
540 |
495 /** |
541 /** |
496 * formToArray() gathers form element data into an array of objects that can |
542 * formToArray() gathers form element data into an array of objects that can |
497 * be passed to any of the following ajax functions: $.get, $.post, or load. |
543 * be passed to any of the following ajax functions: $.get, $.post, or load. |
500 * |
546 * |
501 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] |
547 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] |
502 * |
548 * |
503 * It is this array that is passed to pre-submit callback functions provided to the |
549 * It is this array that is passed to pre-submit callback functions provided to the |
504 * ajaxSubmit() and ajaxForm() methods. |
550 * ajaxSubmit() and ajaxForm() methods. |
505 * |
|
506 * The semantic argument can be used to force form serialization in semantic order. |
|
507 * This is normally true anyway, unless the form contains input elements of type='image'. |
|
508 * If your form must be submitted with name/value pairs in semantic order and your form |
|
509 * contains an input of type='image" then pass true for this arg, otherwise pass false |
|
510 * (or nothing) to avoid the overhead for this logic. |
|
511 * |
|
512 * @example var data = $("#myForm").formToArray(); |
|
513 * $.post( "myscript.cgi", data ); |
|
514 * @desc Collect all the data from a form and submit it to the server. |
|
515 * |
|
516 * @name formToArray |
|
517 * @param semantic true if serialization must maintain strict semantic ordering of elements (slower) |
|
518 * @type Array<Object> |
|
519 * @cat Plugins/Form |
|
520 */ |
551 */ |
521 $.fn.formToArray = function(semantic) { |
552 $.fn.formToArray = function(semantic) { |
522 var a = []; |
553 var a = []; |
523 if (this.length == 0) return a; |
554 if (this.length === 0) { |
524 |
555 return a; |
525 var form = this[0]; |
556 } |
526 var els = semantic ? form.getElementsByTagName('*') : form.elements; |
557 |
527 if (!els) return a; |
558 var form = this[0]; |
528 for(var i=0, max=els.length; i < max; i++) { |
559 var els = semantic ? form.getElementsByTagName('*') : form.elements; |
529 var el = els[i]; |
560 if (!els) { |
530 var n = el.name; |
561 return a; |
531 if (!n) continue; |
562 } |
532 |
563 |
533 if (semantic && form.clk && el.type == "image") { |
564 var i,j,n,v,el,max,jmax; |
534 // handle image inputs on the fly when semantic == true |
565 for(i=0, max=els.length; i < max; i++) { |
535 if(!el.disabled && form.clk == el) |
566 el = els[i]; |
536 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
567 n = el.name; |
537 continue; |
568 if (!n) { |
538 } |
569 continue; |
539 |
570 } |
540 var v = $.fieldValue(el, true); |
571 |
541 if (v && v.constructor == Array) { |
572 if (semantic && form.clk && el.type == "image") { |
542 for(var j=0, jmax=v.length; j < jmax; j++) |
573 // handle image inputs on the fly when semantic == true |
543 a.push({name: n, value: v[j]}); |
574 if(!el.disabled && form.clk == el) { |
544 } |
575 a.push({name: n, value: $(el).val()}); |
545 else if (v !== null && typeof v != 'undefined') |
576 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
546 a.push({name: n, value: v}); |
577 } |
547 } |
578 continue; |
548 |
579 } |
549 if (!semantic && form.clk) { |
580 |
550 // input type=='image' are not found in elements array! handle them here |
581 v = $.fieldValue(el, true); |
551 var inputs = form.getElementsByTagName("input"); |
582 if (v && v.constructor == Array) { |
552 for(var i=0, max=inputs.length; i < max; i++) { |
583 for(j=0, jmax=v.length; j < jmax; j++) { |
553 var input = inputs[i]; |
584 a.push({name: n, value: v[j]}); |
554 var n = input.name; |
585 } |
555 if(n && !input.disabled && input.type == "image" && form.clk == input) |
586 } |
556 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
587 else if (v !== null && typeof v != 'undefined') { |
557 } |
588 a.push({name: n, value: v}); |
558 } |
589 } |
559 return a; |
590 } |
560 }; |
591 |
561 |
592 if (!semantic && form.clk) { |
|
593 // input type=='image' are not found in elements array! handle it here |
|
594 var $input = $(form.clk), input = $input[0]; |
|
595 n = input.name; |
|
596 if (n && !input.disabled && input.type == 'image') { |
|
597 a.push({name: n, value: $input.val()}); |
|
598 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); |
|
599 } |
|
600 } |
|
601 return a; |
|
602 }; |
562 |
603 |
563 /** |
604 /** |
564 * Serializes form data into a 'submittable' string. This method will return a string |
605 * Serializes form data into a 'submittable' string. This method will return a string |
565 * in the format: name1=value1&name2=value2 |
606 * in the format: name1=value1&name2=value2 |
566 * |
|
567 * The semantic argument can be used to force form serialization in semantic order. |
|
568 * If your form must be submitted with name/value pairs in semantic order then pass |
|
569 * true for this arg, otherwise pass false (or nothing) to avoid the overhead for |
|
570 * this logic (which can be significant for very large forms). |
|
571 * |
|
572 * @example var data = $("#myForm").formSerialize(); |
|
573 * $.ajax('POST', "myscript.cgi", data); |
|
574 * @desc Collect all the data from a form into a single string |
|
575 * |
|
576 * @name formSerialize |
|
577 * @param semantic true if serialization must maintain strict semantic ordering of elements (slower) |
|
578 * @type String |
|
579 * @cat Plugins/Form |
|
580 */ |
607 */ |
581 $.fn.formSerialize = function(semantic) { |
608 $.fn.formSerialize = function(semantic) { |
582 //hand off to jQuery.param for proper encoding |
609 //hand off to jQuery.param for proper encoding |
583 return $.param(this.formToArray(semantic)); |
610 return $.param(this.formToArray(semantic)); |
584 }; |
611 }; |
585 |
|
586 |
612 |
587 /** |
613 /** |
588 * Serializes all field elements in the jQuery object into a query string. |
614 * Serializes all field elements in the jQuery object into a query string. |
589 * This method will return a string in the format: name1=value1&name2=value2 |
615 * This method will return a string in the format: name1=value1&name2=value2 |
590 * |
|
591 * The successful argument controls whether or not serialization is limited to |
|
592 * 'successful' controls (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). |
|
593 * The default value of the successful argument is true. |
|
594 * |
|
595 * @example var data = $("input").formSerialize(); |
|
596 * @desc Collect the data from all successful input elements into a query string |
|
597 * |
|
598 * @example var data = $(":radio").formSerialize(); |
|
599 * @desc Collect the data from all successful radio input elements into a query string |
|
600 * |
|
601 * @example var data = $("#myForm :checkbox").formSerialize(); |
|
602 * @desc Collect the data from all successful checkbox input elements in myForm into a query string |
|
603 * |
|
604 * @example var data = $("#myForm :checkbox").formSerialize(false); |
|
605 * @desc Collect the data from all checkbox elements in myForm (even the unchecked ones) into a query string |
|
606 * |
|
607 * @example var data = $(":input").formSerialize(); |
|
608 * @desc Collect the data from all successful input, select, textarea and button elements into a query string |
|
609 * |
|
610 * @name fieldSerialize |
|
611 * @param successful true if only successful controls should be serialized (default is true) |
|
612 * @type String |
|
613 * @cat Plugins/Form |
|
614 */ |
616 */ |
615 $.fn.fieldSerialize = function(successful) { |
617 $.fn.fieldSerialize = function(successful) { |
616 var a = []; |
618 var a = []; |
617 this.each(function() { |
619 this.each(function() { |
618 var n = this.name; |
620 var n = this.name; |
619 if (!n) return; |
621 if (!n) { |
620 var v = $.fieldValue(this, successful); |
622 return; |
621 if (v && v.constructor == Array) { |
623 } |
622 for (var i=0,max=v.length; i < max; i++) |
624 var v = $.fieldValue(this, successful); |
623 a.push({name: n, value: v[i]}); |
625 if (v && v.constructor == Array) { |
624 } |
626 for (var i=0,max=v.length; i < max; i++) { |
625 else if (v !== null && typeof v != 'undefined') |
627 a.push({name: n, value: v[i]}); |
626 a.push({name: this.name, value: v}); |
628 } |
627 }); |
629 } |
628 //hand off to jQuery.param for proper encoding |
630 else if (v !== null && typeof v != 'undefined') { |
629 return $.param(a); |
631 a.push({name: this.name, value: v}); |
630 }; |
632 } |
631 |
633 }); |
|
634 //hand off to jQuery.param for proper encoding |
|
635 return $.param(a); |
|
636 }; |
632 |
637 |
633 /** |
638 /** |
634 * Returns the value(s) of the element in the matched set. For example, consider the following form: |
639 * Returns the value(s) of the element in the matched set. For example, consider the following form: |
635 * |
640 * |
636 * <form><fieldset> |
641 * <form><fieldset> |
637 * <input name="A" type="text" /> |
642 * <input name="A" type="text" /> |
638 * <input name="A" type="text" /> |
643 * <input name="A" type="text" /> |
639 * <input name="B" type="checkbox" value="B1" /> |
644 * <input name="B" type="checkbox" value="B1" /> |
640 * <input name="B" type="checkbox" value="B2"/> |
645 * <input name="B" type="checkbox" value="B2"/> |
641 * <input name="C" type="radio" value="C1" /> |
646 * <input name="C" type="radio" value="C1" /> |
642 * <input name="C" type="radio" value="C2" /> |
647 * <input name="C" type="radio" value="C2" /> |
643 * </fieldset></form> |
648 * </fieldset></form> |
644 * |
649 * |
645 * var v = $(':text').fieldValue(); |
650 * var v = $(':text').fieldValue(); |
646 * // if no values are entered into the text inputs |
651 * // if no values are entered into the text inputs |
647 * v == ['',''] |
652 * v == ['',''] |
664 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). |
669 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). |
665 * The default value of the successful argument is true. If this value is false the value(s) |
670 * The default value of the successful argument is true. If this value is false the value(s) |
666 * for each element is returned. |
671 * for each element is returned. |
667 * |
672 * |
668 * Note: This method *always* returns an array. If no valid value can be determined the |
673 * Note: This method *always* returns an array. If no valid value can be determined the |
669 * array will be empty, otherwise it will contain one or more values. |
674 * array will be empty, otherwise it will contain one or more values. |
670 * |
|
671 * @example var data = $("#myPasswordElement").fieldValue(); |
|
672 * alert(data[0]); |
|
673 * @desc Alerts the current value of the myPasswordElement element |
|
674 * |
|
675 * @example var data = $("#myForm :input").fieldValue(); |
|
676 * @desc Get the value(s) of the form elements in myForm |
|
677 * |
|
678 * @example var data = $("#myForm :checkbox").fieldValue(); |
|
679 * @desc Get the value(s) for the successful checkbox element(s) in the jQuery object. |
|
680 * |
|
681 * @example var data = $("#mySingleSelect").fieldValue(); |
|
682 * @desc Get the value(s) of the select control |
|
683 * |
|
684 * @example var data = $(':text').fieldValue(); |
|
685 * @desc Get the value(s) of the text input or textarea elements |
|
686 * |
|
687 * @example var data = $("#myMultiSelect").fieldValue(); |
|
688 * @desc Get the values for the select-multiple control |
|
689 * |
|
690 * @name fieldValue |
|
691 * @param Boolean successful true if only the values for successful controls should be returned (default is true) |
|
692 * @type Array<String> |
|
693 * @cat Plugins/Form |
|
694 */ |
675 */ |
695 $.fn.fieldValue = function(successful) { |
676 $.fn.fieldValue = function(successful) { |
696 for (var val=[], i=0, max=this.length; i < max; i++) { |
677 for (var val=[], i=0, max=this.length; i < max; i++) { |
697 var el = this[i]; |
678 var el = this[i]; |
698 var v = $.fieldValue(el, successful); |
679 var v = $.fieldValue(el, successful); |
699 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) |
680 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { |
700 continue; |
681 continue; |
701 v.constructor == Array ? $.merge(val, v) : val.push(v); |
682 } |
702 } |
683 v.constructor == Array ? $.merge(val, v) : val.push(v); |
703 return val; |
684 } |
|
685 return val; |
704 }; |
686 }; |
705 |
687 |
706 /** |
688 /** |
707 * Returns the value of the field element. |
689 * Returns the value of the field element. |
708 * |
|
709 * The successful argument controls whether or not the field element must be 'successful' |
|
710 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). |
|
711 * The default value of the successful argument is true. If the given element is not |
|
712 * successful and the successful arg is not false then the returned value will be null. |
|
713 * |
|
714 * Note: If the successful flag is true (default) but the element is not successful, the return will be null |
|
715 * Note: The value returned for a successful select-multiple element will always be an array. |
|
716 * Note: If the element has no value the return value will be undefined. |
|
717 * |
|
718 * @example var data = jQuery.fieldValue($("#myPasswordElement")[0]); |
|
719 * @desc Gets the current value of the myPasswordElement element |
|
720 * |
|
721 * @name fieldValue |
|
722 * @param Element el The DOM element for which the value will be returned |
|
723 * @param Boolean successful true if value returned must be for a successful controls (default is true) |
|
724 * @type String or Array<String> or null or undefined |
|
725 * @cat Plugins/Form |
|
726 */ |
690 */ |
727 $.fieldValue = function(el, successful) { |
691 $.fieldValue = function(el, successful) { |
728 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); |
692 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); |
729 if (typeof successful == 'undefined') successful = true; |
693 if (successful === undefined) { |
730 |
694 successful = true; |
731 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || |
695 } |
732 (t == 'checkbox' || t == 'radio') && !el.checked || |
696 |
733 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || |
697 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || |
734 tag == 'select' && el.selectedIndex == -1)) |
698 (t == 'checkbox' || t == 'radio') && !el.checked || |
735 return null; |
699 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || |
736 |
700 tag == 'select' && el.selectedIndex == -1)) { |
737 if (tag == 'select') { |
701 return null; |
738 var index = el.selectedIndex; |
702 } |
739 if (index < 0) return null; |
703 |
740 var a = [], ops = el.options; |
704 if (tag == 'select') { |
741 var one = (t == 'select-one'); |
705 var index = el.selectedIndex; |
742 var max = (one ? index+1 : ops.length); |
706 if (index < 0) { |
743 for(var i=(one ? index : 0); i < max; i++) { |
707 return null; |
744 var op = ops[i]; |
708 } |
745 if (op.selected) { |
709 var a = [], ops = el.options; |
746 // extra pain for IE... |
710 var one = (t == 'select-one'); |
747 var v = $.browser.msie && !(op.attributes['value'].specified) ? op.text : op.value; |
711 var max = (one ? index+1 : ops.length); |
748 if (one) return v; |
712 for(var i=(one ? index : 0); i < max; i++) { |
749 a.push(v); |
713 var op = ops[i]; |
750 } |
714 if (op.selected) { |
751 } |
715 var v = op.value; |
752 return a; |
716 if (!v) { // extra pain for IE... |
753 } |
717 v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value; |
754 return el.value; |
718 } |
755 }; |
719 if (one) { |
756 |
720 return v; |
|
721 } |
|
722 a.push(v); |
|
723 } |
|
724 } |
|
725 return a; |
|
726 } |
|
727 return $(el).val(); |
|
728 }; |
757 |
729 |
758 /** |
730 /** |
759 * Clears the form data. Takes the following actions on the form's input fields: |
731 * Clears the form data. Takes the following actions on the form's input fields: |
760 * - input text fields will have their 'value' property set to the empty string |
732 * - input text fields will have their 'value' property set to the empty string |
761 * - select elements will have their 'selectedIndex' property set to -1 |
733 * - select elements will have their 'selectedIndex' property set to -1 |
762 * - checkbox and radio inputs will have their 'checked' property set to false |
734 * - checkbox and radio inputs will have their 'checked' property set to false |
763 * - inputs of type submit, button, reset, and hidden will *not* be effected |
735 * - inputs of type submit, button, reset, and hidden will *not* be effected |
764 * - button elements will *not* be effected |
736 * - button elements will *not* be effected |
765 * |
|
766 * @example $('form').clearForm(); |
|
767 * @desc Clears all forms on the page. |
|
768 * |
|
769 * @name clearForm |
|
770 * @type jQuery |
|
771 * @cat Plugins/Form |
|
772 */ |
737 */ |
773 $.fn.clearForm = function() { |
738 $.fn.clearForm = function() { |
774 return this.each(function() { |
739 return this.each(function() { |
775 $('input,select,textarea', this).clearFields(); |
740 $('input,select,textarea', this).clearFields(); |
776 }); |
741 }); |
777 }; |
742 }; |
778 |
743 |
779 /** |
744 /** |
780 * Clears the selected form elements. Takes the following actions on the matched elements: |
745 * Clears the selected form elements. |
781 * - input text fields will have their 'value' property set to the empty string |
|
782 * - select elements will have their 'selectedIndex' property set to -1 |
|
783 * - checkbox and radio inputs will have their 'checked' property set to false |
|
784 * - inputs of type submit, button, reset, and hidden will *not* be effected |
|
785 * - button elements will *not* be effected |
|
786 * |
|
787 * @example $('.myInputs').clearFields(); |
|
788 * @desc Clears all inputs with class myInputs |
|
789 * |
|
790 * @name clearFields |
|
791 * @type jQuery |
|
792 * @cat Plugins/Form |
|
793 */ |
746 */ |
794 $.fn.clearFields = $.fn.clearInputs = function() { |
747 $.fn.clearFields = $.fn.clearInputs = function() { |
795 return this.each(function() { |
748 return this.each(function() { |
796 var t = this.type, tag = this.tagName.toLowerCase(); |
749 var t = this.type, tag = this.tagName.toLowerCase(); |
797 if (t == 'text' || t == 'password' || tag == 'textarea') |
750 if (t == 'text' || t == 'password' || tag == 'textarea') { |
798 this.value = ''; |
751 this.value = ''; |
799 else if (t == 'checkbox' || t == 'radio') |
752 } |
800 this.checked = false; |
753 else if (t == 'checkbox' || t == 'radio') { |
801 else if (tag == 'select') |
754 this.checked = false; |
802 this.selectedIndex = -1; |
755 } |
803 }); |
756 else if (tag == 'select') { |
804 }; |
757 this.selectedIndex = -1; |
805 |
758 } |
|
759 }); |
|
760 }; |
806 |
761 |
807 /** |
762 /** |
808 * Resets the form data. Causes all form elements to be reset to their original value. |
763 * Resets the form data. Causes all form elements to be reset to their original value. |
809 * |
|
810 * @example $('form').resetForm(); |
|
811 * @desc Resets all forms on the page. |
|
812 * |
|
813 * @name resetForm |
|
814 * @type jQuery |
|
815 * @cat Plugins/Form |
|
816 */ |
764 */ |
817 $.fn.resetForm = function() { |
765 $.fn.resetForm = function() { |
818 return this.each(function() { |
766 return this.each(function() { |
819 // guard against an input with the name of 'reset' |
767 // guard against an input with the name of 'reset' |
820 // note that IE reports the reset function as an 'object' |
768 // note that IE reports the reset function as an 'object' |
821 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) |
769 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { |
822 this.reset(); |
770 this.reset(); |
823 }); |
771 } |
824 }; |
772 }); |
825 |
773 }; |
826 |
774 |
827 /** |
775 /** |
828 * Enables or disables any matching elements. |
776 * Enables or disables any matching elements. |
829 * |
777 */ |
830 * @example $(':radio').enabled(false); |
778 $.fn.enable = function(b) { |
831 * @desc Disables all radio buttons |
779 if (b === undefined) { |
832 * |
780 b = true; |
833 * @name select |
781 } |
834 * @type jQuery |
782 return this.each(function() { |
835 * @cat Plugins/Form |
783 this.disabled = !b; |
836 */ |
784 }); |
837 $.fn.enable = function(b) { |
|
838 if (b == undefined) b = true; |
|
839 return this.each(function() { |
|
840 this.disabled = !b |
|
841 }); |
|
842 }; |
785 }; |
843 |
786 |
844 /** |
787 /** |
845 * Checks/unchecks any matching checkboxes or radio buttons and |
788 * Checks/unchecks any matching checkboxes or radio buttons and |
846 * selects/deselects and matching option elements. |
789 * selects/deselects and matching option elements. |
847 * |
790 */ |
848 * @example $(':checkbox').selected(); |
791 $.fn.selected = function(select) { |
849 * @desc Checks all checkboxes |
792 if (select === undefined) { |
850 * |
793 select = true; |
851 * @name select |
794 } |
852 * @type jQuery |
795 return this.each(function() { |
853 * @cat Plugins/Form |
796 var t = this.type; |
854 */ |
797 if (t == 'checkbox' || t == 'radio') { |
855 $.fn.select = function(select) { |
798 this.checked = select; |
856 if (select == undefined) select = true; |
799 } |
857 return this.each(function() { |
800 else if (this.tagName.toLowerCase() == 'option') { |
858 var t = this.type; |
801 var $sel = $(this).parent('select'); |
859 if (t == 'checkbox' || t == 'radio') |
802 if (select && $sel[0] && $sel[0].type == 'select-one') { |
860 this.checked = select; |
803 // deselect all other options |
861 else if (this.tagName.toLowerCase() == 'option') { |
804 $sel.find('option').selected(false); |
862 var $sel = $(this).parent('select'); |
805 } |
863 if (select && $sel[0] && $sel[0].type == 'select-one') { |
806 this.selected = select; |
864 // deselect all other options |
807 } |
865 $sel.find('option').select(false); |
808 }); |
866 } |
809 }; |
867 this.selected = select; |
810 |
868 } |
811 // helper fn for console logging |
869 }); |
812 // set $.fn.ajaxSubmit.debug to true to enable debug logging |
|
813 function log() { |
|
814 if ($.fn.ajaxSubmit.debug) { |
|
815 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); |
|
816 if (window.console && window.console.log) { |
|
817 window.console.log(msg); |
|
818 } |
|
819 else if (window.opera && window.opera.postError) { |
|
820 window.opera.postError(msg); |
|
821 } |
|
822 } |
870 }; |
823 }; |
871 |
824 |
872 })(jQuery); |
825 })(jQuery); |