1 2/* Merged Plone Javascript file 3 * This file is dynamically assembled from separate parts. 4 * Some of these parts have 3rd party licenses or copyright information attached 5 * Such information is valid for that section, 6 * not for the entire composite file 7 * originating files are separated by - filename.js - 8 */ 9 10/* - ++resource++plone.app.jquerytools.overlayhelpers.js - */ 11/***************** 12 13 jQuery Tools overlay helpers. 14 15 Copyright © 2010, The Plone Foundation 16 Licensed under the GPL, see LICENSE.txt for details. 17 18*****************/ 19 20/*jslint browser: true */ 21/*global jQuery, ajax_noresponse_message, close_box_message, window */ 22 23// Name space object for pipbox 24var pb = {spinner: {}, overlay_counter: 1}; 25 26jQuery.tools.overlay.conf.oneInstance = false; 27 28(function($) { 29 // override jqt default effect to take account of position 30 // of parent elements 31 jQuery.tools.overlay.addEffect('default', 32 function(pos, onLoad) { 33 var conf = this.getConf(), 34 w = $(window), 35 ovl = this.getOverlay(), 36 op = ovl.parent().offsetParent().offset(); 37 38 if (!conf.fixed) { 39 pos.top += w.scrollTop() - op.top; 40 pos.left += w.scrollLeft() - op.left; 41 } 42 43 pos.position = conf.fixed ? 'fixed' : 'absolute'; 44 ovl.css(pos).fadeIn(conf.speed, onLoad); 45 46 }, function(onClose) { 47 this.getOverlay().fadeOut(this.getConf().closeSpeed, onClose); 48 } 49 ); 50 51 pb.spinner.show = function () { 52 $('body').css('cursor', 'wait'); 53 }; 54 pb.spinner.hide = function () { 55 $('body').css('cursor', ''); 56 }; 57}(jQuery)); 58 59 60jQuery(function ($) { 61 62 /****** 63 $.fn.prepOverlay jQuery plugin to inject overlay target into DOM and 64 annotate it with the data we'll need in order to display it. 65 ******/ 66 $.fn.prepOverlay = function (pba) { 67 return this.each(function () { 68 var o, pbo, config, onBeforeLoad, onLoad, src, parts; 69 70 o = $(this); 71 72 // copy options so that it's not just a reference 73 // to the parameter. 74 pbo = $.extend(true, {'width':'60%'}, pba); 75 76 // set overlay configuration 77 config = pbo.config || {}; 78 79 // set onBeforeLoad handler 80 onBeforeLoad = pb[pbo.subtype]; 81 if (onBeforeLoad) { 82 config.onBeforeLoad = onBeforeLoad; 83 } 84 onLoad = config.onLoad; 85 config.onLoad = function () { 86 if (onLoad) { 87 onLoad.apply(this, arguments); 88 } 89 pb.fi_focus(this.getOverlay()); 90 }; 91 92 // be promiscuous, pick up the url from 93 // href, src or action attributes 94 src = o.attr('href') || o.attr('src') || o.attr('action'); 95 96 // translate url with config specifications 97 if (pbo.urlmatch) { 98 src = src.replace(new RegExp(pbo.urlmatch), pbo.urlreplace); 99 } 100 101 if (pbo.subtype === 'inline') { 102 // we're going to let tools' overlay do all the real 103 // work. Just get the markers in place. 104 src = src.replace(/^.+#/, '#'); 105 $("[id='" + src.replace('#', '') + "']").addClass('overlay'); 106 o.removeAttr('href').attr('rel', src); 107 // use overlay on the source (clickable) element 108 o.overlay(); 109 } else { 110 // save various bits of information from the pbo options, 111 // and enable the overlay. 112 113 // this is not inline, so in one fashion or another 114 // we'll be loading it via the beforeLoad callback. 115 // create a unique id for a target element 116 pbo.nt = 'pb_' + pb.overlay_counter; 117 pb.overlay_counter += 1; 118 119 pbo.selector = pbo.filter || pbo.selector; 120 if (!pbo.selector) { 121 // see if one's been supplied in the src 122 parts = src.split(' '); 123 src = parts.shift(); 124 pbo.selector = parts.join(' '); 125 } 126 127 pbo.src = src; 128 pbo.config = config; 129 pbo.source = o; 130 131 // remove any existing overlay and overlay handler 132 pb.remove_overlay(o); 133 134 // save options on trigger element 135 o.data('pbo', pbo); 136 137 // mark the source with a rel attribute so we can find 138 // the overlay, and a special class for styling 139 o.attr('rel', '#' + pbo.nt); 140 o.addClass('link-overlay'); 141 142 // for some subtypes, we're setting click handlers 143 // and attaching overlay to the target element. That's 144 // so we'll know the dimensions early. 145 // Others, like iframe, just use overlay. 146 switch (pbo.subtype) { 147 case 'image': 148 o.click(pb.image_click); 149 break; 150 case 'ajax': 151 o.click(pb.ajax_click); 152 break; 153 case 'iframe': 154 pb.create_content_div(pbo); 155 o.overlay(config); 156 break; 157 default: 158 throw "Unsupported overlay type"; 159 } 160 161 // in case the click source wasn't 162 // already a link. 163 o.css('cursor', 'pointer'); 164 } 165 }); 166 }; 167 168 169 /****** 170 pb.remove_overlay 171 Remove the overlay and handler associated with a jquery wrapped 172 trigger object 173 ******/
174 pb.remove_overlay = function (o) { 175 var old_data = o.data('pbo'); 176 if (old_data) { 177 switch (old_data.subtype) { 178 case 'image': 179 o.unbind('click', pb.image_click); 180 break; 181 case 'ajax': 182 o.unbind('click', pb.ajax_click); 183 break; 184 default: 185 // it's probably the jqt overlay click handler, 186 // but we don't know the handler and are forced 187 // to do a generic unbind of click handlers. 188 o.unbind('click'); 189 break; 190 } 191 if (old_data.nt) { 192 $('#' + old_data.nt).remove(); 193 } 194 } 195 }; 196 197 198 /****** 199 pb.create_content_div 200 create a div to act as an overlay; append it to 201 the body; return it 202 ******/ 203 pb.create_content_div = function (pbo, trigger) { 204 var content, 205 close_message, 206 pbw = pbo.width; 207 208 if (typeof close_box_message === 'undefined') { 209 close_message = 'Close this box.'; 210 } else { 211 close_message = close_box_message; 212 } 213 214 content = $( 215 '<div id="' + pbo.nt + 216 '" class="overlay overlay-' + pbo.subtype + 217 ' ' + (pbo.cssclass || '') + 218 '"><div class="close"><a href="#" class="hiddenStructure" title="Close this box">' + 219 close_message + 220 '</a></div></div>'); 221 222 content.data('pbo', pbo); 223 224 // if a width option is specified, set it on the overlay div, 225 // computing against the window width if a % was specified. 226 if (pbw) { 227 if (pbw.indexOf('%') > 0) { 228 content.width(parseInt(pbw, 10) / 100 * $(window).width()); 229 } else { 230 content.width(pbw); 231 } 232 } 233 234 // add the target element at the end of the body. 235 content.appendTo($("body")); 236 237 return content; 238 }; 239 240 241 /****** 242 pb.image_click 243 click handler for image loads 244 ******/ 245 pb.image_click = function (event) { 246 var ethis, content, api, img, el, pbo; 247 248 ethis = $(this); 249 pbo = ethis.data('pbo'); 250 251 // find target container 252 content = $(ethis.attr('rel')); 253 if (!content.length) { 254 content = pb.create_content_div(pbo); 255 content.overlay(pbo.config); 256 } 257 api = content.overlay(); 258 259 // is the image loaded yet? 260 if (content.find('img').length === 0) { 261 // load the image. 262 if (pbo.src) { 263 pb.spinner.show(); 264 265 // create the image and stuff it 266 // into our target 267 img = new Image(); 268 img.src = pbo.src; 269 el = $(img); 270 content.append(el.addClass('pb-image')); 271 272 // Now, we'll cause the overlay to 273 // load when the image is loaded. 274 el.load(function () { 275 pb.spinner.hide(); 276 api.load(); 277 }); 278 279 } 280 } else { 281 api.load(); 282 } 283 284 return false; 285 }; 286 287 288 /****** 289 pb.fi_focus 290 First-input focus inside $ selection. 291 ******/ 292 pb.fi_focus = function (jqo) { 293 if (! jqo.find("form div.error :input:first").focus().length) { 294 jqo.find("form :input:visible:first").focus(); 295 } 296 }; 297 298 299 /****** 300 pb.ajax_error_recover 301 jQuery's ajax load function does not load error responses. 302 This routine returns the cooked error response. 303 ******/ 304 pb.ajax_error_recover = function (responseText, selector) { 305 var tcontent = $('<div/>').append( 306 responseText.replace(/<script(.|\s)*?\/script>/gi, "")); 307 return selector ? tcontent.find(selector) : tcontent; 308 }; 309 310 311 /****** 312 pb.add_ajax_load 313 Adds a hidden ajax_load input to form 314 ******/ 315 pb.add_ajax_load = function (form) { 316 if (form.find('input[name=ajax_load]').length === 0) {
317 form.prepend($('<input type="hidden" name="ajax_load" value="' + 318 (new Date().getTime()) + 319 '" />')); 320 } 321 }; 322 323 /****** 324 pb.prep_ajax_form 325 Set up form with ajaxForm, including success and error handlers. 326 ******/ 327 pb.prep_ajax_form = function (form) { 328 var ajax_parent = form.closest('.pb-ajax'), 329 data_parent = ajax_parent.closest('.overlay-ajax'), 330 pbo = data_parent.data('pbo'), 331 formtarget = pbo.formselector, 332 closeselector = pbo.closeselector, 333 beforepost = pbo.beforepost, 334 afterpost = pbo.afterpost, 335 noform = pbo.noform, 336 api = data_parent.overlay(), 337 selector = pbo.selector, 338 options = {}; 339 340 options.beforeSerialize = function () { 341 pb.spinner.show(); 342 }; 343 344 if (beforepost) { 345 options.beforeSubmit = function (arr, form, options) { 346 return beforepost(form, arr, options); 347 }; 348 } 349 options.success = function (responseText, statusText, xhr, form) { 350 $(document).trigger('formOverlayStart', [this, responseText, statusText, xhr, form]); 351 // success comes in many forms, some of which are errors; 352 // 353 354 var el, myform, success, target, scripts = [], filteredResponseText; 355 356 success = statusText === "success" || statusText === "notmodified"; 357 358 if (! success) { 359 // The responseText parameter is actually xhr 360 responseText = responseText.responseText; 361 } 362 // strip inline script tags 363 filteredResponseText = responseText.replace(/<script(.|\s)*?\/script>/gi, ""); 364 365 // create a div containing the optionally filtered response 366 el = $('<div />').append( 367 selector ? 368 // a lesson learned from the jQuery source: $(responseText) 369 // will not work well unless responseText is well-formed; 370 // appending to a div is more robust, and automagically 371 // removes the html/head/body outer tagging. 372 $('<div />').append(filteredResponseText).find(selector) 373 : 374 filteredResponseText 375 ); 376 377 // afterpost callback 378 if (success && afterpost) { 379 afterpost(el, data_parent); 380 } 381 382 myform = el.find(formtarget); 383 if (success && myform.length) { 384 ajax_parent.empty().append(el); 385 386 // execute inline scripts 387 try { 388 // jQuery 1.7 389 $.buildFragment([responseText], [document], scripts); 390 } catch (e) { 391 // jQuery 1.9 392 $.buildFragment([responseText], document, scripts); 393 } 394 if (scripts.length) { 395 $.each(scripts, function() { 396 $.globalEval( this.text || this.textContent || this.innerHTML || "" ); 397 }); 398 } 399 400 // This may be a complex form. 401 if ($.fn.ploneTabInit) { 402 el.ploneTabInit(); 403 } 404 pb.fi_focus(ajax_parent); 405 406 pb.add_ajax_load(myform); 407 // attach submit handler with the same options 408 myform.ajaxForm(options); 409 410 // attach close to element id'd by closeselector 411 if (closeselector) { 412 el.find(closeselector).click(function (event) { 413 api.close(); 414 return false; 415 }); 416 } 417 $(document).trigger('formOverlayLoadSuccess', [this, myform, api, pb, ajax_parent]); 418 } else { 419 // there's no form in our new content or there's been an error 420 if (success) { 421 if (typeof noform === "function") { 422 // get action from callback 423 noform = noform(el, pbo); 424 } 425 } else { 426 noform = statusText; 427 } 428 429 430 switch (noform) { 431 case 'close': 432 api.close(); 433 break; 434 case 'reload': 435 api.close(); 436 pb.spinner.show(); 437 // location.reload results in a repost 438 // dialog in some browsers; very unlikely to 439 // be what we want. 440 location.replace(location.href); 441 break; 442 case 'redirect': 443 api.close(); 444 pb.spinner.show(); 445 target = pbo.redirect; 446 if (typeof target === "function") { 447 // get target from callback 448 target = target(el, responseText, pbo); 449 } 450 location.replace(target); 451 break; 452 default: 453 if (el.children()) { 454 // show what we've got 455 ajax_parent.empty().append(el); 456 } else { 457 api.close(); 458 } 459 break; 460 } 461 $(document).trigger('formOverlayLoadFailure', [this, myform, api, pb, ajax_parent, noform]); 462 } 463 pb.spinner.hide(); 464 }; 465 // error and success callbacks are the same 466 options.error = options.success; 467 468 pb.add_ajax_load(form); 469 form.ajaxForm(options); 470 }; 471 472 473 /****** 474 pb.ajax_click 475 Click handler for ajax sources. The job of this routine
476 is to do the ajax load of the overlay element, then 477 call the JQT overlay loader. 478 ******/ 479 pb.ajax_click = function (event) { 480 var ethis = $(this), 481 pbo, 482 content, 483 api, 484 src, 485 el, 486 selector, 487 formtarget, 488 closeselector, 489 sep, 490 scripts = [], 491 e; 492 493 e = $.Event(); 494 e.type = "beforeAjaxClickHandled"; 495 $(document).trigger(e, [this, event]); 496 if (e.isDefaultPrevented()) { return false; } 497 498 pbo = ethis.data('pbo'); 499 500 content = pb.create_content_div(pbo, ethis); 501 // pbo.config.top = $(window).height() * 0.1 - ethis.offsetParent().offset().top; 502 content.overlay(pbo.config, ethis); 503 api = content.overlay(); 504 src = pbo.src; 505 selector = pbo.selector; 506 formtarget = pbo.formselector; 507 closeselector = pbo.closeselector; 508 509 pb.spinner.show(); 510 511 // prevent double click warning for this form 512 $(this).find("input.submitting").removeClass('submitting'); 513 514 el = $('div.pb-ajax', content); 515 if (el.length === 0) { 516 el = $('<div class="pb-ajax" />'); 517 content.append(el); 518 } 519 if (api.getConf().fixed) { 520 // don't let it be over 75% of the viewport's height 521 el.css('max-height', Math.floor($(window).height() * 0.75)); 522 } 523 524 // affix a random query argument to prevent 525 // loading from browser cache 526 sep = (src.indexOf('?') >= 0) ? '&': '?'; 527 src += sep + "ajax_load=" + (new Date().getTime()); 528 529 // add selector, if any 530 if (selector) { 531 src += ' ' + selector; 532 } 533 534 // set up callback to be used whenever new contents are loaded 535 // into the overlay, to prepare links and forms to stay within 536 // the overlay 537 el[0].handle_load_inside_overlay = function(responseText, errorText) { 538 var ele, target; 539 ele = $(this); 540 541 if (errorText === 'error') { 542 ele.append(pb.ajax_error_recover(responseText, selector)); 543 } else if (!responseText.length) { 544 ele.append(ajax_noresponse_message || 'No response from server.'); 545 } 546 547 // a non-semantic div here will make sure we can 548 // do enough formatting. 549 ele.wrapInner('<div />'); 550 551 // add the submit handler if we've a formtarget 552 if (formtarget) { 553 target = ele.find(formtarget); 554 if (target.length > 0) { 555 pb.prep_ajax_form(target); 556 } 557 } 558 559 // if a closeselector has been specified, tie it to the overlay's 560 // close method via closure 561 if (closeselector) { 562 ele.find(closeselector).click(function (event) { 563 api.close(); 564 return false; 565 }); 566 } 567 568 // execute inline scripts 569 try { 570 // jQuery 1.7 571 $.buildFragment([responseText], [document], scripts); 572 } catch (e) { 573 // jQuery 1.9 574 $.buildFragment([responseText], document, scripts); 575 } 576 if (scripts.length) { 577 $.each(scripts, function() { 578 $.globalEval( this.text || this.textContent || this.innerHTML || "" ); 579 }); 580 } 581 582 // This may be a complex form. 583 if ($.fn.ploneTabInit) { 584 ele.ploneTabInit(); 585 } 586 587 // remove element on close so that it doesn't congest the DOM 588 api.onClose = function () { 589 content.remove(); 590 }; 591 $(document).trigger('loadInsideOverlay', [this, responseText, errorText, api]); 592 }; 593 594 // and load the div 595 el.load(src, null, function (responseText, errorText) { 596 // post-process the overlay contents 597 el[0].handle_load_inside_overlay.apply(this, [responseText, errorText]); 598 599 // Now, it's all ready to display; hide the 600 // spinner and call JQT overlay load. 601 pb.spinner.hide(); 602 api.load(); 603 604 return true; 605 }); 606 607 // don't do the default action 608 return false;
609 }; 610 611 612 /****** 613 pb.iframe 614 onBeforeLoad handler for iframe overlays. 615 616 Note that the spinner is handled a little differently 617 so that we can keep it displayed while the iframe's 618 content is loading. 619 ******/ 620 pb.iframe = function () { 621 var content, pbo; 622 623 pb.spinner.show(); 624 625 content = this.getOverlay(); 626 pbo = this.getTrigger().data('pbo'); 627 628 if (content.find('iframe').length === 0 && pbo.src) { 629 content.append( 630 '<iframe src="' + pbo.src + '" width="' + 631 content.width() + '" height="' + content.height() + 632 '" onload="pb.spinner.hide()"/>'); 633 } else { 634 pb.spinner.hide(); 635 } 636 return true; 637 }; 638 639}); 640 641 642/* - ++resource++plone.app.jquerytools.form.js - */ 643/*! 644 * jQuery Form Plugin 645 * version: 3.51.0-2014.06.20 646 * Requires jQuery v1.5 or later 647 * Copyright (c) 2014 M. Alsup 648 * Examples and documentation at: http://malsup.com/jquery/form/ 649 * Project repository: https://github.com/malsup/form 650 * Dual licensed under the MIT and GPL licenses. 651 * https://github.com/malsup/form#copyright-and-license 652 */ 653/*global ActiveXObject */ 654 655 656 657/* 658 Usage Note: 659 ----------- 660 Do not use both ajaxSubmit and ajaxForm on the same form. These 661 functions are mutually exclusive. Use ajaxSubmit if you want 662 to bind your own submit handler to the form. For example, 663 664 $(document).ready(function() { 665 $('#myForm').on('submit', function(e) { 666 e.preventDefault(); // <-- important 667 $(this).ajaxSubmit({ 668 target: '#output' 669 }); 670 }); 671 }); 672 673 Use ajaxForm when you want the plugin to manage all the event binding 674 for you. For example, 675 676 $(document).ready(function() { 677 $('#myForm').ajaxForm({ 678 target: '#output' 679 }); 680 }); 681 682 You can also use ajaxForm with delegation (requires jQuery v1.7+), so the 683 form does not have to exist when you invoke ajaxForm: 684 685 $('#myForm').ajaxForm({ 686 delegation: true, 687 target: '#output' 688 }); 689 690 When using ajaxForm, the ajaxSubmit function will be invoked for you 691 at the appropriate time. 692*/ 693 694jQuery( document ).ready(function( $ ) { 695 696/** 697 * Feature detection 698 */ 699var feature = {}; 700feature.fileapi = $("<input type='file'/>").get(0).files !== undefined; 701feature.formdata = window.FormData !== undefined; 702 703var hasProp = !!$.fn.prop; 704 705// attr2 uses prop when it can but checks the return type for 706// an expected string. this accounts for the case where a form 707// contains inputs with names like "action" or "method"; in those 708// cases "prop" returns the element 709$.fn.attr2 = function() { 710 if ( ! hasProp ) { 711 return this.attr.apply(this, arguments); 712 } 713 var val = this.prop.apply(this, arguments); 714 if ( ( val && val.jquery ) || typeof val === 'string' ) { 715 return val; 716 } 717 return this.attr.apply(this, arguments); 718}; 719 720/** 721 * ajaxSubmit() provides a mechanism for immediately submitting 722 * an HTML form using AJAX. 723 */ 724$.fn.ajaxSubmit = function(options) { 725 /*jshint scripturl:true */ 726 727 // fast fail if nothing selected (http://dev.jquery.com/ticket/2752) 728 if (!this.length) { 729 log('ajaxSubmit: skipping submit process - no element selected'); 730 return this; 731 } 732 733 var method, action, url, $form = this; 734 735 if (typeof options == 'function') { 736 options = { success: options }; 737 } 738 else if ( options === undefined ) { 739 options = {}; 740 } 741 742 method = options.type || this.attr2('method'); 743 action = options.url || this.attr2('action'); 744 745 url = (typeof action === 'string') ? $.trim(action) : ''; 746 url = url || window.location.href || ''; 747 if (url) { 748 // clean url (don't include hash vaue) 749 url = (url.match(/^([^#]+)/)||[])[1]; 750 } 751 752 options = $.extend(true, { 753 url: url, 754 success: $.ajaxSettings.success, 755 type: method || $.ajaxSettings.type, 756 iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank' 757 }, options); 758 759 // hook for manipulating the form data before it is extracted; 760 // convenient for use with rich editors like tinyMCE or FCKEditor 761 var veto = {}; 762 this.trigger('form-pre-serialize', [this, options, veto]); 763 if (veto.veto) { 764 log('ajaxSubmit: submit vetoed via form-pre-serialize trigger'); 765 return this; 766 } 767 768 // provide opportunity to alter form data before it is serialized 769 if (options.beforeSerialize && options.beforeSerialize(this, options) === false) { 770 log('ajaxSubmit: submit aborted via beforeSerialize callback'); 771 return this; 772 } 773 774 var traditional = options.traditional; 775 if ( traditional === undefined ) { 776 traditional = $.ajaxSettings.traditional; 777 } 778 779 var elements = []; 780 var qx, a = this.formToArray(options.semantic, elements); 781 if (options.data) { 782 options.extraData = options.data; 783 qx = $.param(options.data, traditional); 784 } 785 786 // give pre-submit callback an opportunity to abort the submit 787 if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) { 788 log('ajaxSubmit: submit aborted via beforeSubmit callback'); 789 return this; 790 } 791 792 // fire vetoable 'validate' event 793 this.trigger('form-submit-validate', [a, this, options, veto]); 794 if (veto.veto) { 795 log('ajaxSubmit: submit vetoed via form-submit-validate trigger'); 796 return this; 797 } 798 799 var q = $.param(a, traditional); 800 if (qx) { 801 q = ( q ? (q + '&' + qx) : qx ); 802 } 803 if (options.type.toUpperCase() == 'GET') { 804 options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q; 805 options.data = null; // data is null for 'get' 806 } 807 else { 808 options.data = q; // data is the query string for 'post' 809 } 810 811 var callbacks = []; 812 if (options.resetForm) { 813 callbacks.push(function() { $form.resetForm(); }); 814 } 815 if (options.clearForm) { 816 callbacks.push(function() { $form.clearForm(options.includeHidden); }); 817 } 818 819 // perform a load on the target only if dataType is not provided 820 if (!options.dataType && options.target) { 821 var oldSuccess = options.success || function(){}; 822 callbacks.push(function(data) { 823 var fn = options.replaceTarget ? 'replaceWith' : 'html'; 824 $(options.target)[fn](data).each(oldSuccess, arguments); 825 }); 826 } 827 else if (options.success) { 828 callbacks.push(options.success); 829 } 830 831 options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg 832 var context = options.context || this ; // jQuery 1.4+ supports scope context 833 for (var i=0, max=callbacks.length; i < max; i++) { 834 callbacks[i].apply(context, [data, status, xhr || $form, $form]); 835 } 836 }; 837 838 if (options.error) { 839 var oldError = options.error; 840 options.error = function(xhr, status, error) { 841 var context = options.context || this; 842 oldError.apply(context, [xhr, status, error, $form]); 843 }; 844 } 845 846 if (options.complete) { 847 var oldComplete = options.complete; 848 options.complete = function(xhr, status) { 849 var context = options.context || this; 850 oldComplete.apply(context, [xhr, status, $form]); 851 }; 852 } 853 854 // are there files to upload? 855 856 // [value] (issue #113), also see comment: 857 // https://github.com/malsup/form/commit/588306aedba1de01388032d5f42a60159eea9228#commitcomment-2180219 858 var fileInputs = $('input[type=file]:enabled', this).filter(function() { return $(this).val() !== ''; }); 859 860 var hasFileInputs = fileInputs.length > 0; 861 var mp = 'multipart/form-data'; 862 var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp); 863 864 var fileAPI = feature.fileapi && feature.formdata; 865 log("fileAPI :" + fileAPI); 866 var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI; 867 868 var jqxhr; 869 870 // options.iframe allows user to force iframe mode 871 // 06-NOV-09: now defaulting to iframe mode if file input is detected 872 if (options.iframe !== false && (options.iframe || shouldUseFrame)) { 873 // hack to fix Safari hang (thanks to Tim Molendijk for this) 874 // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d 875 if (options.closeKeepAlive) { 876 $.get(options.closeKeepAlive, function() { 877 jqxhr = fileUploadIframe(a); 878 }); 879 } 880 else { 881 jqxhr = fileUploadIframe(a); 882 } 883 } 884 else if ((hasFileInputs || multipart) && fileAPI) { 885 jqxhr = fileUploadXhr(a); 886 } 887 else { 888 jqxhr = $.ajax(options); 889 } 890 891 $form.removeData('jqxhr').data('jqxhr', jqxhr); 892 893 // clear element array 894 for (var k=0; k < elements.length; k++) { 895 elements[k] = null; 896 } 897 898 // fire 'notify' event 899 this.trigger('form-submit-notify', [this, options]); 900 return this; 901 902 // utility fn for deep serialization 903 function deepSerialize(extraData){ 904 var serialized = $.param(extraData, options.traditional).split('&'); 905 var len = serialized.length; 906 var result = []; 907 var i, part; 908 for (i=0; i < len; i++) { 909 // #252; undo param space replacement 910 serialized[i] = serialized[i].replace(/\+/g,' '); 911 part = serialized[i].split('='); 912 // #278; use array instead of object storage, favoring array serializations 913 result.push([decodeURIComponent(part[0]), decodeURIComponent(part[1])]); 914 } 915 return result; 916 } 917 918 // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz) 919 function fileUploadXhr(a) { 920 var formdata = new FormData(); 921 922 for (var i=0; i < a.length; i++) { 923 formdata.append(a[i].name, a[i].value); 924 } 925 926 if (options.extraData) { 927 var serializedData = deepSerialize(options.extraData); 928 for (i=0; i < serializedData.length; i++) { 929 if (serializedData[i]) { 930 formdata.append(serializedData[i][0], serializedData[i][1]); 931 } 932 } 933 } 934 935 options.data = null; 936 937 var s = $.extend(true, {}, $.ajaxSettings, options, { 938 contentType: false, 939 processData: false, 940 cache: false, 941 type: method || 'POST' 942 }); 943 944 if (options.uploadProgress) { 945 // workaround because jqXHR does not expose upload property 946 s.xhr = function() { 947 var xhr = $.ajaxSettings.xhr(); 948 if (xhr.upload) { 949 xhr.upload.addEventListener('progress', function(event) { 950 var percent = 0; 951 var position = event.loaded || event.position; /*event.position is deprecated*/ 952 var total = event.total; 953 if (event.lengthComputable) {
954 percent = Math.ceil(position / total * 100); 955 } 956 options.uploadProgress(event, position, total, percent); 957 }, false); 958 } 959 return xhr; 960 }; 961 } 962 963 s.data = null; 964 var beforeSend = s.beforeSend; 965 s.beforeSend = function(xhr, o) { 966 //Send FormData() provided by user 967 if (options.formData) { 968 o.data = options.formData; 969 } 970 else { 971 o.data = formdata; 972 } 973 if(beforeSend) { 974 beforeSend.call(this, xhr, o); 975 } 976 }; 977 return $.ajax(s); 978 } 979 980 // private function for handling file uploads (hat tip to YAHOO!) 981 function fileUploadIframe(a) { 982 var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle; 983 var deferred = $.Deferred(); 984 985 // #341 986 deferred.abort = function(status) { 987 xhr.abort(status); 988 }; 989 990 if (a) { 991 // ensure that every serialized input is still enabled 992 for (i=0; i < elements.length; i++) { 993 el = $(elements[i]); 994 if ( hasProp ) { 995 el.prop('disabled', false); 996 } 997 else { 998 el.removeAttr('disabled'); 999 } 1000 } 1001 } 1002 1003 s = $.extend(true, {}, $.ajaxSettings, options); 1004 s.context = s.context || s; 1005 id = 'jqFormIO' + (new Date().getTime()); 1006 if (s.iframeTarget) { 1007 $io = $(s.iframeTarget); 1008 n = $io.attr2('name'); 1009 if (!n) { 1010 $io.attr2('name', id); 1011 } 1012 else { 1013 id = n; 1014 } 1015 } 1016 else { 1017 $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />'); 1018 $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' }); 1019 } 1020 io = $io[0]; 1021 1022 1023 xhr = { // mock object 1024 aborted: 0, 1025 responseText: null, 1026 responseXML: null, 1027 status: 0, 1028 statusText: 'n/a', 1029 getAllResponseHeaders: function() {}, 1030 getResponseHeader: function() {}, 1031 setRequestHeader: function() {}, 1032 abort: function(status) { 1033 var e = (status === 'timeout' ? 'timeout' : 'aborted'); 1034 log('aborting upload... ' + e); 1035 this.aborted = 1; 1036 1037 try { // #214, #257 1038 if (io.contentWindow.document.execCommand) { 1039 io.contentWindow.document.execCommand('Stop'); 1040 } 1041 } 1042 catch(ignore) {} 1043 1044 $io.attr('src', s.iframeSrc); // abort op in progress 1045 xhr.error = e; 1046 if (s.error) { 1047 s.error.call(s.context, xhr, e, status); 1048 } 1049 if (g) { 1050 $.event.trigger("ajaxError", [xhr, s, e]); 1051 } 1052 if (s.complete) { 1053 s.complete.call(s.context, xhr, e); 1054 } 1055 } 1056 }; 1057 1058 g = s.global; 1059 // trigger ajax global events so that activity/block indicators work like normal 1060 if (g && 0 === $.active++) { 1061 $.event.trigger("ajaxStart"); 1062 } 1063 if (g) { 1064 $.event.trigger("ajaxSend", [xhr, s]); 1065 } 1066 1067 if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) { 1068 if (s.global) { 1069 $.active--; 1070 } 1071 deferred.reject(); 1072 return deferred; 1073 } 1074 if (xhr.aborted) { 1075 deferred.reject(); 1076 return deferred; 1077 } 1078 1079 // add submitting element to data if we know it 1080 sub = form.clk; 1081 if (sub) { 1082 n = sub.name; 1083 if (n && !sub.disabled) { 1084 s.extraData = s.extraData || {}; 1085 s.extraData[n] = sub.value; 1086 if (sub.type == "image") { 1087 s.extraData[n+'.x'] = form.clk_x; 1088 s.extraData[n+'.y'] = form.clk_y; 1089 } 1090 } 1091 } 1092 1093 var CLIENT_TIMEOUT_ABORT = 1; 1094 var SERVER_ABORT = 2; 1095 1096 function getDoc(frame) { 1097 /* it looks like contentWindow or contentDocument do not 1098 * carry the protocol property in ie8, when running under ssl 1099 * frame.document is the only valid response document, since 1100 * the protocol is know but not on the other two objects. strange? 1101 * "Same origin policy" http://en.wikipedia.org/wiki/Same_origin_policy 1102 */ 1103 1104 var doc = null; 1105 1106 // IE8 cascading access check 1107 try { 1108 if (frame.contentWindow) { 1109 doc = frame.contentWindow.document; 1110 } 1111 } catch(err) { 1112 // IE8 access denied under ssl & missing protocol 1113 log('cannot get iframe.contentWindow document: ' + err); 1114 } 1115 1116 if (doc) { // successful getting content 1117 return doc; 1118 } 1119 1120 try { // simply checking may throw in ie8 under ssl or mismatched protocol 1121 doc = frame.contentDocument ? frame.contentDocument : frame.document; 1122 } catch(err) { 1123 // last attempt 1124 log('cannot get iframe.contentDocument: ' + err); 1125 doc = frame.document; 1126 } 1127 return doc; 1128 } 1129 1130 // Rails CSRF hack (thanks to Yvan Barthelemy) 1131 var csrf_token = $('meta[name=csrf-token]').attr('content'); 1132 var csrf_param = $('meta[name=csrf-param]').attr('content'); 1133 if (csrf_param && csrf_token) { 1134 s.extraData = s.extraData || {}; 1135 s.extraData[csrf_param] = csrf_token; 1136 } 1137 1138 // take a breath so that pending repaints get some cpu time before the upload starts 1139 function doSubmit() { 1140 // make sure form attrs are set 1141 var t = $form.attr2('target'), 1142 a = $form.attr2('action'),
1143 mp = 'multipart/form-data', 1144 et = $form.attr('enctype') || $form.attr('encoding') || mp; 1145 1146 // update form attrs in IE friendly way 1147 form.setAttribute('target',id); 1148 if (!method || /post/i.test(method) ) { 1149 form.setAttribute('method', 'POST'); 1150 } 1151 if (a != s.url) { 1152 form.setAttribute('action', s.url); 1153 } 1154 1155 // ie borks in some cases when setting encoding 1156 if (! s.skipEncodingOverride && (!method || /post/i.test(method))) { 1157 $form.attr({ 1158 encoding: 'multipart/form-data', 1159 enctype: 'multipart/form-data' 1160 }); 1161 } 1162 1163 // support timout 1164 if (s.timeout) { 1165 timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout); 1166 } 1167 1168 // look for server aborts 1169 function checkState() { 1170 try { 1171 var state = getDoc(io).readyState; 1172 log('state = ' + state); 1173 if (state && state.toLowerCase() == 'uninitialized') { 1174 setTimeout(checkState,50); 1175 } 1176 } 1177 catch(e) { 1178 log('Server abort: ' , e, ' (', e.name, ')'); 1179 cb(SERVER_ABORT); 1180 if (timeoutHandle) { 1181 clearTimeout(timeoutHandle); 1182 } 1183 timeoutHandle = undefined; 1184 } 1185 } 1186 1187 // add "extra" data to form if provided in options 1188 var extraInputs = []; 1189 try { 1190 if (s.extraData) { 1191 for (var n in s.extraData) { 1192 if (s.extraData.hasOwnProperty(n)) { 1193 // if using the $.param format that allows for multiple values with the same name 1194 if($.isPlainObject(s.extraData[n]) && s.extraData[n].hasOwnProperty('name') && s.extraData[n].hasOwnProperty('value')) { 1195 extraInputs.push( 1196 $('<input type="hidden" name="'+s.extraData[n].name+'">').val(s.extraData[n].value) 1197 .appendTo(form)[0]); 1198 } else { 1199 extraInputs.push( 1200 $('<input type="hidden" name="'+n+'">').val(s.extraData[n]) 1201 .appendTo(form)[0]); 1202 } 1203 } 1204 } 1205 } 1206 1207 if (!s.iframeTarget) { 1208 // add iframe to doc and submit the form 1209 $io.appendTo('body'); 1210 } 1211 if (io.attachEvent) { 1212 io.attachEvent('onload', cb); 1213 } 1214 else { 1215 io.addEventListener('load', cb, false); 1216 } 1217 setTimeout(checkState,15); 1218 1219 try { 1220 form.submit(); 1221 } catch(err) { 1222 // just in case form has element with name/id of 'submit' 1223 var submitFn = document.createElement('form').submit; 1224 submitFn.apply(form); 1225 } 1226 } 1227 finally { 1228 // reset attrs and remove "extra" input elements 1229 form.setAttribute('action',a); 1230 form.setAttribute('enctype', et); // #380 1231 if(t) { 1232 form.setAttribute('target', t); 1233 } else { 1234 $form.removeAttr('target'); 1235 } 1236 $(extraInputs).remove(); 1237 } 1238 } 1239 1240 if (s.forceSync) { 1241 doSubmit(); 1242 } 1243 else { 1244 setTimeout(doSubmit, 10); // this lets dom updates render 1245 } 1246 1247 var data, doc, domCheckCount = 50, callbackProcessed; 1248 1249 function cb(e) { 1250 if (xhr.aborted || callbackProcessed) { 1251 return; 1252 } 1253 1254 doc = getDoc(io); 1255 if(!doc) { 1256 log('cannot access response document'); 1257 e = SERVER_ABORT; 1258 } 1259 if (e === CLIENT_TIMEOUT_ABORT && xhr) { 1260 xhr.abort('timeout'); 1261 deferred.reject(xhr, 'timeout'); 1262 return; 1263 } 1264 else if (e == SERVER_ABORT && xhr) { 1265 xhr.abort('server abort'); 1266 deferred.reject(xhr, 'error', 'server abort'); 1267 return; 1268 } 1269 1270 if (!doc || doc.location.href == s.iframeSrc) { 1271 // response not received yet 1272 if (!timedOut) { 1273 return; 1274 } 1275 } 1276 if (io.detachEvent) { 1277 io.detachEvent('onload', cb); 1278 } 1279 else { 1280 io.removeEventListener('load', cb, false); 1281 } 1282 1283 var status = 'success', errMsg; 1284 try { 1285 if (timedOut) { 1286 throw 'timeout'; 1287 } 1288 1289 var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc); 1290 log('isXml='+isXml); 1291 if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) { 1292 if (--domCheckCount) { 1293 // in some browsers (Opera) the iframe DOM is not always traversable when 1294 // the onload callback fires, so we loop a bit to accommodate 1295 log('requeing onLoad callback, DOM not available'); 1296 setTimeout(cb, 250); 1297 return; 1298 } 1299 // let this fall through because server response could be an empty document 1300 //log('Could not access iframe DOM after mutiple tries.'); 1301 //throw 'DOMException: not available'; 1302 } 1303 1304 //log('response detected'); 1305 var docRoot = doc.body ? doc.body : doc.documentElement; 1306 xhr.responseText = docRoot ? docRoot.innerHTML : null; 1307 xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc; 1308 if (isXml) { 1309 s.dataType = 'xml'; 1310 } 1311 xhr.getResponseHeader = function(header){ 1312 var headers = {'content-type': s.dataType}; 1313 return headers[header.toLowerCase()]; 1314 }; 1315 // support for XHR 'status' & 'statusText' emulation : 1316 if (docRoot) { 1317 xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status; 1318 xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText; 1319 } 1320 1321 var dt = (s.dataType || '').toLowerCase(); 1322 var scr = /(json|script|text)/.test(dt); 1323 if (scr || s.textarea) { 1324 // see if user embedded response in textarea 1325 var ta = doc.getElementsByTagName('textarea')[0]; 1326 if (ta) { 1327 xhr.responseText = ta.value; 1328 // support for XHR 'status' & 'statusText' emulation : 1329 xhr.status = Number( ta.getAttribute('status') ) || xhr.status; 1330 xhr.statusText = ta.getAttribute('statusText') || xhr.statusText; 1331 } 1332 else if (scr) { 1333 // account for browsers injecting pre around json response 1334 var pre = doc.getElementsByTagName('pre')[0]; 1335 var b = doc.getElementsByTagName('body')[0]; 1336 if (pre) { 1337 xhr.responseText = pre.textContent ? pre.textContent : pre.innerText; 1338 } 1339 else if (b) { 1340 xhr.responseText = b.textContent ? b.textContent : b.innerText; 1341 } 1342 } 1343 } 1344 else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) { 1345 xhr.responseXML = toXml(xhr.responseText); 1346 } 1347 1348 try { 1349 data = httpData(xhr, dt, s); 1350 } 1351 catch (err) { 1352 status = 'parsererror'; 1353 xhr.error = errMsg = (err || status); 1354 } 1355 } 1356 catch (err) { 1357 log('error caught: ',err); 1358 status = 'error'; 1359 xhr.error = errMsg = (err || status); 1360 } 1361 1362 if (xhr.aborted) { 1363 log('upload aborted'); 1364 status = null; 1365 } 1366 1367 if (xhr.status) { // we've set xhr.status 1368 status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error'; 1369 } 1370 1371 // ordering of these callbacks/triggers is odd, but that's how $.ajax does it 1372 if (status === 'success') { 1373 if (s.success) { 1374 s.success.call(s.context, data, 'success', xhr); 1375 } 1376 deferred.resolve(xhr.responseText, 'success', xhr); 1377 if (g) { 1378 $.event.trigger("ajaxSuccess", [xhr, s]); 1379 } 1380 } 1381 else if (status) { 1382 if (errMsg === undefined) { 1383 errMsg = xhr.statusText; 1384 } 1385 if (s.error) { 1386 s.error.call(s.context, xhr, status, errMsg); 1387 } 1388 deferred.reject(xhr, 'error', errMsg); 1389 if (g) { 1390 $.event.trigger("ajaxError", [xhr, s, errMsg]); 1391 } 1392 } 1393 1394 if (g) {
1395 $.event.trigger("ajaxComplete", [xhr, s]); 1396 } 1397 1398 if (g && ! --$.active) { 1399 $.event.trigger("ajaxStop"); 1400 } 1401 1402 if (s.complete) { 1403 s.complete.call(s.context, xhr, status); 1404 } 1405 1406 callbackProcessed = true; 1407 if (s.timeout) { 1408 clearTimeout(timeoutHandle); 1409 } 1410 1411 // clean up 1412 setTimeout(function() { 1413 if (!s.iframeTarget) { 1414 $io.remove(); 1415 } 1416 else { //adding else to clean up existing iframe response. 1417 $io.attr('src', s.iframeSrc); 1418 } 1419 xhr.responseXML = null; 1420 }, 100); 1421 } 1422 1423 var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+) 1424 if (window.ActiveXObject) { 1425 doc = new ActiveXObject('Microsoft.XMLDOM'); 1426 doc.async = 'false'; 1427 doc.loadXML(s); 1428 } 1429 else { 1430 doc = (new DOMParser()).parseFromString(s, 'text/xml'); 1431 } 1432 return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null; 1433 }; 1434 var parseJSON = $.parseJSON || function(s) { 1435 /*jslint evil:true */ 1436 return window['eval']('(' + s + ')'); 1437 }; 1438 1439 var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4 1440 1441 var ct = xhr.getResponseHeader('content-type') || '', 1442 xml = type === 'xml' || !type && ct.indexOf('xml') >= 0, 1443 data = xml ? xhr.responseXML : xhr.responseText; 1444 1445 if (xml && data.documentElement.nodeName === 'parsererror') { 1446 if ($.error) { 1447 $.error('parsererror'); 1448 } 1449 } 1450 if (s && s.dataFilter) { 1451 data = s.dataFilter(data, type); 1452 } 1453 if (typeof data === 'string') { 1454 if (type === 'json' || !type && ct.indexOf('json') >= 0) { 1455 data = parseJSON(data); 1456 } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) { 1457 $.globalEval(data); 1458 } 1459 } 1460 return data; 1461 }; 1462 1463 return deferred; 1464 } 1465}; 1466 1467/** 1468 * ajaxForm() provides a mechanism for fully automating form submission. 1469 * 1470 * The advantages of using this method instead of ajaxSubmit() are: 1471 * 1472 * 1: This method will include coordinates for <input type="image" /> elements (if the element 1473 * is used to submit the form). 1474 * 2. This method will include the submit element's name/value data (for the element that was 1475 * used to submit the form). 1476 * 3. This method binds the submit() method to the form for you. 1477 * 1478 * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely 1479 * passes the options argument along after properly binding events for submit elements and 1480 * the form itself. 1481 */ 1482$.fn.ajaxForm = function(options) { 1483 options = options || {}; 1484 options.delegation = options.delegation && $.isFunction($.fn.on); 1485 1486 // in jQuery 1.3+ we can fix mistakes with the ready state 1487 if (!options.delegation && this.length === 0) { 1488 var o = { s: this.selector, c: this.context }; 1489 if (!$.isReady && o.s) { 1490 log('DOM not ready, queuing ajaxForm'); 1491 $(function() { 1492 $(o.s,o.c).ajaxForm(options); 1493 }); 1494 return this; 1495 } 1496 // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready() 1497 log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)')); 1498 return this; 1499 } 1500 1501 if ( options.delegation ) { 1502 $(document) 1503 .off('submit.form-plugin', this.selector, doAjaxSubmit) 1504 .off('click.form-plugin', this.selector, captureSubmittingElement) 1505 .on('submit.form-plugin', this.selector, options, doAjaxSubmit) 1506 .on('click.form-plugin', this.selector, options, captureSubmittingElement); 1507 return this; 1508 } 1509 1510 return this.ajaxFormUnbind() 1511 .bind('submit.form-plugin', options, doAjaxSubmit) 1512 .bind('click.form-plugin', options, captureSubmittingElement); 1513}; 1514 1515// private event handlers 1516function doAjaxSubmit(e) { 1517 /*jshint validthis:true */ 1518 var options = e.data; 1519 if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed 1520 e.preventDefault(); 1521 $(e.target).ajaxSubmit(options); // #365 1522 } 1523} 1524 1525function captureSubmittingElement(e) { 1526 /*jshint validthis:true */ 1527 var target = e.target; 1528 var $el = $(target); 1529 if (!($el.is("[type=submit],[type=image]"))) { 1530 // is this a child element of the submit el? (ex: a span within a button) 1531 var t = $el.closest('[type=submit]'); 1532 if (t.length === 0) { 1533 return; 1534 } 1535 target = t[0]; 1536 } 1537 var form = this; 1538 form.clk = target; 1539 if (target.type == 'image') { 1540 if (e.offsetX !== undefined) { 1541 form.clk_x = e.offsetX; 1542 form.clk_y = e.offsetY; 1543 } else if (typeof $.fn.offset == 'function') { 1544 var offset = $el.offset(); 1545 form.clk_x = e.pageX - offset.left; 1546 form.clk_y = e.pageY - offset.top; 1547 } else { 1548 form.clk_x = e.pageX - target.offsetLeft; 1549 form.clk_y = e.pageY - target.offsetTop; 1550 } 1551 } 1552 // clear form vars 1553 setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100); 1554} 1555 1556 1557// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm 1558$.fn.ajaxFormUnbind = function() { 1559 return this.unbind('submit.form-plugin click.form-plugin'); 1560}; 1561 1562/** 1563 * formToArray() gathers form element data into an array of objects that can 1564 * be passed to any of the following ajax functions: $.get, $.post, or load. 1565 * Each object in the array has both a 'name' and 'value' property. An example of 1566 * an array for a simple login form might be: 1567 * 1568 * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ] 1569 * 1570 * It is this array that is passed to pre-submit callback functions provided to the 1571 * ajaxSubmit() and ajaxForm() methods. 1572 */ 1573$.fn.formToArray = function(semantic, elements) { 1574 var a = []; 1575 if (this.length === 0) { 1576 return a; 1577 } 1578 1579 var form = this[0]; 1580 var formId = this.attr('id'); 1581 var els = semantic ? form.getElementsByTagName('*') : form.elements; 1582 var els2; 1583 1584 if (els && !/MSIE [678]/.test(navigator.userAgent)) { // #390 1585 els = $(els).get(); // convert to standard array 1586 } 1587 1588 // #386; account for inputs outside the form which use the 'form' attribute 1589 if ( formId ) { 1590 els2 = $(':input[form="' + formId + '"]').get(); // hat tip @thet 1591 if ( els2.length ) { 1592 els = (els || []).concat(els2); 1593 } 1594 } 1595 1596 if (!els || !els.length) { 1597 return a; 1598 } 1599 1600 var i,j,n,v,el,max,jmax; 1601 for(i=0, max=els.length; i < max; i++) { 1602 el = els[i]; 1603 n = el.name; 1604 if (!n || el.disabled) { 1605 continue; 1606 } 1607 1608 if (semantic && form.clk && el.type == "image") { 1609 // handle image inputs on the fly when semantic == true 1610 if(form.clk == el) { 1611 a.push({name: n, value: $(el).val(), type: el.type }); 1612 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 1613 } 1614 continue; 1615 } 1616 1617 v = $.fieldValue(el, true); 1618 if (v && v.constructor == Array) { 1619 if (elements) { 1620 elements.push(el); 1621 } 1622 for(j=0, jmax=v.length; j < jmax; j++) { 1623 a.push({name: n, value: v[j]}); 1624 } 1625 } 1626 else if (feature.fileapi && el.type == 'file') { 1627 if (elements) { 1628 elements.push(el); 1629 } 1630 var files = el.files; 1631 if (files.length) { 1632 for (j=0; j < files.length; j++) { 1633 a.push({name: n, value: files[j], type: el.type}); 1634 } 1635 } 1636 else { 1637 // #180 1638 a.push({ name: n, value: '', type: el.type }); 1639 } 1640 } 1641 else if (v !== null && typeof v != 'undefined') { 1642 if (elements) { 1643 elements.push(el); 1644 } 1645 a.push({name: n, value: v, type: el.type, required: el.required}); 1646 } 1647 } 1648 1649 if (!semantic && form.clk) { 1650 // input type=='image' are not found in elements array! handle it here 1651 var $input = $(form.clk), input = $input[0]; 1652 n = input.name; 1653 if (n && !input.disabled && input.type == 'image') { 1654 a.push({name: n, value: $input.val()}); 1655 a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y}); 1656 } 1657 } 1658 return a; 1659}; 1660 1661/** 1662 * Serializes form data into a 'submittable' string. This method will return a string 1663 * in the format: name1=value1&name2=value2 1664 */
1665$.fn.formSerialize = function(semantic) { 1666 //hand off to jQuery.param for proper encoding 1667 return $.param(this.formToArray(semantic)); 1668}; 1669 1670/** 1671 * Serializes all field elements in the jQuery object into a query string. 1672 * This method will return a string in the format: name1=value1&name2=value2 1673 */ 1674$.fn.fieldSerialize = function(successful) { 1675 var a = []; 1676 this.each(function() { 1677 var n = this.name; 1678 if (!n) { 1679 return; 1680 } 1681 var v = $.fieldValue(this, successful); 1682 if (v && v.constructor == Array) { 1683 for (var i=0,max=v.length; i < max; i++) { 1684 a.push({name: n, value: v[i]}); 1685 } 1686 } 1687 else if (v !== null && typeof v != 'undefined') { 1688 a.push({name: this.name, value: v}); 1689 } 1690 }); 1691 //hand off to jQuery.param for proper encoding 1692 return $.param(a); 1693}; 1694 1695/** 1696 * Returns the value(s) of the element in the matched set. For example, consider the following form: 1697 * 1698 * <form><fieldset> 1699 * <input name="A" type="text" /> 1700 * <input name="A" type="text" /> 1701 * <input name="B" type="checkbox" value="B1" /> 1702 * <input name="B" type="checkbox" value="B2"/> 1703 * <input name="C" type="radio" value="C1" /> 1704 * <input name="C" type="radio" value="C2" /> 1705 * </fieldset></form> 1706 * 1707 * var v = $('input[type=text]').fieldValue(); 1708 * // if no values are entered into the text inputs 1709 * v == ['',''] 1710 * // if values entered into the text inputs are 'foo' and 'bar' 1711 * v == ['foo','bar'] 1712 * 1713 * var v = $('input[type=checkbox]').fieldValue(); 1714 * // if neither checkbox is checked 1715 * v === undefined 1716 * // if both checkboxes are checked 1717 * v == ['B1', 'B2'] 1718 * 1719 * var v = $('input[type=radio]').fieldValue(); 1720 * // if neither radio is checked 1721 * v === undefined 1722 * // if first radio is checked 1723 * v == ['C1'] 1724 * 1725 * The successful argument controls whether or not the field element must be 'successful' 1726 * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls). 1727 * The default value of the successful argument is true. If this value is false the value(s) 1728 * for each element is returned. 1729 * 1730 * Note: This method *always* returns an array. If no valid value can be determined the 1731 * array will be empty, otherwise it will contain one or more values. 1732 */ 1733$.fn.fieldValue = function(successful) { 1734 for (var val=[], i=0, max=this.length; i < max; i++) { 1735 var el = this[i]; 1736 var v = $.fieldValue(el, successful); 1737 if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) { 1738 continue; 1739 } 1740 if (v.constructor == Array) { 1741 $.merge(val, v); 1742 } 1743 else { 1744 val.push(v); 1745 } 1746 } 1747 return val; 1748}; 1749 1750/** 1751 * Returns the value of the field element. 1752 */ 1753$.fieldValue = function(el, successful) { 1754 var n = el.name, t = el.type, tag = el.tagName.toLowerCase(); 1755 if (successful === undefined) { 1756 successful = true; 1757 } 1758 1759 if (successful && (!n || el.disabled || t == 'reset' || t == 'button' || 1760 (t == 'checkbox' || t == 'radio') && !el.checked || 1761 (t == 'submit' || t == 'image') && el.form && el.form.clk != el || 1762 tag == 'select' && el.selectedIndex == -1)) { 1763 return null; 1764 } 1765 1766 if (tag == 'select') { 1767 var index = el.selectedIndex; 1768 if (index < 0) { 1769 return null; 1770 } 1771 var a = [], ops = el.options; 1772 var one = (t == 'select-one'); 1773 var max = (one ? index+1 : ops.length); 1774 for(var i=(one ? index : 0); i < max; i++) { 1775 var op = ops[i]; 1776 if (op.selected) { 1777 var v = op.value; 1778 if (!v) { // extra pain for IE... 1779 v = (op.attributes && op.attributes.value && !(op.attributes.value.specified)) ? op.text : op.value; 1780 } 1781 if (one) { 1782 return v; 1783 } 1784 a.push(v); 1785 } 1786 } 1787 return a; 1788 } 1789 return $(el).val(); 1790}; 1791 1792/** 1793 * Clears the form data. Takes the following actions on the form's input fields: 1794 * - input text fields will have their 'value' property set to the empty string 1795 * - select elements will have their 'selectedIndex' property set to -1 1796 * - checkbox and radio inputs will have their 'checked' property set to false 1797 * - inputs of type submit, button, reset, and hidden will *not* be effected 1798 * - button elements will *not* be effected 1799 */ 1800$.fn.clearForm = function(includeHidden) { 1801 return this.each(function() { 1802 $('input,select,textarea', this).clearFields(includeHidden); 1803 }); 1804}; 1805 1806/** 1807 * Clears the selected form elements. 1808 */ 1809$.fn.clearFields = $.fn.clearInputs = function(includeHidden) { 1810 var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list 1811 return this.each(function() { 1812 var t = this.type, tag = this.tagName.toLowerCase(); 1813 if (re.test(t) || tag == 'textarea') { 1814 this.value = ''; 1815 } 1816 else if (t == 'checkbox' || t == 'radio') { 1817 this.checked = false;
1818 } 1819 else if (tag == 'select') { 1820 this.selectedIndex = -1; 1821 } 1822 else if (t == "file") { 1823 if (/MSIE/.test(navigator.userAgent)) { 1824 $(this).replaceWith($(this).clone(true)); 1825 } else { 1826 $(this).val(''); 1827 } 1828 } 1829 else if (includeHidden) { 1830 // includeHidden can be the value true, or it can be a selector string 1831 // indicating a special test; for example: 1832 // $('#myForm').clearForm('.special:hidden') 1833 // the above would clean hidden inputs that have the class of 'special' 1834 if ( (includeHidden === true && /hidden/.test(t)) || 1835 (typeof includeHidden == 'string' && $(this).is(includeHidden)) ) { 1836 this.value = ''; 1837 } 1838 } 1839 }); 1840}; 1841 1842/** 1843 * Resets the form data. Causes all form elements to be reset to their original value. 1844 */ 1845$.fn.resetForm = function() { 1846 return this.each(function() { 1847 // guard against an input with the name of 'reset' 1848 // note that IE reports the reset function as an 'object' 1849 if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) { 1850 this.reset(); 1851 } 1852 }); 1853}; 1854 1855/** 1856 * Enables or disables any matching elements. 1857 */ 1858$.fn.enable = function(b) { 1859 if (b === undefined) { 1860 b = true; 1861 } 1862 return this.each(function() { 1863 this.disabled = !b; 1864 }); 1865}; 1866 1867/** 1868 * Checks/unchecks any matching checkboxes or radio buttons and 1869 * selects/deselects and matching option elements. 1870 */ 1871$.fn.selected = function(select) { 1872 if (select === undefined) { 1873 select = true; 1874 } 1875 return this.each(function() { 1876 var t = this.type; 1877 if (t == 'checkbox' || t == 'radio') { 1878 this.checked = select; 1879 } 1880 else if (this.tagName.toLowerCase() == 'option') { 1881 var $sel = $(this).parent('select'); 1882 if (select && $sel[0] && $sel[0].type == 'select-one') { 1883 // deselect all other options 1884 $sel.find('option').selected(false); 1885 } 1886 this.selected = select; 1887 } 1888 }); 1889}; 1890 1891// expose debug var 1892$.fn.ajaxSubmit.debug = false; 1893 1894// helper fn for console logging 1895function log() { 1896 if (!$.fn.ajaxSubmit.debug) { 1897 return; 1898 } 1899 var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,''); 1900 if (window.console && window.console.log) { 1901 window.console.log(msg); 1902 } 1903 else if (window.opera && window.opera.postError) { 1904 window.opera.postError(msg); 1905 } 1906} 1907}); // JAC: bug javascript_resgistries 1908 1909/* - register_function.js - */ 1910/* 1911 Deprecated utility functions - Use jQuery directly instead. 1912 These will be gone in Plone 5. 1913*/ 1914 1915/* 1916 Provides globals: 1917 W3CDOM, bugRiddenCrashPronePieceOfJunk, getContentArea, 1918 registerEventListener, registerPloneFunction, unRegisterEventListener 1919*/ 1920 1921/*global navigator */ 1922 1923 1924// check for ie5 mac 1925var bugRiddenCrashPronePieceOfJunk = ( 1926 navigator.userAgent.indexOf('MSIE 5') !== -1 && navigator.userAgent.indexOf('Mac') !== -1 1927); 1928 1929// check for W3CDOM compatibility 1930var W3CDOM = (!bugRiddenCrashPronePieceOfJunk && 1931 typeof document.getElementsByTagName !== 'undefined' && 1932 typeof document.createElement !== 'undefined' ); 1933 1934// cross browser function for registering event handlers 1935var registerEventListener = function(elem, event, func) { 1936 jQuery(elem).bind(event, func); 1937}; 1938 1939// cross browser function for unregistering event handlers 1940var unRegisterEventListener = function(elem, event, func) { 1941 jQuery(elem).unbind(event, func); 1942}; 1943 1944var registerPloneFunction = jQuery; 1945 1946function getContentArea() { 1947 // returns our content area element 1948 var node = jQuery('#region-content,#content'); 1949 return node.length ? node[0] : null; 1950} 1951 1952 1953/* - select_all.js - */ 1954// Functions for selecting all/none checkboxes in folder_contents/search_form view 1955 1956/* 1957 Provides global toggleSelect; see search_form.pt for example usage. 1958*/ 1959 1960/*global portal_url */ 1961 1962function toggleSelect(selectbutton, id, initialState, formName) { 1963 /* required selectbutton: you can pass any object that will function as a toggle 1964 * optional id: id of the the group of checkboxes that needs to be toggled (default=ids:list 1965 * optional initialState: initial state of the group. (default=false) 1966 * e.g. folder_contents is false, search_form=true because the item boxes 1967 * are checked initially. 1968 * optional formName: name of the form in which the boxes reside, use this if there are more 1969 * forms on the page with boxes with the same name 1970 */ 1971 var fid, state, base; 1972 1973 fid = id || 'ids:list'; // defaults to ids:list, this is the most common usage 1974 state = selectbutton.isSelected; 1975 if (state === undefined) { 1976 state = Boolean(initialState); 1977 } 1978 1979 // create and use a property on the button itself so you don't have to 1980 // use a global variable and we can have as much groups on a page as we like. 1981 selectbutton.isSelected = !state; 1982 jQuery(selectbutton).attr('src', portal_url+'/select_'+(state?'all':'none')+'_icon.png'); 1983 base = formName ? jQuery(document.forms[formName]) : jQuery(document); 1984 base.find('input[name=' + fid + ']:checkbox').attr('checked', !state); 1985} 1986 1987 1988/* - collapsiblesections.js - */ 1989/* 1990 * This is the code for the collapsibles. It uses the following markup: 1991 * 1992 * <dl class="collapsible"> 1993 * <dt class="collapsibleHeader"> 1994 * A Title 1995 * </dt> 1996 * <dd class="collapsibleContent"> 1997 * <!-- Here can be any content you want --> 1998 * </dd> 1999 * </dl> 2000 * 2001 * When the collapsible is toggled, then the dl will get an additional class 2002 * which switches between 'collapsedBlockCollapsible' and 2003 * 'expandedBlockCollapsible'. You can use this to style it accordingly, for 2004 * example: 2005 * 2006 * .expandedBlockCollapsible .collapsibleContent { 2007 * display: block; 2008 * } 2009 * 2010 * .collapsedBlockCollapsible .collapsibleContent { 2011 * display: none; 2012 * } 2013 * 2014 * If you add the 'collapsedOnLoad' class to the dl, then it will get 2015 * collapsed on page load, this is done, so the content is accessible even when 2016 * javascript is disabled. 2017 * 2018 * If you add the 'inline' class to the dl, then it will toggle between 2019 * 'collapsedInlineCollapsible' and 'expandedInlineCollapsible' instead of 2020 * 'collapsedBlockCollapsible' and 'expandedBlockCollapsible'. 2021 * 2022 */ 2023 2024function activateCollapsibles() { 2025(function($) { 2026 $('dl.collapsible:not([class$=Collapsible])').find('dt.collapsibleHeader:first').click(function() { 2027 var $container = $(this).parents('dl.collapsible:first'), 2028 $type; 2029 if (!$container) { 2030 return true; 2031 } 2032 2033 $type = $container.hasClass('inline') ? 'Inline' :'Block'; 2034 // toggle between collapsed and expanded classes 2035 $container.toggleClass('collapsed' + $type + 'Collapsible') 2036 .toggleClass('expanded' + $type + 'Collapsible'); 2037 }).end().each(function() { 2038 var $state = $(this).hasClass('collapsedOnLoad') ? 2039 'collapsed' : 'expanded', 2040 $type = $(this).hasClass('inline') ? 'Inline' :'Block'; 2041 $(this).removeClass('collapsedOnLoad') 2042 .addClass($state + $type + 'Collapsible'); 2043 }); 2044}(jQuery)); 2045} 2046 2047jQuery(function($) { 2048$(activateCollapsibles); 2049}); 2050 2051 2052/* - popupforms.js - */ 2053/****** 2054 Set up standard Plone popups 2055 2056 Provides globals: common_content_filter 2057 2058 Extends jQuery.tools.overlay.conf to set up common Plone effects and 2059 visuals. 2060******/ 2061 2062 2063var common_content_filter = '#content>*:not(div.configlet),dl.portalMessage.error,dl.portalMessage.info'; 2064 2065jQuery.extend(jQuery.tools.overlay.conf, 2066 { 2067 fixed:false, 2068 speed:'fast', 2069 mask:{color:'#fff',opacity: 0.4,loadSpeed:0,closeSpeed:0} 2070 }); 2071 2072 2073(function($) { 2074 2075 // static constructs 2076 $.plonepopups = $.plonepopups || {}; 2077 2078 $.extend($.plonepopups, 2079 { 2080 // method to show error message in a noform 2081 // situation. 2082 noformerrorshow: function noformerrorshow(el, noform) { 2083 var o = $(el), 2084 emsg = o.find('dl.portalMessage.error'); 2085 if (emsg.length) { 2086 o.children().replaceWith(emsg); 2087 return false;
2088 } else { 2089 return noform; 2090 } 2091 }, 2092 // After deletes we need to redirect to the target page. 2093 redirectbasehref: function redirectbasehref(el, responseText) { 2094 var mo = responseText.match(/<base href="(\S+?)"/i); 2095 if (mo.length === 2) { 2096 return mo[1]; 2097 } 2098 return location; 2099 } 2100 }) 2101})(jQuery); 2102 2103jQuery(function($){ 2104 2105 if (jQuery.browser.msie && parseInt(jQuery.browser.version, 10) < 7) { 2106 // it's not realistic to think we can deal with all the bugs 2107 // of IE 6 and lower. Fortunately, all this is just progressive 2108 // enhancement. 2109 return; 2110 } 2111 2112 // login form 2113 $('#portal-personaltools a[href$="/login"], #portal-personaltools a[href$="/login_form"], .discussion a[href$="/login"], .discussion a[href$="/login_form"]').prepOverlay( 2114 { 2115 subtype: 'ajax', 2116 filter: common_content_filter, 2117 formselector: 'form#login_form', 2118 noform: function () { 2119 if (location.href.search(/pwreset_finish$/) >= 0) { 2120 return 'redirect'; 2121 } else { 2122 return 'reload'; 2123 } 2124 }, 2125 redirect: function () { 2126 var href = location.href; 2127 if (href.search(/pwreset_finish$/) >= 0) { 2128 return href.slice(0, href.length-14) + 'logged_in'; 2129 } else { 2130 return href; 2131 } 2132 } 2133 } 2134 ); 2135 2136 // contact form 2137 $('#siteaction-contact a').prepOverlay( 2138 { 2139 subtype: 'ajax', 2140 filter: common_content_filter, 2141 formselector: 'form[name="feedback_form"]', 2142 noform: function(el) {return $.plonepopups.noformerrorshow(el, 'close');} 2143 } 2144 ); 2145 2146 // comment form 2147 $('form[name=reply]').prepOverlay( 2148 { 2149 subtype: 'ajax', 2150 filter: common_content_filter, 2151 formselector: 'form:has(input[name="discussion_reply:method"])', 2152 noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');}, 2153 redirect: $.plonepopups.redirectbasehref 2154 } 2155 ); 2156 2157 2158 // display: select content item / change content item 2159 // $('#contextSetDefaultPage, #folderChangeDefaultPage').prepOverlay( 2160 // { 2161 // subtype: 'ajax', 2162 // filter: common_content_filter, 2163 // formselector: 'form[name="default_page_form"]', 2164 // noform: function(el) {return $.plonepopups.noformerrorshow(el, 'reload');}, 2165 // closeselector: '[name=form.button.Cancel]', 2166 // width:'40%' 2167 // } 2168 //); 2169 2170 // advanced state 2171 // This form needs additional JS and CSS for the calendar widget. 2172 // The AJAX form doesn't load it from the javascript_head_slot. 2173 // $('dl#plone-contentmenu-workflow a#advanced').prepOverlay( 2174 // { 2175 // subtype: 'ajax', 2176 // filter: common_content_filter, 2177 // formselector: 'form', 2178 // noform: function(el) {return $.plonepopups.noformerrorshow(el, 'reload');}, 2179 // closeselector: '[name=form.button.Cancel]' 2180 // } 2181 // ); 2182 2183 // Delete dialog 2184 $('dl#plone-contentmenu-actions a#delete').prepOverlay( 2185 { 2186 subtype: 'ajax', 2187 filter: common_content_filter, 2188 formselector: '#delete_confirmation', 2189 noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');}, 2190 redirect: $.plonepopups.redirectbasehref, 2191 closeselector: '[name=form.button.Cancel]', 2192 width:'50%' 2193 } 2194 ); 2195 2196 // Rename dialog 2197 $('dl#plone-contentmenu-actions a#rename').prepOverlay( 2198 { 2199 subtype: 'ajax', 2200 filter: common_content_filter, 2201 closeselector: '[name=form.button.Cancel]', 2202 width:'40%' 2203 } 2204 ); 2205 2206 // registration 2207 $('#portal-personaltools a[href$="/@@register"]').prepOverlay( 2208 { 2209 subtype: 'ajax', 2210 filter: common_content_filter, 2211 formselector: 'form.kssattr-formname-register' 2212 } 2213 ); 2214 2215 // add new user, group
2216 $('form[name=users_add], form[name=groups_add]').prepOverlay( 2217 { 2218 subtype: 'ajax', 2219 filter: common_content_filter, 2220 formselector: 'form.kssattr-formname-new-user, form[name="groups"]', 2221 noform: function(el) {return $.plonepopups.noformerrorshow(el, 'redirect');}, 2222 redirect: function () {return location.href;} 2223 } 2224 ); 2225 2226 // Content history popup 2227 $('#content-history a').prepOverlay({ 2228 subtype: 'ajax', 2229 filter: 'h2, #content-history', 2230 urlmatch: '@@historyview', 2231 urlreplace: '@@contenthistorypopup' 2232 }); 2233 2234}); 2235 2236
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.