Commit cd27aeadde4ccc81cdf140ce0366e2b4381c03d5

Authored by Antonio Terceiro
1 parent e739076b

Fix media upload after jQuery upgrade

The solr merge brought a jQuery upgrade from 1.4 to 1.5 (WTF?), and that
broke the jQuery Form plugin. I am adding the very latest form
plugin but jQuery 1.5 is not that recent; that is probably why they
don't seem to interact very well. Note how I had to workaround the
dataType of the response ... weird. :-/
public/javascripts/article.js
... ... @@ -148,7 +148,6 @@ jQuery(function($) {
148 148 });
149 149  
150 150 $('#media-upload-form form').ajaxForm({
151   - dataType: 'json',
152 151 resetForm: true,
153 152 beforeSubmit:
154 153 function() {
... ... @@ -156,7 +155,8 @@ jQuery(function($) {
156 155 $('#media-upload-box .header').toggleClass('icon-loading');
157 156 },
158 157 success:
159   - function(data) {
  158 + function(text) {
  159 + var data = $.parseJSON(text);
160 160 list_items(data, '#media-upload-results .items', true);
161 161 if (data.length && data.length > 0) {
162 162 $('#media-upload-results').slideDown();
... ... @@ -168,6 +168,7 @@ jQuery(function($) {
168 168 $('#media-upload-more-files').click(function() {
169 169 $('#media-upload-results').hide();
170 170 $('#media-upload-form').show();
  171 + return false;
171 172 });
172 173  
173 174 });
... ...
public/javascripts/jquery.form.js
1 1 /*!
2 2 * jQuery Form Plugin
3   - * version: 2.43 (12-MAR-2010)
  3 + * version: 3.10 (20-JUL-2012)
4 4 * @requires jQuery v1.3.2 or later
5 5 *
6 6 * Examples and documentation at: http://malsup.com/jquery/form/
  7 + * Project repository: https://github.com/malsup/form
7 8 * Dual licensed under the MIT and GPL licenses:
8   - * http://www.opensource.org/licenses/mit-license.php
9   - * http://www.gnu.org/licenses/gpl.html
  9 + * http://malsup.github.com/mit-license.txt
  10 + * http://malsup.github.com/gpl-license-v2.txt
10 11 */
  12 +/*global ActiveXObject alert */
11 13 ;(function($) {
  14 +"use strict";
12 15  
13 16 /*
14   - Usage Note:
15   - -----------
16   - Do not use both ajaxSubmit and ajaxForm on the same form. These
17   - functions are intended to be exclusive. Use ajaxSubmit if you want
18   - to bind your own submit handler to the form. For example,
19   -
20   - $(document).ready(function() {
21   - $('#myForm').bind('submit', function() {
22   - $(this).ajaxSubmit({
23   - target: '#output'
24   - });
25   - return false; // <-- important!
26   - });
27   - });
28   -
29   - Use ajaxForm when you want the plugin to manage all the event binding
30   - for you. For example,
31   -
32   - $(document).ready(function() {
33   - $('#myForm').ajaxForm({
34   - target: '#output'
35   - });
36   - });
37   -
38   - When using ajaxForm, the ajaxSubmit function will be invoked for you
39   - at the appropriate time.
  17 + Usage Note:
  18 + -----------
  19 + Do not use both ajaxSubmit and ajaxForm on the same form. These
  20 + functions are mutually exclusive. Use ajaxSubmit if you want
  21 + to bind your own submit handler to the form. For example,
  22 +
  23 + $(document).ready(function() {
  24 + $('#myForm').on('submit', function(e) {
  25 + e.preventDefault(); // <-- important
  26 + $(this).ajaxSubmit({
  27 + target: '#output'
  28 + });
  29 + });
  30 + });
  31 +
  32 + Use ajaxForm when you want the plugin to manage all the event binding
  33 + for you. For example,
  34 +
  35 + $(document).ready(function() {
  36 + $('#myForm').ajaxForm({
  37 + target: '#output'
  38 + });
  39 + });
  40 +
  41 + You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
  42 + form does not have to exist when you invoke ajaxForm:
  43 +
  44 + $('#myForm').ajaxForm({
  45 + delegation: true,
  46 + target: '#output'
  47 + });
  48 +
  49 + When using ajaxForm, the ajaxSubmit function will be invoked for you
  50 + at the appropriate time.
40 51 */
41 52  
42 53 /**
  54 + * Feature detection
  55 + */
  56 +var feature = {};
  57 +feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
  58 +feature.formdata = window.FormData !== undefined;
  59 +
  60 +/**
43 61 * ajaxSubmit() provides a mechanism for immediately submitting
44 62 * an HTML form using AJAX.
45 63 */
46 64 $.fn.ajaxSubmit = function(options) {
47   - // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
48   - if (!this.length) {
49   - log('ajaxSubmit: skipping submit process - no element selected');
50   - return this;
51   - }
52   -
53   - if (typeof options == 'function')
54   - options = { success: options };
55   -
56   - var url = $.trim(this.attr('action'));
57   - if (url) {
58   - // clean url (don't include hash vaue)
59   - url = (url.match(/^([^#]+)/)||[])[1];
60   - }
61   - url = url || window.location.href || '';
62   -
63   - options = $.extend({
64   - url: url,
65   - type: this.attr('method') || 'GET',
66   - iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
67   - }, options || {});
68   -
69   - // hook for manipulating the form data before it is extracted;
70   - // convenient for use with rich editors like tinyMCE or FCKEditor
71   - var veto = {};
72   - this.trigger('form-pre-serialize', [this, options, veto]);
73   - if (veto.veto) {
74   - log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
75   - return this;
76   - }
77   -
78   - // provide opportunity to alter form data before it is serialized
79   - if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
80   - log('ajaxSubmit: submit aborted via beforeSerialize callback');
81   - return this;
82   - }
83   -
84   - var a = this.formToArray(options.semantic);
85   - if (options.data) {
86   - options.extraData = options.data;
87   - for (var n in options.data) {
88   - if(options.data[n] instanceof Array) {
89   - for (var k in options.data[n])
90   - a.push( { name: n, value: options.data[n][k] } );
91   - }
92   - else
93   - a.push( { name: n, value: options.data[n] } );
94   - }
95   - }
96   -
97   - // give pre-submit callback an opportunity to abort the submit
98   - if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
99   - log('ajaxSubmit: submit aborted via beforeSubmit callback');
100   - return this;
101   - }
102   -
103   - // fire vetoable 'validate' event
104   - this.trigger('form-submit-validate', [a, this, options, veto]);
105   - if (veto.veto) {
106   - log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
107   - return this;
108   - }
109   -
110   - var q = $.param(a);
111   -
112   - if (options.type.toUpperCase() == 'GET') {
113   - options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
114   - options.data = null; // data is null for 'get'
115   - }
116   - else
117   - options.data = q; // data is the query string for 'post'
118   -
119   - var $form = this, callbacks = [];
120   - if (options.resetForm) callbacks.push(function() { $form.resetForm(); });
121   - if (options.clearForm) callbacks.push(function() { $form.clearForm(); });
122   -
123   - // perform a load on the target only if dataType is not provided
124   - if (!options.dataType && options.target) {
125   - var oldSuccess = options.success || function(){};
126   - callbacks.push(function(data) {
127   - var fn = options.replaceTarget ? 'replaceWith' : 'html';
128   - $(options.target)[fn](data).each(oldSuccess, arguments);
129   - });
130   - }
131   - else if (options.success)
132   - callbacks.push(options.success);
133   -
134   - options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
135   - for (var i=0, max=callbacks.length; i < max; i++)
136   - callbacks[i].apply(options, [data, status, xhr || $form, $form]);
137   - };
138   -
139   - // are there files to upload?
140   - var files = $('input:file', this).fieldValue();
141   - var found = false;
142   - for (var j=0; j < files.length; j++)
143   - if (files[j])
144   - found = true;
145   -
146   - var multipart = false;
147   -// var mp = 'multipart/form-data';
148   -// multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
149   -
150   - // options.iframe allows user to force iframe mode
151   - // 06-NOV-09: now defaulting to iframe mode if file input is detected
152   - if ((files.length && options.iframe !== false) || options.iframe || found || multipart) {
153   - // hack to fix Safari hang (thanks to Tim Molendijk for this)
154   - // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
155   - if (options.closeKeepAlive)
156   - $.get(options.closeKeepAlive, fileUpload);
157   - else
158   - fileUpload();
159   - }
160   - else
161   - $.ajax(options);
162   -
163   - // fire 'notify' event
164   - this.trigger('form-submit-notify', [this, options]);
165   - return this;
166   -
167   -
168   - // private function for handling file uploads (hat tip to YAHOO!)
169   - function fileUpload() {
170   - var form = $form[0];
171   -
172   - if ($(':input[name=submit]', form).length) {
173   - alert('Error: Form elements must not be named "submit".');
174   - return;
175   - }
176   -
177   - var opts = $.extend({}, $.ajaxSettings, options);
178   - var s = $.extend(true, {}, $.extend(true, {}, $.ajaxSettings), opts);
179   -
180   - var id = 'jqFormIO' + (new Date().getTime());
181   - var $io = $('<iframe id="' + id + '" name="' + id + '" src="'+ opts.iframeSrc +'" onload="(jQuery(this).data(\'form-plugin-onload\'))()" />');
182   - var io = $io[0];
183   -
184   - $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
185   -
186   - var xhr = { // mock object
187   - aborted: 0,
188   - responseText: null,
189   - responseXML: null,
190   - status: 0,
191   - statusText: 'n/a',
192   - getAllResponseHeaders: function() {},
193   - getResponseHeader: function() {},
194   - setRequestHeader: function() {},
195   - abort: function() {
196   - this.aborted = 1;
197   - $io.attr('src', opts.iframeSrc); // abort op in progress
198   - }
199   - };
200   -
201   - var g = opts.global;
202   - // trigger ajax global events so that activity/block indicators work like normal
203   - if (g && ! $.active++) $.event.trigger("ajaxStart");
204   - if (g) $.event.trigger("ajaxSend", [xhr, opts]);
205   -
206   - if (s.beforeSend && s.beforeSend(xhr, s) === false) {
207   - s.global && $.active--;
208   - return;
209   - }
210   - if (xhr.aborted)
211   - return;
212   -
213   - var cbInvoked = false;
214   - var timedOut = 0;
215   -
216   - // add submitting element to data if we know it
217   - var sub = form.clk;
218   - if (sub) {
219   - var n = sub.name;
220   - if (n && !sub.disabled) {
221   - opts.extraData = opts.extraData || {};
222   - opts.extraData[n] = sub.value;
223   - if (sub.type == "image") {
224   - opts.extraData[n+'.x'] = form.clk_x;
225   - opts.extraData[n+'.y'] = form.clk_y;
226   - }
227   - }
228   - }
229   -
230   - // take a breath so that pending repaints get some cpu time before the upload starts
231   - function doSubmit() {
232   - // make sure form attrs are set
233   - var t = $form.attr('target'), a = $form.attr('action');
234   -
235   - // update form attrs in IE friendly way
236   - form.setAttribute('target',id);
237   - if (form.getAttribute('method') != 'POST')
238   - form.setAttribute('method', 'POST');
239   - if (form.getAttribute('action') != opts.url)
240   - form.setAttribute('action', opts.url);
241   -
242   - // ie borks in some cases when setting encoding
243   - if (! opts.skipEncodingOverride) {
244   - $form.attr({
245   - encoding: 'multipart/form-data',
246   - enctype: 'multipart/form-data'
247   - });
248   - }
249   -
250   - // support timout
251   - if (opts.timeout)
252   - setTimeout(function() { timedOut = true; cb(); }, opts.timeout);
253   -
254   - // add "extra" data to form if provided in options
255   - var extraInputs = [];
256   - try {
257   - if (opts.extraData)
258   - for (var n in opts.extraData)
259   - extraInputs.push(
260   - $('<input type="hidden" name="'+n+'" value="'+opts.extraData[n]+'" />')
261   - .appendTo(form)[0]);
262   -
263   - // add iframe to doc and submit the form
264   - $io.appendTo('body');
265   - $io.data('form-plugin-onload', cb);
266   - form.submit();
267   - }
268   - finally {
269   - // reset attrs and remove "extra" input elements
270   - form.setAttribute('action',a);
271   - t ? form.setAttribute('target', t) : $form.removeAttr('target');
272   - $(extraInputs).remove();
273   - }
274   - };
275   -
276   - if (opts.forceSync)
277   - doSubmit();
278   - else
279   - setTimeout(doSubmit, 10); // this lets dom updates render
280   -
281   - var domCheckCount = 100;
282   -
283   - function cb() {
284   - if (cbInvoked)
285   - return;
286   -
287   - var ok = true;
288   - try {
289   - if (timedOut) throw 'timeout';
290   - // extract the server response from the iframe
291   - var data, doc;
292   -
293   - doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
294   -
295   - var isXml = opts.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
296   - log('isXml='+isXml);
297   - if (!isXml && (doc.body == null || doc.body.innerHTML == '')) {
298   - if (--domCheckCount) {
299   - // in some browsers (Opera) the iframe DOM is not always traversable when
300   - // the onload callback fires, so we loop a bit to accommodate
301   - log('requeing onLoad callback, DOM not available');
302   - setTimeout(cb, 250);
303   - return;
304   - }
305   - log('Could not access iframe DOM after 100 tries.');
306   - return;
307   - }
308   -
309   - log('response detected');
310   - cbInvoked = true;
311   - xhr.responseText = doc.body ? doc.body.innerHTML : null;
312   - xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
313   - xhr.getResponseHeader = function(header){
314   - var headers = {'content-type': opts.dataType};
315   - return headers[header];
316   - };
317   -
318   - if (opts.dataType == 'json' || opts.dataType == 'script') {
319   - // see if user embedded response in textarea
320   - var ta = doc.getElementsByTagName('textarea')[0];
321   - if (ta)
322   - xhr.responseText = ta.value;
323   - else {
324   - // account for browsers injecting pre around json response
325   - var pre = doc.getElementsByTagName('pre')[0];
326   - if (pre)
327   - xhr.responseText = pre.innerHTML;
328   - }
329   - }
330   - else if (opts.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
331   - xhr.responseXML = toXml(xhr.responseText);
332   - }
333   - data = $.httpData(xhr, opts.dataType);
334   - }
335   - catch(e){
336   - log('error caught:',e);
337   - ok = false;
338   - xhr.error = e;
339   - $.handleError(opts, xhr, 'error', e);
340   - }
341   -
342   - // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
343   - if (ok) {
344   - opts.success(data, 'success');
345   - if (g) $.event.trigger("ajaxSuccess", [xhr, opts]);
346   - }
347   - if (g) $.event.trigger("ajaxComplete", [xhr, opts]);
348   - if (g && ! --$.active) $.event.trigger("ajaxStop");
349   - if (opts.complete) opts.complete(xhr, ok ? 'success' : 'error');
350   -
351   - // clean up
352   - setTimeout(function() {
353   - $io.removeData('form-plugin-onload');
354   - $io.remove();
355   - xhr.responseXML = null;
356   - }, 100);
357   - };
358   -
359   - function toXml(s, doc) {
360   - if (window.ActiveXObject) {
361   - doc = new ActiveXObject('Microsoft.XMLDOM');
362   - doc.async = 'false';
363   - doc.loadXML(s);
364   - }
365   - else
366   - doc = (new DOMParser()).parseFromString(s, 'text/xml');
367   - return (doc && doc.documentElement && doc.documentElement.tagName != 'parsererror') ? doc : null;
368   - };
369   - };
  65 + /*jshint scripturl:true */
  66 +
  67 + // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
  68 + if (!this.length) {
  69 + log('ajaxSubmit: skipping submit process - no element selected');
  70 + return this;
  71 + }
  72 +
  73 + var method, action, url, $form = this;
  74 +
  75 + if (typeof options == 'function') {
  76 + options = { success: options };
  77 + }
  78 +
  79 + method = this.attr('method');
  80 + action = this.attr('action');
  81 + url = (typeof action === 'string') ? $.trim(action) : '';
  82 + url = url || window.location.href || '';
  83 + if (url) {
  84 + // clean url (don't include hash vaue)
  85 + url = (url.match(/^([^#]+)/)||[])[1];
  86 + }
  87 +
  88 + options = $.extend(true, {
  89 + url: url,
  90 + success: $.ajaxSettings.success,
  91 + type: method || 'GET',
  92 + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
  93 + }, options);
  94 +
  95 + // hook for manipulating the form data before it is extracted;
  96 + // convenient for use with rich editors like tinyMCE or FCKEditor
  97 + var veto = {};
  98 + this.trigger('form-pre-serialize', [this, options, veto]);
  99 + if (veto.veto) {
  100 + log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
  101 + return this;
  102 + }
  103 +
  104 + // provide opportunity to alter form data before it is serialized
  105 + if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
  106 + log('ajaxSubmit: submit aborted via beforeSerialize callback');
  107 + return this;
  108 + }
  109 +
  110 + var traditional = options.traditional;
  111 + if ( traditional === undefined ) {
  112 + traditional = $.ajaxSettings.traditional;
  113 + }
  114 +
  115 + var elements = [];
  116 + var qx, a = this.formToArray(options.semantic, elements);
  117 + if (options.data) {
  118 + options.extraData = options.data;
  119 + qx = $.param(options.data, traditional);
  120 + }
  121 +
  122 + // give pre-submit callback an opportunity to abort the submit
  123 + if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
  124 + log('ajaxSubmit: submit aborted via beforeSubmit callback');
  125 + return this;
  126 + }
  127 +
  128 + // fire vetoable 'validate' event
  129 + this.trigger('form-submit-validate', [a, this, options, veto]);
  130 + if (veto.veto) {
  131 + log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
  132 + return this;
  133 + }
  134 +
  135 + var q = $.param(a, traditional);
  136 + if (qx) {
  137 + q = ( q ? (q + '&' + qx) : qx );
  138 + }
  139 + if (options.type.toUpperCase() == 'GET') {
  140 + options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
  141 + options.data = null; // data is null for 'get'
  142 + }
  143 + else {
  144 + options.data = q; // data is the query string for 'post'
  145 + }
  146 +
  147 + var callbacks = [];
  148 + if (options.resetForm) {
  149 + callbacks.push(function() { $form.resetForm(); });
  150 + }
  151 + if (options.clearForm) {
  152 + callbacks.push(function() { $form.clearForm(options.includeHidden); });
  153 + }
  154 +
  155 + // perform a load on the target only if dataType is not provided
  156 + if (!options.dataType && options.target) {
  157 + var oldSuccess = options.success || function(){};
  158 + callbacks.push(function(data) {
  159 + var fn = options.replaceTarget ? 'replaceWith' : 'html';
  160 + $(options.target)[fn](data).each(oldSuccess, arguments);
  161 + });
  162 + }
  163 + else if (options.success) {
  164 + callbacks.push(options.success);
  165 + }
  166 +
  167 + options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
  168 + var context = options.context || options; // jQuery 1.4+ supports scope context
  169 + for (var i=0, max=callbacks.length; i < max; i++) {
  170 + callbacks[i].apply(context, [data, status, xhr || $form, $form]);
  171 + }
  172 + };
  173 +
  174 + // are there files to upload?
  175 + var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
  176 + var hasFileInputs = fileInputs.length > 0;
  177 + var mp = 'multipart/form-data';
  178 + var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
  179 +
  180 + var fileAPI = feature.fileapi && feature.formdata;
  181 + log("fileAPI :" + fileAPI);
  182 + var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
  183 +
  184 + // options.iframe allows user to force iframe mode
  185 + // 06-NOV-09: now defaulting to iframe mode if file input is detected
  186 + if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
  187 + // hack to fix Safari hang (thanks to Tim Molendijk for this)
  188 + // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
  189 + if (options.closeKeepAlive) {
  190 + $.get(options.closeKeepAlive, function() {
  191 + fileUploadIframe(a);
  192 + });
  193 + }
  194 + else {
  195 + fileUploadIframe(a);
  196 + }
  197 + }
  198 + else if ((hasFileInputs || multipart) && fileAPI) {
  199 + fileUploadXhr(a);
  200 + }
  201 + else {
  202 + $.ajax(options);
  203 + }
  204 +
  205 + // clear element array
  206 + for (var k=0; k < elements.length; k++)
  207 + elements[k] = null;
  208 +
  209 + // fire 'notify' event
  210 + this.trigger('form-submit-notify', [this, options]);
  211 + return this;
  212 +
  213 + // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
  214 + function fileUploadXhr(a) {
  215 + var formdata = new FormData();
  216 +
  217 + for (var i=0; i < a.length; i++) {
  218 + formdata.append(a[i].name, a[i].value);
  219 + }
  220 +
  221 + if (options.extraData) {
  222 + for (var p in options.extraData)
  223 + if (options.extraData.hasOwnProperty(p))
  224 + formdata.append(p, options.extraData[p]);
  225 + }
  226 +
  227 + options.data = null;
  228 +
  229 + var s = $.extend(true, {}, $.ajaxSettings, options, {
  230 + contentType: false,
  231 + processData: false,
  232 + cache: false,
  233 + type: 'POST'
  234 + });
  235 +
  236 + if (options.uploadProgress) {
  237 + // workaround because jqXHR does not expose upload property
  238 + s.xhr = function() {
  239 + var xhr = jQuery.ajaxSettings.xhr();
  240 + if (xhr.upload) {
  241 + xhr.upload.onprogress = function(event) {
  242 + var percent = 0;
  243 + var position = event.loaded || event.position; /*event.position is deprecated*/
  244 + var total = event.total;
  245 + if (event.lengthComputable) {
  246 + percent = Math.ceil(position / total * 100);
  247 + }
  248 + options.uploadProgress(event, position, total, percent);
  249 + };
  250 + }
  251 + return xhr;
  252 + };
  253 + }
  254 +
  255 + s.data = null;
  256 + var beforeSend = s.beforeSend;
  257 + s.beforeSend = function(xhr, o) {
  258 + o.data = formdata;
  259 + if(beforeSend)
  260 + beforeSend.call(this, xhr, o);
  261 + };
  262 + $.ajax(s);
  263 + }
  264 +
  265 + // private function for handling file uploads (hat tip to YAHOO!)
  266 + function fileUploadIframe(a) {
  267 + var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
  268 + var useProp = !!$.fn.prop;
  269 +
  270 + if ($(':input[name=submit],:input[id=submit]', form).length) {
  271 + // if there is an input with a name or id of 'submit' then we won't be
  272 + // able to invoke the submit fn on the form (at least not x-browser)
  273 + alert('Error: Form elements must not have name or id of "submit".');
  274 + return;
  275 + }
  276 +
  277 + if (a) {
  278 + // ensure that every serialized input is still enabled
  279 + for (i=0; i < elements.length; i++) {
  280 + el = $(elements[i]);
  281 + if ( useProp )
  282 + el.prop('disabled', false);
  283 + else
  284 + el.removeAttr('disabled');
  285 + }
  286 + }
  287 +
  288 + s = $.extend(true, {}, $.ajaxSettings, options);
  289 + s.context = s.context || s;
  290 + id = 'jqFormIO' + (new Date().getTime());
  291 + if (s.iframeTarget) {
  292 + $io = $(s.iframeTarget);
  293 + n = $io.attr('name');
  294 + if (!n)
  295 + $io.attr('name', id);
  296 + else
  297 + id = n;
  298 + }
  299 + else {
  300 + $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
  301 + $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
  302 + }
  303 + io = $io[0];
  304 +
  305 +
  306 + xhr = { // mock object
  307 + aborted: 0,
  308 + responseText: null,
  309 + responseXML: null,
  310 + status: 0,
  311 + statusText: 'n/a',
  312 + getAllResponseHeaders: function() {},
  313 + getResponseHeader: function() {},
  314 + setRequestHeader: function() {},
  315 + abort: function(status) {
  316 + var e = (status === 'timeout' ? 'timeout' : 'aborted');
  317 + log('aborting upload... ' + e);
  318 + this.aborted = 1;
  319 + $io.attr('src', s.iframeSrc); // abort op in progress
  320 + xhr.error = e;
  321 + if (s.error)
  322 + s.error.call(s.context, xhr, e, status);
  323 + if (g)
  324 + $.event.trigger("ajaxError", [xhr, s, e]);
  325 + if (s.complete)
  326 + s.complete.call(s.context, xhr, e);
  327 + }
  328 + };
  329 +
  330 + g = s.global;
  331 + // trigger ajax global events so that activity/block indicators work like normal
  332 + if (g && 0 === $.active++) {
  333 + $.event.trigger("ajaxStart");
  334 + }
  335 + if (g) {
  336 + $.event.trigger("ajaxSend", [xhr, s]);
  337 + }
  338 +
  339 + if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
  340 + if (s.global) {
  341 + $.active--;
  342 + }
  343 + return;
  344 + }
  345 + if (xhr.aborted) {
  346 + return;
  347 + }
  348 +
  349 + // add submitting element to data if we know it
  350 + sub = form.clk;
  351 + if (sub) {
  352 + n = sub.name;
  353 + if (n && !sub.disabled) {
  354 + s.extraData = s.extraData || {};
  355 + s.extraData[n] = sub.value;
  356 + if (sub.type == "image") {
  357 + s.extraData[n+'.x'] = form.clk_x;
  358 + s.extraData[n+'.y'] = form.clk_y;
  359 + }
  360 + }
  361 + }
  362 +
  363 + var CLIENT_TIMEOUT_ABORT = 1;
  364 + var SERVER_ABORT = 2;
  365 +
  366 + function getDoc(frame) {
  367 + var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
  368 + return doc;
  369 + }
  370 +
  371 + // Rails CSRF hack (thanks to Yvan Barthelemy)
  372 + var csrf_token = $('meta[name=csrf-token]').attr('content');
  373 + var csrf_param = $('meta[name=csrf-param]').attr('content');
  374 + if (csrf_param && csrf_token) {
  375 + s.extraData = s.extraData || {};
  376 + s.extraData[csrf_param] = csrf_token;
  377 + }
  378 +
  379 + // take a breath so that pending repaints get some cpu time before the upload starts
  380 + function doSubmit() {
  381 + // make sure form attrs are set
  382 + var t = $form.attr('target'), a = $form.attr('action');
  383 +
  384 + // update form attrs in IE friendly way
  385 + form.setAttribute('target',id);
  386 + if (!method) {
  387 + form.setAttribute('method', 'POST');
  388 + }
  389 + if (a != s.url) {
  390 + form.setAttribute('action', s.url);
  391 + }
  392 +
  393 + // ie borks in some cases when setting encoding
  394 + if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
  395 + $form.attr({
  396 + encoding: 'multipart/form-data',
  397 + enctype: 'multipart/form-data'
  398 + });
  399 + }
  400 +
  401 + // support timout
  402 + if (s.timeout) {
  403 + timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
  404 + }
  405 +
  406 + // look for server aborts
  407 + function checkState() {
  408 + try {
  409 + var state = getDoc(io).readyState;
  410 + log('state = ' + state);
  411 + if (state && state.toLowerCase() == 'uninitialized')
  412 + setTimeout(checkState,50);
  413 + }
  414 + catch(e) {
  415 + log('Server abort: ' , e, ' (', e.name, ')');
  416 + cb(SERVER_ABORT);
  417 + if (timeoutHandle)
  418 + clearTimeout(timeoutHandle);
  419 + timeoutHandle = undefined;
  420 + }
  421 + }
  422 +
  423 + // add "extra" data to form if provided in options
  424 + var extraInputs = [];
  425 + try {
  426 + if (s.extraData) {
  427 + for (var n in s.extraData) {
  428 + if (s.extraData.hasOwnProperty(n)) {
  429 + extraInputs.push(
  430 + $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
  431 + .appendTo(form)[0]);
  432 + }
  433 + }
  434 + }
  435 +
  436 + if (!s.iframeTarget) {
  437 + // add iframe to doc and submit the form
  438 + $io.appendTo('body');
  439 + if (io.attachEvent)
  440 + io.attachEvent('onload', cb);
  441 + else
  442 + io.addEventListener('load', cb, false);
  443 + }
  444 + setTimeout(checkState,15);
  445 + form.submit();
  446 + }
  447 + finally {
  448 + // reset attrs and remove "extra" input elements
  449 + form.setAttribute('action',a);
  450 + if(t) {
  451 + form.setAttribute('target', t);
  452 + } else {
  453 + $form.removeAttr('target');
  454 + }
  455 + $(extraInputs).remove();
  456 + }
  457 + }
  458 +
  459 + if (s.forceSync) {
  460 + doSubmit();
  461 + }
  462 + else {
  463 + setTimeout(doSubmit, 10); // this lets dom updates render
  464 + }
  465 +
  466 + var data, doc, domCheckCount = 50, callbackProcessed;
  467 +
  468 + function cb(e) {
  469 + if (xhr.aborted || callbackProcessed) {
  470 + return;
  471 + }
  472 + try {
  473 + doc = getDoc(io);
  474 + }
  475 + catch(ex) {
  476 + log('cannot access response document: ', ex);
  477 + e = SERVER_ABORT;
  478 + }
  479 + if (e === CLIENT_TIMEOUT_ABORT && xhr) {
  480 + xhr.abort('timeout');
  481 + return;
  482 + }
  483 + else if (e == SERVER_ABORT && xhr) {
  484 + xhr.abort('server abort');
  485 + return;
  486 + }
  487 +
  488 + if (!doc || doc.location.href == s.iframeSrc) {
  489 + // response not received yet
  490 + if (!timedOut)
  491 + return;
  492 + }
  493 + if (io.detachEvent)
  494 + io.detachEvent('onload', cb);
  495 + else
  496 + io.removeEventListener('load', cb, false);
  497 +
  498 + var status = 'success', errMsg;
  499 + try {
  500 + if (timedOut) {
  501 + throw 'timeout';
  502 + }
  503 +
  504 + var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
  505 + log('isXml='+isXml);
  506 + if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
  507 + if (--domCheckCount) {
  508 + // in some browsers (Opera) the iframe DOM is not always traversable when
  509 + // the onload callback fires, so we loop a bit to accommodate
  510 + log('requeing onLoad callback, DOM not available');
  511 + setTimeout(cb, 250);
  512 + return;
  513 + }
  514 + // let this fall through because server response could be an empty document
  515 + //log('Could not access iframe DOM after mutiple tries.');
  516 + //throw 'DOMException: not available';
  517 + }
  518 +
  519 + //log('response detected');
  520 + var docRoot = doc.body ? doc.body : doc.documentElement;
  521 + xhr.responseText = docRoot ? docRoot.innerHTML : null;
  522 + xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
  523 + if (isXml)
  524 + s.dataType = 'xml';
  525 + xhr.getResponseHeader = function(header){
  526 + var headers = {'content-type': s.dataType};
  527 + return headers[header];
  528 + };
  529 + // support for XHR 'status' & 'statusText' emulation :
  530 + if (docRoot) {
  531 + xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
  532 + xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
  533 + }
  534 +
  535 + var dt = (s.dataType || '').toLowerCase();
  536 + var scr = /(json|script|text)/.test(dt);
  537 + if (scr || s.textarea) {
  538 + // see if user embedded response in textarea
  539 + var ta = doc.getElementsByTagName('textarea')[0];
  540 + if (ta) {
  541 + xhr.responseText = ta.value;
  542 + // support for XHR 'status' & 'statusText' emulation :
  543 + xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
  544 + xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
  545 + }
  546 + else if (scr) {
  547 + // account for browsers injecting pre around json response
  548 + var pre = doc.getElementsByTagName('pre')[0];
  549 + var b = doc.getElementsByTagName('body')[0];
  550 + if (pre) {
  551 + xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
  552 + }
  553 + else if (b) {
  554 + xhr.responseText = b.textContent ? b.textContent : b.innerText;
  555 + }
  556 + }
  557 + }
  558 + else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
  559 + xhr.responseXML = toXml(xhr.responseText);
  560 + }
  561 +
  562 + try {
  563 + data = httpData(xhr, dt, s);
  564 + }
  565 + catch (e) {
  566 + status = 'parsererror';
  567 + xhr.error = errMsg = (e || status);
  568 + }
  569 + }
  570 + catch (e) {
  571 + log('error caught: ',e);
  572 + status = 'error';
  573 + xhr.error = errMsg = (e || status);
  574 + }
  575 +
  576 + if (xhr.aborted) {
  577 + log('upload aborted');
  578 + status = null;
  579 + }
  580 +
  581 + if (xhr.status) { // we've set xhr.status
  582 + status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
  583 + }
  584 +
  585 + // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
  586 + if (status === 'success') {
  587 + if (s.success)
  588 + s.success.call(s.context, data, 'success', xhr);
  589 + if (g)
  590 + $.event.trigger("ajaxSuccess", [xhr, s]);
  591 + }
  592 + else if (status) {
  593 + if (errMsg === undefined)
  594 + errMsg = xhr.statusText;
  595 + if (s.error)
  596 + s.error.call(s.context, xhr, status, errMsg);
  597 + if (g)
  598 + $.event.trigger("ajaxError", [xhr, s, errMsg]);
  599 + }
  600 +
  601 + if (g)
  602 + $.event.trigger("ajaxComplete", [xhr, s]);
  603 +
  604 + if (g && ! --$.active) {
  605 + $.event.trigger("ajaxStop");
  606 + }
  607 +
  608 + if (s.complete)
  609 + s.complete.call(s.context, xhr, status);
  610 +
  611 + callbackProcessed = true;
  612 + if (s.timeout)
  613 + clearTimeout(timeoutHandle);
  614 +
  615 + // clean up
  616 + setTimeout(function() {
  617 + if (!s.iframeTarget)
  618 + $io.remove();
  619 + xhr.responseXML = null;
  620 + }, 100);
  621 + }
  622 +
  623 + var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
  624 + if (window.ActiveXObject) {
  625 + doc = new ActiveXObject('Microsoft.XMLDOM');
  626 + doc.async = 'false';
  627 + doc.loadXML(s);
  628 + }
  629 + else {
  630 + doc = (new DOMParser()).parseFromString(s, 'text/xml');
  631 + }
  632 + return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
  633 + };
  634 + var parseJSON = $.parseJSON || function(s) {
  635 + /*jslint evil:true */
  636 + return window['eval']('(' + s + ')');
  637 + };
  638 +
  639 + var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
  640 +
  641 + var ct = xhr.getResponseHeader('content-type') || '',
  642 + xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
  643 + data = xml ? xhr.responseXML : xhr.responseText;
  644 +
  645 + if (xml && data.documentElement.nodeName === 'parsererror') {
  646 + if ($.error)
  647 + $.error('parsererror');
  648 + }
  649 + if (s && s.dataFilter) {
  650 + data = s.dataFilter(data, type);
  651 + }
  652 + if (typeof data === 'string') {
  653 + if (type === 'json' || !type && ct.indexOf('json') >= 0) {
  654 + data = parseJSON(data);
  655 + } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
  656 + $.globalEval(data);
  657 + }
  658 + }
  659 + return data;
  660 + };
  661 + }
370 662 };
371 663  
372 664 /**
... ... @@ -375,9 +667,9 @@ $.fn.ajaxSubmit = function(options) {
375 667 * The advantages of using this method instead of ajaxSubmit() are:
376 668 *
377 669 * 1: This method will include coordinates for <input type="image" /> elements (if the element
378   - * is used to submit the form).
  670 + * is used to submit the form).
379 671 * 2. This method will include the submit element's name/value data (for the element that was
380   - * used to submit the form).
  672 + * used to submit the form).
381 673 * 3. This method binds the submit() method to the form for you.
382 674 *
383 675 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
... ... @@ -385,42 +677,83 @@ $.fn.ajaxSubmit = function(options) {
385 677 * the form itself.
386 678 */
387 679 $.fn.ajaxForm = function(options) {
388   - return this.ajaxFormUnbind().bind('submit.form-plugin', function(e) {
389   - e.preventDefault();
390   - $(this).ajaxSubmit(options);
391   - }).bind('click.form-plugin', function(e) {
392   - var target = e.target;
393   - var $el = $(target);
394   - if (!($el.is(":submit,input:image"))) {
395   - // is this a child element of the submit el? (ex: a span within a button)
396   - var t = $el.closest(':submit');
397   - if (t.length == 0)
398   - return;
399   - target = t[0];
400   - }
401   - var form = this;
402   - form.clk = target;
403   - if (target.type == 'image') {
404   - if (e.offsetX != undefined) {
405   - form.clk_x = e.offsetX;
406   - form.clk_y = e.offsetY;
407   - } else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
408   - var offset = $el.offset();
409   - form.clk_x = e.pageX - offset.left;
410   - form.clk_y = e.pageY - offset.top;
411   - } else {
412   - form.clk_x = e.pageX - target.offsetLeft;
413   - form.clk_y = e.pageY - target.offsetTop;
414   - }
415   - }
416   - // clear form vars
417   - setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
418   - });
  680 + options = options || {};
  681 + options.delegation = options.delegation && $.isFunction($.fn.on);
  682 +
  683 + // in jQuery 1.3+ we can fix mistakes with the ready state
  684 + if (!options.delegation && this.length === 0) {
  685 + var o = { s: this.selector, c: this.context };
  686 + if (!$.isReady && o.s) {
  687 + log('DOM not ready, queuing ajaxForm');
  688 + $(function() {
  689 + $(o.s,o.c).ajaxForm(options);
  690 + });
  691 + return this;
  692 + }
  693 + // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
  694 + log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
  695 + return this;
  696 + }
  697 +
  698 + if ( options.delegation ) {
  699 + $(document)
  700 + .off('submit.form-plugin', this.selector, doAjaxSubmit)
  701 + .off('click.form-plugin', this.selector, captureSubmittingElement)
  702 + .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
  703 + .on('click.form-plugin', this.selector, options, captureSubmittingElement);
  704 + return this;
  705 + }
  706 +
  707 + return this.ajaxFormUnbind()
  708 + .bind('submit.form-plugin', options, doAjaxSubmit)
  709 + .bind('click.form-plugin', options, captureSubmittingElement);
419 710 };
420 711  
  712 +// private event handlers
  713 +function doAjaxSubmit(e) {
  714 + /*jshint validthis:true */
  715 + var options = e.data;
  716 + if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
  717 + e.preventDefault();
  718 + $(this).ajaxSubmit(options);
  719 + }
  720 +}
  721 +
  722 +function captureSubmittingElement(e) {
  723 + /*jshint validthis:true */
  724 + var target = e.target;
  725 + var $el = $(target);
  726 + if (!($el.is(":submit,input:image"))) {
  727 + // is this a child element of the submit el? (ex: a span within a button)
  728 + var t = $el.closest(':submit');
  729 + if (t.length === 0) {
  730 + return;
  731 + }
  732 + target = t[0];
  733 + }
  734 + var form = this;
  735 + form.clk = target;
  736 + if (target.type == 'image') {
  737 + if (e.offsetX !== undefined) {
  738 + form.clk_x = e.offsetX;
  739 + form.clk_y = e.offsetY;
  740 + } else if (typeof $.fn.offset == 'function') {
  741 + var offset = $el.offset();
  742 + form.clk_x = e.pageX - offset.left;
  743 + form.clk_y = e.pageY - offset.top;
  744 + } else {
  745 + form.clk_x = e.pageX - target.offsetLeft;
  746 + form.clk_y = e.pageY - target.offsetTop;
  747 + }
  748 + }
  749 + // clear form vars
  750 + setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
  751 +}
  752 +
  753 +
421 754 // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
422 755 $.fn.ajaxFormUnbind = function() {
423   - return this.unbind('submit.form-plugin click.form-plugin');
  756 + return this.unbind('submit.form-plugin click.form-plugin');
424 757 };
425 758  
426 759 /**
... ... @@ -434,45 +767,74 @@ $.fn.ajaxFormUnbind = function() {
434 767 * It is this array that is passed to pre-submit callback functions provided to the
435 768 * ajaxSubmit() and ajaxForm() methods.
436 769 */
437   -$.fn.formToArray = function(semantic) {
438   - var a = [];
439   - if (this.length == 0) return a;
440   -
441   - var form = this[0];
442   - var els = semantic ? form.getElementsByTagName('*') : form.elements;
443   - if (!els) return a;
444   - for(var i=0, max=els.length; i < max; i++) {
445   - var el = els[i];
446   - var n = el.name;
447   - if (!n) continue;
448   -
449   - if (semantic && form.clk && el.type == "image") {
450   - // handle image inputs on the fly when semantic == true
451   - if(!el.disabled && form.clk == el) {
452   - a.push({name: n, value: $(el).val()});
453   - a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
454   - }
455   - continue;
456   - }
457   -
458   - var v = $.fieldValue(el, true);
459   - if (v && v.constructor == Array) {
460   - for(var j=0, jmax=v.length; j < jmax; j++)
461   - a.push({name: n, value: v[j]});
462   - }
463   - else if (v !== null && typeof v != 'undefined')
464   - a.push({name: n, value: v});
465   - }
466   -
467   - if (!semantic && form.clk) {
468   - // input type=='image' are not found in elements array! handle it here
469   - var $input = $(form.clk), input = $input[0], n = input.name;
470   - if (n && !input.disabled && input.type == 'image') {
471   - a.push({name: n, value: $input.val()});
472   - a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
473   - }
474   - }
475   - return a;
  770 +$.fn.formToArray = function(semantic, elements) {
  771 + var a = [];
  772 + if (this.length === 0) {
  773 + return a;
  774 + }
  775 +
  776 + var form = this[0];
  777 + var els = semantic ? form.getElementsByTagName('*') : form.elements;
  778 + if (!els) {
  779 + return a;
  780 + }
  781 +
  782 + var i,j,n,v,el,max,jmax;
  783 + for(i=0, max=els.length; i < max; i++) {
  784 + el = els[i];
  785 + n = el.name;
  786 + if (!n) {
  787 + continue;
  788 + }
  789 +
  790 + if (semantic && form.clk && el.type == "image") {
  791 + // handle image inputs on the fly when semantic == true
  792 + if(!el.disabled && form.clk == el) {
  793 + a.push({name: n, value: $(el).val(), type: el.type });
  794 + a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
  795 + }
  796 + continue;
  797 + }
  798 +
  799 + v = $.fieldValue(el, true);
  800 + if (v && v.constructor == Array) {
  801 + if (elements)
  802 + elements.push(el);
  803 + for(j=0, jmax=v.length; j < jmax; j++) {
  804 + a.push({name: n, value: v[j]});
  805 + }
  806 + }
  807 + else if (feature.fileapi && el.type == 'file' && !el.disabled) {
  808 + if (elements)
  809 + elements.push(el);
  810 + var files = el.files;
  811 + if (files.length) {
  812 + for (j=0; j < files.length; j++) {
  813 + a.push({name: n, value: files[j], type: el.type});
  814 + }
  815 + }
  816 + else {
  817 + // #180
  818 + a.push({ name: n, value: '', type: el.type });
  819 + }
  820 + }
  821 + else if (v !== null && typeof v != 'undefined') {
  822 + if (elements)
  823 + elements.push(el);
  824 + a.push({name: n, value: v, type: el.type, required: el.required});
  825 + }
  826 + }
  827 +
  828 + if (!semantic && form.clk) {
  829 + // input type=='image' are not found in elements array! handle it here
  830 + var $input = $(form.clk), input = $input[0];
  831 + n = input.name;
  832 + if (n && !input.disabled && input.type == 'image') {
  833 + a.push({name: n, value: $input.val()});
  834 + a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
  835 + }
  836 + }
  837 + return a;
476 838 };
477 839  
478 840 /**
... ... @@ -480,8 +842,8 @@ $.fn.formToArray = function(semantic) {
480 842 * in the format: name1=value1&amp;name2=value2
481 843 */
482 844 $.fn.formSerialize = function(semantic) {
483   - //hand off to jQuery.param for proper encoding
484   - return $.param(this.formToArray(semantic));
  845 + //hand off to jQuery.param for proper encoding
  846 + return $.param(this.formToArray(semantic));
485 847 };
486 848  
487 849 /**
... ... @@ -489,32 +851,36 @@ $.fn.formSerialize = function(semantic) {
489 851 * This method will return a string in the format: name1=value1&amp;name2=value2
490 852 */
491 853 $.fn.fieldSerialize = function(successful) {
492   - var a = [];
493   - this.each(function() {
494   - var n = this.name;
495   - if (!n) return;
496   - var v = $.fieldValue(this, successful);
497   - if (v && v.constructor == Array) {
498   - for (var i=0,max=v.length; i < max; i++)
499   - a.push({name: n, value: v[i]});
500   - }
501   - else if (v !== null && typeof v != 'undefined')
502   - a.push({name: this.name, value: v});
503   - });
504   - //hand off to jQuery.param for proper encoding
505   - return $.param(a);
  854 + var a = [];
  855 + this.each(function() {
  856 + var n = this.name;
  857 + if (!n) {
  858 + return;
  859 + }
  860 + var v = $.fieldValue(this, successful);
  861 + if (v && v.constructor == Array) {
  862 + for (var i=0,max=v.length; i < max; i++) {
  863 + a.push({name: n, value: v[i]});
  864 + }
  865 + }
  866 + else if (v !== null && typeof v != 'undefined') {
  867 + a.push({name: this.name, value: v});
  868 + }
  869 + });
  870 + //hand off to jQuery.param for proper encoding
  871 + return $.param(a);
506 872 };
507 873  
508 874 /**
509 875 * Returns the value(s) of the element in the matched set. For example, consider the following form:
510 876 *
511 877 * <form><fieldset>
512   - * <input name="A" type="text" />
513   - * <input name="A" type="text" />
514   - * <input name="B" type="checkbox" value="B1" />
515   - * <input name="B" type="checkbox" value="B2"/>
516   - * <input name="C" type="radio" value="C1" />
517   - * <input name="C" type="radio" value="C2" />
  878 + * <input name="A" type="text" />
  879 + * <input name="A" type="text" />
  880 + * <input name="B" type="checkbox" value="B1" />
  881 + * <input name="B" type="checkbox" value="B2"/>
  882 + * <input name="C" type="radio" value="C1" />
  883 + * <input name="C" type="radio" value="C2" />
518 884 * </fieldset></form>
519 885 *
520 886 * var v = $(':text').fieldValue();
... ... @@ -541,51 +907,63 @@ $.fn.fieldSerialize = function(successful) {
541 907 * for each element is returned.
542 908 *
543 909 * Note: This method *always* returns an array. If no valid value can be determined the
544   - * array will be empty, otherwise it will contain one or more values.
  910 + * array will be empty, otherwise it will contain one or more values.
545 911 */
546 912 $.fn.fieldValue = function(successful) {
547   - for (var val=[], i=0, max=this.length; i < max; i++) {
548   - var el = this[i];
549   - var v = $.fieldValue(el, successful);
550   - if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length))
551   - continue;
552   - v.constructor == Array ? $.merge(val, v) : val.push(v);
553   - }
554   - return val;
  913 + for (var val=[], i=0, max=this.length; i < max; i++) {
  914 + var el = this[i];
  915 + var v = $.fieldValue(el, successful);
  916 + if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
  917 + continue;
  918 + }
  919 + if (v.constructor == Array)
  920 + $.merge(val, v);
  921 + else
  922 + val.push(v);
  923 + }
  924 + return val;
555 925 };
556 926  
557 927 /**
558 928 * Returns the value of the field element.
559 929 */
560 930 $.fieldValue = function(el, successful) {
561   - var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
562   - if (typeof successful == 'undefined') successful = true;
563   -
564   - if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
565   - (t == 'checkbox' || t == 'radio') && !el.checked ||
566   - (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
567   - tag == 'select' && el.selectedIndex == -1))
568   - return null;
569   -
570   - if (tag == 'select') {
571   - var index = el.selectedIndex;
572   - if (index < 0) return null;
573   - var a = [], ops = el.options;
574   - var one = (t == 'select-one');
575   - var max = (one ? index+1 : ops.length);
576   - for(var i=(one ? index : 0); i < max; i++) {
577   - var op = ops[i];
578   - if (op.selected) {
579   - var v = op.value;
580   - if (!v) // extra pain for IE...
581   - v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
582   - if (one) return v;
583   - a.push(v);
584   - }
585   - }
586   - return a;
587   - }
588   - return el.value;
  931 + var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
  932 + if (successful === undefined) {
  933 + successful = true;
  934 + }
  935 +
  936 + if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
  937 + (t == 'checkbox' || t == 'radio') && !el.checked ||
  938 + (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
  939 + tag == 'select' && el.selectedIndex == -1)) {
  940 + return null;
  941 + }
  942 +
  943 + if (tag == 'select') {
  944 + var index = el.selectedIndex;
  945 + if (index < 0) {
  946 + return null;
  947 + }
  948 + var a = [], ops = el.options;
  949 + var one = (t == 'select-one');
  950 + var max = (one ? index+1 : ops.length);
  951 + for(var i=(one ? index : 0); i < max; i++) {
  952 + var op = ops[i];
  953 + if (op.selected) {
  954 + var v = op.value;
  955 + if (!v) { // extra pain for IE...
  956 + v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
  957 + }
  958 + if (one) {
  959 + return v;
  960 + }
  961 + a.push(v);
  962 + }
  963 + }
  964 + return a;
  965 + }
  966 + return $(el).val();
589 967 };
590 968  
591 969 /**
... ... @@ -596,47 +974,63 @@ $.fieldValue = function(el, successful) {
596 974 * - inputs of type submit, button, reset, and hidden will *not* be effected
597 975 * - button elements will *not* be effected
598 976 */
599   -$.fn.clearForm = function() {
600   - return this.each(function() {
601   - $('input,select,textarea', this).clearFields();
602   - });
  977 +$.fn.clearForm = function(includeHidden) {
  978 + return this.each(function() {
  979 + $('input,select,textarea', this).clearFields(includeHidden);
  980 + });
603 981 };
604 982  
605 983 /**
606 984 * Clears the selected form elements.
607 985 */
608   -$.fn.clearFields = $.fn.clearInputs = function() {
609   - return this.each(function() {
610   - var t = this.type, tag = this.tagName.toLowerCase();
611   - if (t == 'text' || t == 'password' || tag == 'textarea')
612   - this.value = '';
613   - else if (t == 'checkbox' || t == 'radio')
614   - this.checked = false;
615   - else if (tag == 'select')
616   - this.selectedIndex = -1;
617   - });
  986 +$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
  987 + var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
  988 + return this.each(function() {
  989 + var t = this.type, tag = this.tagName.toLowerCase();
  990 + if (re.test(t) || tag == 'textarea') {
  991 + this.value = '';
  992 + }
  993 + else if (t == 'checkbox' || t == 'radio') {
  994 + this.checked = false;
  995 + }
  996 + else if (tag == 'select') {
  997 + this.selectedIndex = -1;
  998 + }
  999 + else if (includeHidden) {
  1000 + // includeHidden can be the valud true, or it can be a selector string
  1001 + // indicating a special test; for example:
  1002 + // $('#myForm').clearForm('.special:hidden')
  1003 + // the above would clean hidden inputs that have the class of 'special'
  1004 + if ( (includeHidden === true && /hidden/.test(t)) ||
  1005 + (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
  1006 + this.value = '';
  1007 + }
  1008 + });
618 1009 };
619 1010  
620 1011 /**
621 1012 * Resets the form data. Causes all form elements to be reset to their original value.
622 1013 */
623 1014 $.fn.resetForm = function() {
624   - return this.each(function() {
625   - // guard against an input with the name of 'reset'
626   - // note that IE reports the reset function as an 'object'
627   - if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType))
628   - this.reset();
629   - });
  1015 + return this.each(function() {
  1016 + // guard against an input with the name of 'reset'
  1017 + // note that IE reports the reset function as an 'object'
  1018 + if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
  1019 + this.reset();
  1020 + }
  1021 + });
630 1022 };
631 1023  
632 1024 /**
633 1025 * Enables or disables any matching elements.
634 1026 */
635 1027 $.fn.enable = function(b) {
636   - if (b == undefined) b = true;
637   - return this.each(function() {
638   - this.disabled = !b;
639   - });
  1028 + if (b === undefined) {
  1029 + b = true;
  1030 + }
  1031 + return this.each(function() {
  1032 + this.disabled = !b;
  1033 + });
640 1034 };
641 1035  
642 1036 /**
... ... @@ -644,32 +1038,39 @@ $.fn.enable = function(b) {
644 1038 * selects/deselects and matching option elements.
645 1039 */
646 1040 $.fn.selected = function(select) {
647   - if (select == undefined) select = true;
648   - return this.each(function() {
649   - var t = this.type;
650   - if (t == 'checkbox' || t == 'radio')
651   - this.checked = select;
652   - else if (this.tagName.toLowerCase() == 'option') {
653   - var $sel = $(this).parent('select');
654   - if (select && $sel[0] && $sel[0].type == 'select-one') {
655   - // deselect all other options
656   - $sel.find('option').selected(false);
657   - }
658   - this.selected = select;
659   - }
660   - });
  1041 + if (select === undefined) {
  1042 + select = true;
  1043 + }
  1044 + return this.each(function() {
  1045 + var t = this.type;
  1046 + if (t == 'checkbox' || t == 'radio') {
  1047 + this.checked = select;
  1048 + }
  1049 + else if (this.tagName.toLowerCase() == 'option') {
  1050 + var $sel = $(this).parent('select');
  1051 + if (select && $sel[0] && $sel[0].type == 'select-one') {
  1052 + // deselect all other options
  1053 + $sel.find('option').selected(false);
  1054 + }
  1055 + this.selected = select;
  1056 + }
  1057 + });
661 1058 };
662 1059  
  1060 +// expose debug var
  1061 +$.fn.ajaxSubmit.debug = false;
  1062 +
663 1063 // helper fn for console logging
664   -// set $.fn.ajaxSubmit.debug to true to enable debug logging
665 1064 function log() {
666   - if ($.fn.ajaxSubmit.debug) {
667   - var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
668   - if (window.console && window.console.log)
669   - window.console.log(msg);
670   - else if (window.opera && window.opera.postError)
671   - window.opera.postError(msg);
672   - }
673   -};
  1065 + if (!$.fn.ajaxSubmit.debug)
  1066 + return;
  1067 + var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
  1068 + if (window.console && window.console.log) {
  1069 + window.console.log(msg);
  1070 + }
  1071 + else if (window.opera && window.opera.postError) {
  1072 + window.opera.postError(msg);
  1073 + }
  1074 +}
674 1075  
675 1076 })(jQuery);
... ...