diff --git a/openslides/projector/static/javascript/jquery.form.js b/openslides/projector/static/javascript/jquery.form.js index 796db12fb..64a916062 100644 --- a/openslides/projector/static/javascript/jquery.form.js +++ b/openslides/projector/static/javascript/jquery.form.js @@ -1,7 +1,7 @@ /*! * jQuery Form Plugin - * version: 3.09 (16-APR-2012) - * @requires jQuery v1.3.2 or later + * version: 3.26.0-2013.01.28 + * @requires jQuery v1.5 or later * * Examples and documentation at: http://malsup.com/jquery/form/ * Project repository: https://github.com/malsup/form @@ -37,7 +37,7 @@ target: '#output' }); }); - + You can also use ajaxForm with delegation (requires jQuery v1.7+), so the form does not have to exist when you invoke ajaxForm: @@ -45,7 +45,7 @@ delegation: true, target: '#output' }); - + When using ajaxForm, the ajaxSubmit function will be invoked for you at the appropriate time. */ @@ -69,7 +69,7 @@ $.fn.ajaxSubmit = function(options) { log('ajaxSubmit: skipping submit process - no element selected'); return this; } - + var method, action, url, $form = this; if (typeof options == 'function') { @@ -111,7 +111,7 @@ $.fn.ajaxSubmit = function(options) { if ( traditional === undefined ) { traditional = $.ajaxSettings.traditional; } - + var elements = []; var qx, a = this.formToArray(options.semantic, elements); if (options.data) { @@ -135,7 +135,7 @@ $.fn.ajaxSubmit = function(options) { var q = $.param(a, traditional); if (qx) { q = ( q ? (q + '&' + qx) : qx ); - } + } if (options.type.toUpperCase() == 'GET') { options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; options.data = null; // data is null for 'get' @@ -165,14 +165,18 @@ $.fn.ajaxSubmit = function(options) { } options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg - var context = options.context || options; // jQuery 1.4+ supports scope context + var context = options.context || this ; // jQuery 1.4+ supports scope context for (var i=0, max=callbacks.length; i < max; i++) { callbacks[i].apply(context, [data, status, xhr || $form, $form]); } }; // are there files to upload? - var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113) + + // [value] (issue #113), also see comment: + // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 + var fileInputs = $('input[type=file]:enabled[value!=""]', this); + var hasFileInputs = fileInputs.length > 0; var mp = 'multipart/form-data'; var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); @@ -181,6 +185,8 @@ $.fn.ajaxSubmit = function(options) { log("fileAPI :" + fileAPI); var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; + var jqxhr; + // options.iframe allows user to force iframe mode // 06-NOV-09: now defaulting to iframe mode if file input is detected if (options.iframe !== false && (options.iframe || shouldUseFrame)) { @@ -188,20 +194,22 @@ $.fn.ajaxSubmit = function(options) { // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d if (options.closeKeepAlive) { $.get(options.closeKeepAlive, function() { - fileUploadIframe(a); + jqxhr = fileUploadIframe(a); }); } - else { - fileUploadIframe(a); - } + else { + jqxhr = fileUploadIframe(a); + } } else if ((hasFileInputs || multipart) && fileAPI) { - fileUploadXhr(a); + jqxhr = fileUploadXhr(a); } else { - $.ajax(options); + jqxhr = $.ajax(options); } + $form.removeData('jqxhr').data('jqxhr', jqxhr); + // clear element array for (var k=0; k < elements.length; k++) elements[k] = null; @@ -210,6 +218,22 @@ $.fn.ajaxSubmit = function(options) { this.trigger('form-submit-notify', [this, options]); return this; + // utility fn for deep serialization + function deepSerialize(extraData){ + var serialized = $.param(extraData).split('&'); + var len = serialized.length; + var result = []; + var i, part; + for (i=0; i < len; i++) { + // #252; undo param space replacement + serialized[i] = serialized[i].replace(/\+/g,' '); + part = serialized[i].split('='); + // #278; use array instead of object storage, favoring array serializations + result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); + } + return result; + } + // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) function fileUploadXhr(a) { var formdata = new FormData(); @@ -219,9 +243,10 @@ $.fn.ajaxSubmit = function(options) { } if (options.extraData) { - for (var p in options.extraData) - if (options.extraData.hasOwnProperty(p)) - formdata.append(p, options.extraData[p]); + var serializedData = deepSerialize(options.extraData); + for (i=0; i < serializedData.length; i++) + if (serializedData[i]) + formdata.append(serializedData[i][0], serializedData[i][1]); } options.data = null; @@ -230,9 +255,9 @@ $.fn.ajaxSubmit = function(options) { contentType: false, processData: false, cache: false, - type: 'POST' + type: method || 'POST' }); - + if (options.uploadProgress) { // workaround because jqXHR does not expose upload property s.xhr = function() { @@ -253,27 +278,21 @@ $.fn.ajaxSubmit = function(options) { } s.data = null; - var beforeSend = s.beforeSend; - s.beforeSend = function(xhr, o) { - o.data = formdata; - if(beforeSend) - beforeSend.call(o, xhr, options); + var beforeSend = s.beforeSend; + s.beforeSend = function(xhr, o) { + o.data = formdata; + if(beforeSend) + beforeSend.call(this, xhr, o); }; - $.ajax(s); + return $.ajax(s); } // private function for handling file uploads (hat tip to YAHOO!) function fileUploadIframe(a) { var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; var useProp = !!$.fn.prop; + var deferred = $.Deferred(); - if ($(':input[name=submit],:input[id=submit]', form).length) { - // if there is an input with a name or id of 'submit' then we won't be - // able to invoke the submit fn on the form (at least not x-browser) - alert('Error: Form elements must not have name or id of "submit".'); - return; - } - if (a) { // ensure that every serialized input is still enabled for (i=0; i < elements.length; i++) { @@ -316,6 +335,14 @@ $.fn.ajaxSubmit = function(options) { var e = (status === 'timeout' ? 'timeout' : 'aborted'); log('aborting upload... ' + e); this.aborted = 1; + + try { // #214, #257 + if (io.contentWindow.document.execCommand) { + io.contentWindow.document.execCommand('Stop'); + } + } + catch(ignore) {} + $io.attr('src', s.iframeSrc); // abort op in progress xhr.error = e; if (s.error) @@ -340,10 +367,12 @@ $.fn.ajaxSubmit = function(options) { if (s.global) { $.active--; } - return; + deferred.reject(); + return deferred; } if (xhr.aborted) { - return; + deferred.reject(); + return deferred; } // add submitting element to data if we know it @@ -359,7 +388,7 @@ $.fn.ajaxSubmit = function(options) { } } } - + var CLIENT_TIMEOUT_ABORT = 1; var SERVER_ABORT = 2; @@ -367,7 +396,7 @@ $.fn.ajaxSubmit = function(options) { var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document; return doc; } - + // Rails CSRF hack (thanks to Yvan Barthelemy) var csrf_token = $('meta[name=csrf-token]').attr('content'); var csrf_param = $('meta[name=csrf-param]').attr('content'); @@ -402,7 +431,7 @@ $.fn.ajaxSubmit = function(options) { if (s.timeout) { timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); } - + // look for server aborts function checkState() { try { @@ -426,9 +455,16 @@ $.fn.ajaxSubmit = function(options) { if (s.extraData) { for (var n in s.extraData) { if (s.extraData.hasOwnProperty(n)) { - extraInputs.push( - $('').attr('value',s.extraData[n]) - .appendTo(form)[0]); + // if using the $.param format that allows for multiple values with the same name + if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { + extraInputs.push( + $('').val(s.extraData[n].value) + .appendTo(form)[0]); + } else { + extraInputs.push( + $('').val(s.extraData[n]) + .appendTo(form)[0]); + } } } } @@ -442,7 +478,9 @@ $.fn.ajaxSubmit = function(options) { io.addEventListener('load', cb, false); } setTimeout(checkState,15); - form.submit(); + // just in case form has element with name/id of 'submit' + var submitFn = document.createElement('form').submit; + submitFn.apply(form); } finally { // reset attrs and remove "extra" input elements @@ -478,10 +516,12 @@ $.fn.ajaxSubmit = function(options) { } if (e === CLIENT_TIMEOUT_ABORT && xhr) { xhr.abort('timeout'); + deferred.reject(xhr, 'timeout'); return; } else if (e == SERVER_ABORT && xhr) { xhr.abort('server abort'); + deferred.reject(xhr, 'error', 'server abort'); return; } @@ -492,7 +532,7 @@ $.fn.ajaxSubmit = function(options) { } if (io.detachEvent) io.detachEvent('onload', cb); - else + else io.removeEventListener('load', cb, false); var status = 'success', errMsg; @@ -586,6 +626,7 @@ $.fn.ajaxSubmit = function(options) { if (status === 'success') { if (s.success) s.success.call(s.context, data, 'success', xhr); + deferred.resolve(xhr.responseText, 'success', xhr); if (g) $.event.trigger("ajaxSuccess", [xhr, s]); } @@ -594,6 +635,7 @@ $.fn.ajaxSubmit = function(options) { errMsg = xhr.statusText; if (s.error) s.error.call(s.context, xhr, status, errMsg); + deferred.reject(xhr, 'error', errMsg); if (g) $.event.trigger("ajaxError", [xhr, s, errMsg]); } @@ -658,6 +700,8 @@ $.fn.ajaxSubmit = function(options) { } return data; }; + + return deferred; } }; @@ -679,7 +723,7 @@ $.fn.ajaxSubmit = function(options) { $.fn.ajaxForm = function(options) { options = options || {}; options.delegation = options.delegation && $.isFunction($.fn.on); - + // in jQuery 1.3+ we can fix mistakes with the ready state if (!options.delegation && this.length === 0) { var o = { s: this.selector, c: this.context }; @@ -709,7 +753,7 @@ $.fn.ajaxForm = function(options) { .bind('click.form-plugin', options, captureSubmittingElement); }; -// private event handlers +// private event handlers function doAjaxSubmit(e) { /*jshint validthis:true */ var options = e.data; @@ -718,14 +762,14 @@ function doAjaxSubmit(e) { $(this).ajaxSubmit(options); } } - + function captureSubmittingElement(e) { /*jshint validthis:true */ var target = e.target; var $el = $(target); - if (!($el.is(":submit,input:image"))) { + if (!($el.is("[type=submit],[type=image]"))) { // is this a child element of the submit el? (ex: a span within a button) - var t = $el.closest(':submit'); + var t = $el.closest('[type=submit]'); if (t.length === 0) { return; } @@ -798,14 +842,14 @@ $.fn.formToArray = function(semantic, elements) { v = $.fieldValue(el, true); if (v && v.constructor == Array) { - if (elements) + if (elements) elements.push(el); for(j=0, jmax=v.length; j < jmax; j++) { a.push({name: n, value: v[j]}); } } else if (feature.fileapi && el.type == 'file' && !el.disabled) { - if (elements) + if (elements) elements.push(el); var files = el.files; if (files.length) { @@ -819,7 +863,7 @@ $.fn.formToArray = function(semantic, elements) { } } else if (v !== null && typeof v != 'undefined') { - if (elements) + if (elements) elements.push(el); a.push({name: n, value: v, type: el.type, required: el.required}); } @@ -883,19 +927,19 @@ $.fn.fieldSerialize = function(successful) { * * * - * var v = $(':text').fieldValue(); + * var v = $('input[type=text]').fieldValue(); * // if no values are entered into the text inputs * v == ['',''] * // if values entered into the text inputs are 'foo' and 'bar' * v == ['foo','bar'] * - * var v = $(':checkbox').fieldValue(); + * var v = $('input[type=checkbox]').fieldValue(); * // if neither checkbox is checked * v === undefined * // if both checkboxes are checked * v == ['B1', 'B2'] * - * var v = $(':radio').fieldValue(); + * var v = $('input[type=radio]').fieldValue(); * // if neither radio is checked * v === undefined * // if first radio is checked @@ -996,8 +1040,15 @@ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) { else if (tag == 'select') { this.selectedIndex = -1; } + else if (t == "file") { + if (/MSIE/.test(navigator.userAgent)) { + $(this).replaceWith($(this).clone()); + } else { + $(this).val(''); + } + } else if (includeHidden) { - // includeHidden can be the valud true, or it can be a selector string + // includeHidden can be the value true, or it can be a selector string // indicating a special test; for example: // $('#myForm').clearForm('.special:hidden') // the above would clean hidden inputs that have the class of 'special' @@ -1062,7 +1113,7 @@ $.fn.ajaxSubmit.debug = false; // helper fn for console logging function log() { - if (!$.fn.ajaxSubmit.debug) + if (!$.fn.ajaxSubmit.debug) return; var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); if (window.console && window.console.log) {