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