1 2 3/* ==== INCLUDE: /js/weblets/responsive/menu.js ==== */ 4 5jQuery( function($) { 6 7 function calcHorizontalLeaning( $menuitem ) { 8 9 var left = $menuitem.offset().left; 10 var width = $menuitem.outerWidth(); 11 var screen = $(document).width(); 12 13 var rightEdge = screen - ( left + width ); 14 var tolerance = screen / 4; // quarter of screen 15 console.log( "rightedge", rightEdge, "tolerance", tolerance ); 16 if( rightEdge < tolerance ) { 17 return "left"; 18 } 19 else { 20 return ""; 21 } 22 } 23 24 $( document ).on ( "click", ".mrp-menu-resp-wrapper .top-handle", function( e ) { 25 e.stopImmediatePropagation(); 26 $(this).toggleClass( "on" ); 27 $("html").toggleClass( "mrp-menu-on" ); 28 return false; 29 }); 30 31 $( document ).on( "click", ".mrp-menu-resp-wrapper li", function(e) { 32 //if( e.target && e.target.nodeName && e.target.nodeName.toLowerCase() == "a" ) { 33 if( e.target && e.target.nodeName && $(e.target).closest("a").length > 0 ) { 34 var $target = $(e.target).closest("a"); 35 if( $target.attr( "href" ) && $target.attr( "href" ) != "javascript:;" ) { 36 return; 37 } 38 } 39 var $on = $( this ).closest( "li" ); 40 var $menu = $(this).closest( ".mrp-menu-resp-wrapper" ); 41 if( $on.hasClass( "on" ) ) { 42 $on.parent().removeClass( "on" ); 43 $on.removeClass( "on" ); 44 $on.find( ".on" ).removeClass( "on left" ); 45 return false; 46 } 47 $menu.find( "li.on" ).filter( function() { 48 return !( $.contains( this, $on[0] ) ); // this not $(this)!! it's correct 49 }).removeClass( "on left" ).parent().removeClass( "on" ); 50 51 $on.addClass( "on" ); 52 $on.addClass( calcHorizontalLeaning( $on ) ); 53 $on.parent().addClass( "on" ); 54 55 return false; 56 }); 57 58 $( document ).on( "click.mrp-menu-close", function() { 59 $( ".mrp-menu-resp-wrapper .on" ).removeClass( "on left" ); 60 $("html").removeClass( "mrp-menu-on" ); 61 }); 62}); 63 64/* ==== INCLUDE: /w/w=realtorchad.ca/dwr/engine.js ==== */ 65 66/* 67 * Copyright 2005 Joe Walker 68 * 69 * Licensed under the Apache License, Version 2.0 (the "License"); 70 * you may not use this file except in compliance with the License. 71 * You may obtain a copy of the License at 72 * 73 * http://www.apache.org/licenses/LICENSE-2.0 74 * 75 * Unless required by applicable law or agreed to in writing, software 76 * distributed under the License is distributed on an "AS IS" BASIS, 77 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 78 * See the License for the specific language governing permissions and 79 * limitations under the License. 80 */ 81 82/** 83 * The DWR object is also defined by dwr.util etc. 84 */ 85if (typeof dwr == 'undefined') dwr = {}; 86 87(function() { 88 if (!dwr.engine) dwr.engine = {}; 89 90 /** 91 * Set an alternative error handler from the default alert box. 92 * @param {Function} handler The function to call when an error happens 93 */ 94 dwr.engine.setErrorHandler = function(handler) { 95 dwr.engine._errorHandler = handler; 96 }; 97 98 /** 99 * Set an alternative warning handler from the default alert box. 100 * @param {Function} handler The function to call when a warning happens 101 */ 102 dwr.engine.setWarningHandler = function(handler) { 103 dwr.engine._warningHandler = handler; 104 }; 105 106 /** 107 * @deprecated - In favor of the errorHandler being called with an exception name:"dwr.engine.textHtmlReply" 108 * containing a responseText property if available. 109 * 110 * Setter for the text/html handler - what happens if a DWR request gets an HTML 111 * reply rather than the expected Javascript. Often due to login timeout 112 * @param {Function} handler The function to call on an unexpected text/html content type 113 */ 114 dwr.engine.setTextHtmlHandler = function(handler) { 115 dwr.engine._textHtmlHandler = handler; 116 }; 117 118 dwr.engine.setPollStatusHandler = function(handler) { 119 dwr.engine._pollStatusHandler = handler; 120 }; 121 122 /** 123 * Set a default timeout value for all calls. 0 (the default) turns timeouts off. 124 * @param {Function} handler The function to call when we get bored of waiting for a call 125 */ 126 dwr.engine.setTimeout = function(timeout) { 127 dwr.engine._timeout = timeout; 128 }; 129 130 /**
131 * Set a timeout value for Reverse Ajax polls. 0 (the default) turns timeouts off. 132 */ 133 dwr.engine.setPollTimeout = function(timeout) { 134 dwr.engine._pollTimeout = timeout; 135 }; 136 137 /** 138 * The Pre-Hook is called before any DWR remoting is done. 139 * @param {Function} handler The function to call before any remote calls 140 */ 141 dwr.engine.setPreHook = function(handler) { 142 dwr.engine._preHook = handler; 143 }; 144 145 /** 146 * The Post-Hook is called after any DWR remoting is done. 147 * @param {Function} handler The function to call after any remote calls 148 */ 149 dwr.engine.setPostHook = function(handler) { 150 dwr.engine._postHook = handler; 151 }; 152 153 /** 154 * Set a custom path to the DWR servlet (may be a full URL for cross-domain usage) 155 * @param {string} path path or URL 156 */ 157 dwr.engine.setOverridePath = function(path) { 158 dwr.engine._overridePath = path; 159 }; 160 161 /** 162 * Set a custom contextPath (typically used when rewriting paths through a web server) 163 * @param {string} path path 164 */ 165 dwr.engine.setOverrideContextPath = function(path) { 166 dwr.engine._overrideContextPath = path; 167 }; 168 169 /** 170 * Extra attributes to append to the DWRSESSIONID cookie (domain, secure, etc) 171 * @param {string} attributeString attribute string according to cookie syntax 172 */ 173 dwr.engine.setCookieAttributes = function(attributeString) { 174 dwr.engine._cookieAttributes = attributeString; 175 }; 176 177 /** 178 * Custom headers for all DWR calls 179 * @param {Object} headers Object containing name/value pairs for extra headers 180 */ 181 dwr.engine.setHeaders = function(headers) { 182 dwr.engine._headers = headers; 183 }; 184 185 /** 186 * Custom attributes transferred to server for all DWR calls 187 * @param {Object} attributes Object containing name/value pairs for attributes 188 */ 189 dwr.engine.setAttributes = function(attributes) { 190 dwr.engine._attributes = attributes; 191 }; 192 193 /** 194 * Ensure that remote calls happen in the order in which they were sent? (Default: false) 195 * @param {boolean} ordered true to enable ordered processing 196 */ 197 dwr.engine.setOrdered = function(ordered) { 198 dwr.engine._ordered = ordered; 199 }; 200 201 /** 202 * Do we ask the XHR object to be asynchronous? (Default: true) 203 * Warning: it is <strong>highly</strong> advised to use the default of async 204 * processing, especially when dealing with Internet based requests. 205 * @param {boolean} async false to enable sync processing for XHR queries 206 */ 207 dwr.engine.setAsync = function(async) { 208 dwr.engine._async = async; 209 }; 210 211 /** 212 * Does the client actively check the server for updates? (Default: false) 213 * @param {boolean} async true to enable low latency reverse ajax 214 */ 215 dwr.engine.setActiveReverseAjax = function(activeReverseAjax) { 216 if (activeReverseAjax) { 217 // Bail if we are already started 218 if (dwr.engine._activeReverseAjax) return; 219 // We always want a retry policy when reverse AJAX is enabled. 220 if (!dwr.engine._retryIntervals || dwr.engine._retryIntervals.length === 0) { dwr.engine._retryIntervals = dwr.engine._defaultRetryIntervals; } 221 dwr.engine._activeReverseAjax = true; 222 if (dwr.engine._initializing) return; // We'll start after init is done 223 dwr.engine._poll(); 224 } 225 else { 226 // Can we cancel an existing request? 227 if (dwr.engine._activeReverseAjax && dwr.engine._pollBatch) { 228 dwr.engine.transport.abort(dwr.engine._pollBatch); 229 } 230 dwr.engine._activeReverseAjax = false; 231 } 232 // TODO: in iframe mode, if we start, stop, start then the second start may 233 // well kick off a second iframe while the first is still about to return 234 // we should cope with this but we don't 235 }; 236 237 /** 238 * Turn server notification of page unload on and off 239 * @param {boolean} notify true or false depending on if we want to turn unload notification on or off 240 */ 241 dwr.engine.setNotifyServerOnPageUnload = function(notify, asyncUnload) { 242 dwr.engine._asyncUnload = (asyncUnload !== undefined) ? asyncUnload : false; 243 dwr.engine._isNotifyServerOnPageUnload = notify; 244 }; 245 246 /** 247 * Turn server notification of page load on and off 248 * @param {boolean} notify true or false depending on if we want to turn load notification on or off 249 */ 250 dwr.engine.setNotifyServerOnPageLoad = function(notify) { 251 dwr.engine._isNotifyServerOnPageLoad = notify; 252 if (notify && !dwr.engine._initializing && !dwr.engine._isNotifyServerOnPageLoadSent) { 253 eval("dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'pageLoaded', [ function() { dwr.engine._ordered = false; }]);"); 254 dwr.engine._isNotifyServerOnPageLoadSent = true; 255 } 256 }; 257 258 /* 259 * The maximum number of retries, when reached we will no longer attempt to check for server status. 260 * @param - maxRetries 261 */ 262 dwr.engine.setMaxRetries = function(maxRetries) { 263 dwr.engine._maxRetries = maxRetries; 264 }; 265 266 /* 267 * The intervals between successive retries in seconds 268 * @param - array of integers representing the retry interval in seconds. 269 */ 270 dwr.engine.setRetryIntervals = function(intervalsArray) { 271 dwr.engine._retryIntervals = intervalsArray; 272 }; 273 274 /** 275 * The default message handler. 276 * @param {String} message The text of the error message 277 * @param {Object}
277 ex An error object containing at least a name and message 278 */ 279 dwr.engine.defaultErrorHandler = function(message, ex) { 280 dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true); 281 if (!message) message = "A server error has occurred."; 282 // Ignore NS_ERROR_NOT_AVAILABLE if Mozilla is being narky 283 if (message.indexOf("0x80040111") != -1) return; 284 if ("true" == "true") alert(message); 285 }; 286 287 /** 288 * The default warning handler. 289 * @param {String} message The text of the error message 290 * @param {Object} ex An error object containing at least a name and message 291 */ 292 dwr.engine.defaultWarningHandler = function(message, ex) { 293 dwr.engine._debug(message); 294 }; 295 296 /** 297 * The default poll status handler. 298 * @param {boolean} newStatus - true = online, false = offline 299 * @param {object} ex - The exception if one exists (offline). 300 */ 301 dwr.engine.defaultPollStatusHandler = function(newStatus, ex) { 302 dwr.engine.util.logHandlerEx(function() { 303 if (newStatus === false && dwr.engine._errorHandler) dwr.engine._errorHandler(ex.message, ex); 304 }); 305 }; 306 307 /** 308 * For reduced latency you can group several remote calls together using a batch. 309 */ 310 dwr.engine.beginBatch = function() { 311 if (dwr.engine._batch) { 312 dwr.engine._handleError(null, { name:"dwr.engine.batchBegun", message:"Batch already begun" }); 313 return; 314 } 315 dwr.engine._batch = dwr.engine.batch.create(); 316 }; 317 318 /** 319 * Finished grouping a set of remote calls together. Go and execute them all. 320 * @param {Object} options A options object to customize processing 321 */ 322 dwr.engine.endBatch = function(options) { 323 var batch = dwr.engine._batch; 324 if (batch == null) { 325 dwr.engine._handleError(null, { name:"dwr.engine.batchNotBegun", message:"No batch in progress" }); 326 return; 327 } 328 dwr.engine._batch = null; 329 if (batch.map.callCount === 0) { 330 return; 331 } 332 333 // The hooks need to be merged carefully to preserve ordering 334 if (options) { 335 dwr.engine.batch.merge(batch, options); 336 } 337 338 // In ordered mode, we don't send unless the list of sent items is empty 339 // (queuing is not possible for sync calls so let them slip through) 340 if (batch.async && (dwr.engine._ordered || dwr.engine._internalOrdered) && dwr.engine._batchesLength !== 0) { 341 dwr.engine._batchQueue[dwr.engine._batchQueue.length] = batch; 342 } 343 else { 344 return dwr.engine.transport.send(batch); 345 } 346 }; 347 348 /** 349 * For use with file downloads. When a DWR function returns a binary download 350 * you can prompt the user to save it using this function 351 * @param {Object} data The binary data passed from DWR 352 */ 353 dwr.engine.openInDownload = function(data) { 354 var div = document.createElement("div"); 355 document.body.appendChild(div); 356 div.innerHTML = "<iframe width='0' height='0' scrolling='no' frameborder='0' src='" + data + "'></iframe>"; 357 }; 358 359 /** 360 * What is the current version DWR number 361 * DWR version numbers are of the form "Version 1.2.3.3128[.beta]", where: 362 * 1 is the major release number. Changes in major version number indicate 363 * significant enhancements in functionality 364 * 2 is the minor release number. Changes in minor version number indicate 365 * less significant changes in functionality 366 * 3 is the revision release number. Changes here typically indicate bug 367 * fixes only 368 * 3128 is the build number. This number increments for each build 369 * .beta is a release title that is generally only used for non production 370 * releases to indicate the purpose/quality of the release 371 * The label is these strings concatenated 372 */ 373 dwr.version = { 374 /** 375 * Changes in major version number indicate significant enhancements 376 */ 377 major:parseInt("3", 10), 378 379 /** 380 * Changes in minor version number indicate smaller enhancements 381 */ 382 minor:parseInt("0", 10), 383 384 /** 385 * Changes with the revision number typically indicate bug-fixes only 386 */ 387 revision:parseInt("3", 10), 388 389 /** 390 * The build number increments for each build 391 */ 392 build:parseInt("-1", 10), 393 394 /** 395 * Only used for non production releases to indicate the purpose/quality of 396 * the release. Example titles include 'milestone1' or 'beta3'. 397 */ 398 title:"dev", 399 400 /** 401 * The strings above concatenated 402 */ 403 label:"3.0.3-dev" 404 }; 405 406 //============================================================================== 407 // Only private stuff below here 408 //============================================================================== 409 410 /** Keep track of page load initialization */ 411 dwr.engine._initializing = true; 412 413 /** Is GET enabled? */ 414 dwr.engine._allowGetButMakeForgeryEasier = "true"; 415 416 /** The script prefix to strip in the case of scriptTagProtection. */ 417 dwr.engine._scriptTagProtection = "throw 'allowScriptTagRemoting is false.';"; 418 419 /** The default path to the DWR servlet */ 420 dwr.engine._pathToDwrServlet = "/w/w=realtorchad.ca/dwr"; 421 422 /** Custom path to the DWR servlet */ 423 dwr.engine._overridePath = ""; 424 if (typeof pathToDwrServlet != "undefined") { 425 dwr.engine._overridePath = pathToDwrServlet; 426 } 427 428 dwr.engine._effectivePath = function() { 429 return dwr.engine._overridePath || dwr.engine._pathToDwrServlet; 430 }; 431 432 /** The webapp's contextPath */ 433 dwr.engine._contextPath = ""; 434 435 /** Custom contextPath */ 436 dwr.engine._overrideContextPath = ""; 437 438 dwr.engine._effectiveContextPath = function() { 439 return dwr.engine._overrideContextPath || dwr.engine._contextPath; 440 }; 441
442 /** Extra attributes to append to the DWRSESSIONID cookie (domain, secure, etc) */ 443 dwr.engine._cookieAttributes = ""; 444 445 /** Do we use XHR for reverse ajax because we are not streaming? */ 446 dwr.engine._useStreamingPoll = "true"; 447 448 dwr.engine._pollOnline = true; 449 450 /** These URLs can be configured from the server */ 451 dwr.engine._ModePlainCall = "/call/plaincall/"; 452 dwr.engine._ModePlainPoll = "/call/plainpoll/"; 453 dwr.engine._ModeHtmlCall = "/call/htmlcall/"; 454 dwr.engine._ModeHtmlPoll = "/call/htmlpoll/"; 455 456 /** Do we make the calls async? Default to 'true' */ 457 dwr.engine._async = Boolean("true"); 458 459 /** The local page id */ 460 dwr.engine._pageId = null; 461 462 /** The browser instance id */ 463 dwr.engine._dwrSessionId = null; 464 465 /** The global page id (browser instance + local page id) */ 466 dwr.engine._scriptSessionId = ""; // we must use "" and not null 467 468 /** A function to be called before requests are marshalled. Can be null. */ 469 dwr.engine._preHook = null; 470 471 /** A function to be called after replies are received. Can be null. */ 472 dwr.engine._postHook = null; 473 474 /** A map of the batches that we have sent and are awaiting a reply on. */ 475 dwr.engine._batches = {}; 476 477 /** A count of the number of outstanding batches. Should be == to _batches.length unless prototype has messed things up */ 478 dwr.engine._batchesLength = 0; 479 480 /** In ordered mode, the array of batches waiting to be sent */ 481 dwr.engine._batchQueue = []; 482 483 /** User setting for saying that calls should execute one by one in the order in which they were sent */ 484 dwr.engine._ordered = false; 485 486 /** Internal state forcing calls to execute one by one in the order in which they were sent */ 487 dwr.engine._internalOrdered = false; 488 489 /** The current batch (if we are in batch mode) */ 490 dwr.engine._batch = null; 491 492 /** The global timeout */ 493 dwr.engine._timeout = 0; 494 495 /** Are we doing comet or polling? */ 496 dwr.engine._activeReverseAjax = false; 497 498 /** All reverse ajax scripts before this index have been received and executed */ 499 dwr.engine._nextReverseAjaxIndex = 0; 500 501 /** Queue of reverse ajax scripts (functions) that have been received but not yet executed due to receiving in wrong order */ 502 dwr.engine._reverseAjaxQueue = {}; 503 504 /** The batch that we are using to poll */ 505 dwr.engine._pollBatch = null; 506 507 /** Maximum time we wait for a poll to return (0 = no timeout) */ 508 dwr.engine._pollTimeout = 0; 509 510 /** How many milliseconds between internal comet polls */ 511 dwr.engine._pollCometInterval = 200; 512 513 /** How many times have we re-tried a call? */ 514 dwr.engine._retries = 1; 515 dwr.engine._maxRetries = -1; 516 517 /** The intervals between successive retries in seconds */ 518 dwr.engine._retryIntervals = []; 519 520 /** Used as the default for reverse ajax/polling 521 * Retry immediately twice first with a one second interval, next with a 3 second interval, then go offline. 522 * Therafter, retry every 3 seconds. 523 */ 524 dwr.engine._defaultRetryIntervals = [ 1, 3, 3 ]; 525 526 dwr.engine._textHtmlHandler = null; 527 528 /** If you wish to send custom headers with every request */ 529 dwr.engine._headers = null; 530 531 /** If you wish to send extra custom request attributes with each request */ 532 if (typeof attributes != "undefined") { 533 dwr.engine._attributes = attributes; 534 } 535 else { 536 dwr.engine._attributes = null; 537 } 538 539 /** Batch ids allow us to know which batch the server is answering */ 540 dwr.engine._nextBatchId = 0; 541 542 /** The instance id helps us distinguish multiple engine.js loaded in one page */ 543 dwr.engine._instanceId = -1; 544 545 /** A list of the properties that need merging from calls to a batch */ 546 dwr.engine._propnames = [ "async", "timeout", "errorHandler", "warningHandler", "textHtmlHandler" ]; 547 548 /** Do we stream, or can be hacked to do so? */ 549 dwr.engine._partialResponseNo = 0; 550 dwr.engine._partialResponseYes = 1; 551 552 /** Are we notifying the server on page unload? */ 553 dwr.engine._isNotifyServerOnPageUnload = false; 554 555 /** Are we notifying the server on page load? */ 556 dwr.engine._isNotifyServerOnPageLoad = false; 557 dwr.engine._isNotifyServerOnPageLoadSent = false;
558 559 /** Should the unload call be asynchronous? If true it may not be called by the browser. */ 560 dwr.engine._asyncUnload = false; 561 562 /** 563 * A map of all mapped classes whose class declarations have been loaded 564 * (dwrClassName -> constructor function) 565 */ 566 dwr.engine._mappedClasses = {}; 567 568 /** A function to call if something fails. */ 569 dwr.engine._errorHandler = dwr.engine.defaultErrorHandler; 570 571 /** For debugging when something unexplained happens. */ 572 dwr.engine._warningHandler = dwr.engine.defaultWarningHandler; 573 574 dwr.engine._pollStatusHandler = dwr.engine.defaultPollStatusHandler; 575 576 /** Undocumented interceptors - do not use */ 577 dwr.engine._postSeperator = "\n"; 578 dwr.engine._defaultInterceptor = function(data) { return data; }; 579 dwr.engine._urlRewriteHandler = dwr.engine._defaultInterceptor; 580 dwr.engine._contentRewriteHandler = dwr.engine._defaultInterceptor; 581 dwr.engine._replyRewriteHandler = dwr.engine._defaultInterceptor; 582 583 /** Object attributes to ignore when serializing */ 584 dwr.engine._excludeObjectAttributes = { 585 "$dwrClassName": true, 586 "$dwrByRef": true, 587 "$_dwrConversionRef": true 588 }; 589 590 dwr.engine._ieConditions = {}; 591 592 /** Is this page doing beforeunload processing? */ 593 dwr.engine._beforeUnloading = false; 594 595 /** A place to temporarily store exception before reporting */ 596 dwr.engine._queuedBatchException = null; 597 598 /** @private Keep beforeunload flag set during beforeunload processing (including potential 599 * "Are you sure?" popups). */ 600 dwr.engine._beforeUnloader = function() { 601 dwr.engine._beforeUnloading = true; 602 // We want to reset the flag if unloading is aborted by "Are you sure?" popup. We detect 603 // return to interactive mode with a timeout that normally will execute directly after 604 // unload is aborted through popup. 605 setTimeout(function() { 606 // Some browsers (f ex Firefox) will execute the timeout even if unload is not aborted. 607 // To keep a continuous unloading state we therefore prolong the beforeunload state for 608 // a second so the unload flag gets time to trigger. 609 setTimeout(function() { 610 dwr.engine._beforeUnloading = false; 611 }, 1000); 612 }, 1); 613 }; 614 615 /** Is this page in the process of unloading? */ 616 dwr.engine._unloading = false; 617 618 /** @private Abort any XHRs in progress at page unload (solves zombie socket problems in IE). */ 619 dwr.engine._unloader = function() { 620 dwr.engine._unloading = true; 621 622 // Empty queue of waiting ordered requests 623 dwr.engine._batchQueue.length = 0; 624 625 // Abort any ongoing requests and clear their batches 626 var batch; 627 for (var batchId in dwr.engine._batches) { 628 batch = dwr.engine._batches[batchId]; 629 if (batch.transport && batch.transport.abort) { 630 batch.transport.abort(batch); 631 } 632 } 633 634 // If we have used reverse ajax then we try to tell the server we are gone 635 if (dwr.engine._isNotifyServerOnPageUnload) { 636 dwr.engine._debug("calling unloader for: " + dwr.engine._scriptSessionId); 637 batch = { 638 map:{ 639 callCount:1, 640 'c0-scriptName':'__System', 641 'c0-methodName':'pageUnloaded', 642 'c0-id':0 643 }, 644 paramCount:0, isPoll:false, async:dwr.engine._asyncUnload, 645 headers:{}, preHooks:[], postHooks:[], 646 timeout:dwr.engine._timeout, 647 errorHandler:null, globalErrorHandler:dwr.engine._errorHandler, warningHandler:null, textHtmlHandler:null, globalTextHtmlHandler:null, 648 handlers:[{ exceptionHandler:null, callback:null }] 649 }; 650 dwr.engine.transport.send(batch); 651 dwr.engine._isNotifyServerOnPageUnload = false; 652 } 653 }; 654 655 function ignoreIfUnloading(batch, f) { 656 // Do nothing if we are unloading 657 if (dwr.engine._unloading) return; 658 // Retry later if beforeunload is in progress and this is not a sync call 659 if (dwr.engine._beforeUnloading && (batch == null || batch.async)) { 660 setTimeout(function() { 661 ignoreIfUnloading(batch, f); 662 }, 100); 663 } 664 // Otherwise trigger the target func 665 else { 666 return f(); 667 } 668 } 669 670 dwr.engine._initializer = { 671 /* 672 * Load-time initializations 673 */ 674 preInit: function() { 675 // Make random local page id 676 dwr.engine._pageId = dwr.engine.util.tokenify(new Date().getTime()) + "-" + dwr.engine.util.tokenify(Math.random() * 1E16); 677 678 // Reuse any existing dwr session 679 dwr.engine.transport.updateDwrSessionFromCookie(); 680 681 // Register unload handlers 682 dwr.engine.util.addEventListener(window, 'beforeunload', dwr.engine._beforeUnloader); 683 dwr.engine.util.addEventListener(window, 'unload', dwr.engine._unloader); 684 685 // Register this engine instance globally 686 var g = dwr.engine._global; 687 if (!g.dwr) g.dwr = {}; 688 if (!g.dwr._) g.dwr._ = []; 689 dwr.engine._instanceId = g.dwr._.length; 690 g.dwr._[dwr.engine._instanceId] = dwr; 691 }, 692 693 loadDwrConfig: function() { 694 if (typeof dwrConfig != "undefined") { 695 for(p in dwrConfig) { 696 var methodName = "set" + p.charAt(0).toUpperCase() + p.substring(1); 697 var setter = dwr.engine[methodName]; 698 if (setter) setter(dwrConfig[p]); 699 } 700 } 701 }, 702 703 init: function() { 704 dwr.engine._initializer.preInit(); 705 dwr.engine._initializer.loadDwrConfig(); 706 dwr.engine._initializing = false;
707 // Now we can start sending requests 708 if (dwr.engine._isNotifyServerOnPageLoad) { 709 eval("dwr.engine._execute(dwr.engine._pathToDwrServlet, '__System', 'pageLoaded', [ function() { dwr.engine._ordered = false; }]);"); 710 dwr.engine._isNotifyServerOnPageLoadSent = true; 711 } 712 if (dwr.engine._activeReverseAjax) { 713 dwr.engine._poll(); 714 } 715 } 716 }; 717 718 /** 719 * Send a request. Called by the JavaScript interface stub 720 * @private 721 * @param path part of URL after the host and before the exec bit without leading or trailing /s 722 * @param scriptName The class to execute 723 * @param methodName The method on said class to execute 724 * @param func The callback function to which any returned data should be passed 725 * if this is null, any returned data will be ignored 726 * @param args The parameters to passed to the above method 727 */ 728 dwr.engine._execute = function(overridePath, scriptName, methodName, args) { 729 var path = overridePath || dwr.engine._effectivePath(); 730 dwr.engine._singleShot = false; 731 if (dwr.engine._batch == null) { 732 dwr.engine.beginBatch(); 733 dwr.engine._singleShot = true; 734 } 735 736 var batch = dwr.engine._batch; 737 738 // All the paths MUST be to the same servlet 739 if (batch.path == null) { 740 batch.path = path; 741 } 742 else { 743 if (batch.path != path) { 744 dwr.engine._handleError(batch, { name:"dwr.engine.multipleServlets", message:"Can't batch requests to multiple DWR Servlets." }); 745 return; 746 } 747 } 748 749 dwr.engine.batch.addCall(batch, scriptName, methodName, args); 750 751 if (dwr.engine._isHeartbeatBatch(batch)) { 752 // Heartbeats should fail fast. 753 batch.timeout = 750; 754 } 755 756 // Now we have finished remembering the call, we increment the call count 757 batch.map.callCount++; 758 if (dwr.engine._singleShot) { 759 return dwr.engine.endBatch(); 760 } 761 }; 762 763 /** 764 * Poll the server to see if there is any data waiting 765 * @private 766 */ 767 dwr.engine._poll = function() { 768 if (!dwr.engine._activeReverseAjax) { 769 return; 770 } 771 dwr.engine._pollBatch = dwr.engine.batch.createPoll(); 772 dwr.engine.transport.send(dwr.engine._pollBatch); 773 }; 774 775 /** @private Utility to execute incoming scripts */ 776 dwr.engine._executeScript = function(script) { 777 if (script == null) { 778 return null; 779 } 780 if (script === "") { 781 dwr.engine._debug("Warning: blank script", true); 782 return null; 783 } 784 // Using Function instead of eval as the latter has memory problems on IE9 785 (new Function("dwr", script))(dwr); 786 }; 787 788 /** @private call all the post hooks for a batch */ 789 dwr.engine._callPostHooks = function(batch) { 790 if (batch.postHooks) { 791 for (var i = 0; i < batch.postHooks.length; i++) { 792 batch.postHooks[i](); 793 } 794 batch.postHooks = null; 795 } 796 }; 797 798 /** 799 * Is this a batch that is checking for a heartbeat? 800 * If so we want to ignore errors/warnings, etc. 801 * @private 802 * @param {Object} batch 803 */ 804 dwr.engine._isHeartbeatBatch = function(batch) { 805 return (batch.map && batch.map['c0-methodName'] === 'checkHeartbeat' && batch.map['c0-scriptName'] === '__System'); 806 }; 807 808 /** 809 * Generic error handling routing to save having null checks everywhere 810 * @private 811 * @param {Object} batch 812 * @param {Object} ex 813 */ 814 dwr.engine._handleError = function(batch, ex) { 815 if (batch && dwr.engine._isHeartbeatBatch(batch)) { // Ignore errors that happen while checking for a heartbeat. 816 return; 817 } 818 var errorHandlers = []; 819 var anyCallWithoutErrorHandler = false; 820 if (batch && batch.isPoll) { // No error reporting and only retry for polls 821 dwr.engine._handlePollRetry(batch, ex); 822 } else { 823 // Perform error cleanup synchronously 824 if (batch) { 825 for (var i = 0; i < batch.map.callCount; i++) { 826 var handlers = batch.handlers[i]; 827 if (!handlers.completed) { 828 if (typeof handlers.errorHandler == "function") 829 errorHandlers.push(handlers.errorHandler); 830 else 831 anyCallWithoutErrorHandler = true; 832 handlers.completed = true; 833 } 834 } 835 } 836 // Perform error reporting asynchronously (possibly) 837 ignoreIfUnloading(batch, function() { 838 if (batch) { 839 var textHtmlHandler = batch.textHtmlHandler || batch.globalTextHtmlHandler; 840 if (ex.name === "dwr.engine.textHtmlReply" && textHtmlHandler) { 841 dwr.engine.util.logHandlerEx(function() { 842 textHtmlHandler(ex); 843 }); 844 return; 845 } 846 } 847 dwr.engine._prepareException(ex); 848 var errorHandler; 849 while(errorHandlers.length > 0) { 850 errorHandler = errorHandlers.shift(); 851 dwr.engine.util.logHandlerEx(function() { 852 errorHandler(ex.message, ex); 853 }); 854 } 855 if (batch) { 856 dwr.engine.util.logHandlerEx(function() { 857 if (typeof batch.errorHandler == "function") batch.errorHandler(ex.message, ex); 858 else if (anyCallWithoutErrorHandler) { 859 if (typeof batch.globalErrorHandler == "function") batch.globalErrorHandler(ex.message, ex); 860 } 861 }); 862 dwr.engine.batch.remove(batch); 863 } 864 }); 865 } 866 }; 867 868 /** 869 * Handle retries for polling as well as online/offline status. 870 * @private 871 * @param {Object} batch 872 * @param {Object} ex 873 */ 874 dwr.engine._handlePollRetry = function(batch, ex) { 875 var retryInterval = dwr.engine._getRetryInterval(); 876 // If number of retries is equal to the retryIntervals array length, call the supplied pollStatusHandler and go offline. 877 if (dwr.engine._retries === dwr.engine._retryIntervals.length - 1) { 878 dwr.engine._debug("poll retry - going offline: " + retryInterval/1000 + " seconds"); 879 dwr.engine._handlePollStatusChange(false, ex, batch); 880 // We are offline, continue to check for a heartbeat until _retries is > _maxRetries. 881 var heartbeatInterval = setInterval(function() { 882 if (dwr.engine._maxRetries === -1 || dwr.engine._retries < dwr.engine._maxRetries) { 883 dwr.engine._execute(null, '__System', 'checkHeartbeat', [ function() { 884 // We found a heartbeat, we are back online! 885 clearInterval(heartbeatInterval); 886 dwr.engine._poll(); 887 }]); 888 dwr.engine._retries++; 889 dwr.engine._debug("DWR Offline - poll retry - interval: " + retryInterval/1000 + " seconds"); 890 } else { 891 // maxRetries has been reached, stop the heartbeat check. 892 clearInterval(heartbeatInterval); 893 dwr.engine._debug("max retries reached, stop polling for server status."); 894 dwr.engine._handlePollStatusChange(false, ex, batch); 895 } 896 }, retryInterval); 897 } else { // We are still online, poll again. 898 dwr.engine._retries++; 899 dwr.engine.batch.remove(batch); 900 dwr.engine._debug("DWR Online - poll retry - interval: " + retryInterval/1000 + " seconds"); 901 setTimeout(dwr.engine._poll, retryInterval); 902 } 903 }; 904 905 dwr.engine._getRetryInterval = function() { 906 var retryInterval; 907 if (dwr.engine._retries < dwr.engine._retryIntervals.length) { 908 retryInterval = dwr.engine._retryIntervals[dwr.engine._retries] * 1000; 909 } else { 910 // The last interval in retryIntervals is the number that will be used to poll when offline. 911 retryInterval = dwr.engine._retryIntervals[dwr.engine._retryIntervals.length - 1] * 1000; 912 } 913 return retryInterval; 914 }; 915 916 /** 917 * Handles polling status changes - online or offline. 918 * @param {boolean} newStatus - true = online, false = offline 919 * @param {object} ex - The exception if one exists (offline). 920 */ 921 dwr.engine._handlePollStatusChange = function(newStatus, ex, batch) { 922 if (batch.isPoll || dwr.engine._isHeartbeatBatch(batch)) { 923 var changed = dwr.engine._pollOnline !== newStatus; 924 var maxRetriesReached = dwr.engine._maxRetries === dwr.engine._retries; 925 dwr.engine._pollOnline = newStatus; 926 if ((changed || maxRetriesReached) && typeof dwr.engine._pollStatusHandler === "function") 927 dwr.engine.util.logHandlerEx(function() { 928 dwr.engine._pollStatusHandler(newStatus, ex, maxRetriesReached); 929 }); 930 if (newStatus) { 931 dwr.engine._retries = 1; 932 } 933 } 934 }; 935 936 /** 937 * Generic error handling routing to save having null checks everywhere. 938 * @private 939 * @param {Object} batch 940 * @param {Object} ex 941 */ 942 dwr.engine._handleWarning = function(batch, ex) { 943 if (batch && dwr.engine._isHeartbeatBatch(batch)) { // Ignore warnings that happen while checking for a heartbeat. 944 return; 945 } 946 ignoreIfUnloading(batch, function() { 947 // If this is a poll, we should retry! 948 dwr.engine._prepareException(ex); 949 dwr.engine.util.logHandlerEx(function() { 950 if (batch && typeof batch.warningHandler == "function") batch.warningHandler(ex.message, ex); 951 else if (dwr.engine._warningHandler) dwr.engine._warningHandler(ex.message, ex); 952 }); 953 if (batch) dwr.engine.batch.remove(batch); 954 }); 955 }; 956 957 /** 958 * Prepares an exception for an error/warning handler. 959 * @private 960 * @param {Object} ex 961 */ 962 dwr.engine._prepareException = function(ex) { 963 if (typeof ex == "string") ex = { name:"unknown", message:ex }; 964 if (ex.message == null) ex.message = ""; 965 if (ex.name == null) ex.name = "unknown"; 966 }; 967 968 /** 969 * Create a new object that delegates to obj 970 * @param obj 971 */ 972 dwr.engine._delegate = (function() { 973 function F(){} 974 return (function(obj){ 975 F.prototype = obj; 976 return new F(); 977 }); 978 })(); 979 980 /** 981 * Create a new object that delegates to obj 982 * @param obj 983 */ 984 dwr.engine._createFromMap = function(map) { 985 var obj = new this(); // this should be set to a constructor function! 986 for(prop in map) if (map.hasOwnProperty(prop)) obj[prop] = map[prop]; 987 return obj; 988 }; 989 990 /** 991 * A reference to the global context (window when in a browser) 992 */ 993 dwr.engine._global = (function(){return this;}).call(null); 994 995 /** 996 * Navigates properties from the global scope and down to fetch a property 997 * value. 998 * @param prop hierarchical property name 999 * @return property value, or undefined if doesn't exist 1000 */ 1001 dwr.engine._getObject = function(prop) { 1002 var parts = prop.split("."); 1003 var value = undefined; 1004 var scope = dwr.engine._global; 1005 while(parts.length > 0) { 1006 var currprop = parts.shift(); 1007 if (!scope) return undefined; 1008 value = scope[currprop]; 1009 if (value && value.tagName && document.getElementById(currprop) == value) return undefined; 1010 scope = value; 1011 } 1012 return value; 1013 }; 1014 1015 /** 1016 * Navigates properties from the global scope and down to set a value. 1017 * @param prop hierarchical property name 1018 * @param obj property value to set 1019 */ 1020 dwr.engine._setObject = function(prop, obj) { 1021 var parts = prop.split("."); 1022 var level; 1023 var scope = dwr.engine._global; 1024 while(parts.length > 0) { 1025 var currprop = parts.shift(); 1026 if (parts.length === 0) { 1027 scope[currprop] = obj; 1028 } 1029 else { 1030 level = scope[currprop]; 1031 if (level == null || level.tagName && document.getElementById(currprop) == level) { 1032 scope[currprop] = level = {}; 1033 } 1034 scope = level; 1035 } 1036 } 1037 }; 1038 1039 /** 1040 * Used internally when some message needs to get to the programmer 1041 * @private 1042 * @param {String} message 1043 * @param {Object} stacktrace 1044 */ 1045 dwr.engine._debug = function(message, stacktrace) { 1046 var written = false;
1047 try { 1048 if (window.console) { 1049 if (stacktrace && window.console.trace) window.console.trace(); 1050 if (window.console.log) window.console.log(message); 1051 written = true; 1052 } 1053 else if (window.opera && window.opera.postError) { 1054 window.opera.postError(message); 1055 written = true; 1056 } 1057 } 1058 catch (ex) { /* ignore */ } 1059 1060 if (!written) { 1061 var debug = document.getElementById("dwr-debug"); 1062 if (debug) { 1063 var contents = message + "<br/>" + debug.innerHTML; 1064 if (contents.length > 2048) contents = contents.substring(0, 2048); 1065 debug.innerHTML = contents; 1066 } 1067 } 1068 }; 1069 1070 /** 1071 * Functions called by the server 1072 */ 1073 dwr.engine.remote = { 1074 /** 1075 * Execute a callback 1076 * @private 1077 * @param {int} batchId The ID of the batch that we are replying to 1078 * @param {int} callId The call ID that the script relates to 1079 * @param {String} reply The script to execute 1080 */ 1081 handleCallback:function(batchId, callId, reply) { 1082 var batch = dwr.engine._batches[batchId]; 1083 if (!batch) return; 1084 1085 // We store the reply in the batch so we can return the data when in sync mode 1086 batch.reply = reply; 1087 1088 // Error handlers inside here indicate an error that is nothing to do 1089 // with DWR so we handle them differently. 1090 try { 1091 var handlers = batch.handlers[callId]; 1092 if (!handlers) { 1093 dwr.engine._debug("Warning: Missing handlers. callId=" + callId, true); 1094 } 1095 else { 1096 batch.handlers[callId].completed = true; 1097 if (typeof handlers.callback == "function") { 1098 dwr.engine.util.logHandlerEx(function() { 1099 if(handlers.callbackArg !== undefined) 1100 handlers.callback.call(handlers.callbackScope, reply, handlers.callbackArg); 1101 else 1102 handlers.callback.call(handlers.callbackScope, reply); 1103 }); 1104 } 1105 } 1106 } 1107 catch (ex) { 1108 dwr.engine._handleError(batch, ex); 1109 } 1110 }, 1111 1112 /** 1113 * Called by the server when a JavascriptFunction is executed 1114 * @param id The ID of the serialized function 1115 * @param args The arguments to pass to the function 1116 */ 1117 handleFunctionCall:function(id, args) { 1118 var func = dwr.engine.serialize.remoteFunctions[id]; 1119 func.apply(window, args); 1120 }, 1121 1122 /** 1123 * Called by the server when a JavascriptFunction is executed 1124 * @param id The ID of the serialized function 1125 * @param args The arguments to pass to the function 1126 */ 1127 handleObjectCall:function(id, methodName, args) { 1128 var obj = dwr.engine.serialize.remoteFunctions[id]; 1129 obj[methodName].apply(obj, args); 1130 }, 1131 1132 /** 1133 * Called by the server when a JavascriptFunction is executed 1134 * @param propertyName The ID of the serialized function 1135 * @param data The arguments to pass to the function 1136 */ 1137 handleSetCall:function(id, propertyName, data) { 1138 var obj = dwr.engine.serialize.remoteFunctions[id]; 1139 obj[propertyName] = data; 1140 }, 1141 1142 /** 1143 * Called by the server when a JavascriptFunction is closed 1144 * @param id The ID of the serialized function 1145 */ 1146 handleFunctionClose:function(id) { 1147 delete dwr.engine.serialize.remoteFunctions[id]; 1148 }, 1149 1150 /** 1151 * Called by the server: Handle an exception for a call 1152 * @private 1153 * @param {int} batchId The ID of the batch that we are replying to 1154 * @param {int} callId The call ID that the script relates to 1155 * @param {String} reply The script to execute 1156 */ 1157 handleException:function(batchId, callId, ex) { 1158 var batch = dwr.engine._batches[batchId]; 1159 if (!batch) return; 1160 1161 var handlers = batch.handlers[callId]; 1162 batch.handlers[callId].completed = true; 1163 if (handlers == null) { 1164 dwr.engine._debug("Warning: null handlers in remoteHandleException", true); 1165 return; 1166 } 1167 1168 if (ex.message === undefined) { 1169 ex.message = ""; 1170 } 1171 1172 dwr.engine.util.logHandlerEx(function() { 1173 if (typeof handlers.exceptionHandler == "function") { 1174 if (handlers.exceptionArg !== undefined) 1175 handlers.exceptionHandler.call(handlers.exceptionScope, ex.message, ex, handlers.exceptionArg); 1176 else 1177 handlers.exceptionHandler.call(handlers.exceptionScope, ex.message, ex); 1178 } 1179 else if (typeof handlers.errorHandler == "function") { 1180 handlers.errorHandler(ex.message, ex); 1181 } 1182 else if (typeof batch.errorHandler == "function") { 1183 batch.errorHandler(ex.message, ex); 1184 } 1185 else if (typeof batch.globalErrorHandler == "function") { 1186 batch.globalErrorHandler(ex.message, ex); 1187 } 1188 }); 1189 }, 1190 1191 /** 1192 * Called by the server when a new Reverse Ajax script arrives 1193 */ 1194 handleReverseAjax:function(reverseAjaxIndex, reverseAjaxFunc) {
1195 if (reverseAjaxIndex < dwr.engine._nextReverseAjaxIndex) return; 1196 dwr.engine._reverseAjaxQueue[reverseAjaxIndex] = reverseAjaxFunc; 1197 while(true) { 1198 var nextFunc = dwr.engine._reverseAjaxQueue[dwr.engine._nextReverseAjaxIndex]; 1199 if (!nextFunc) return; 1200 dwr.engine.util.logHandlerEx(function() { 1201 nextFunc(); 1202 }); 1203 delete dwr.engine._reverseAjaxQueue[dwr.engine._nextReverseAjaxIndex]; 1204 dwr.engine._nextReverseAjaxIndex++; 1205 } 1206 }, 1207 1208 /** 1209 * Called by the server: The whole batch is broken 1210 * @private 1211 * @param {Object} ex The data about what broke 1212 * @param {int} batchId The ID of the batch that we are replying to 1213 */ 1214 handleBatchException:function(ex, batchId) { 1215 var batch = dwr.engine._batches[batchId]; 1216 if (ex.message === undefined) ex.message = ""; 1217 if (batch) { 1218 dwr.engine._handleError(batch, ex); 1219 } else { 1220 dwr.engine._queuedBatchException = ex; 1221 } 1222 }, 1223 1224 /** 1225 * Called by the server: Reverse ajax should not be used 1226 * @private 1227 * @param {Object} ex 1228 * @param {int} batchId 1229 */ 1230 pollCometDisabled:function(ex, batchId){ 1231 dwr.engine.setActiveReverseAjax(false); 1232 var batch = dwr.engine._batches[batchId]; 1233 if (ex.message === undefined) { 1234 ex.message = ""; 1235 } 1236 dwr.engine._handleError(batch, ex); 1237 }, 1238 1239 /** 1240 * Called by the server: Create a new object of a mapped class 1241 * @private 1242 * @param {string} dwrClassName the name of the mapped class 1243 * @param {Object} memberMap the object's data members 1244 */ 1245 newObject:function(dwrClassName, memberMap){ 1246 var classfunc = dwr.engine._mappedClasses[dwrClassName]; 1247 if (classfunc && classfunc.createFromMap) { 1248 return classfunc.createFromMap(memberMap); 1249 } 1250 else { 1251 memberMap.$dwrClassName = dwrClassName; 1252 return memberMap; 1253 } 1254 } 1255 }; 1256 1257 /** 1258 * Functions to serialize a data set into a list of parameters 1259 */ 1260 dwr.engine.serialize = { 1261 /** 1262 * ActiveX objects to use when we want to convert an xml string into a DOM object 1263 */ 1264 domDocument:[ 1265 "Msxml2.DOMDocument.6.0", 1266 "Msxml2.DOMDocument.5.0", 1267 "Msxml2.DOMDocument.4.0", 1268 "Msxml2.DOMDocument.3.0", 1269 "MSXML2.DOMDocument", 1270 "MSXML.DOMDocument", 1271 "Microsoft.XMLDOM" 1272 ], 1273 1274 /** 1275 * A holder for functions that we have serialized for remote calling. 1276 */ 1277 remoteFunctions:{}, 1278 1279 /** 1280 * The ID of the next function that we serialize 1281 */ 1282 funcId:0, 1283 1284 /** 1285 * Convert a text representation of XML into a DOM element 1286 * @param {String} xml An xml string 1287 */ 1288 toDomElement:function(xml) { 1289 return dwr.engine.serialize.toDomDocument(xml).documentElement; 1290 }, 1291 1292 /** 1293 * Convert a text representation of XML into a DOM document 1294 * @param {String} xml An xml string 1295 */ 1296 toDomDocument:function(xml) { 1297 var dom; 1298 if (window.DOMParser) { 1299 var parser = new DOMParser(); 1300 dom = parser.parseFromString(xml, "text/xml"); 1301 if (!dom.documentElement || dom.documentElement.tagName == "parsererror") { 1302 var message = dom.documentElement.firstChild.data; 1303 message += "\n" + dom.documentElement.firstChild.nextSibling.firstChild.data; 1304 throw message; 1305 } 1306 return dom; 1307 } 1308 else if (window.ActiveXObject) { 1309 dom = dwr.engine.util.newActiveXObject(dwr.engine.serialize.domDocument); 1310 dom.loadXML(xml); // What happens on parse fail with IE? 1311 return dom; 1312 } 1313 else { 1314 var div = document.createElement("div"); 1315 div.innerHTML = xml; 1316 return div; 1317 } 1318 }, 1319 1320 /** 1321 * Marshall a data item 1322 * @private 1323 * @param batch A map of variables to how they have been marshalled 1324 * @param directrefmap A map of already marshalled variables to prevent recurrsion (these have direct links from their data object) 1325 * @param otherrefmap A map of already marshalled variables to prevent recursion (these have no links from data objects) 1326 * @param data The data to be marshalled 1327 * @param name The name of the data being marshalled 1328 */ 1329 convert:function(batch, directrefmap, otherrefmap, data, name, depth) { 1330 if (data == null) { 1331 batch.map[name] = "null:null"; 1332 return; 1333 } 1334 1335 switch (typeof data) { 1336 case "boolean": 1337 batch.map[name] = "boolean:" + data; 1338 break; 1339 case "number": 1340 batch.map[name] = "number:" + data; 1341 break; 1342 case "string": 1343 batch.map[name] = "string:" + encodeURIComponent(data); 1344 break; 1345 case "object": 1346 var ref = dwr.engine.serialize.lookup(directrefmap, otherrefmap, data, name); 1347 var objstr = Object.prototype.toString.call(data); 1348 if (data.$dwrByRef) batch.map[name] = dwr.engine.serialize.convertByReference(batch, directrefmap, otherrefmap, data, name, depth + 1); 1349 else if (ref != null) batch.map[name] = ref; 1350 else if (objstr == "[object String]") batch.map[name] = "string:" + encodeURIComponent(data); 1351 else if (objstr == "[object Boolean]") batch.map[name] = "boolean:" + data; 1352 else if (objstr == "[object Number]") batch.map[name] = "number:" + data; 1353 else if (objstr == "[object Date]") batch.map[name] = "date:" + data.getTime(); 1354 else if (objstr == "[object Array]") batch.map[name] = dwr.engine.serialize.convertArray(batch, directrefmap, otherrefmap, data, name, depth + 1); 1355 else if (data && data.tagName && data.tagName.toLowerCase() == "input" && data.type && data.type.toLowerCase() == "file") { 1356 batch.fileUpload = true; 1357 batch.map[name] = data; 1358 } 1359 else { 1360 // This check for an HTML is not complete, but is there a better way? 1361 // Maybe we should add: data.hasChildNodes typeof "function" == true 1362 if (data.nodeName && data.nodeType) { 1363 batch.map[name] = dwr.engine.serialize.convertDom(data); 1364 } 1365 else { 1366 batch.map[name] = dwr.engine.serialize.convertObject(batch, directrefmap, otherrefmap, data, name, depth + 1); 1367 } 1368 } 1369 break; 1370 case "function": 1371 // Ignore functions unless they are directly passed in 1372 if (depth === 0) { 1373 batch.map[name] = dwr.engine.serialize.convertByReference(batch, directrefmap, otherrefmap, data, name, depth + 1); 1374 } 1375 break; 1376 default: 1377 dwr.engine._handleWarning(null, { name:"dwr.engine.unexpectedType", message:"Unexpected type: " + typeof data + ", attempting default converter." }); 1378 batch.map[name] = "default:" + data; 1379 break; 1380 } 1381 }, 1382 1383 /** 1384 * Marshall an object by reference 1385 * @private 1386 * @see dwr.engine.serialize.convert() for parameter details 1387 */ 1388 convertByReference:function(batch, directrefmap, otherrefmap, data, name, depth) { 1389 var funcId = "f" + dwr.engine.serialize.funcId; 1390 dwr.engine.serialize.remoteFunctions[funcId] = data; 1391 dwr.engine.serialize.funcId++; 1392 return "byref:" + funcId; 1393 }, 1394 1395 /** 1396 * Marshall an array 1397 * @private 1398 * @see dwr.engine.serialize.convert() for parameter details 1399 */ 1400 convertArray:function(batch, directrefmap, otherrefmap, data, name, depth) { 1401 var childName, i; 1402 if (dwr.engine.util.ieCondition("if lte IE 7")) { 1403 // Use array joining on IE1-7 (fastest) 1404 var buf = ["array:["]; 1405 for (i = 0; i < data.length; i++) { 1406 if (i !== 0) buf.push(","); 1407 batch.paramCount++; 1408 childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount; 1409 dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[i], childName, depth + 1); 1410 buf.push("reference:"); 1411 buf.push(childName); 1412 } 1413 buf.push("]"); 1414 reply = buf.join(""); 1415 } 1416 else { 1417 // Use string concat on other browsers (fastest) 1418 var reply = "array:["; 1419 for (i = 0; i < data.length; i++) { 1420 if (i !== 0) reply += ","; 1421 batch.paramCount++; 1422 childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount; 1423 dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[i], childName, depth + 1); 1424 reply += "reference:"; 1425 reply += childName; 1426 } 1427 reply += "]"; 1428 } 1429 1430 return reply; 1431 }, 1432 1433 /** 1434 * Marshall an object by value 1435 * @private 1436 * @see dwr.engine.serialize.convert() for parameter details 1437 */ 1438 convertObject:function(batch, directrefmap, otherrefmap, data, name, depth) { 1439 // treat objects as an associative arrays 1440 var reply = "Object_" + dwr.engine.serialize.getObjectClassName(
1440data).replace(/:/g, "?") + ":{"; 1441 var elementset = (data.constructor && data.constructor.$dwrClassMembers ? data.constructor.$dwrClassMembers : data); 1442 var element; 1443 for (element in elementset) { 1444 if (typeof data[element] != "function" && !dwr.engine._excludeObjectAttributes[element]) { 1445 batch.paramCount++; 1446 var childName = "c" + dwr.engine._batch.map.callCount + "-e" + batch.paramCount; 1447 dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, data[element], childName, depth + 1); 1448 reply += encodeURIComponent(element) + ":reference:" + childName + ", "; 1449 } 1450 } 1451 1452 if (reply.substring(reply.length - 2) == ", ") { 1453 reply = reply.substring(0, reply.length - 2); 1454 } 1455 reply += "}"; 1456 1457 return reply; 1458 }, 1459 1460 /** 1461 * Marshall an object 1462 * @private 1463 * @see dwr.engine.serialize.convert() for parameter details 1464 */ 1465 convertDom:function(data) { 1466 return "xml:" + encodeURIComponent(dwr.engine.serialize.serializeDom(data)); 1467 }, 1468 serializeDom:function(data) { 1469 var output; 1470 if (window.XMLSerializer) output = new XMLSerializer().serializeToString(data); 1471 else if (data.xml) output = data.xml; 1472 else throw new Error("The browser doesn't support XML serialization of: " + data); 1473 return output; 1474 }, 1475 1476 /** 1477 * Have we already converted this object? 1478 * @private 1479 * @see dwr.engine.serialize.convert() for parameter details 1480 */ 1481 lookup:function(directrefmap, otherrefmap, data, name) { 1482 // Look for reference to previous conversion 1483 var ref; 1484 if ("$_dwrConversionRef" in data) { 1485 ref = data.$_dwrConversionRef; 1486 if (ref && directrefmap[ref] != data) ref = null; 1487 } 1488 if (ref == null) { 1489 for(r in otherrefmap) { 1490 if (otherrefmap[r] == data) { 1491 ref = r; 1492 break; 1493 } 1494 } 1495 } 1496 if (ref != null) return "reference:" + ref; 1497 // This was a new conversion - save the reference! 1498 try { 1499 data.$_dwrConversionRef = name; 1500 directrefmap[name] = data; 1501 } 1502 catch(err) { 1503 otherrefmap[name] = data; 1504 } 1505 return null; 1506 }, 1507 1508 /** 1509 * Clean up our conversion markers from user data 1510 * @private 1511 */ 1512 cleanup:function(directrefmap) { 1513 for(var refName in directrefmap) { 1514 var data = directrefmap[refName]; 1515 try { 1516 delete data.$_dwrConversionRef; 1517 } 1518 catch(err) { 1519 data.$_dwrConversionRef = undefined; 1520 } 1521 } 1522 }, 1523 1524 /** 1525 * Returns the classname of supplied argument obj. Similar to typeof, but 1526 * which returns the name of the constructor that created the object rather 1527 * than 'object' 1528 * @private 1529 * @param {Object} obj The object to detect the type of 1530 * @return The name of the object 1531 */ 1532 getObjectClassName:function(obj) { 1533 // Different handling depending on if, and what type of, class-mapping is used 1534 if (obj.$dwrClassName) 1535 return obj.$dwrClassName; // Light class-mapping uses the classname from a property on the instance 1536 else if (obj.constructor && obj.constructor.$dwrClassName) 1537 return obj.constructor.$dwrClassName; // Full class-mapping uses the classname from a property on the constructor function 1538 else 1539 return "Object"; 1540 } 1541 }; 1542 1543 /** 1544 * Functions to handle the various remoting transport 1545 */ 1546 dwr.engine.transport = { 1547 /** 1548 * Manage the DWR session and then send. 1549 * @private 1550 * @param {Object} batch 1551 */ 1552 send:function(batch) { 1553 if (batch.path == null) { 1554 batch.path = dwr.engine._effectivePath(); 1555 } 1556 dwr.engine.transport.updateDwrSessionFromCookie(); 1557 if (!dwr.engine._dwrSessionId) { 1558 dwr.engine._internalOrdered = true; 1559 var retval; 1560 var idbatch = { 1561 map:{ 1562 callCount:1, 1563 'c0-scriptName':'__System', 1564 'c0-methodName':'generateId', 1565 'c0-id':0 1566 }, 1567 paramCount:0, isPoll:false, async:batch.async, 1568 headers:{}, preHooks:[], 1569 postHooks:[function() { 1570 dwr.engine._internalOrdered = false;
1571 }], 1572 timeout:dwr.engine._timeout, 1573 errorHandler:batch.errorHandler, globalErrorHandler:batch.globalErrorHandler, warningHandler:batch.warningHandler, textHtmlHandler:batch.textHtmlHandler, globalTextHtmlHandler:batch.globalTextHtmlHandler, 1574 path:batch.path, 1575 handlers:[{ 1576 exceptionHandler:null, 1577 callback:function(id) { 1578 dwr.engine.transport.updateDwrSessionFromCookie(); 1579 if (!dwr.engine._dwrSessionId) { 1580 dwr.engine.transport.setDwrSession(id); 1581 } 1582 retval = dwr.engine.transport.send2(batch); 1583 } 1584 }] 1585 }; 1586 dwr.engine.transport.send2(idbatch); 1587 return retval; 1588 } 1589 else { 1590 return dwr.engine.transport.send2(batch); 1591 } 1592 }, 1593 1594 /** 1595 * Actually send the block of data in the batch object. 1596 * @private 1597 * @param {Object} batch 1598 */ 1599 send2:function(batch) { 1600 dwr.engine.batch.prepareToSend(batch); 1601 1602 // Work out if we are going cross domain 1603 var isCrossDomain = false; 1604 if (batch.path.indexOf("http://") === 0 || batch.path.indexOf("https://") === 0) { 1605 var dwrShortPath = batch.path.split("/", 3).join("/"); 1606 var hrefShortPath = window.location.href.split("/", 3).join("/"); 1607 isCrossDomain = (dwrShortPath != hrefShortPath); 1608 } 1609 1610 if (batch.fileUpload) { 1611 if (isCrossDomain) { 1612 throw new Error("Cross domain file uploads are not possible with this release of DWR"); 1613 } 1614 batch.transport = dwr.engine.transport.iframe; 1615 } 1616 else if (isCrossDomain) { 1617 batch.transport = dwr.engine.transport.scriptTag; 1618 } 1619 else if (batch.isPoll && dwr.engine._useStreamingPoll == "true" && dwr.engine.util.ieCondition("if (IE 8)|(IE 9)")) { 1620 batch.transport = dwr.engine.transport.iframe; 1621 } 1622 else { 1623 batch.transport = dwr.engine.transport.xhr; 1624 } 1625 1626 return batch.transport.send(batch); 1627 }, 1628 1629 /** 1630 * Called to signal that the batch response has been delivered 1631 */ 1632 complete:function(batch) { 1633 dwr.engine.batch.validate(batch); 1634 dwr.engine.transport.remove(batch); 1635 }, 1636 1637 /** 1638 * Called as a result of a request timeout 1639 * @private 1640 * @param {Object} batch The batch that is aborting 1641 */ 1642 abort:function(batch) { 1643 var transport = batch.transport; 1644 if (transport.abort) { 1645 transport.abort(batch); 1646 } 1647 dwr.engine.transport.remove(batch); 1648 }, 1649 1650 /** 1651 * Remove all remoting artifacts 1652 * @param {Object} batch The batch that has completed 1653 */ 1654 remove:function(batch) { 1655 if (batch.transport) { 1656 dwr.engine._callPostHooks(batch); 1657 batch.transport.remove(batch); 1658 batch.transport = null; 1659 } 1660 dwr.engine.batch.remove(batch); 1661 }, 1662 1663 setDwrSession:function(dwrsess) { 1664 dwr.engine._dwrSessionId = dwrsess; 1665 var attrs = ""; 1666 if (!dwr.engine._cookieAttributes.match(/^path=/i)) { 1667 attrs = "; path=" + (dwr.engine._effectiveContextPath() || "/"); 1668 } 1669 if (dwr.engine._cookieAttributes) { 1670 attrs += "; " + dwr.engine._cookieAttributes; 1671 } 1672 document.cookie = "DWRSESSIONID=" + dwrsess + attrs; 1673 dwr.engine._scriptSessionId = dwrsess + "/" + dwr.engine._pageId; 1674 }, 1675 1676 updateDwrSessionFromCookie:function() { 1677 if (!dwr.engine._dwrSessionId) { 1678 var match = document.cookie.match(/(?:^|; )DWRSESSIONID=([^;]+)/); 1679 if (match) { 1680 dwr.engine.transport.setDwrSession(match[1]); 1681 } 1682 } 1683 }, 1684 1685 /** 1686 * Remoting through XHR 1687 */ 1688 xhr:{ 1689 /** 1690 * The default HTTP method to use 1691 */ 1692 httpMethod:"POST", 1693 1694 /** 1695 * The ActiveX objects to use when we want to do an XMLHttpRequest call. 1696 * TODO: We arrived at this by trial and error. Other toolkits use 1697 * different strings, maybe there is an officially correct version? 1698 */ 1699 XMLHTTP:["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.5.0", "Msxml2.XMLHTTP.4.0", "MSXML2.XMLHTTP.3.0", "MSXML2.XMLHTTP", "Microsoft.XMLHTTP"], 1700 1701 /** 1702 * Setup a batch for transfer through XHR 1703 * @param {Object} batch The batch to alter for XHR transmit 1704 */ 1705 send:function(batch) { 1706 if (batch.isPoll) { 1707 batch.map.partialResponse = dwr.engine._partialResponseYes; 1708 } 1709 1710 // Are we told to use early closing mode? 1711 if (batch.isPoll && dwr.engine._useStreamingPoll == "false") { 1712 batch.map.partialResponse = dwr.engine._partialResponseNo; 1713 } 1714 if (batch.isPoll && dwr.engine.util.ieCondition("if lte IE 9")) { 1715 batch.map.partialResponse = dwr.engine._partialResponseNo; 1716 } 1717 1718 if (window.XMLHttpRequest) { 1719 batch.req = new XMLHttpRequest(); 1720 } 1721 else if (window.ActiveXObject) {
1722 batch.req = dwr.engine.util.newActiveXObject(dwr.engine.transport.xhr.XMLHTTP); 1723 } 1724 1725 // Proceed using XMLHttpRequest 1726 if (batch.async === true) { 1727 batch.req.onreadystatechange = function() { 1728 if (typeof dwr != 'undefined') { 1729 dwr.engine.transport.xhr.stateChange(batch); 1730 } 1731 }; 1732 } 1733 1734 httpMethod = dwr.engine.transport.xhr.httpMethod; 1735 1736 batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall; 1737 var request = dwr.engine.batch.constructRequest(batch, httpMethod); 1738 1739 try { 1740 batch.req.open(httpMethod, request.url, batch.async); 1741 try { 1742 for (var prop in batch.headers) { 1743 var value = batch.headers[prop]; 1744 if (typeof value == "string") { 1745 batch.req.setRequestHeader(prop, value); 1746 } 1747 } 1748 if (!batch.headers["Content-Type"]) { 1749 batch.req.setRequestHeader("Content-Type", "text/plain"); 1750 } 1751 } 1752 catch (ex) { 1753 dwr.engine._handleWarning(batch, ex); 1754 } 1755 1756 batch.req.send(request.body); 1757 if (batch.async === false) { 1758 dwr.engine.transport.xhr.stateChange(batch); 1759 } 1760 } 1761 catch (ex2) { 1762 dwr.engine._handleError(batch, ex2); 1763 } 1764 1765 if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) { 1766 dwr.engine.transport.xhr.checkCometPoll(); 1767 } 1768 1769 // This is only of any use in sync mode to return the reply data 1770 return batch.reply; 1771 }, 1772 1773 /** 1774 * Called by XMLHttpRequest to indicate that something has happened 1775 * @private 1776 * @param {Object} batch The current remote operation 1777 */ 1778 stateChange:function(batch) { 1779 var toEval; 1780 1781 if (batch.aborted) return; 1782 if (batch.completed) { 1783 if (batch.transport) dwr.engine._debug("Error: _stateChange() with batch.completed"); 1784 return; 1785 } 1786 1787 // Try to get the response HTTP status if applicable 1788 var req = batch.req; 1789 var status = 0; 1790 try { 1791 if (req.readyState >= 2) { 1792 status = req.status; // causes Mozilla to except on page moves 1793 } 1794 } 1795 catch(ignore) {} 1796 1797 // If we couldn't get the status we bail out, unless the request is 1798 // complete, which means error (handled further below) 1799 if (status === 0 && req.readyState < 4) { 1800 return; 1801 } 1802 1803 // If the status is 200, we are now online. 1804 // Future improvement per Mike W. - A solution where we only use the callbacks/handlers of the poll call to trigger 1805 // the retry handling would be ideal. We would need something like a new internal callback that reports 1806 // progress back to the caller, and the design should be compatible with getting it to work with iframes as well. 1807 if (status === 200) { 1808 dwr.engine._handlePollStatusChange(true, null, batch); 1809 } 1810 1811 // The rest of this function only deals with request completion 1812 if (req.readyState != 4) { 1813 return; 1814 } 1815 1816 if (dwr.engine._unloading) { 1817 dwr.engine._debug("Ignoring reply from server as page is unloading."); 1818 return; 1819 } 1820 1821 try { 1822 var reply = req.responseText; 1823 reply = dwr.engine._replyRewriteHandler(reply); 1824 var contentType = req.getResponseHeader("Content-Type"); 1825 if (status >= 200 && status < 300) { 1826 if (contentType.indexOf("text/plain") < 0 && contentType.indexOf("text/javascript") < 0) { 1827 if (contentType.indexOf("text/html") == 0) { 1828 dwr.engine._handleError(batch, { name:"dwr.engine.textHtmlReply", message:"HTML reply from the server.", responseText:reply||"" }); 1829 } else { 1830 dwr.engine._handleError(batch, { name:"dwr.engine.invalidMimeType", message:"Invalid content type: '" + contentType + "'" }); 1831 } 1832 } else if (reply == null || reply === "") { 1833 dwr.engine._handleError(batch, { name:"dwr.engine.missingData", message:"No data received from server" }); 1834 } else { 1835 if (batch.isPoll && batch.map.partialResponse == dwr.engine._partialResponseYes) { 1836 dwr.engine.transport.xhr.processCometResponse(reply, batch); 1837 } else { 1838 if (reply.search("//#DWR") === -1) { 1839 dwr.engine._handleError(batch, { name:"dwr.engine.invalidReply", message:"Invalid reply from server" }); 1840 } else { 1841 toEval = reply; 1842 } 1843 } 1844 } 1845 } else if (status >= 400) { 1846 var statusText = "statusText could not be read."; 1847 try {
1848 statusText = req.statusText; 1849 } catch (ex) { 1850 // Eat this, if the server just went down an exception can occur reading statusText. 1851 } 1852 dwr.engine._handleError(batch, { name:"dwr.engine.http." + status, message:statusText }); 1853 } 1854 1855 if (toEval != null) toEval = toEval.replace(dwr.engine._scriptTagProtection, ""); 1856 dwr.engine._executeScript(toEval); 1857 dwr.engine.transport.complete(batch); 1858 1859 } catch (ex2) { 1860 dwr.engine._handleError(batch, ex2); 1861 } 1862 }, 1863 1864 /** 1865 * Check for reverse Ajax activity 1866 * @private 1867 */ 1868 checkCometPoll:function() { 1869 var req = dwr.engine._pollBatch && dwr.engine._pollBatch.req; 1870 if (req) { 1871 var text = req.responseText; 1872 if (text != null) { 1873 dwr.engine.transport.xhr.processCometResponse(text, dwr.engine._pollBatch); 1874 } 1875 } 1876 // If the poll resources are still there, come back again 1877 if (dwr.engine._pollBatch) { 1878 setTimeout(dwr.engine.transport.xhr.checkCometPoll, dwr.engine._pollCometInterval); 1879 } 1880 }, 1881 1882 /** 1883 * Some more text might have come in, test and execute the new stuff. 1884 * This method could also be called by the iframe transport 1885 * @private 1886 * @param {Object} response from xhr.responseText 1887 * @param {Object} batch The batch that the XHR object pertains to 1888 */ 1889 processCometResponse:function(response, batch) { 1890 if (batch.charsProcessed == response.length) return; 1891 if (response.length === 0) { 1892 batch.charsProcessed = 0; 1893 return; 1894 } 1895 1896 var firstStartTag = response.indexOf("//#DWR-START#", batch.charsProcessed); 1897 if (firstStartTag == -1) { 1898 // dwr.engine._debug("No start tag (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed) + "'"); 1899 return; 1900 } 1901 // if (firstStartTag > 0) { 1902 // dwr.engine._debug("Start tag not at start (search from " + batch.charsProcessed + "). skipping '" + response.substring(batch.charsProcessed, firstStartTag) + "'"); 1903 // } 1904 1905 var lastEndTag = response.lastIndexOf("//#DWR-END#"); 1906 if (lastEndTag == -1) { 1907 // dwr.engine._debug("No end tag. unchanged charsProcessed=" + batch.charsProcessed); 1908 return; 1909 } 1910 1911 // Skip the end tag too for next time, remembering CR and LF 1912 if (response.charCodeAt(lastEndTag + 11) == 13 && response.charCodeAt(lastEndTag + 12) == 10) { 1913 batch.charsProcessed = lastEndTag + 13; 1914 } 1915 else { 1916 batch.charsProcessed = lastEndTag + 11; 1917 } 1918 1919 var exec = response.substring(firstStartTag + 13, lastEndTag); 1920 1921 try { 1922 dwr.engine._executeScript(exec); 1923 } 1924 catch (ex) { 1925 // This is one of these annoying points where we might be executing 1926 // while the window is being destroyed. If dwr == null, bail out. 1927 if (dwr != null) { 1928 dwr.engine._handleError(batch, ex); 1929 } 1930 } 1931 }, 1932 1933 /** 1934 * Aborts ongoing request (for timeouts etc) 1935 */ 1936 abort:function(batch) { 1937 if (batch.req) { 1938 batch.aborted = true; 1939 batch.req.abort(); 1940 } 1941 }, 1942 1943 /** 1944 * Tidy-up when an XHR call is done 1945 * @param {Object} batch 1946 */ 1947 remove:function(batch) { 1948 // XHR tidyup: avoid IE handles increase 1949 if (batch.req) { 1950 delete batch.req; 1951 } 1952 } 1953 }, 1954 1955 /** 1956 * Functions for remoting through IFrame 1957 */ 1958 iframe:{ 1959 /** 1960 * The default HTTP method to use 1961 */ 1962 httpMethod:"POST", 1963 1964 /** 1965 * Setup a batch for transfer through IFrame 1966 * @param {Object} batch The batch to alter for IFrame transmit 1967 */ 1968 send:function(batch) { 1969 if (document.body == null) { 1970 setTimeout(function(){dwr.engine.transport.iframe.send(batch);}, 100); 1971 return; 1972 } 1973 batch.httpMethod = dwr.engine.transport.iframe.httpMethod; 1974 if (batch.fileUpload) { 1975 batch.httpMethod = "POST"; 1976 batch.encType = "multipart/form-data"; 1977 } 1978 batch.loadingStarted = false;
1979 if (window.location.hostname != document.domain) batch.map.documentDomain = document.domain; 1980 var idname = dwr.engine.transport.iframe.getId(batch); 1981 batch.div1 = document.createElement("div"); 1982 batch.div1.innerHTML = "<iframe frameborder='0' style='width:0px;height:0px;border:0;display:none;' id='" + idname + "' name='" + idname + "'></iframe>"; 1983 batch.iframe = batch.div1.firstChild; 1984 batch.document = document; 1985 batch.iframe.batch = batch; 1986 batch.fileInputs = []; 1987 dwr.engine.util.addEventListener(batch.iframe, "load", function(ev) { 1988 // Bail out if this is a premature load event (IE) 1989 if (!batch.loadingStarted) return; 1990 // Put back file inputs 1991 while(batch.fileInputs.length > 0) { 1992 var entry = batch.fileInputs.pop(); 1993 entry.original.setAttribute("id", entry.clone.getAttribute("id")); 1994 entry.original.setAttribute("name", entry.clone.getAttribute("name")); 1995 entry.original.style.display = entry.clone.style.display; 1996 entry.clone.parentNode.replaceChild(entry.original, entry.clone); 1997 } 1998 // Bail out if the batch was completed the normal way 1999 if (batch.completed) return; 2000 // Check for a queued batch exception 2001 if (dwr.engine._queuedBatchException) { 2002 dwr.engine._handleError(batch, dwr.engine._queuedBatchException); 2003 dwr.engine._queuedBatchException = null; 2004 return; 2005 } 2006 // The batch hasn't been completed the normal way, trigger a textHtmlReply error 2007 try { 2008 var contentDoc = batch.iframe.contentDocument || batch.iframe.contentWindow.document; 2009 } catch(ex) { 2010 var contentEx = ex; // may be caused by a HTML error page reply when document.domain is used 2011 } 2012 if (typeof dwr != "undefined") { 2013 var htmlResponse = contentDoc && contentDoc.documentElement ? contentDoc.documentElement.outerHTML : "(Could not extract HTML response: " + contentEx.message + ")"; 2014 dwr.engine._handleError(batch, {name:"dwr.engine.textHtmlReply", message:"HTML reply from the server.", responseText:htmlResponse}); 2015 } 2016 }); 2017 // This needs to be after the event listener is added, for IE. 2018 // http://stackoverflow.com/questions/18414964/load-event-for-iframe-does-not-fire-in-ie 2019 document.body.appendChild(batch.div1); 2020 dwr.engine.transport.iframe.beginLoader(batch, idname); 2021 }, 2022 2023 /** 2024 * Create a unique ID so multiple iframes can fire at the same time 2025 * @param {Object} batch A source of a unique number for the batch 2026 * @return {String} a name prefix for created elements 2027 */ 2028 getId:function(batch) { 2029 return batch.isPoll ? "dwr-if-poll-" + dwr.engine._instanceId + "-" + batch.map.batchId : "dwr-if-" + dwr.engine._instanceId + "-" + batch.map.batchId; 2030 }, 2031 2032 /** 2033 * Setup a form or construct a src attribute to use the iframe. 2034 * @param {Object} batch 2035 */ 2036 beginLoader:function(batch, idname) { 2037 if (batch.isPoll) { 2038 batch.map.partialResponse = dwr.engine._partialResponseYes; 2039 } 2040 batch.mode = batch.isPoll ? dwr.engine._ModeHtmlPoll : dwr.engine._ModeHtmlCall; 2041 var request = dwr.engine.batch.constructRequest(batch, batch.httpMethod); 2042 if (batch.httpMethod === "GET") { 2043 batch.iframe.setAttribute("src", request.url); 2044 } 2045 else { 2046 // TODO: On firefox we can now get the values of file fields, maybe we should use this 2047 // See http://soakedandsoaped.com/articles/read/firefox-3-native-ajax-file-upload 2048 // setting enctype via the DOM does not work in IE, create the form using innerHTML instead 2049 batch.div2 = document.createElement("div"); 2050 document.body.appendChild(batch.div2); 2051 batch.div2.innerHTML = "<form" + (batch.encType ? " encType='" + batch.encType + "' encoding='" + batch.encType + "'" : "") + "></form>"; 2052 batch.form = batch.div2.firstChild; 2053 batch.form.setAttribute("action", request.url); 2054 batch.form.setAttribute("target", idname); 2055 batch.form.setAttribute("style", "display:none"); 2056 batch.form.setAttribute("method", batch.httpMethod); 2057 for (var prop in batch.map) { 2058 var value = batch.map[prop]; 2059 if (typeof value != "function") { 2060 if (value && value.tagName && value.tagName.toLowerCase() == "input" && value.type && value.type.toLowerCase() == "file") { 2061 // Since we can not set the value of a file object, we must post the actual file object 2062 // that the user clicked browse on. We will put a clone in it's place that we will restore later. 2063 // (note that the clone doesn't retain the selected file in most browsers) 2064 if (value.parentNode) { 2065 var clone = value.cloneNode(true); 2066 value.parentNode.replaceChild(clone, value); 2067 batch.fileInputs.push({original:value, clone:clone}); 2068 } 2069 value.removeAttribute("id"); 2070 value.setAttribute("name", prop); 2071 value.style.display = "none"; 2072 batch.form.appendChild(value); 2073 } else { 2074 var formInput = batch.document.createElement("input"); 2075 formInput.setAttribute("type", "hidden"); 2076 formInput.setAttribute("name", prop); 2077 formInput.setAttribute("value", value); 2078 batch.form.appendChild(formInput); 2079 } 2080 } 2081 } 2082 batch.loadingStarted = true; 2083 batch.form.submit(); 2084 } 2085 }, 2086 2087 /** 2088 * Functions designed to be called by the server 2089 */ 2090 remote:{ 2091 /** 2092 * Called by the server: An IFrame reply is about to start 2093 * @private 2094 * @param {Object} iframe 2095 * @param {int} batchId 2096 */ 2097 beginIFrameResponse:function(iframe, batchId) { 2098 }, 2099 2100 /** 2101 * Called by the server: An IFrame script tag has completed 2102 * @private 2103 * @param {Object} iframe 2104 */ 2105 endChunk:function(iframeWindow) { 2106 setTimeout(function() { 2107 // Delete the first script tag in the iframe 2108 var scriptTags = iframeWindow.document.body.getElementsByTagName("script"); 2109 if (scriptTags.length > 0) { 2110 var s = scriptTags[0]; 2111 s.parentNode.removeChild(s); 2112 } 2113 }, 0); 2114 }, 2115 2116 /** 2117 * Called by the server: An IFrame reply is just completing 2118 * @private 2119 * @param {int} batchId 2120 */ 2121 endIFrameResponse:function(batchId) { 2122 var batch = dwr.engine._batches[batchId]; 2123 if (batch) dwr.engine.transport.complete(batch); 2124 } 2125 }, 2126 2127 remove:function(batch) { 2128 // Safari 3 and Chrome 1 will show endless loading spinner if removing 2129 // iframe during execution of iframe script, so we delay it a bit 2130 setTimeout(function(){ 2131 if (batch.iframe && batch.iframe.parentNode) { 2132 batch.iframe.parentNode.removeChild(batch.iframe); 2133 batch.iframe = null; 2134 } 2135 if (batch.div1 && batch.div1.parentNode) { 2136 batch.div1.parentNode.removeChild(batch.div1); 2137 batch.div1 = null; 2138 } 2139 if (batch.form && batch.form.parentNode) { 2140 batch.form.parentNode.removeChild(batch.form); 2141 batch.form = null; 2142 } 2143 if (batch.div2 && batch.div2.parentNode) { 2144 batch.div2.parentNode.removeChild(batch.div2); 2145 batch.div2 = null; 2146 } 2147 }, 100); 2148 } 2149 }, 2150 2151 /** 2152 * Functions for remoting through Script Tags 2153 */ 2154 scriptTag:{ 2155 /** 2156 * Setup a batch for transfer through a script tag 2157 * @param {Object} batch The batch to alter for script tag transmit 2158 */ 2159 send:function(batch) { 2160 if (batch.isPoll) { 2161 batch.map.partialResponse = dwr.engine._partialResponseNo; 2162 } 2163 batch.mode = batch.isPoll ? dwr.engine._ModePlainPoll : dwr.engine._ModePlainCall; 2164 var request = dwr.engine.batch.constructRequest(batch, "GET"); 2165 // The best option is DOM manipulation 2166 batch.script = document.createElement("script"); 2167 batch.script.id = "dwr-st-" + batch.map.batchId; 2168 batch.script.src = request.url; 2169 batch.script.type = "text/javascript"; 2170 batch.script.async = true; 2171 dwr.engine.util.addEventListener(batch.script, "load", function(ev) { 2172 if (typeof dwr != "undefined") dwr.engine.transport.scriptTag.complete(batch); 2173 }); 2174 dwr.engine.util.addEventListener(batch.script, "error", function(ev) { 2175 if (typeof dwr != "undefined") dwr.engine.transport.scriptTag.complete(batch); 2176 }); 2177 dwr.engine.util.addEventListener(batch.script, "readystatechange", function(ev) { 2178 if (typeof dwr != "undefined") {
2179 if (batch.completed) return; 2180 if (batch.script.readyState == "complete" || batch.script.readyState == "loaded") { 2181 dwr.engine.transport.scriptTag.complete(batch); 2182 } 2183 } 2184 }); 2185 document.getElementsByTagName("head")[0].appendChild(batch.script); 2186 }, 2187 2188 /** 2189 * Notified when the script tag has been loaded and executed 2190 */ 2191 complete:function(batch) { 2192 dwr.engine.transport.complete(batch); 2193 }, 2194 2195 /** 2196 * Cleanup script tag 2197 */ 2198 remove:function(batch) { 2199 // Bail out if we were already called 2200 if (!batch.script) return; 2201 2202 // Cleanup script tag 2203 batch.script.parentNode.removeChild(batch.script); 2204 batch.script = null; 2205 } 2206 } 2207 }; 2208 2209 /** 2210 * Functions to manipulate batches 2211 * @private 2212 */ 2213 dwr.engine.batch = { 2214 /** 2215 * Generate a new standard batch 2216 * @private 2217 */ 2218 create:function() { 2219 var batch = { 2220 async:dwr.engine._async, 2221 charsProcessed:0, 2222 handlers:[], 2223 isPoll:false, 2224 map:{ callCount:0 }, 2225 paramCount:0, 2226 preHooks:[], 2227 postHooks:[], 2228 timeout:dwr.engine._timeout, 2229 errorHandler:null, 2230 globalErrorHandler:dwr.engine._errorHandler, 2231 warningHandler:dwr.engine._warningHandler, 2232 textHtmlHandler:null, 2233 globalTextHtmlHandler:dwr.engine._textHtmlHandler 2234 }; 2235 if (!dwr.engine._activeReverseAjax) batch.map.nextReverseAjaxIndex = dwr.engine._nextReverseAjaxIndex; 2236 2237 if (dwr.engine._preHook) { 2238 batch.preHooks.push(dwr.engine._preHook); 2239 } 2240 if (dwr.engine._postHook) { 2241 batch.postHooks.push(dwr.engine._postHook); 2242 } 2243 2244 dwr.engine.batch.populateHeadersAndAttributes(batch); 2245 return batch; 2246 }, 2247 2248 /** 2249 * Generate a new batch for polling 2250 * @private 2251 * @see dwr.engine.batch.create() 2252 */ 2253 createPoll:function() { 2254 var batch = { 2255 async:true, 2256 charsProcessed:0, 2257 handlers:[{ 2258 callback:function(pause) { 2259 dwr.engine._pollBatch = null; 2260 setTimeout(dwr.engine._poll, pause); 2261 } 2262 }], 2263 isPoll:true, 2264 map:{ callCount:1, nextReverseAjaxIndex:dwr.engine._nextReverseAjaxIndex}, 2265 paramCount:0, 2266 preHooks:[], 2267 postHooks:[], 2268 timeout:dwr.engine._pollTimeout 2269 }; 2270 dwr.engine.batch.populateHeadersAndAttributes(batch); 2271 return batch; 2272 }, 2273 2274 /** 2275 * Copy the global headers and parameters into this batch object 2276 * @private 2277 * @param {Object} batch The destination 2278 */ 2279 populateHeadersAndAttributes:function(batch) { 2280 var propname, data; 2281 batch.headers = {}; 2282 if (dwr.engine._headers) { 2283 for (propname in dwr.engine._headers) { 2284 data = dwr.engine._headers[propname]; 2285 if (typeof data != "function") batch.headers[propname] = data; 2286 } 2287 } 2288 batch.attributes = {}; // props to add as request attributes 2289 if (dwr.engine._attributes) { 2290 for (propname in dwr.engine._attributes) { 2291 data = dwr.engine._attributes[propname]; 2292 if (typeof data != "function") batch.attributes[propname] = data; 2293 } 2294 } 2295 }, 2296 2297 /** 2298 * Augment this batch with a new call 2299 * @private 2300 */ 2301 addCall:function(batch, scriptName, methodName, args) { 2302 // From the other params, work out which is the function (or object with 2303 // call meta-data) and which is the call parameters 2304 var callData, stopAt; 2305 var lastArg = args[args.length - 1]; 2306 if (lastArg == null || typeof lastArg == "function") { 2307 callData = { callback:lastArg }; 2308 stopAt = args.length - 1; 2309 } 2310 else if (dwr.engine.util.isCallOptionArgument(lastArg)) { 2311 callData = lastArg; 2312 stopAt = args.length - 1; 2313 } 2314 else { 2315 callData = {}; 2316 stopAt = args.length; 2317 } 2318 2319 // Merge from the callData into the batch 2320 if (dwr.engine._singleShot) dwr.engine.batch.merge(batch, callData); 2321 batch.handlers[batch.map.callCount] = { 2322 callback:callData.callbackHandler || callData.callback, 2323 callbackArg:callData.callbackArg || callData.arg, 2324 callbackScope:callData.callbackScope || callData.scope || window, 2325 exceptionHandler:callData.exceptionHandler, 2326 exceptionArg:callData.exceptionArg || callData.arg, 2327 exceptionScope:callData.exceptionScope || callData.scope || window, 2328 errorHandler:callData.errorHandler 2329 }; 2330 2331 // Copy to the map the things that need serializing 2332 var prefix = "c" + batch.map.callCount + "-"; 2333 batch.map[prefix + "scriptName"] = scriptName; 2334 batch.map[prefix + "methodName"] = methodName; 2335 batch.map[prefix + "id"] = batch.map.callCount; 2336 var directrefmap = {}, otherrefmap = {}; 2337 for (var i = 0; i < stopAt; i++) { 2338 dwr.engine.serialize.convert(batch, directrefmap, otherrefmap, args[i], prefix + "param" + i, 0); 2339 } 2340 dwr.engine.serialize.cleanup(directrefmap); 2341 }, 2342 2343 /** 2344 * Take further options and merge them into a batch 2345 * @private 2346 * @param {Object} batch The batch that we are altering 2347 * @param {Object}
2347 overrides The object containing properties to copy into batch 2348 */ 2349 merge:function(batch, overrides) { 2350 var propname, data; 2351 for (var i = 0; i < dwr.engine._propnames.length; i++) { 2352 propname = dwr.engine._propnames[i]; 2353 if (dwr.engine._singleShot && propname == "errorHandler") continue; 2354 if (overrides[propname] != null) batch[propname] = overrides[propname]; 2355 } 2356 if (overrides.preHook != null) batch.preHooks.unshift(overrides.preHook); 2357 if (overrides.postHook != null) batch.postHooks.push(overrides.postHook); 2358 if (overrides.headers) { 2359 for (propname in overrides.headers) { 2360 data = overrides.headers[propname]; 2361 if (typeof data != "function") batch.headers[propname] = data; 2362 } 2363 } 2364 var attrs = null; 2365 if (overrides.attributes) attrs = overrides.attributes; 2366 if (attrs) { 2367 for (propname in attrs) { 2368 data = attrs[propname]; 2369 if (typeof data != "function") batch.attributes[propname] = data; 2370 } 2371 } 2372 }, 2373 2374 /** 2375 * Executed just before a transport sends the batch 2376 * @private 2377 * @param {Object} batch The batch to prepare for sending 2378 */ 2379 prepareToSend:function(batch) { 2380 batch.map.batchId = dwr.engine._nextBatchId; 2381 dwr.engine._nextBatchId++; 2382 dwr.engine._batches[batch.map.batchId] = batch; 2383 dwr.engine._batchesLength++; 2384 batch.completed = false; 2385 batch.map.instanceId = dwr.engine._instanceId; 2386 2387 // security details are filled in late so previous batches have completed 2388 batch.map.page = encodeURIComponent(window.location.pathname + window.location.search); 2389 batch.map.scriptSessionId = dwr.engine._scriptSessionId; 2390 2391 for (var i = 0; i < batch.preHooks.length; i++) { 2392 batch.preHooks[i](); 2393 } 2394 batch.preHooks = null; 2395 // Set a timeout 2396 if (batch.timeout && batch.timeout !== 0) { 2397 batch.timeoutId = setTimeout(function() { 2398 dwr.engine.transport.abort(batch); 2399 dwr.engine._handleError(batch, { name:"dwr.engine.timeout", message:"Timeout" }); 2400 }, batch.timeout); 2401 } 2402 }, 2403 2404 /** 2405 * Work out what the URL should look like 2406 * @private 2407 * @param {Object} batch the data that we are sending 2408 * @param {Object} httpMethod Are we using GET/POST etc? 2409 */ 2410 constructRequest:function(batch, httpMethod) { 2411 // A quick string to help people that use web log analysers 2412 var urlBuffer = []; 2413 urlBuffer.push(batch.path); 2414 urlBuffer.push(batch.mode); 2415 if (batch.isPoll) { 2416 urlBuffer.push("ReverseAjax.dwr"); 2417 } 2418 else if (batch.map.callCount == 1) { 2419 urlBuffer.push(batch.map["c0-scriptName"]); 2420 urlBuffer.push("."); 2421 urlBuffer.push(batch.map["c0-methodName"]); 2422 urlBuffer.push(".dwr"); 2423 } 2424 else { 2425 urlBuffer.push("Multiple."); 2426 urlBuffer.push(batch.map.callCount); 2427 urlBuffer.push(".dwr"); 2428 } 2429 // Play nice with url re-writing 2430 var sessionMatchExpr = new RegExp( 2431 "^" + // start of string 2432 "[^;\\?#]+" + // protocol, host, and path (up to first of ; ? or #) 2433 "(;[^\\?#]+)"); // group 1: ; and sessionid (up to first of ? or #) 2434 var sessionMatch = location.href.match(sessionMatchExpr); 2435 if (sessionMatch != null) { 2436 urlBuffer.push(sessionMatch[1]); 2437 } 2438 2439 // Add attributes to the output map 2440 if (batch.attributes) { 2441 for (var attrname in batch.attributes) { 2442 var data = batch.attributes[attrname]; 2443 if (typeof data != "function") batch.map["a-" + attrname] = "" + data; 2444 } 2445 } 2446 2447 var request = {}; 2448 var prop; 2449 if (httpMethod == "GET") { 2450 // Some browsers (Opera/Safari2) seem to fail to convert the callCount value 2451 // to a string in the loop below so we do it manually here. 2452 batch.map.callCount = "" + batch.map.callCount; 2453 urlBuffer.push("?"); 2454 for (prop in batch.map) { 2455 if (typeof batch.map[prop] != "function") { 2456 urlBuffer.push(encodeURIComponent(prop)); 2457 urlBuffer.push("="); 2458 urlBuffer.push(encodeURIComponent(batch.map[prop])); 2459 urlBuffer.push("&"); 2460 } 2461 } 2462 urlBuffer.pop(); // remove the trailing & 2463 request.body = null; 2464 } 2465 else { 2466 // PERFORMANCE: for iframe mode this is thrown away. 2467 request.body = ""; 2468 if (dwr.engine.util.ieCondition("if lte IE 7")) { 2469 // Use array joining on IE1-7 (fastest) 2470 var buf = []; 2471 for (prop in batch.map) { 2472 if (typeof batch.map[prop] != "function") { 2473 buf.push(prop + "=" + batch.map[prop] + dwr.engine._postSeperator); 2474 } 2475 } 2476 request.body = buf.join(""); 2477 } 2478 else { 2479 // Use string concat on other browsers (fastest) 2480 for (prop in batch.map) { 2481 if (typeof batch.map[prop] != "function") { 2482 request.body += prop + "=" + batch.map[prop] + dwr.engine._postSeperator; 2483 } 2484 } 2485 } 2486 request.body = dwr.engine._contentRewriteHandler(request.body); 2487 } 2488 request.url = dwr.engine._urlRewriteHandler(urlBuffer.join("")); 2489 return request; 2490 }, 2491 2492 /** 2493 * @private This function is invoked when a batch reply is received. 2494 * It checks that there is a response for every call in the batch. Otherwise, 2495 * an error will be signaled (a call without a response indicates that the 2496 * server failed to send complete batch response). 2497 */ 2498 validate:function(batch) { 2499 if (!batch.completed) { 2500 var repliesReceived = 0; 2501 for (var i = 0; i < batch.map.callCount; i++) { 2502 if (batch.handlers[i].completed === true) { 2503 repliesReceived++; 2504 } 2505 } 2506 if (repliesReceived == 0 && dwr.engine._queuedBatchException) { 2507 dwr.engine._handleError(batch, dwr.engine._queuedBatchException); 2508 dwr.engine._queuedBatchException = null; 2509 } else if (repliesReceived < batch.map.callCount) { 2510 dwr.engine._handleError(batch, { name:"dwr.engine.incompleteReply", message:"Incomplete reply from server" }); 2511 } 2512 } 2513 }, 2514 2515 /** 2516 * A call has finished by whatever means and we need to shut it all down. 2517 * @private 2518 * @param {Object} batch The batch that we are altering 2519 */ 2520 remove:function(batch) { 2521 if (!batch) { 2522 dwr.engine._debug("Warning: null batch in dwr.engine.batch.remove()", true); 2523 return; 2524 } 2525
2526 if (batch.completed) { 2527 return; 2528 } 2529 batch.completed = true; 2530 2531 // Transport tidyup 2532 dwr.engine.transport.remove(batch); 2533 2534 // Timeout tidyup 2535 if (batch.timeoutId != null) { 2536 clearTimeout(batch.timeoutId); 2537 delete batch.timeoutId; 2538 } 2539 2540 // TODO: co-locate all the functions that work on a set of batches 2541 if (batch.map && (batch.map.batchId != null)) { 2542 delete dwr.engine._batches[batch.map.batchId]; 2543 dwr.engine._batchesLength--; 2544 } 2545 if (batch == dwr.engine._batch) dwr.engine._batch = null; 2546 if (batch == dwr.engine._pollBatch) dwr.engine._pollBatch = null; 2547 2548 // If there is anything on the queue waiting to go out, then send it. 2549 // We don't need to check for ordered mode, here because when ordered mode 2550 // gets turned off, we still process *waiting* batches in an ordered way. 2551 if (dwr.engine._batchQueue.length !== 0) { 2552 var sendbatch = dwr.engine._batchQueue.shift(); 2553 dwr.engine.transport.send(sendbatch); 2554 } 2555 } 2556 }; 2557 2558 /** 2559 * Various utility functions 2560 * @private 2561 */ 2562 dwr.engine.util = { 2563 /** 2564 * Create one of a number of ActiveX strings 2565 * @param {Object} axarray An array of strings to attempt to create ActiveX objects from 2566 */ 2567 newActiveXObject:function(axarray) { 2568 var returnValue; 2569 for (var i = 0; i < axarray.length; i++) { 2570 try { 2571 returnValue = new ActiveXObject(axarray[i]); 2572 break; 2573 } 2574 catch (ex) { /* ignore */ } 2575 } 2576 return returnValue; 2577 }, 2578 2579 ieCondition:function(cond) { 2580 if (!(cond in dwr.engine._ieConditions)) { 2581 var div = document.createElement("div"); 2582 div.innerHTML = "<!--[" + cond + "]><p><![endif]-->"; 2583 dwr.engine._ieConditions[cond] = !!(div.getElementsByTagName("p").length); 2584 } 2585 return dwr.engine._ieConditions[cond]; 2586 }, 2587 2588 /** 2589 * Transform a number into a token string suitable for ids 2590 */ 2591 tokenify: function(number) { 2592 var tokenbuf = []; 2593 var charmap = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*$"; 2594 var remainder = number; 2595 while (remainder > 0) 2596 { 2597 tokenbuf.push(charmap.charAt(remainder & 0x3F)); 2598 remainder = Math.floor(remainder / 64); // Can't use shift operator due to 32-bit limit in JS 2599 } 2600 return tokenbuf.join(""); 2601 }, 2602 2603 addEventListener: function(elem, name, func) { 2604 if (elem.addEventListener) 2605 elem.addEventListener(name, func, false); 2606 else 2607 elem.attachEvent("on" + name, func); 2608 }, 2609 2610 isCallOptionArgument: function(lastArg) { 2611 return (typeof lastArg === "object" && (typeof lastArg.callback === "function" || 2612 typeof lastArg.exceptionHandler === "function" || typeof lastArg.callbackHandler === "function" || 2613 typeof lastArg.errorHandler === "function" || typeof lastArg.warningHandler === "function" || lastArg.hasOwnProperty("async"))); 2614 }, 2615 2616 logHandlerEx: function(func) { 2617 try { 2618 func(); 2619 } catch(ex) { 2620 dwr.engine._debug("Exception occured in user-specified handler:"); 2621 dwr.engine._debug(ex); 2622 } 2623 } 2624 }; 2625 2626 // Initialize DWR. 2627 dwr.engine._initializer.init(); 2628 2629 /** 2630 * Routines for the DWR pubsub hub 2631 */ 2632 dwr.hub = { 2633 /** 2634 * Publish some data to a given topic 2635 * @param {Object} topicName The topic to publish to 2636 * @param {Object} data The data to publish 2637 */ 2638 publish:function(topicName, data) { 2639 dwr.engine._execute(null, '__System', 'publish', topicName, data, {}); 2640 }, 2641 2642 /** 2643 * Subscribe to get notifications of publish events to a given topic 2644 * @param {String} topicName The topic to subscribe to 2645 * @param {Function} callback The function to call when a publish happens 2646 * @param {Object} scope The 'this' object on which the callback executes (optional) 2647 * @param {Object} subscriberData Data that the subscriber wishes to remember (optional) 2648 * @return An opaque type for use with unsubscribe 2649 */ 2650 subscribe:function(topicName, callback, scope, subscriberData) { 2651 var subscription = "" + dwr.hub._subscriptionId; 2652 dwr.hub._subscriptionId++; 2653 dwr.hub._subscriptions[subscription] = { 2654 callback:callback, 2655 scope:scope, 2656 subscriberData:subscriberData 2657 }; 2658 dwr.engine._execute(null, '__System', 'subscribe', topicName, subscription, {}); 2659 return subscription; 2660 }, 2661 2662 /** 2663 * Called by the server: A publish event has happened that we care about 2664 * @private 2665 * @param {Object} subscriptionId 2666 * @param {Object} publishData 2667 */ 2668 _remotePublish:function(subscriptionId, publishData) { 2669 var subscriptionData = dwr.hub._subscriptions[subscriptionId]; 2670 if (!subscriptionData) return; 2671 subscriptionData.callback.call(subscriptionData.scope, publishData, subscriptionData.subscriberData); 2672 }, 2673 2674 /** 2675 * Each time we subscribe to something, we use a unique number 2676 */ 2677 _subscriptionId:0, 2678 2679 /** 2680 * We need to remember what we are subscribed to so we can recall the callback 2681 */ 2682 _subscriptions:{} 2683 }; 2684 2685/** 2686 * High level data-sync API for use by Widget libraries like a Dojo-Data-Store. 2687 * For full documentation see org.directwebremoting.export.Data 2688 */ 2689dwr.data = { 2690 2691 /** 2692 * This is just documentation that defines how the listener parameter must act 2693 * in order to receive asynchronous updates 2694 */ 2695 StoreChangeListener:{ 2696 /** 2697 * Something has removed an item from the store 2698 * @param {StoreProvider} source The store from which it was moved 2699 * @param {string} itemId The ID of the item 2700 */ 2701 itemRemoved:function(source, itemId) { }, 2702 2703 /** 2704 * Something has added an item to the store 2705 * @param {StoreProvider} source The store from which it was moved 2706 * @param {Item} item The thing that has changed 2707 */ 2708 itemAdded:function(source, item) { }, 2709 2710 /** 2711 * Something has updated an item in the store 2712 * @param {StoreProvider} source The store from which it was moved 2713 * @param {Item} item The thing that has changed 2714 * @param {string[]} changedAttributes A list of changed attributes. If null then 2715 * you should assume that everything has changed 2716 */ 2717 itemChanged:function(source, item, changedAttributes) { } 2718 }, 2719 2720 /**
2721 * Create a cache object containing the functions to interact with a server 2722 * side StoreProvider 2723 * @param {string} storeId ID of server provided storage 2724 * @param {dwr.data.StoreChangeListener} listener See server documentation 2725 * This is likely to be true if dwr.engine.activeReverseAjax == true 2726 */ 2727 Cache:function(storeId, listener) { 2728 this.storeId = storeId; 2729 this.listener = listener; 2730 } 2731}; 2732 2733 /** 2734 * Notes that there is a region of a page that wishes to subscribe to server 2735 * side data and registers a callback function to receive the data. 2736 * @param {Object} region filtering and sorting options. Includes: 2737 * - start: The beginning of the region of specific interest 2738 * - count: The number of items being viewed 2739 * - sort: The sort criteria 2740 * - query: The filter criteria 2741 * @param {function|object} callbackObj A standard DWR callback object 2742 * @return 2743 */ 2744 dwr.data.Cache.prototype.viewRegion = function(region, callbackObj) { 2745 if (!region) region = { }; 2746 if (!region.start) region.start = 0; 2747 if (!region.count) region.count = -1; 2748 if (!region.sort) region.sort = []; 2749 else { 2750 for (var index = 0; index < region.sort.length; index++) { 2751 if (typeof region.sort[index].descending == "undefined") { 2752 region.sort[index].descending = false; 2753 } 2754 } 2755 } 2756 if (!region.query) region.query = {}; 2757 2758 return dwr.engine._execute(null, '__Data', 'viewRegion', [ this.storeId, region, this.listener, callbackObj ]); 2759 }; 2760 2761 /** 2762 * As dwr.data.Cache.viewRegion() except that we only want to see a single item. 2763 * @param {string} itemId ID of object within the given store 2764 * @param {function|object} callbackObj A standard DWR callback object 2765 */ 2766 dwr.data.Cache.prototype.viewItem = function(itemId, callbackObj) { 2767 return dwr.engine._execute(null, '__Data', 'viewItem', [ this.storeId, itemId, this.listener, callbackObj ]); 2768 }; 2769 2770 /** 2771 * Undo the action of dwr.data.view() 2772 * @param {function|object} callbackObj A standard DWR callback object 2773 */ 2774 dwr.data.Cache.prototype.unsubscribe = function(callbackObj) { 2775 if (this.listener) { 2776 return dwr.engine._execute(null, '__Data', 'unsubscribe', [ this.storeId, this.listener, callbackObj ]); 2777 } 2778 }; 2779 2780 /** 2781 * Request an update to server side data 2782 * @param {Object} items An array of update descriptions 2783 * @param {function|object} callbackObj A standard DWR callback object 2784 */ 2785 dwr.data.Cache.prototype.update = function(items, callbackObj) { 2786 return dwr.engine._execute(null, '__Data', 'update', [ this.storeId, items, callbackObj ]); 2787 }; 2788 2789})(); 2790 2791 2792 2793/* ==== INCLUDE: /js/weblets/FeaturedListingsWebletV2.js ==== */ 2794 2795document.featuredListingsRefreshInterval = 5000; 2796document.featuredListingsPhotoRefreshInterval = 5000; 2797 2798try 2799{ 2800 dwr.engine._allowGetForSafariButMakeForgeryEasier = true; 2801} 2802catch( e ) 2803{ 2804} 2805 2806// Effect copied from rico 2807/** 2808 * 2809 * Copyright 2005 Sabre Airline Solutions 2810 * 2811 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 2812 * file except in compliance with the License. You may obtain a copy of the License at 2813 * 2814 * http://www.apache.org/licenses/LICENSE-2.0 2815 * 2816 * Unless required by applicable law or agreed to in writing, software distributed under the 2817 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 2818 * either express or implied. See the License for the specific language governing permissions 2819 * and limitations under the License. 2820 **/ 2821if ( window.Effect == undefined ) 2822 Effect = {}; 2823 2824Effect.FadeTo = function() { 2825 this.initialize.apply(this, arguments); 2826}; 2827 2828Effect.FadeTo.prototype = { 2829 2830 initialize: function( element, opacity, duration, steps, options) { 2831 if( typeof element == "string" ) { 2832 this.element = document.getElementById( element ); 2833 } 2834 else { 2835 this.element = element; 2836 } 2837 2838 this.opacity = opacity; 2839 this.duration = duration; 2840 this.steps = steps; 2841 this.options = arguments[4] || {}; 2842 this.fadeTo(); 2843 }, 2844 2845 fadeTo: function() { 2846 if (this.isFinished()) { 2847 this.element.ricoOpacity = null; 2848 if(this.options.complete) this.options.complete(this); 2849 return; 2850 } 2851 if (this.timer) 2852 clearTimeout(this.timer); 2853 2854 var stepDuration = Math.round(this.duration/this.steps) ; 2855 var currentOpacity = this.getElementOpacity(); 2856 var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0; 2857 2858 this.changeOpacityBy(delta); 2859 this.duration -= stepDuration; 2860 this.steps--; 2861 2862 this.timer = setTimeout(this.fadeTo.bind(this), stepDuration); 2863 }, 2864 2865 changeOpacityBy: function(v) { 2866 var currentOpacity = this.getElementOpacity();
2867 var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1)); 2868 this.element.ricoOpacity = newOpacity; 2869 // bill: bug note: in IE changing opacity appears to whack seemingly random elements 2870 // on page, disabling for IE, assining imgs[i].id == ... also seems to contribute 2871 // not sure what this is!!!, see another bug not below 2872 if( !browser.isIE ) { 2873 //this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")"; 2874 this.element.style.opacity = newOpacity; /*//*/; 2875 } 2876 }, 2877 2878 isFinished: function() { 2879 return this.steps <= 0; 2880 }, 2881 2882 getElementOpacity: function() { 2883 if ( this.element.ricoOpacity == undefined ) { 2884 var opacity; 2885 if ( this.element.currentStyle ) { 2886 opacity = this.element.currentStyle.opacity; 2887 } 2888 else if ( document.defaultView.getComputedStyle != undefined ) { 2889 opacity = document.defaultView.getComputedStyle(this.element, null).getPropertyValue('opacity'); 2890 } 2891 2892 this.element.ricoOpacity = opacity != undefined ? opacity : 1.0; 2893 } 2894 2895 return parseFloat(this.element.ricoOpacity); 2896 } 2897} 2898 2899 2900function rotateFeaturedListingsPhotos( id, interval ) 2901{ 2902 if( document.hidden ) { 2903 return; 2904 } 2905 if( !interval ) { 2906 interval = document.featuredListingsPhotoRefreshInterval; 2907 } 2908 for( var listingId in document.featuredListingsHash[id].images ) { 2909 var images = document.featuredListingsHash[id].images[listingId]; 2910 if( images.length > 1 ) { 2911 setTimeout( "showFeaturedListingPhoto1('" + id + "', '" + listingId + 2912 "', 1, " + interval + " )", interval ); 2913 } 2914 } 2915} 2916 2917function showFeaturedListingPhoto1( id, listingId, index, interval ) 2918{ 2919 var w = document.getElementById(id); 2920 var imgId = id + "-" + listingId + "-image"; 2921 var imgs = w.getElementsByTagName( "img" ); 2922 var img = null; 2923 if( imgs && imgs.length > 0 ) { 2924 for( var i=0; i<imgs.length; ++i ) { 2925 2926 2927 if( imgs[i].src && !imgs[i].classList.contains( "leadin_overlay" ) && 2928 //imgs[i].src.indexOf( "/" + listingId + "/" ) != -1 2929 imgs[i].classList.contains("mrp-auto-image") && 2930 imgs[i].closest('.mrp-leadin').querySelector('a').getAttribute('href').indexOf( listingId ) !== -1 && 2931 imgs[i].getAttribute("id")==null ) { 2932 2933 // if( imgs[i] && imgs[i].parentNode && imgs[i].parentNode.getAttribute( "data-auto-image" ) != "ready" ) { 2934 // setTimeout( function() { 2935 // showFeaturedListingPhoto1( id, listingId, index, interval ); 2936 // }, 1000 ); 2937 // return; 2938 // } 2939 2940 // bill: bug note: instead of assining id, we are assigning to "rel" 2941 // connected to disappearing elements in IE bug, see above 2942 imgs[i].rel = imgId; 2943 img = imgs[i]; 2944 break; 2945 } 2946 } 2947 } 2948 if( !img ) { 2949 return; 2950 } 2951 // img.removeAttribute('srcset'); 2952 // img.removeAttribute('sizes'); 2953 var bg = document.getElementById(id + "-" + listingId + "-shadow"); 2954 if (!bg) { 2955 bg = new Image(); 2956 bg.setAttribute("id", id + "-" + listingId + "-shadow"); 2957 bg.style.position = "absolute"; 2958 bg.style.display = "block"; 2959 bg.style.visibility = "visible"; 2960 bg.style.margin = img.margin; 2961 bg.style.border = img.border; 2962 bg.className = "mrpl-image"; 2963 img.parentNode.insertBefore( bg, img ); 2964 } 2965 bg.style.left = img.style.left; 2966 bg.style.top = img.style.top; 2967 bg.style.width = img.style.width; 2968 bg.style.height = img.style.height; 2969 bg.style.opacity = "0"; // bill; this effectively negates teh bg trick, but it's due to flicker 2970 bg.src = img.src; 2971 // console.log( 'bg src', bg.src ); 2972 new Effect.FadeTo( img, 0, 1, 1, { 2973 complete: function() { showFeaturedListingPhoto2( id, listingId, index, interval ); } 2974 } ); 2975} 2976 2977function showFeaturedListingPhoto2( id, listingId, index, interval ) 2978{ 2979 if( !interval ) { 2980 interval = document.featuredListingsPhotoRefreshInterval; 2981 } 2982 2983 var imgId = id + "-" + listingId + "-image"; 2984 2985 var images = document.featuredListingsHash[id].images[listingId]; 2986 if( !images ) { 2987 return; 2988 } 2989 if( images.length <= index ) { 2990 index = 0; 2991 } 2992 2993 var img = null; 2994 var imgId = id + "-" + listingId + "-image"; 2995 var w = document.getElementById(id); 2996 var imgs = w.getElementsByTagName( "img" ); 2997 if( imgs && imgs.length > 0 ) { 2998 for( var i=0; i<imgs.length; ++i ) { 2999 if( imgs[i].rel == imgId ) { 3000 img = imgs[i]; 3001 break; 3002 } 3003 } 3004 } 3005 3006 //var img = $(id + "-" + listingId + "-image"); 3007 if( !img ) { 3008 return; 3009 } 3010 img.onload = function() { 3011 new Effect.FadeTo( img, 1, 500, 50 ); 3012 3013 setTimeout( "showFeaturedListingPhoto1('" + id + "', '" + listingId + "', " + index + ", " + interval + " )", interval ); 3014 }; 3015 index++; 3016 // console.log( 'img src before', img.src ); 3017 setTimeout(function() { 3018 if( img.src != images[index]) { 3019 img.src = images[index]; 3020 } 3021 }, 100 ); 3022 // console.log( 'img src after', img.src ); 3023 3024 3025} 3026 3027var prefetchedListingsNG = {}; 3028 3029function rotateFeaturedListingsNG( id, listingIds, interval ) 3030{ 3031 if( document.hidden ) { 3032 return; 3033 } 3034 var w = document.getElementById(id); 3035 webletGateway.renderWeblet( document.pageName, id, 3036 { "ListingIds": listingIds, "ajaxGetAll" : "true" }, { 3037 callback: function( result ) { 3038 if( result.content ) { 3039 3040 // strip the wrapper 3041 var content = result.content; 3042 var idx = content.indexOf( '>' ); 3043 if( idx != -1 ) { 3044 content = content.substring( idx + 1 ); 3045 idx = content.indexOf( '>' ); 3046 if( idx != -1 ) { 3047 content = content.substring( idx + 1 ); 3048 } 3049 } 3050 idx = content.lastIndexOf( "</div></div>" ); 3051 if( idx != -1 ) { 3052 content = content.substring( 0, idx ); 3053 } 3054 3055 var tks = content.split( "\=\_\=\_\=\_\=\_\=" ); 3056 prefetchedListingsNG[id] = tks; 3057 3058 var pos = 1; 3059 if( tks.length == 1 ) { 3060 pos = 0; 3061 } 3062 3063 new Effect.FadeTo( id, 0.1, 100, 10, { 3064 complete: function() { rotateFeaturedListingsAfterFadeNG( id, pos, interval ); } 3065 }); 3066 } 3067 }, 3068 errorHandler: function( error ) { 3069 return; 3070 } 3071 }); 3072} 3073 3074function getMatches(string, regex, index) { 3075 index || (index = 1); // default to the first capturing group 3076 var matches = []; 3077 var match; 3078 while (match = regex.exec(string)) { 3079 matches.push(match[index]); 3080 } 3081 return matches; 3082} 3083 3084function rotateFeaturedListingsAfterFadeNG( id, pos, interval ) 3085{ 3086 if( !prefetchedListingsNG[id] ) { 3087 return; 3088 } 3089 if( pos >= prefetchedListingsNG[id].length ) { 3090 return; // bail: something is wrong 3091 } 3092 var content = prefetchedListingsNG[id][pos]; 3093 var scripts = getMatches(content, /<script[^>]*>(.*?)<\/script>/gmi).join('\n'); 3094 content = content.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gmi, "") 3095 /*var idx = content.indexOf( "<script>" ); 3096 if( idx != -1 ) { 3097 content = content.substring( 0, idx ); 3098 }*/ 3099 3100 var elem = document.getElementById(id); 3101 3102 elem = elem.children[0]; // inner-weblet 3103 3104 var h2 = null; 3105 var viewAll = null; 3106 3107 var h2s = elem.getElementsByTagName( "h2" ); 3108 if( h2s && h2s.length > 0 && h2s[0] && h2s[0].className === "weblet_featured-listings-title" ) { 3109 h2 = h2s[0]; 3110 } 3111 3112 if( elem.children[elem.children.length-1] && 3113 elem.children[elem.children.length-1].className === "weblet_featured-listings-view-all" ) { 3114 viewAll = elem.children[elem.children.length-1]; 3115 } 3116 3117 if( !elem.existingRRectId ) { 3118 var existingRRectId = null; 3119 var divs = elem.getElementsByTagName("div"); 3120 for( var i=0; i<divs.length; ++i ) { 3121 if( divs[i].className && 3122 divs[i].className.indexOf( "leadin_style" ) != -1 && 3123 divs[i].id ) { 3124 existingRRectId = divs[i].id; 3125 break; 3126 } 3127 } 3128 elem.existingRRectId = existingRRectId; 3129 } 3130 3131 elem.innerHTML = content; 3132 if( h2 ) { 3133 elem.insertBefore( h2, elem.children[0] ); 3134 } 3135 3136 if( viewAll ) { 3137 // no need to re-append what is already there 3138 //elem.appendChild( viewAll ); 3139 } 3140 eval(scripts); 3141 3142 if( elem.existingRRectId ) { 3143 setTimeout( function() { 3144 var fx = allFx[elem.existingRRectId]; 3145 if( fx && fx.applyOnResizeFx ) { 3146 var divs = elem.getElementsByTagName("div"); 3147 for( var i=0; i<divs.length; ++i ) { 3148 if( divs[i].className && 3149 divs[i].className.indexOf( "leadin_style" ) != -1 && 3150 divs[i].id ) { 3151 fx.applyOnLoadFx( divs[i].id ); 3152 } 3153 } 3154 } 3155 }, 102 ); 3156 } 3157 3158 new Effect.FadeTo( id, 1, 100, 10 ); 3159 var newPos = pos+1; 3160 if( newPos >= prefetchedListingsNG[id].length ) { 3161 newPos = 0; 3162 } 3163 3164 if( !interval ) { 3165 interval = document.featuredListingsRefreshInterval; 3166 } 3167 3168 if( window.location.href.indexOf( 'm=__manage__' ) != -1 ) { 3169 return; 3170 } 3171 3172 setTimeout( function() { 3173 new Effect.FadeTo( id, 0.1, 100, 10, { 3174 complete: function() { rotateFeaturedListingsAfterFadeNG( id, newPos, interval ); } 3175 }); 3176 }, interval ); 3177} 3178 3179function rotateFeaturedListings( id, listingIds, interval ) 3180{ 3181 if( window.location.href.indexOf( 'm=__manage__' ) != -1 ) { 3182 return; 3183 } 3184 3185 if( true ) { 3186 rotateFeaturedListingsNG( id, listingIds, interval ); 3187 return; 3188 } 3189} 3190 3191/* ==== INCLUDE: /js/weblets/responsive/leadins.js ==== */ 3192
3193/* leadins js */ 3194jQuery( function() { 3195 jQuery( document ).on( "click", ".mrp-leadin *", function(e) { 3196 var $p = jQuery( this ).closest( ".mrp-leadin" ); 3197 3198 if( $(this).hasClass( 'ed3-editable') && $("body").hasClass( "ed3-inline-edit-on" ) ) { 3199 return false; 3200 } 3201 var $target = jQuery( e.target ); 3202 if( $target && $target.length > 0 && "A" == $target.prop("tagName" ) || $target.closest( "a" ).length > 0 ) { 3203 return true; 3204 } 3205 var url = $p.attr( "data-link" ); 3206 if( url ) { 3207 e.stopImmediatePropagation(); 3208 if( $p.attr( "data-target" ) ) { 3209 window.open( url ); 3210 } 3211 else { 3212 window.location = url; 3213 } 3214 console.log( url ); 3215 return false; 3216 } 3217 return true; 3218 }); 3219}); 3220 3221/* ==== INCLUDE: /js/weblets/responsive/image-banner.js ==== */ 3222 3223function mrp_image_banner_rotate( id, index ) { 3224 3225 var $weblet = jQuery( "#" + id ); 3226 3227 if( index >= 0 ) { 3228 var interval = $weblet.data( "animation-interval" ); 3229 if( !isNaN( interval ) ) { 3230 clearInterval( interval ); 3231 } 3232 } 3233 3234 var $active = $weblet.find( "ul.images li.mrp-resp-banner-show" ); 3235 if( isNaN( index ) || index < 0 ) {
3236 index = parseInt( $active.attr( "data-idx" ) ); 3237 if( isNaN( index ) ) { 3238 return; 3239 } 3240 index++; 3241 } 3242 3243 var $next = $weblet.find( "ul.images li[data-idx=" + index + "]" ); 3244 if( $next.length == 0 ) { 3245 // first 3246 index = 0; 3247 } 3248 $next = $weblet.find( "ul.images li[data-idx=" + index + "]" ); 3249 3250 if( $next.length > 0 ) { 3251 $weblet.find( "ul.images li" ).removeClass( "mrp-resp-banner-show" ); 3252 $next.addClass( "mrp-resp-banner-show" ); 3253 3254 $weblet.find( ".bullet-nav span.active" ).removeClass( "active" ); 3255 $weblet.find( ".bullet-nav span[data-idx=" + index + "]" ).addClass( "active" ); 3256 } 3257} 3258 3259function mrp_banner_nav_next( id ) { 3260 3261 var $weblet = jQuery( "#" + id ); 3262 var $active = $weblet.find( "ul.images li.mrp-resp-banner-show" ); 3263 var idx = parseInt( $active.attr( "data-idx" ) ) || 0; 3264 idx = idx + 1; 3265 if( idx > $weblet.find( ".images > li img" ).length - 1 ) { 3266 idx = 0; 3267 } 3268 mrp_image_banner_rotate( id, idx ); 3269 3270 try { 3271 var interval = $weblet.data( "animation-interval" ); 3272 if( !isNaN( interval ) ) { 3273 clearInterval( interval ); 3274 } 3275 } 3276 catch( e ) {} 3277} 3278 3279function mrp_banner_nav_prev( id ) { 3280 3281 var $weblet = jQuery( "#" + id ); 3282 var $active = $weblet.find( "ul.images li.mrp-resp-banner-show" );
3283 var idx = parseInt( $active.attr( "data-idx" ) ) || 0; 3284 idx = idx - 1; 3285 if( idx < 0 ) { 3286 idx = $weblet.find( ".images > li img" ).length - 1; 3287 } 3288 mrp_image_banner_rotate( id, idx ); 3289 3290 3291 try { 3292 var interval = $weblet.data( "animation-interval" ); 3293 if( !isNaN( interval ) ) { 3294 clearInterval( interval ); 3295 } 3296 } 3297 catch( e ) {} 3298} 3299 3300/* ==== INCLUDE: /js/manage/jquery.form.min.js ==== */ 3301 3302!function(e){function t(){if(e.fn.ajaxSubmit.debug){var t="[jquery.form] "+Array.prototype.join.call(arguments,"");window.console&&window.console.log?window.console.log(t):window.opera&&window.opera.postError&&window.opera.postError(t)}}e.fn.ajaxSubmit=function(a){if(!this.length)return t("ajaxSubmit: skipping submit process - no element selected"),this;"function"==typeof a&&(a={success:a});var r=e.trim(this.attr("action"));r&&(r=(r.match(/^([^#]+)/)||[])[1]),r=r||window.location.href||"",a=e.extend(!0,{url:r,type:this.attr("method")||"GET",iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},a);var n={};if(this.trigger("form-pre-serialize",[this,a,n]),n.veto)return t("ajaxSubmit: submit vetoed via form-pre-serialize trigger"),this;if(a.beforeSerialize&&!1===a.beforeSerialize(this,a))return t("ajaxSubmit: submit aborted via beforeSerialize callback"),this;var i,o,s=this.formToArray(a.semantic);if(a.data)for(i in a.extraData=a.data,a.data)if(a.data[i]instanceof Array)for(var l in a.data[i])s.push({name:i,value:a.data[i][l]});else o=a.data[i],o=e.isFunction(o)?o():o,s.push({name:i,value:o});if(a.beforeSubmit&&!1===a.beforeSubmit(s,this,a))return t("ajaxSubmit: submit aborted via beforeSubmit callback"),this;if(this.trigger("form-submit-validate",[s,this,a,n]),n.veto)return t("ajaxSubmit: submit vetoed via form-submit-validate trigger"),this;var u=e.param(s);"GET"==a.type.toUpperCase()?(a.url+=(a.url.indexOf("?")>=0?"&":"?")+u,a.data=null):a.data=u;var c=this,f=[];if(a.resetForm&&f.push(function(){c.resetForm()}
3302),a.clearForm&&f.push(function(){c.clearForm()}),!a.dataType&&a.target){var m=a.success||function(){};f.push(function(t){var r=a.replaceTarget?"replaceWith":"html";e(a.target)[r](t).each(m,arguments)})}else a.success&&f.push(a.success);a.success=function(e,t,r){for(var n=a.context||a,i=0,o=f.length;i<o;i++)f[i].apply(n,[e,t,r||c,c])};var d=e("input:file",this).length>0,p="multipart/form-data",h=c.attr("enctype")==p||c.attr("encoding")==p;return!1!==a.iframe&&(d||a.iframe||h)?a.closeKeepAlive?e.get(a.closeKeepAlive,v):v():e.ajax(a),this.trigger("form-submit-notify",[this,a]),this;function v(){var r=c[0];if(e(":input[name=submit],:input[id=submit]",r).length)alert('Error: Form elements must not have name or id of "submit".');else{var n=e.extend(!0,{},e.ajaxSettings,a);n.context=n.context||n;var i="jqFormIO"+(new Date).getTime(),o="_"+i;window[o]=function(){var e=s.data("form-plugin-onload");if(e){e(),window[o]=void 0;try{delete window[o]}catch(e){}}};var s=e('<iframe id="'+i+'" name="'+i+'" src="'+n.iframeSrc+'" onload="window[\'_\'+this.id]()" />'),l=s[0];s.css({position:"absolute",top:"-1000px",left:"-1000px"});var u={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(){this.aborted=1,s.attr("src",n.iframeSrc)}},f=n.global;if(f&&!e.active++&&e.event.trigger("ajaxStart"),f&&e.event.trigger("ajaxSend",[u,n]),n.beforeSend&&!1===n.beforeSend.call(n.context,u,n))n.global&&e.active--;else if(!u.aborted){var m=!1,d=0,p=r.clk;if(p){var h=p.name;h&&!p.disabled&&(n.extraData=n.extraData||{},n.extraData[h]=p.value,"image"==p.type&&(n.extraData[h+".x"]=r.clk_x,n.extraData[h+".y"]=r.clk_y))}n.forceSync?x():setTimeout(x,10);var v,g,b=50}}function x(){var t=c.attr("target"),a=c.attr("action");r.setAttribute("target",i),"POST"!=r.getAttribute("method")&&r.setAttribute("method","POST"),r.getAttribute("action")!=n.url&&r.setAttribute("action",n.url),n.skipEncodingOverride||c.attr({encoding:"multipart/form-data",enctype:"multipart/form-data"}),n.timeout&&setTimeout(function(){d=!0,y()},n.timeout);var o=[];try{if(n.extraData)for(var l in n.extraData)o.push(e('<input type="hidden" name="'+l+'" value="'+n.extraData[l]+'" />').appendTo(r)[0]);s.appendTo("body"),s.data("form-plugin-onload",y),r.submit()}finally{r.setAttribute("action",a),t?r.setAttribute("target",t):c.removeAttr("target"),e(o).remove()}}function y(){if(!m){s.removeData("form-plugin-onload");var a=!0;try{if(d)throw"timeout";g=l.contentWindow?l.contentWindow.document:l.contentDocument?l.contentDocument:l.document;var r="xml"==n.dataType||g.XMLDocument||e.isXMLDoc(g);if(t("isXml="+r),!r&&window.opera&&(null==g.body||""==g.body.innerHTML)&&--b)return t("requeing onLoad callback, DOM not available"),void setTimeout(y,250);m=!0,u.responseText=g.documentElement?g.documentElement.innerHTML:null,u.responseXML=g.XMLDocument?g.XMLDocument:g,u.getResponseHeader=function(e){return{"content-type":n.dataType}[e]};
3302var i=/(json|script)/.test(n.dataType);if(i||n.textarea){var o=g.getElementsByTagName("textarea")[0];if(o)u.responseText=o.value;else if(i){var c=g.getElementsByTagName("pre")[0],p=g.getElementsByTagName("body")[0];c?u.responseText=c.innerHTML:p&&(u.responseText=p.innerHTML)}}else"xml"!=n.dataType||u.responseXML||null==u.responseText||(u.responseXML=function(e,t){window.ActiveXObject?((t=new ActiveXObject("Microsoft.XMLDOM")).async="false",t.loadXML(e)):t=(new DOMParser).parseFromString(e,"text/xml");return t&&t.documentElement&&"parsererror"!=t.documentElement.tagName?t:null}(u.responseText));v=e.httpData(u,n.dataType)}catch(r){t("error caught:",r),a=!1,u.error=r,e.handleError(n,u,"error",r)}a&&(n.success.call(n.context,v,"success",u),f&&e.event.trigger("ajaxSuccess",[u,n])),f&&e.event.trigger("ajaxComplete",[u,n]),f&&!--e.active&&e.event.trigger("ajaxStop"),n.complete&&n.complete.call(n.context,u,a?"success":"error"),setTimeout(function(){s.removeData("form-plugin-onload"),s.remove(),u.responseXML=null},100)}}}},e.fn.ajaxForm=function(a){if(0===this.length){var r={s:this.selector,c:this.context};return!e.isReady&&r.s?(t("DOM not ready, queuing ajaxForm"),e(function(){e(r.s,r.c).ajaxForm(a)}),this):(t("terminating; zero elements found by selector"+(e.isReady?"":" (DOM not ready)")),this)}return this.ajaxFormUnbind().bind("submit.form-plugin",function(t){t.isDefaultPrevented()||(t.preventDefault(),e(this).ajaxSubmit(a))}).bind("click.form-plugin",function(t){var a=t.target,r=e(a);if(!r.is(":submit,input:image")){var n=r.closest(":submit");if(0==n.length)return;a=n[0]}var i=this;if(i.clk=a,"image"==a.type)if(null!=t.offsetX)i.clk_x=t.offsetX,i.clk_y=t.offsetY;else if("function"==typeof e.fn.offset){var o=r.offset();i.clk_x=t.pageX-o.left,i.clk_y=t.pageY-o.top}else i.clk_x=t.pageX-a.offsetLeft,i.clk_y=t.pageY-a.offsetTop;setTimeout(function(){i.clk=i.clk_x=i.clk_y=null},100)})},e.fn.ajaxFormUnbind=function(){return this.unbind("submit.form-plugin click.form-plugin")},e.fn.formToArray=function(t){var a=[];if(0===this.length)return a;var r,n,i,o,s,l,u,c=this[0],f=t?c.getElementsByTagName("*"):c.elements;if(!f)return a;for(r=0,l=f.length;r<l;r++)if(i=(s=f[r]).name)if(t&&c.clk&&"image"==s.type)s.disabled||c.clk!=s||(a.push({name:i,value:e(s).val()}),a.push({name:i+".x",value:c.clk_x},{name:i+".y",value:c.clk_y}));else if((o=e.fieldValue(s,!0))&&o.constructor==Array)for(n=0,u=o.length;n<u;n++)a.push({name:i,value:o[n]});else null!=o&&a.push({name:i,value:o});if(!t&&c.clk){var m=e(c.clk),d=m[0];(i=d.name)&&!d.disabled&&"image"==d.type&&(a.push({name:i,value:m.val()}),a.push({name:i+".x",value:c.clk_x},{name:i+".y",value:c.clk_y}))}return a},e.fn.formSerialize=function(t){return e.param(this.formToArray(t))},e.fn.fieldSerialize=function(t){var a=[];return this.each(function(){var r=this.name;if(r){var n=e.fieldValue(this,t);if(n&&n.constructor==Array)for(var i=0,o=n.length;i<o;i++)a.push({name:r,value:n[i]});else null!=n&&a.push({name:this.name,value:n})}}),e.param(a)},e.fn.fieldValue=function(t){for(var a=[],r=0,n=this.length;r<n;r++){var i=this[r],o=e.fieldValue(i,t);null==o||o.constructor==Array&&!o.length||(o.constructor==Array?e.merge(a,o):a.push(o))}return a},e.fieldValue=function(t,a){var r=t.name,n=t.type,i=t.tagName.toLowerCase();if(void 0===a&&(a=!0),a&&(!r||t.disabled||"reset"==n||"button"==n||("checkbox"==n||"radio"==n)&&!t.checked||("submit"==n||"image"==n)&&t.form&&t.form.clk!=t||"select"==i&&-1==t.selectedIndex))return null;if("select"==i){var o=t.selectedIndex;if(o<0)return null;for(var s=[],l=t.options,u="select-one"==n,c=u?o+1:l.length,f=u?o:0;f<c;f++){var m=l[f];if(m.selected){var d=m.value;if(d||(d=m.attributes&&m.attributes.value&&!m.attributes.value.specified?m.text:m.value),u)return d;s.push(d)}}return s}return e(t).val()},e.fn.clearForm=function(){return this.each(function(){e("input,select,textarea",this).clearFields()})},e.fn.clearFields=e.fn.clearInputs=function(){return this.each(function(){var e=this.type,t=this.tagName.toLowerCase();"text"==e||"password"==e||"textarea"==t?this.value="":"checkbox"==e||"radio"==e?this.checked=!1:"select"==t&&(this.selectedIndex=-1)})},e.fn.resetForm=function(){return this.each(function(){("function"==typeof this.reset||"object"==typeof this.reset&&!this.reset.nodeType)&&this.reset()})},e.fn.enable=function(e){return void 0===e&&(e=!0),this.each(function(){this.disabled=!e})},e.fn.selected=function(t){return void 0===t&&(t=!0),this.each(function(){var a=this.type;if("checkbox"==a||"radio"==a)this.checked=t;else if("option"==this.tagName.toLowerCase()){var r=e(this).parent("select");t&&r[0]&&"select-one"==r[0].type&&r.find("option").selected(!1),this.selected=t}})}}(jQuery); 3303 3304/* ==== INCLUDE: /js/weblets/responsive/forms.js ==== */ 3305
3306/* forms */ 3307jQuery( function($) { 3308 3309 var $form = $( "form.mrp-dynamic-form" ); 3310 3311 window.__hookupAjaxForm = function( $form ) { 3312 $form.ajaxForm({ 3313 contentType: "application/x-www-form-urlencoded; charset=UTF-8", 3314 beforeSubmit: function(arr, $form, options) { 3315 //$form.find( ".sending-feedback" ).show(); 3316 var $submit = $form.find( ".submit-button" ); 3317 var submitText = $submit.text(); 3318 $submit.attr( "data-orig-text", submitText || ""); 3319 $submit.attr("disabled","disabled").text("Sending..."); 3320 console.log( "SUBMITTING" ); 3321 return true; 3322 }, 3323 success : function( response, status, xhr, $form ) { 3324 //console.log( "SUCCESS", response ); 3325 //$form.find( ".sending-feedback" ).hide(); 3326 var $submit = $form.find( ".submit-button"); 3327 var origText = $submit.attr("data-orig-text") || "Submit"; 3328 $submit.removeAttr("disabled"); 3329 $submit.text(origText); 3330 if( xhr.getResponseHeader( "Location" ) ) { 3331 window.location = xhr.getResponseHeader( "Location" ); 3332 return; 3333 } 3334 var $doc = jQuery( response ); 3335 var $inform = $doc.find( "#" + $form.attr( "id" ) ); 3336 console.log( $inform.html() ); 3337 //$form.empty(); 3338 //$form.html( $inform.html() ); 3339 var $message = $inform.find( ".form-success" ); 3340 $message.remove(); 3341 3342 var $error = $inform.find( ".form-error" ); 3343 3344 // to support old <center> case, just in case 3345 var $center = $form.children( "center" ); 3346 if( $center.length > 0 ) { 3347 $form.append( $center.contents() ); 3348 } 3349 3350 $form.contents().filter( function() { 3351 if( $(this).attr( "name" ) == "__sh" ) { 3352 return false; 3353 } 3354 else if( $(this).attr( "name" ) == "__src_url" ) { 3355 return false; 3356 } 3357 return true; 3358 }).remove(); 3359 3360 $inform.find( "script" ).remove(); 3361 3362 var match = /.*name=\"__sh\" value=\"([^\"]*)\".*/gm.exec(response); 3363 var hash = match && match[1] ? match[1] : null 3364 3365 $form.append( $inform.contents().filter( function() { 3366 if( this.nodeName == "SCRIPT" || this.nodeName == "script" ) { 3367 return false; 3368 } 3369 return true; 3370 })); 3371 3372 if( hash ) { 3373 $form.find( "[name=__sh]").val(hash) 3374 } 3375 3376 if( $message.text() ) { 3377 showQuickMessage( $message.text() ); 3378 } 3379 setTimeout(function() { 3380 if( $error.length > 0 ) { 3381 $error[0].scrollIntoView({ 3382 behavior: 'smooth', 3383 block: 'center', 3384 }) 3385 } 3386 }, 100 ) 3387 3388 if( !$error.text() && window.dataLayer ) { 3389 window.dataLayer.push( { 3390 event: 'formSubmitted', 3391 formURL: window.location.href, 3392 title: document.title, 3393 }) 3394 } 3395 }, 3396 error : function() { 3397 3398 } 3399 }); 3400 3401 // listen for `invalid` events on all form inputs 3402 $form.find(':input').on('invalid', function(event) { 3403 3404 var input = $(this); 3405 3406 // the first invalid element in the form 3407 var first = $form.find(':invalid').first(); 3408 3409 // only handle if this is the first invalid input 3410 if (input[0] === first[0]) { 3411 3412 // schedule another submit on invalid to make popups show 3413 // otherwise, they disappear after scroll 3414 3415 if( !$form.validating ) { 3416 setTimeout( function() { 3417 $form[0].reportValidity(); 3418 $form.validating = false; 3419 }, 500 ); 3420 } 3421 3422 $form.validating = true; 3423 } 3424 }); 3425 } 3426 3427 window.__hookupAjaxForm( $form ); 3428 3429});
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.