blob: 61b9738e36fbb0d9bd85e150f6c3a57ff097f70c [file] [log] [blame]
genia.likes.science@gmail.comc5f82a22013-05-08 02:44:35 -07001/**
2 * easyXDM
3 * http://easyxdm.net/
4 * Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24(function (window, document, location, setTimeout, decodeURIComponent, encodeURIComponent) {
25/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
26/*global JSON, XMLHttpRequest, window, escape, unescape, ActiveXObject */
27//
28// easyXDM
29// http://easyxdm.net/
30// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
31//
32// Permission is hereby granted, free of charge, to any person obtaining a copy
33// of this software and associated documentation files (the "Software"), to deal
34// in the Software without restriction, including without limitation the rights
35// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
36// copies of the Software, and to permit persons to whom the Software is
37// furnished to do so, subject to the following conditions:
38//
39// The above copyright notice and this permission notice shall be included in
40// all copies or substantial portions of the Software.
41//
42// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
43// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
44// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
45// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
46// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
47// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
48// THE SOFTWARE.
49//
50
51var global = this;
52var channelId = Math.floor(Math.random() * 10000); // randomize the initial id in case of multiple closures loaded
53var emptyFn = Function.prototype;
54var reURI = /^((http.?:)\/\/([^:\/\s]+)(:\d+)*)/; // returns groups for protocol (2), domain (3) and port (4)
55var reParent = /[\-\w]+\/\.\.\//; // matches a foo/../ expression
56var reDoubleSlash = /([^:])\/\//g; // matches // anywhere but in the protocol
57var namespace = ""; // stores namespace under which easyXDM object is stored on the page (empty if object is global)
58var easyXDM = {};
59var _easyXDM = window.easyXDM; // map over global easyXDM in case of overwrite
60var IFRAME_PREFIX = "easyXDM_";
61var HAS_NAME_PROPERTY_BUG;
62var useHash = false; // whether to use the hash over the query
63var flashVersion; // will be set if using flash
64var HAS_FLASH_THROTTLED_BUG;
65var _trace = emptyFn;
66
67
68// http://peter.michaux.ca/articles/feature-detection-state-of-the-art-browser-scripting
69function isHostMethod(object, property){
70 var t = typeof object[property];
71 return t == 'function' ||
72 (!!(t == 'object' && object[property])) ||
73 t == 'unknown';
74}
75
76function isHostObject(object, property){
77 return !!(typeof(object[property]) == 'object' && object[property]);
78}
79
80// end
81
82// http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
83function isArray(o){
84 return Object.prototype.toString.call(o) === '[object Array]';
85}
86
87// end
88function hasFlash(){
89 var name = "Shockwave Flash", mimeType = "application/x-shockwave-flash";
90
91 if (!undef(navigator.plugins) && typeof navigator.plugins[name] == "object") {
92 // adapted from the swfobject code
93 var description = navigator.plugins[name].description;
94 if (description && !undef(navigator.mimeTypes) && navigator.mimeTypes[mimeType] && navigator.mimeTypes[mimeType].enabledPlugin) {
95 flashVersion = description.match(/\d+/g);
96 }
97 }
98 if (!flashVersion) {
99 var flash;
100 try {
101 flash = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
102 flashVersion = Array.prototype.slice.call(flash.GetVariable("$version").match(/(\d+),(\d+),(\d+),(\d+)/), 1);
103 flash = null;
104 }
105 catch (notSupportedException) {
106 }
107 }
108 if (!flashVersion) {
109 return false;
110 }
111 var major = parseInt(flashVersion[0], 10), minor = parseInt(flashVersion[1], 10);
112 HAS_FLASH_THROTTLED_BUG = major > 9 && minor > 0;
113 return true;
114}
115
116/*
117 * Cross Browser implementation for adding and removing event listeners.
118 */
119var on, un;
120if (isHostMethod(window, "addEventListener")) {
121 on = function(target, type, listener){
122 _trace("adding listener " + type);
123 target.addEventListener(type, listener, false);
124 };
125 un = function(target, type, listener){
126 _trace("removing listener " + type);
127 target.removeEventListener(type, listener, false);
128 };
129}
130else if (isHostMethod(window, "attachEvent")) {
131 on = function(object, sEvent, fpNotify){
132 _trace("adding listener " + sEvent);
133 object.attachEvent("on" + sEvent, fpNotify);
134 };
135 un = function(object, sEvent, fpNotify){
136 _trace("removing listener " + sEvent);
137 object.detachEvent("on" + sEvent, fpNotify);
138 };
139}
140else {
141 throw new Error("Browser not supported");
142}
143
144/*
145 * Cross Browser implementation of DOMContentLoaded.
146 */
147var domIsReady = false, domReadyQueue = [], readyState;
148if ("readyState" in document) {
149 // If browser is WebKit-powered, check for both 'loaded' (legacy browsers) and
150 // 'interactive' (HTML5 specs, recent WebKit builds) states.
151 // https://bugs.webkit.org/show_bug.cgi?id=45119
152 readyState = document.readyState;
153 domIsReady = readyState == "complete" || (~ navigator.userAgent.indexOf('AppleWebKit/') && (readyState == "loaded" || readyState == "interactive"));
154}
155else {
156 // If readyState is not supported in the browser, then in order to be able to fire whenReady functions apropriately
157 // when added dynamically _after_ DOM load, we have to deduce wether the DOM is ready or not.
158 // We only need a body to add elements to, so the existence of document.body is enough for us.
159 domIsReady = !!document.body;
160}
161
162function dom_onReady(){
163 if (domIsReady) {
164 return;
165 }
166 domIsReady = true;
167 _trace("firing dom_onReady");
168 for (var i = 0; i < domReadyQueue.length; i++) {
169 domReadyQueue[i]();
170 }
171 domReadyQueue.length = 0;
172}
173
174
175if (!domIsReady) {
176 if (isHostMethod(window, "addEventListener")) {
177 on(document, "DOMContentLoaded", dom_onReady);
178 }
179 else {
180 on(document, "readystatechange", function(){
181 if (document.readyState == "complete") {
182 dom_onReady();
183 }
184 });
185 if (document.documentElement.doScroll && window === top) {
186 var doScrollCheck = function(){
187 if (domIsReady) {
188 return;
189 }
190 // http://javascript.nwbox.com/IEContentLoaded/
191 try {
192 document.documentElement.doScroll("left");
193 }
194 catch (e) {
195 setTimeout(doScrollCheck, 1);
196 return;
197 }
198 dom_onReady();
199 };
200 doScrollCheck();
201 }
202 }
203
204 // A fallback to window.onload, that will always work
205 on(window, "load", dom_onReady);
206}
207/**
208 * This will add a function to the queue of functions to be run once the DOM reaches a ready state.
209 * If functions are added after this event then they will be executed immediately.
210 * @param {function} fn The function to add
211 * @param {Object} scope An optional scope for the function to be called with.
212 */
213function whenReady(fn, scope){
214 if (domIsReady) {
215 fn.call(scope);
216 return;
217 }
218 domReadyQueue.push(function(){
219 fn.call(scope);
220 });
221}
222
223/**
224 * Returns an instance of easyXDM from the parent window with
225 * respect to the namespace.
226 *
227 * @return An instance of easyXDM (in the parent window)
228 */
229function getParentObject(){
230 var obj = parent;
231 if (namespace !== "") {
232 for (var i = 0, ii = namespace.split("."); i < ii.length; i++) {
233 if (!obj) {
234 throw new Error(ii.slice(0, i + 1).join('.') + ' is not an object');
235 }
236 obj = obj[ii[i]];
237 }
238 }
239 if (!obj || !obj.easyXDM) {
240 throw new Error('Could not find easyXDM in parent.' + namespace);
241 }
242 return obj.easyXDM;
243}
244
245/**
246 * Removes easyXDM variable from the global scope. It also returns control
247 * of the easyXDM variable to whatever code used it before.
248 *
249 * @param {String} ns A string representation of an object that will hold
250 * an instance of easyXDM.
251 * @return An instance of easyXDM
252 */
253function noConflict(ns){
254 if (typeof ns != "string" || !ns) {
255 throw new Error('namespace must be a non-empty string');
256 }
257 _trace("Settings namespace to '" + ns + "'");
258
259 window.easyXDM = _easyXDM;
260 namespace = ns;
261 if (namespace) {
262 IFRAME_PREFIX = "easyXDM_" + namespace.replace(".", "_") + "_";
263 }
264 return easyXDM;
265}
266
267/*
268 * Methods for working with URLs
269 */
270/**
271 * Get the domain name from a url.
272 * @param {String} url The url to extract the domain from.
273 * @return The domain part of the url.
274 * @type {String}
275 */
276function getDomainName(url){
277 if (!url) {
278 throw new Error("url is undefined or empty");
279 }
280 return url.match(reURI)[3];
281}
282
283/**
284 * Get the port for a given URL, or "" if none
285 * @param {String} url The url to extract the port from.
286 * @return The port part of the url.
287 * @type {String}
288 */
289function getPort(url){
290 if (!url) {
291 throw new Error("url is undefined or empty");
292 }
293 return url.match(reURI)[4] || "";
294}
295
296/**
297 * Returns a string containing the schema, domain and if present the port
298 * @param {String} url The url to extract the location from
299 * @return {String} The location part of the url
300 */
301function getLocation(url){
302 if (!url) {
303 throw new Error("url is undefined or empty");
304 }
305 if (/^file/.test(url)) {
306 throw new Error("The file:// protocol is not supported");
307 }
308 var m = url.toLowerCase().match(reURI);
309 var proto = m[2], domain = m[3], port = m[4] || "";
310 if ((proto == "http:" && port == ":80") || (proto == "https:" && port == ":443")) {
311 port = "";
312 }
313 return proto + "//" + domain + port;
314}
315
316/**
317 * Resolves a relative url into an absolute one.
318 * @param {String} url The path to resolve.
319 * @return {String} The resolved url.
320 */
321function resolveUrl(url){
322 if (!url) {
323 throw new Error("url is undefined or empty");
324 }
325
326 // replace all // except the one in proto with /
327 url = url.replace(reDoubleSlash, "$1/");
328
329 // If the url is a valid url we do nothing
330 if (!url.match(/^(http||https):\/\//)) {
331 // If this is a relative path
332 var path = (url.substring(0, 1) === "/") ? "" : location.pathname;
333 if (path.substring(path.length - 1) !== "/") {
334 path = path.substring(0, path.lastIndexOf("/") + 1);
335 }
336
337 url = location.protocol + "//" + location.host + path + url;
338 }
339
340 // reduce all 'xyz/../' to just ''
341 while (reParent.test(url)) {
342 url = url.replace(reParent, "");
343 }
344
345 _trace("resolved url '" + url + "'");
346 return url;
347}
348
349/**
350 * Appends the parameters to the given url.<br/>
351 * The base url can contain existing query parameters.
352 * @param {String} url The base url.
353 * @param {Object} parameters The parameters to add.
354 * @return {String} A new valid url with the parameters appended.
355 */
356function appendQueryParameters(url, parameters){
357 if (!parameters) {
358 throw new Error("parameters is undefined or null");
359 }
360
361 var hash = "", indexOf = url.indexOf("#");
362 if (indexOf !== -1) {
363 hash = url.substring(indexOf);
364 url = url.substring(0, indexOf);
365 }
366 var q = [];
367 for (var key in parameters) {
368 if (parameters.hasOwnProperty(key)) {
369 q.push(key + "=" + encodeURIComponent(parameters[key]));
370 }
371 }
372 return url + (useHash ? "#" : (url.indexOf("?") == -1 ? "?" : "&")) + q.join("&") + hash;
373}
374
375
376// build the query object either from location.query, if it contains the xdm_e argument, or from location.hash
377var query = (function(input){
378 input = input.substring(1).split("&");
379 var data = {}, pair, i = input.length;
380 while (i--) {
381 pair = input[i].split("=");
382 data[pair[0]] = decodeURIComponent(pair[1]);
383 }
384 return data;
385}(/xdm_e=/.test(location.search) ? location.search : location.hash));
386
387/*
388 * Helper methods
389 */
390/**
391 * Helper for checking if a variable/property is undefined
392 * @param {Object} v The variable to test
393 * @return {Boolean} True if the passed variable is undefined
394 */
395function undef(v){
396 return typeof v === "undefined";
397}
398
399/**
400 * A safe implementation of HTML5 JSON. Feature testing is used to make sure the implementation works.
401 * @return {JSON} A valid JSON conforming object, or null if not found.
402 */
403var getJSON = function(){
404 var cached = {};
405 var obj = {
406 a: [1, 2, 3]
407 }, json = "{\"a\":[1,2,3]}";
408
409 if (typeof JSON != "undefined" && typeof JSON.stringify === "function" && JSON.stringify(obj).replace((/\s/g), "") === json) {
410 // this is a working JSON instance
411 return JSON;
412 }
413 if (Object.toJSON) {
414 if (Object.toJSON(obj).replace((/\s/g), "") === json) {
415 // this is a working stringify method
416 cached.stringify = Object.toJSON;
417 }
418 }
419
420 if (typeof String.prototype.evalJSON === "function") {
421 obj = json.evalJSON();
422 if (obj.a && obj.a.length === 3 && obj.a[2] === 3) {
423 // this is a working parse method
424 cached.parse = function(str){
425 return str.evalJSON();
426 };
427 }
428 }
429
430 if (cached.stringify && cached.parse) {
431 // Only memoize the result if we have valid instance
432 getJSON = function(){
433 return cached;
434 };
435 return cached;
436 }
437 return null;
438};
439
440/**
441 * Applies properties from the source object to the target object.<br/>
442 * @param {Object} target The target of the properties.
443 * @param {Object} source The source of the properties.
444 * @param {Boolean} noOverwrite Set to True to only set non-existing properties.
445 */
446function apply(destination, source, noOverwrite){
447 var member;
448 for (var prop in source) {
449 if (source.hasOwnProperty(prop)) {
450 if (prop in destination) {
451 member = source[prop];
452 if (typeof member === "object") {
453 apply(destination[prop], member, noOverwrite);
454 }
455 else if (!noOverwrite) {
456 destination[prop] = source[prop];
457 }
458 }
459 else {
460 destination[prop] = source[prop];
461 }
462 }
463 }
464 return destination;
465}
466
467// This tests for the bug in IE where setting the [name] property using javascript causes the value to be redirected into [submitName].
468function testForNamePropertyBug(){
469 var form = document.body.appendChild(document.createElement("form")), input = form.appendChild(document.createElement("input"));
470 input.name = IFRAME_PREFIX + "TEST" + channelId; // append channelId in order to avoid caching issues
471 HAS_NAME_PROPERTY_BUG = input !== form.elements[input.name];
472 document.body.removeChild(form);
473 _trace("HAS_NAME_PROPERTY_BUG: " + HAS_NAME_PROPERTY_BUG);
474}
475
476/**
477 * Creates a frame and appends it to the DOM.
478 * @param config {object} This object can have the following properties
479 * <ul>
480 * <li> {object} prop The properties that should be set on the frame. This should include the 'src' property.</li>
481 * <li> {object} attr The attributes that should be set on the frame.</li>
482 * <li> {DOMElement} container Its parent element (Optional).</li>
483 * <li> {function} onLoad A method that should be called with the frames contentWindow as argument when the frame is fully loaded. (Optional)</li>
484 * </ul>
485 * @return The frames DOMElement
486 * @type DOMElement
487 */
488function createFrame(config){
489 _trace("creating frame: " + config.props.src);
490 if (undef(HAS_NAME_PROPERTY_BUG)) {
491 testForNamePropertyBug();
492 }
493 var frame;
494 // This is to work around the problems in IE6/7 with setting the name property.
495 // Internally this is set as 'submitName' instead when using 'iframe.name = ...'
496 // This is not required by easyXDM itself, but is to facilitate other use cases
497 if (HAS_NAME_PROPERTY_BUG) {
498 frame = document.createElement("<iframe name=\"" + config.props.name + "\"/>");
499 }
500 else {
501 frame = document.createElement("IFRAME");
502 frame.name = config.props.name;
503 }
504
505 frame.id = frame.name = config.props.name;
506 delete config.props.name;
507
508 if (typeof config.container == "string") {
509 config.container = document.getElementById(config.container);
510 }
511
512 if (!config.container) {
513 // This needs to be hidden like this, simply setting display:none and the like will cause failures in some browsers.
514 apply(frame.style, {
515 position: "absolute",
516 top: "-2000px",
517 // Avoid potential horizontal scrollbar
518 left: "0px"
519 });
520 config.container = document.body;
521 }
522
523 // HACK: IE cannot have the src attribute set when the frame is appended
524 // into the container, so we set it to "javascript:false" as a
525 // placeholder for now. If we left the src undefined, it would
526 // instead default to "about:blank", which causes SSL mixed-content
527 // warnings in IE6 when on an SSL parent page.
528 var src = config.props.src;
529 config.props.src = "javascript:false";
530
531 // transfer properties to the frame
532 apply(frame, config.props);
533
534 frame.border = frame.frameBorder = 0;
535 frame.allowTransparency = true;
536 config.container.appendChild(frame);
537
538 if (config.onLoad) {
539 on(frame, "load", config.onLoad);
540 }
541
542 // set the frame URL to the proper value (we previously set it to
543 // "javascript:false" to work around the IE issue mentioned above)
544 if(config.usePost) {
545 var form = config.container.appendChild(document.createElement('form')), input;
546 form.target = frame.name;
547 form.action = src;
548 form.method = 'POST';
549 if (typeof(config.usePost) === 'object') {
550 for (var i in config.usePost) {
551 if (config.usePost.hasOwnProperty(i)) {
552 if (HAS_NAME_PROPERTY_BUG) {
553 input = document.createElement('<input name="' + i + '"/>');
554 } else {
555 input = document.createElement("INPUT");
556 input.name = i;
557 }
558 input.value = config.usePost[i];
559 form.appendChild(input);
560 }
561 }
562 }
563 form.submit();
564 form.parentNode.removeChild(form);
565 } else {
566 frame.src = src;
567 }
568 config.props.src = src;
569
570 return frame;
571}
572
573/**
574 * Check whether a domain is allowed using an Access Control List.
575 * The ACL can contain * and ? as wildcards, or can be regular expressions.
576 * If regular expressions they need to begin with ^ and end with $.
577 * @param {Array/String} acl The list of allowed domains
578 * @param {String} domain The domain to test.
579 * @return {Boolean} True if the domain is allowed, false if not.
580 */
581function checkAcl(acl, domain){
582 // normalize into an array
583 if (typeof acl == "string") {
584 acl = [acl];
585 }
586 var re, i = acl.length;
587 while (i--) {
588 re = acl[i];
589 re = new RegExp(re.substr(0, 1) == "^" ? re : ("^" + re.replace(/(\*)/g, ".$1").replace(/\?/g, ".") + "$"));
590 if (re.test(domain)) {
591 return true;
592 }
593 }
594 return false;
595}
596
597/*
598 * Functions related to stacks
599 */
600/**
601 * Prepares an array of stack-elements suitable for the current configuration
602 * @param {Object} config The Transports configuration. See easyXDM.Socket for more.
603 * @return {Array} An array of stack-elements with the TransportElement at index 0.
604 */
605function prepareTransportStack(config){
606 var protocol = config.protocol, stackEls;
607 config.isHost = config.isHost || undef(query.xdm_p);
608 useHash = config.hash || false;
609 _trace("preparing transport stack");
610
611 if (!config.props) {
612 config.props = {};
613 }
614 if (!config.isHost) {
615 _trace("using parameters from query");
616 config.channel = query.xdm_c.replace(/["'<>\\]/g, "");
617 config.secret = query.xdm_s;
618 config.remote = query.xdm_e.replace(/["'<>\\]/g, "");
619 ;
620 protocol = query.xdm_p;
621 if (config.acl && !checkAcl(config.acl, config.remote)) {
622 throw new Error("Access denied for " + config.remote);
623 }
624 }
625 else {
626 config.remote = resolveUrl(config.remote);
627 config.channel = config.channel || "default" + channelId++;
628 config.secret = Math.random().toString(16).substring(2);
629 if (undef(protocol)) {
630 if (getLocation(location.href) == getLocation(config.remote)) {
631 /*
632 * Both documents has the same origin, lets use direct access.
633 */
634 protocol = "4";
635 }
636 else if (isHostMethod(window, "postMessage") || isHostMethod(document, "postMessage")) {
637 /*
638 * This is supported in IE8+, Firefox 3+, Opera 9+, Chrome 2+ and Safari 4+
639 */
640 protocol = "1";
641 }
642 else if (config.swf && isHostMethod(window, "ActiveXObject") && hasFlash()) {
643 /*
644 * The Flash transport superseedes the NixTransport as the NixTransport has been blocked by MS
645 */
646 protocol = "6";
647 }
648 else if (navigator.product === "Gecko" && "frameElement" in window && navigator.userAgent.indexOf('WebKit') == -1) {
649 /*
650 * This is supported in Gecko (Firefox 1+)
651 */
652 protocol = "5";
653 }
654 else if (config.remoteHelper) {
655 /*
656 * This is supported in all browsers that retains the value of window.name when
657 * navigating from one domain to another, and where parent.frames[foo] can be used
658 * to get access to a frame from the same domain
659 */
660 protocol = "2";
661 }
662 else {
663 /*
664 * This is supported in all browsers where [window].location is writable for all
665 * The resize event will be used if resize is supported and the iframe is not put
666 * into a container, else polling will be used.
667 */
668 protocol = "0";
669 }
670 _trace("selecting protocol: " + protocol);
671 }
672 else {
673 _trace("using protocol: " + protocol);
674 }
675 }
676 config.protocol = protocol; // for conditional branching
677 switch (protocol) {
678 case "0":// 0 = HashTransport
679 apply(config, {
680 interval: 100,
681 delay: 2000,
682 useResize: true,
683 useParent: false,
684 usePolling: false
685 }, true);
686 if (config.isHost) {
687 if (!config.local) {
688 _trace("looking for image to use as local");
689 // If no local is set then we need to find an image hosted on the current domain
690 var domain = location.protocol + "//" + location.host, images = document.body.getElementsByTagName("img"), image;
691 var i = images.length;
692 while (i--) {
693 image = images[i];
694 if (image.src.substring(0, domain.length) === domain) {
695 config.local = image.src;
696 break;
697 }
698 }
699 if (!config.local) {
700 _trace("no image found, defaulting to using the window");
701 // If no local was set, and we are unable to find a suitable file, then we resort to using the current window
702 config.local = window;
703 }
704 }
705
706 var parameters = {
707 xdm_c: config.channel,
708 xdm_p: 0
709 };
710
711 if (config.local === window) {
712 // We are using the current window to listen to
713 config.usePolling = true;
714 config.useParent = true;
715 config.local = location.protocol + "//" + location.host + location.pathname + location.search;
716 parameters.xdm_e = config.local;
717 parameters.xdm_pa = 1; // use parent
718 }
719 else {
720 parameters.xdm_e = resolveUrl(config.local);
721 }
722
723 if (config.container) {
724 config.useResize = false;
725 parameters.xdm_po = 1; // use polling
726 }
727 config.remote = appendQueryParameters(config.remote, parameters);
728 }
729 else {
730 apply(config, {
731 channel: query.xdm_c,
732 remote: query.xdm_e,
733 useParent: !undef(query.xdm_pa),
734 usePolling: !undef(query.xdm_po),
735 useResize: config.useParent ? false : config.useResize
736 });
737 }
738 stackEls = [new easyXDM.stack.HashTransport(config), new easyXDM.stack.ReliableBehavior({}), new easyXDM.stack.QueueBehavior({
739 encode: true,
740 maxLength: 4000 - config.remote.length
741 }), new easyXDM.stack.VerifyBehavior({
742 initiate: config.isHost
743 })];
744 break;
745 case "1":
746 stackEls = [new easyXDM.stack.PostMessageTransport(config)];
747 break;
748 case "2":
749 config.remoteHelper = resolveUrl(config.remoteHelper);
750 stackEls = [new easyXDM.stack.NameTransport(config), new easyXDM.stack.QueueBehavior(), new easyXDM.stack.VerifyBehavior({
751 initiate: config.isHost
752 })];
753 break;
754 case "3":
755 stackEls = [new easyXDM.stack.NixTransport(config)];
756 break;
757 case "4":
758 stackEls = [new easyXDM.stack.SameOriginTransport(config)];
759 break;
760 case "5":
761 stackEls = [new easyXDM.stack.FrameElementTransport(config)];
762 break;
763 case "6":
764 if (!flashVersion) {
765 hasFlash();
766 }
767 stackEls = [new easyXDM.stack.FlashTransport(config)];
768 break;
769 }
770 // this behavior is responsible for buffering outgoing messages, and for performing lazy initialization
771 stackEls.push(new easyXDM.stack.QueueBehavior({
772 lazy: config.lazy,
773 remove: true
774 }));
775 return stackEls;
776}
777
778/**
779 * Chains all the separate stack elements into a single usable stack.<br/>
780 * If an element is missing a necessary method then it will have a pass-through method applied.
781 * @param {Array} stackElements An array of stack elements to be linked.
782 * @return {easyXDM.stack.StackElement} The last element in the chain.
783 */
784function chainStack(stackElements){
785 var stackEl, defaults = {
786 incoming: function(message, origin){
787 this.up.incoming(message, origin);
788 },
789 outgoing: function(message, recipient){
790 this.down.outgoing(message, recipient);
791 },
792 callback: function(success){
793 this.up.callback(success);
794 },
795 init: function(){
796 this.down.init();
797 },
798 destroy: function(){
799 this.down.destroy();
800 }
801 };
802 for (var i = 0, len = stackElements.length; i < len; i++) {
803 stackEl = stackElements[i];
804 apply(stackEl, defaults, true);
805 if (i !== 0) {
806 stackEl.down = stackElements[i - 1];
807 }
808 if (i !== len - 1) {
809 stackEl.up = stackElements[i + 1];
810 }
811 }
812 return stackEl;
813}
814
815/**
816 * This will remove a stackelement from its stack while leaving the stack functional.
817 * @param {Object} element The elment to remove from the stack.
818 */
819function removeFromStack(element){
820 element.up.down = element.down;
821 element.down.up = element.up;
822 element.up = element.down = null;
823}
824
825/*
826 * Export the main object and any other methods applicable
827 */
828/**
829 * @class easyXDM
830 * A javascript library providing cross-browser, cross-domain messaging/RPC.
831 * @version 2.4.17.1
832 * @singleton
833 */
834apply(easyXDM, {
835 /**
836 * The version of the library
837 * @type {string}
838 */
839 version: "2.4.17.1",
840 /**
841 * This is a map containing all the query parameters passed to the document.
842 * All the values has been decoded using decodeURIComponent.
843 * @type {object}
844 */
845 query: query,
846 /**
847 * @private
848 */
849 stack: {},
850 /**
851 * Applies properties from the source object to the target object.<br/>
852 * @param {object} target The target of the properties.
853 * @param {object} source The source of the properties.
854 * @param {boolean} noOverwrite Set to True to only set non-existing properties.
855 */
856 apply: apply,
857
858 /**
859 * A safe implementation of HTML5 JSON. Feature testing is used to make sure the implementation works.
860 * @return {JSON} A valid JSON conforming object, or null if not found.
861 */
862 getJSONObject: getJSON,
863 /**
864 * This will add a function to the queue of functions to be run once the DOM reaches a ready state.
865 * If functions are added after this event then they will be executed immediately.
866 * @param {function} fn The function to add
867 * @param {object} scope An optional scope for the function to be called with.
868 */
869 whenReady: whenReady,
870 /**
871 * Removes easyXDM variable from the global scope. It also returns control
872 * of the easyXDM variable to whatever code used it before.
873 *
874 * @param {String} ns A string representation of an object that will hold
875 * an instance of easyXDM.
876 * @return An instance of easyXDM
877 */
878 noConflict: noConflict
879});
880
881// Expose helper functions so we can test them
882apply(easyXDM, {
883 checkAcl: checkAcl,
884 getDomainName: getDomainName,
885 getLocation: getLocation,
886 appendQueryParameters: appendQueryParameters
887});
888/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
889/*global console, _FirebugCommandLine, easyXDM, window, escape, unescape, isHostObject, undef, _trace, domIsReady, emptyFn, namespace */
890//
891// easyXDM
892// http://easyxdm.net/
893// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
894//
895// Permission is hereby granted, free of charge, to any person obtaining a copy
896// of this software and associated documentation files (the "Software"), to deal
897// in the Software without restriction, including without limitation the rights
898// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
899// copies of the Software, and to permit persons to whom the Software is
900// furnished to do so, subject to the following conditions:
901//
902// The above copyright notice and this permission notice shall be included in
903// all copies or substantial portions of the Software.
904//
905// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
906// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
907// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
908// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
909// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
910// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
911// THE SOFTWARE.
912//
913
914var debug = {
915 _deferred: [],
916 flush: function(){
917 this.trace("... deferred messages ...");
918 for (var i = 0, len = this._deferred.length; i < len; i++) {
919 this.trace(this._deferred[i]);
920 }
921 this._deferred.length = 0;
922 this.trace("... end of deferred messages ...");
923 },
924 getTime: function(){
925 var d = new Date(), h = d.getHours() + "", m = d.getMinutes() + "", s = d.getSeconds() + "", ms = d.getMilliseconds() + "", zeros = "000";
926 if (h.length == 1) {
927 h = "0" + h;
928 }
929 if (m.length == 1) {
930 m = "0" + m;
931 }
932 if (s.length == 1) {
933 s = "0" + s;
934 }
935 ms = zeros.substring(ms.length) + ms;
936 return h + ":" + m + ":" + s + "." + ms;
937 },
938 /**
939 * Logs the message to console.log if available
940 * @param {String} msg The message to log
941 */
942 log: function(msg){
943 // Uses memoizing to cache the implementation
944 if (!isHostObject(window, "console") || undef(console.log)) {
945 /**
946 * Sets log to be an empty function since we have no output available
947 * @ignore
948 */
949 this.log = emptyFn;
950 }
951 else {
952 /**
953 * Sets log to be a wrapper around console.log
954 * @ignore
955 * @param {String} msg
956 */
957 this.log = function(msg){
958 console.log(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ": " + msg);
959 };
960 }
961 this.log(msg);
962 },
963 /**
964 * Will try to trace the given message either to a DOMElement with the id "log",
965 * or by using console.info.
966 * @param {String} msg The message to trace
967 */
968 trace: function(msg){
969 // Uses memoizing to cache the implementation
970 if (!domIsReady) {
971 if (this._deferred.length === 0) {
972 easyXDM.whenReady(debug.flush, debug);
973 }
974 this._deferred.push(msg);
975 this.log(msg);
976 }
977 else {
978 var el = document.getElementById("log");
979 // is there a log element present?
980 if (el) {
981 /**
982 * Sets trace to be a function that outputs the messages to the DOMElement with id "log"
983 * @ignore
984 * @param {String} msg
985 */
986 this.trace = function(msg){
987 try {
988 el.appendChild(document.createElement("div")).appendChild(document.createTextNode(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ":" + msg));
989 el.scrollTop = el.scrollHeight;
990 }
991 catch (e) {
992 //In case we are unloading
993 }
994 };
995 }
996 else if (isHostObject(window, "console") && !undef(console.info)) {
997 /**
998 * Sets trace to be a wrapper around console.info
999 * @ignore
1000 * @param {String} msg
1001 */
1002 this.trace = function(msg){
1003 console.info(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ":" + msg);
1004 };
1005 }
1006 else {
1007 /**
1008 * Create log window
1009 * @ignore
1010 */
1011 var domain = location.host, windowname = domain.replace(/[\-.:]/g, "") + "easyxdm_log", logWin;
1012 try {
1013 logWin = window.open("", windowname, "width=800,height=200,status=0,navigation=0,scrollbars=1");
1014 }
1015 catch (e) {
1016 }
1017 if (logWin) {
1018 var doc = logWin.document;
1019 el = doc.getElementById("log");
1020 if (!el) {
1021 doc.write("<html><head><title>easyXDM log " + domain + "</title></head>");
1022 doc.write("<body><div id=\"log\"></div></body></html>");
1023 doc.close();
1024 el = doc.getElementById("log");
1025 }
1026 this.trace = function(msg){
1027 try {
1028 el.appendChild(doc.createElement("div")).appendChild(doc.createTextNode(location.host + (namespace ? ":" + namespace : "") + " - " + this.getTime() + ":" + msg));
1029 el.scrollTop = el.scrollHeight;
1030 }
1031 catch (e) {
1032 //In case we are unloading
1033 }
1034 };
1035 this.trace("---- new logger at " + location.href);
1036 }
1037
1038 if (!el) {
1039 // We are unable to use any logging
1040 this.trace = emptyFn;
1041 }
1042 }
1043 this.trace(msg);
1044 }
1045 },
1046 /**
1047 * Creates a method usable for tracing.
1048 * @param {String} name The name the messages should be marked with
1049 * @return {Function} A function that accepts a single string as argument.
1050 */
1051 getTracer: function(name){
1052 return function(msg){
1053 debug.trace(name + ": " + msg);
1054 };
1055 }
1056};
1057debug.log("easyXDM present on '" + location.href);
1058easyXDM.Debug = debug;
1059_trace = debug.getTracer("{Private}");
1060/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1061/*global easyXDM, window, escape, unescape, isHostObject, isHostMethod, un, on, createFrame, debug */
1062//
1063// easyXDM
1064// http://easyxdm.net/
1065// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1066//
1067// Permission is hereby granted, free of charge, to any person obtaining a copy
1068// of this software and associated documentation files (the "Software"), to deal
1069// in the Software without restriction, including without limitation the rights
1070// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1071// copies of the Software, and to permit persons to whom the Software is
1072// furnished to do so, subject to the following conditions:
1073//
1074// The above copyright notice and this permission notice shall be included in
1075// all copies or substantial portions of the Software.
1076//
1077// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1078// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1079// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1080// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1081// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1082// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1083// THE SOFTWARE.
1084//
1085
1086/**
1087 * @class easyXDM.DomHelper
1088 * Contains methods for dealing with the DOM
1089 * @singleton
1090 */
1091easyXDM.DomHelper = {
1092 /**
1093 * Provides a consistent interface for adding eventhandlers
1094 * @param {Object} target The target to add the event to
1095 * @param {String} type The name of the event
1096 * @param {Function} listener The listener
1097 */
1098 on: on,
1099 /**
1100 * Provides a consistent interface for removing eventhandlers
1101 * @param {Object} target The target to remove the event from
1102 * @param {String} type The name of the event
1103 * @param {Function} listener The listener
1104 */
1105 un: un,
1106 /**
1107 * Checks for the presence of the JSON object.
1108 * If it is not present it will use the supplied path to load the JSON2 library.
1109 * This should be called in the documents head right after the easyXDM script tag.
1110 * http://json.org/json2.js
1111 * @param {String} path A valid path to json2.js
1112 */
1113 requiresJSON: function(path){
1114 if (!isHostObject(window, "JSON")) {
1115 debug.log("loading external JSON");
1116 // we need to encode the < in order to avoid an illegal token error
1117 // when the script is inlined in a document.
1118 document.write('<' + 'script type="text/javascript" src="' + path + '"><' + '/script>');
1119 }
1120 else {
1121 debug.log("native JSON found");
1122 }
1123 }
1124};
1125/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1126/*global easyXDM, window, escape, unescape, debug */
1127//
1128// easyXDM
1129// http://easyxdm.net/
1130// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1131//
1132// Permission is hereby granted, free of charge, to any person obtaining a copy
1133// of this software and associated documentation files (the "Software"), to deal
1134// in the Software without restriction, including without limitation the rights
1135// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1136// copies of the Software, and to permit persons to whom the Software is
1137// furnished to do so, subject to the following conditions:
1138//
1139// The above copyright notice and this permission notice shall be included in
1140// all copies or substantial portions of the Software.
1141//
1142// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1143// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1144// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1145// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1146// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1147// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1148// THE SOFTWARE.
1149//
1150
1151(function(){
1152 // The map containing the stored functions
1153 var _map = {};
1154
1155 /**
1156 * @class easyXDM.Fn
1157 * This contains methods related to function handling, such as storing callbacks.
1158 * @singleton
1159 * @namespace easyXDM
1160 */
1161 easyXDM.Fn = {
1162 /**
1163 * Stores a function using the given name for reference
1164 * @param {String} name The name that the function should be referred by
1165 * @param {Function} fn The function to store
1166 * @namespace easyXDM.fn
1167 */
1168 set: function(name, fn){
1169 this._trace("storing function " + name);
1170 _map[name] = fn;
1171 },
1172 /**
1173 * Retrieves the function referred to by the given name
1174 * @param {String} name The name of the function to retrieve
1175 * @param {Boolean} del If the function should be deleted after retrieval
1176 * @return {Function} The stored function
1177 * @namespace easyXDM.fn
1178 */
1179 get: function(name, del){
1180 this._trace("retrieving function " + name);
1181 var fn = _map[name];
1182 if (!fn) {
1183 this._trace(name + " not found");
1184 }
1185
1186 if (del) {
1187 delete _map[name];
1188 }
1189 return fn;
1190 }
1191 };
1192
1193 easyXDM.Fn._trace = debug.getTracer("easyXDM.Fn");
1194}());
1195/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1196/*global easyXDM, window, escape, unescape, chainStack, prepareTransportStack, getLocation, debug */
1197//
1198// easyXDM
1199// http://easyxdm.net/
1200// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1201//
1202// Permission is hereby granted, free of charge, to any person obtaining a copy
1203// of this software and associated documentation files (the "Software"), to deal
1204// in the Software without restriction, including without limitation the rights
1205// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1206// copies of the Software, and to permit persons to whom the Software is
1207// furnished to do so, subject to the following conditions:
1208//
1209// The above copyright notice and this permission notice shall be included in
1210// all copies or substantial portions of the Software.
1211//
1212// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1213// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1214// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1215// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1216// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1217// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1218// THE SOFTWARE.
1219//
1220
1221/**
1222 * @class easyXDM.Socket
1223 * This class creates a transport channel between two domains that is usable for sending and receiving string-based messages.<br/>
1224 * The channel is reliable, supports queueing, and ensures that the message originates from the expected domain.<br/>
1225 * Internally different stacks will be used depending on the browsers features and the available parameters.
1226 * <h2>How to set up</h2>
1227 * Setting up the provider:
1228 * <pre><code>
1229 * var socket = new easyXDM.Socket({
1230 * &nbsp; local: "name.html",
1231 * &nbsp; onReady: function(){
1232 * &nbsp; &nbsp; &#47;&#47; you need to wait for the onReady callback before using the socket
1233 * &nbsp; &nbsp; socket.postMessage("foo-message");
1234 * &nbsp; },
1235 * &nbsp; onMessage: function(message, origin) {
1236 * &nbsp;&nbsp; alert("received " + message + " from " + origin);
1237 * &nbsp; }
1238 * });
1239 * </code></pre>
1240 * Setting up the consumer:
1241 * <pre><code>
1242 * var socket = new easyXDM.Socket({
1243 * &nbsp; remote: "http:&#47;&#47;remotedomain/page.html",
1244 * &nbsp; remoteHelper: "http:&#47;&#47;remotedomain/name.html",
1245 * &nbsp; onReady: function(){
1246 * &nbsp; &nbsp; &#47;&#47; you need to wait for the onReady callback before using the socket
1247 * &nbsp; &nbsp; socket.postMessage("foo-message");
1248 * &nbsp; },
1249 * &nbsp; onMessage: function(message, origin) {
1250 * &nbsp;&nbsp; alert("received " + message + " from " + origin);
1251 * &nbsp; }
1252 * });
1253 * </code></pre>
1254 * If you are unable to upload the <code>name.html</code> file to the consumers domain then remove the <code>remoteHelper</code> property
1255 * and easyXDM will fall back to using the HashTransport instead of the NameTransport when not able to use any of the primary transports.
1256 * @namespace easyXDM
1257 * @constructor
1258 * @cfg {String/Window} local The url to the local name.html document, a local static file, or a reference to the local window.
1259 * @cfg {Boolean} lazy (Consumer only) Set this to true if you want easyXDM to defer creating the transport until really needed.
1260 * @cfg {String} remote (Consumer only) The url to the providers document.
1261 * @cfg {String} remoteHelper (Consumer only) The url to the remote name.html file. This is to support NameTransport as a fallback. Optional.
1262 * @cfg {Number} delay The number of milliseconds easyXDM should try to get a reference to the local window. Optional, defaults to 2000.
1263 * @cfg {Number} interval The interval used when polling for messages. Optional, defaults to 300.
1264 * @cfg {String} channel (Consumer only) The name of the channel to use. Can be used to set consistent iframe names. Must be unique. Optional.
1265 * @cfg {Function} onMessage The method that should handle incoming messages.<br/> This method should accept two arguments, the message as a string, and the origin as a string. Optional.
1266 * @cfg {Function} onReady A method that should be called when the transport is ready. Optional.
1267 * @cfg {DOMElement|String} container (Consumer only) The element, or the id of the element that the primary iframe should be inserted into. If not set then the iframe will be positioned off-screen. Optional.
1268 * @cfg {Array/String} acl (Provider only) Here you can specify which '[protocol]://[domain]' patterns that should be allowed to act as the consumer towards this provider.<br/>
1269 * This can contain the wildcards ? and *. Examples are 'http://example.com', '*.foo.com' and '*dom?.com'. If you want to use reqular expressions then you pattern needs to start with ^ and end with $.
1270 * If none of the patterns match an Error will be thrown.
1271 * @cfg {Object} props (Consumer only) Additional properties that should be applied to the iframe. This can also contain nested objects e.g: <code>{style:{width:"100px", height:"100px"}}</code>.
1272 * Properties such as 'name' and 'src' will be overrided. Optional.
1273 */
1274easyXDM.Socket = function(config){
1275 var trace = debug.getTracer("easyXDM.Socket");
1276 trace("constructor");
1277
1278 // create the stack
1279 var stack = chainStack(prepareTransportStack(config).concat([{
1280 incoming: function(message, origin){
1281 config.onMessage(message, origin);
1282 },
1283 callback: function(success){
1284 if (config.onReady) {
1285 config.onReady(success);
1286 }
1287 }
1288 }])), recipient = getLocation(config.remote);
1289
1290 // set the origin
1291 this.origin = getLocation(config.remote);
1292
1293 /**
1294 * Initiates the destruction of the stack.
1295 */
1296 this.destroy = function(){
1297 stack.destroy();
1298 };
1299
1300 /**
1301 * Posts a message to the remote end of the channel
1302 * @param {String} message The message to send
1303 */
1304 this.postMessage = function(message){
1305 stack.outgoing(message, recipient);
1306 };
1307
1308 stack.init();
1309};
1310/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1311/*global easyXDM, window, escape, unescape, undef,, chainStack, prepareTransportStack, debug, getLocation */
1312//
1313// easyXDM
1314// http://easyxdm.net/
1315// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1316//
1317// Permission is hereby granted, free of charge, to any person obtaining a copy
1318// of this software and associated documentation files (the "Software"), to deal
1319// in the Software without restriction, including without limitation the rights
1320// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1321// copies of the Software, and to permit persons to whom the Software is
1322// furnished to do so, subject to the following conditions:
1323//
1324// The above copyright notice and this permission notice shall be included in
1325// all copies or substantial portions of the Software.
1326//
1327// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1328// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1329// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1330// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1331// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1332// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1333// THE SOFTWARE.
1334//
1335
1336/**
1337 * @class easyXDM.Rpc
1338 * Creates a proxy object that can be used to call methods implemented on the remote end of the channel, and also to provide the implementation
1339 * of methods to be called from the remote end.<br/>
1340 * The instantiated object will have methods matching those specified in <code>config.remote</code>.<br/>
1341 * This requires the JSON object present in the document, either natively, using json.org's json2 or as a wrapper around library spesific methods.
1342 * <h2>How to set up</h2>
1343 * <pre><code>
1344 * var rpc = new easyXDM.Rpc({
1345 * &nbsp; &#47;&#47; this configuration is equal to that used by the Socket.
1346 * &nbsp; remote: "http:&#47;&#47;remotedomain/...",
1347 * &nbsp; onReady: function(){
1348 * &nbsp; &nbsp; &#47;&#47; you need to wait for the onReady callback before using the proxy
1349 * &nbsp; &nbsp; rpc.foo(...
1350 * &nbsp; }
1351 * },{
1352 * &nbsp; local: {..},
1353 * &nbsp; remote: {..}
1354 * });
1355 * </code></pre>
1356 *
1357 * <h2>Exposing functions (procedures)</h2>
1358 * <pre><code>
1359 * var rpc = new easyXDM.Rpc({
1360 * &nbsp; ...
1361 * },{
1362 * &nbsp; local: {
1363 * &nbsp; &nbsp; nameOfMethod: {
1364 * &nbsp; &nbsp; &nbsp; method: function(arg1, arg2, success, error){
1365 * &nbsp; &nbsp; &nbsp; &nbsp; ...
1366 * &nbsp; &nbsp; &nbsp; }
1367 * &nbsp; &nbsp; },
1368 * &nbsp; &nbsp; &#47;&#47; with shorthand notation
1369 * &nbsp; &nbsp; nameOfAnotherMethod: function(arg1, arg2, success, error){
1370 * &nbsp; &nbsp; }
1371 * &nbsp; },
1372 * &nbsp; remote: {...}
1373 * });
1374 * </code></pre>
1375
1376 * The function referenced by [method] will receive the passed arguments followed by the callback functions <code>success</code> and <code>error</code>.<br/>
1377 * To send a successfull result back you can use
1378 * <pre><code>
1379 * return foo;
1380 * </pre></code>
1381 * or
1382 * <pre><code>
1383 * success(foo);
1384 * </pre></code>
1385 * To return an error you can use
1386 * <pre><code>
1387 * throw new Error("foo error");
1388 * </code></pre>
1389 * or
1390 * <pre><code>
1391 * error("foo error");
1392 * </code></pre>
1393 *
1394 * <h2>Defining remotely exposed methods (procedures/notifications)</h2>
1395 * The definition of the remote end is quite similar:
1396 * <pre><code>
1397 * var rpc = new easyXDM.Rpc({
1398 * &nbsp; ...
1399 * },{
1400 * &nbsp; local: {...},
1401 * &nbsp; remote: {
1402 * &nbsp; &nbsp; nameOfMethod: {}
1403 * &nbsp; }
1404 * });
1405 * </code></pre>
1406 * To call a remote method use
1407 * <pre><code>
1408 * rpc.nameOfMethod("arg1", "arg2", function(value) {
1409 * &nbsp; alert("success: " + value);
1410 * }, function(message) {
1411 * &nbsp; alert("error: " + message + );
1412 * });
1413 * </code></pre>
1414 * Both the <code>success</code> and <code>errror</code> callbacks are optional.<br/>
1415 * When called with no callback a JSON-RPC 2.0 notification will be executed.
1416 * Be aware that you will not be notified of any errors with this method.
1417 * <br/>
1418 * <h2>Specifying a custom serializer</h2>
1419 * If you do not want to use the JSON2 library for non-native JSON support, but instead capabilities provided by some other library
1420 * then you can specify a custom serializer using <code>serializer: foo</code>
1421 * <pre><code>
1422 * var rpc = new easyXDM.Rpc({
1423 * &nbsp; ...
1424 * },{
1425 * &nbsp; local: {...},
1426 * &nbsp; remote: {...},
1427 * &nbsp; serializer : {
1428 * &nbsp; &nbsp; parse: function(string){ ... },
1429 * &nbsp; &nbsp; stringify: function(object) {...}
1430 * &nbsp; }
1431 * });
1432 * </code></pre>
1433 * If <code>serializer</code> is set then the class will not attempt to use the native implementation.
1434 * @namespace easyXDM
1435 * @constructor
1436 * @param {Object} config The underlying transports configuration. See easyXDM.Socket for available parameters.
1437 * @param {Object} jsonRpcConfig The description of the interface to implement.
1438 */
1439easyXDM.Rpc = function(config, jsonRpcConfig){
1440 var trace = debug.getTracer("easyXDM.Rpc");
1441 trace("constructor");
1442
1443 // expand shorthand notation
1444 if (jsonRpcConfig.local) {
1445 for (var method in jsonRpcConfig.local) {
1446 if (jsonRpcConfig.local.hasOwnProperty(method)) {
1447 var member = jsonRpcConfig.local[method];
1448 if (typeof member === "function") {
1449 jsonRpcConfig.local[method] = {
1450 method: member
1451 };
1452 }
1453 }
1454 }
1455 }
1456
1457 // create the stack
1458 var stack = chainStack(prepareTransportStack(config).concat([new easyXDM.stack.RpcBehavior(this, jsonRpcConfig), {
1459 callback: function(success){
1460 if (config.onReady) {
1461 config.onReady(success);
1462 }
1463 }
1464 }]));
1465
1466 // set the origin
1467 this.origin = getLocation(config.remote);
1468
1469
1470 /**
1471 * Initiates the destruction of the stack.
1472 */
1473 this.destroy = function(){
1474 stack.destroy();
1475 };
1476
1477 stack.init();
1478};
1479/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1480/*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, un, on, apply, whenReady, getParentObject, IFRAME_PREFIX*/
1481//
1482// easyXDM
1483// http://easyxdm.net/
1484// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1485//
1486// Permission is hereby granted, free of charge, to any person obtaining a copy
1487// of this software and associated documentation files (the "Software"), to deal
1488// in the Software without restriction, including without limitation the rights
1489// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1490// copies of the Software, and to permit persons to whom the Software is
1491// furnished to do so, subject to the following conditions:
1492//
1493// The above copyright notice and this permission notice shall be included in
1494// all copies or substantial portions of the Software.
1495//
1496// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1497// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1498// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1499// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1500// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1501// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1502// THE SOFTWARE.
1503//
1504
1505/**
1506 * @class easyXDM.stack.SameOriginTransport
1507 * SameOriginTransport is a transport class that can be used when both domains have the same origin.<br/>
1508 * This can be useful for testing and for when the main application supports both internal and external sources.
1509 * @namespace easyXDM.stack
1510 * @constructor
1511 * @param {Object} config The transports configuration.
1512 * @cfg {String} remote The remote document to communicate with.
1513 */
1514easyXDM.stack.SameOriginTransport = function(config){
1515 var trace = debug.getTracer("easyXDM.stack.SameOriginTransport");
1516 trace("constructor");
1517 var pub, frame, send, targetOrigin;
1518
1519 return (pub = {
1520 outgoing: function(message, domain, fn){
1521 send(message);
1522 if (fn) {
1523 fn();
1524 }
1525 },
1526 destroy: function(){
1527 trace("destroy");
1528 if (frame) {
1529 frame.parentNode.removeChild(frame);
1530 frame = null;
1531 }
1532 },
1533 onDOMReady: function(){
1534 trace("init");
1535 targetOrigin = getLocation(config.remote);
1536
1537 if (config.isHost) {
1538 // set up the iframe
1539 apply(config.props, {
1540 src: appendQueryParameters(config.remote, {
1541 xdm_e: location.protocol + "//" + location.host + location.pathname,
1542 xdm_c: config.channel,
1543 xdm_p: 4 // 4 = SameOriginTransport
1544 }),
1545 name: IFRAME_PREFIX + config.channel + "_provider"
1546 });
1547 frame = createFrame(config);
1548 easyXDM.Fn.set(config.channel, function(sendFn){
1549 send = sendFn;
1550 setTimeout(function(){
1551 pub.up.callback(true);
1552 }, 0);
1553 return function(msg){
1554 pub.up.incoming(msg, targetOrigin);
1555 };
1556 });
1557 }
1558 else {
1559 send = getParentObject().Fn.get(config.channel, true)(function(msg){
1560 pub.up.incoming(msg, targetOrigin);
1561 });
1562 setTimeout(function(){
1563 pub.up.callback(true);
1564 }, 0);
1565 }
1566 },
1567 init: function(){
1568 whenReady(pub.onDOMReady, pub);
1569 }
1570 });
1571};
1572/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1573/*global global, easyXDM, window, getLocation, appendQueryParameters, createFrame, debug, apply, whenReady, IFRAME_PREFIX, namespace, resolveUrl, getDomainName, HAS_FLASH_THROTTLED_BUG, getPort, query*/
1574//
1575// easyXDM
1576// http://easyxdm.net/
1577// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1578//
1579// Permission is hereby granted, free of charge, to any person obtaining a copy
1580// of this software and associated documentation files (the "Software"), to deal
1581// in the Software without restriction, including without limitation the rights
1582// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1583// copies of the Software, and to permit persons to whom the Software is
1584// furnished to do so, subject to the following conditions:
1585//
1586// The above copyright notice and this permission notice shall be included in
1587// all copies or substantial portions of the Software.
1588//
1589// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1590// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1591// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1592// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1593// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1594// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1595// THE SOFTWARE.
1596//
1597
1598/**
1599 * @class easyXDM.stack.FlashTransport
1600 * FlashTransport is a transport class that uses an SWF with LocalConnection to pass messages back and forth.
1601 * @namespace easyXDM.stack
1602 * @constructor
1603 * @param {Object} config The transports configuration.
1604 * @cfg {String} remote The remote domain to communicate with.
1605 * @cfg {String} secret the pre-shared secret used to secure the communication.
1606 * @cfg {String} swf The path to the swf file
1607 * @cfg {Boolean} swfNoThrottle Set this to true if you want to take steps to avoid beeing throttled when hidden.
1608 * @cfg {String || DOMElement} swfContainer Set this if you want to control where the swf is placed
1609 */
1610easyXDM.stack.FlashTransport = function(config){
1611 var trace = debug.getTracer("easyXDM.stack.FlashTransport");
1612 trace("constructor");
1613 if (!config.swf) {
1614 throw new Error("Path to easyxdm.swf is missing");
1615 }
1616 var pub, // the public interface
1617 frame, send, targetOrigin, swf, swfContainer;
1618
1619 function onMessage(message, origin){
1620 setTimeout(function(){
1621 trace("received message");
1622 pub.up.incoming(message, targetOrigin);
1623 }, 0);
1624 }
1625
1626 /**
1627 * This method adds the SWF to the DOM and prepares the initialization of the channel
1628 */
1629 function addSwf(domain){
1630 trace("creating factory with SWF from " + domain);
1631 // the differentiating query argument is needed in Flash9 to avoid a caching issue where LocalConnection would throw an error.
1632 var url = config.swf + "?host=" + config.isHost;
1633 var id = "easyXDM_swf_" + Math.floor(Math.random() * 10000);
1634
1635 // prepare the init function that will fire once the swf is ready
1636 easyXDM.Fn.set("flash_loaded" + domain.replace(/[\-.]/g, "_"), function(){
1637 easyXDM.stack.FlashTransport[domain].swf = swf = swfContainer.firstChild;
1638 var queue = easyXDM.stack.FlashTransport[domain].queue;
1639 for (var i = 0; i < queue.length; i++) {
1640 queue[i]();
1641 }
1642 queue.length = 0;
1643 });
1644
1645 if (config.swfContainer) {
1646 swfContainer = (typeof config.swfContainer == "string") ? document.getElementById(config.swfContainer) : config.swfContainer;
1647 }
1648 else {
1649 // create the container that will hold the swf
1650 swfContainer = document.createElement('div');
1651
1652 // http://bugs.adobe.com/jira/browse/FP-4796
1653 // http://tech.groups.yahoo.com/group/flexcoders/message/162365
1654 // https://groups.google.com/forum/#!topic/easyxdm/mJZJhWagoLc
1655 apply(swfContainer.style, HAS_FLASH_THROTTLED_BUG && config.swfNoThrottle ? {
1656 height: "20px",
1657 width: "20px",
1658 position: "fixed",
1659 right: 0,
1660 top: 0
1661 } : {
1662 height: "1px",
1663 width: "1px",
1664 position: "absolute",
1665 overflow: "hidden",
1666 right: 0,
1667 top: 0
1668 });
1669 document.body.appendChild(swfContainer);
1670 }
1671
1672 // create the object/embed
1673 var flashVars = "callback=flash_loaded" + domain.replace(/[\-.]/g, "_") + "&proto=" + global.location.protocol + "&domain=" + getDomainName(global.location.href) + "&port=" + getPort(global.location.href) + "&ns=" + namespace;
1674 flashVars += "&log=true";
1675 swfContainer.innerHTML = "<object height='20' width='20' type='application/x-shockwave-flash' id='" + id + "' data='" + url + "'>" +
1676 "<param name='allowScriptAccess' value='always'></param>" +
1677 "<param name='wmode' value='transparent'>" +
1678 "<param name='movie' value='" +
1679 url +
1680 "'></param>" +
1681 "<param name='flashvars' value='" +
1682 flashVars +
1683 "'></param>" +
1684 "<embed type='application/x-shockwave-flash' FlashVars='" +
1685 flashVars +
1686 "' allowScriptAccess='always' wmode='transparent' src='" +
1687 url +
1688 "' height='1' width='1'></embed>" +
1689 "</object>";
1690 }
1691
1692 return (pub = {
1693 outgoing: function(message, domain, fn){
1694 swf.postMessage(config.channel, message.toString());
1695 if (fn) {
1696 fn();
1697 }
1698 },
1699 destroy: function(){
1700 trace("destroy");
1701 try {
1702 swf.destroyChannel(config.channel);
1703 }
1704 catch (e) {
1705 }
1706 swf = null;
1707 if (frame) {
1708 frame.parentNode.removeChild(frame);
1709 frame = null;
1710 }
1711 },
1712 onDOMReady: function(){
1713 trace("init");
1714
1715 targetOrigin = config.remote;
1716
1717 // Prepare the code that will be run after the swf has been intialized
1718 easyXDM.Fn.set("flash_" + config.channel + "_init", function(){
1719 setTimeout(function(){
1720 trace("firing onReady");
1721 pub.up.callback(true);
1722 });
1723 });
1724
1725 // set up the omMessage handler
1726 easyXDM.Fn.set("flash_" + config.channel + "_onMessage", onMessage);
1727
1728 config.swf = resolveUrl(config.swf); // reports have been made of requests gone rogue when using relative paths
1729 var swfdomain = getDomainName(config.swf);
1730 var fn = function(){
1731 // set init to true in case the fn was called was invoked from a separate instance
1732 easyXDM.stack.FlashTransport[swfdomain].init = true;
1733 swf = easyXDM.stack.FlashTransport[swfdomain].swf;
1734 // create the channel
1735 swf.createChannel(config.channel, config.secret, getLocation(config.remote), config.isHost);
1736
1737 if (config.isHost) {
1738 // if Flash is going to be throttled and we want to avoid this
1739 if (HAS_FLASH_THROTTLED_BUG && config.swfNoThrottle) {
1740 apply(config.props, {
1741 position: "fixed",
1742 right: 0,
1743 top: 0,
1744 height: "20px",
1745 width: "20px"
1746 });
1747 }
1748 // set up the iframe
1749 apply(config.props, {
1750 src: appendQueryParameters(config.remote, {
1751 xdm_e: getLocation(location.href),
1752 xdm_c: config.channel,
1753 xdm_p: 6, // 6 = FlashTransport
1754 xdm_s: config.secret
1755 }),
1756 name: IFRAME_PREFIX + config.channel + "_provider"
1757 });
1758 frame = createFrame(config);
1759 }
1760 };
1761
1762 if (easyXDM.stack.FlashTransport[swfdomain] && easyXDM.stack.FlashTransport[swfdomain].init) {
1763 // if the swf is in place and we are the consumer
1764 fn();
1765 }
1766 else {
1767 // if the swf does not yet exist
1768 if (!easyXDM.stack.FlashTransport[swfdomain]) {
1769 // add the queue to hold the init fn's
1770 easyXDM.stack.FlashTransport[swfdomain] = {
1771 queue: [fn]
1772 };
1773 addSwf(swfdomain);
1774 }
1775 else {
1776 easyXDM.stack.FlashTransport[swfdomain].queue.push(fn);
1777 }
1778 }
1779 },
1780 init: function(){
1781 whenReady(pub.onDOMReady, pub);
1782 }
1783 });
1784};
1785/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1786/*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, un, on, apply, whenReady, IFRAME_PREFIX*/
1787//
1788// easyXDM
1789// http://easyxdm.net/
1790// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1791//
1792// Permission is hereby granted, free of charge, to any person obtaining a copy
1793// of this software and associated documentation files (the "Software"), to deal
1794// in the Software without restriction, including without limitation the rights
1795// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1796// copies of the Software, and to permit persons to whom the Software is
1797// furnished to do so, subject to the following conditions:
1798//
1799// The above copyright notice and this permission notice shall be included in
1800// all copies or substantial portions of the Software.
1801//
1802// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1803// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1804// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1805// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1806// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1807// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1808// THE SOFTWARE.
1809//
1810
1811/**
1812 * @class easyXDM.stack.PostMessageTransport
1813 * PostMessageTransport is a transport class that uses HTML5 postMessage for communication.<br/>
1814 * <a href="http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx">http://msdn.microsoft.com/en-us/library/ms644944(VS.85).aspx</a><br/>
1815 * <a href="https://developer.mozilla.org/en/DOM/window.postMessage">https://developer.mozilla.org/en/DOM/window.postMessage</a>
1816 * @namespace easyXDM.stack
1817 * @constructor
1818 * @param {Object} config The transports configuration.
1819 * @cfg {String} remote The remote domain to communicate with.
1820 */
1821easyXDM.stack.PostMessageTransport = function(config){
1822 var trace = debug.getTracer("easyXDM.stack.PostMessageTransport");
1823 trace("constructor");
1824 var pub, // the public interface
1825 frame, // the remote frame, if any
1826 callerWindow, // the window that we will call with
1827 targetOrigin; // the domain to communicate with
1828 /**
1829 * Resolves the origin from the event object
1830 * @private
1831 * @param {Object} event The messageevent
1832 * @return {String} The scheme, host and port of the origin
1833 */
1834 function _getOrigin(event){
1835 if (event.origin) {
1836 // This is the HTML5 property
1837 return getLocation(event.origin);
1838 }
1839 if (event.uri) {
1840 // From earlier implementations
1841 return getLocation(event.uri);
1842 }
1843 if (event.domain) {
1844 // This is the last option and will fail if the
1845 // origin is not using the same schema as we are
1846 return location.protocol + "//" + event.domain;
1847 }
1848 throw "Unable to retrieve the origin of the event";
1849 }
1850
1851 /**
1852 * This is the main implementation for the onMessage event.<br/>
1853 * It checks the validity of the origin and passes the message on if appropriate.
1854 * @private
1855 * @param {Object} event The messageevent
1856 */
1857 function _window_onMessage(event){
1858 var origin = _getOrigin(event);
1859 trace("received message '" + event.data + "' from " + origin);
1860 if (origin == targetOrigin && event.data.substring(0, config.channel.length + 1) == config.channel + " ") {
1861 pub.up.incoming(event.data.substring(config.channel.length + 1), origin);
1862 }
1863 }
1864
1865 return (pub = {
1866 outgoing: function(message, domain, fn){
1867 callerWindow.postMessage(config.channel + " " + message, domain || targetOrigin);
1868 if (fn) {
1869 fn();
1870 }
1871 },
1872 destroy: function(){
1873 trace("destroy");
1874 un(window, "message", _window_onMessage);
1875 if (frame) {
1876 callerWindow = null;
1877 frame.parentNode.removeChild(frame);
1878 frame = null;
1879 }
1880 },
1881 onDOMReady: function(){
1882 trace("init");
1883 targetOrigin = getLocation(config.remote);
1884 if (config.isHost) {
1885 // add the event handler for listening
1886 var waitForReady = function(event){
1887 if (event.data == config.channel + "-ready") {
1888 trace("firing onReady");
1889 // replace the eventlistener
1890 callerWindow = ("postMessage" in frame.contentWindow) ? frame.contentWindow : frame.contentWindow.document;
1891 un(window, "message", waitForReady);
1892 on(window, "message", _window_onMessage);
1893 setTimeout(function(){
1894 pub.up.callback(true);
1895 }, 0);
1896 }
1897 };
1898 on(window, "message", waitForReady);
1899
1900 // set up the iframe
1901 apply(config.props, {
1902 src: appendQueryParameters(config.remote, {
1903 xdm_e: getLocation(location.href),
1904 xdm_c: config.channel,
1905 xdm_p: 1 // 1 = PostMessage
1906 }),
1907 name: IFRAME_PREFIX + config.channel + "_provider"
1908 });
1909 frame = createFrame(config);
1910 }
1911 else {
1912 // add the event handler for listening
1913 on(window, "message", _window_onMessage);
1914 callerWindow = ("postMessage" in window.parent) ? window.parent : window.parent.document;
1915 callerWindow.postMessage(config.channel + "-ready", targetOrigin);
1916
1917 setTimeout(function(){
1918 pub.up.callback(true);
1919 }, 0);
1920 }
1921 },
1922 init: function(){
1923 whenReady(pub.onDOMReady, pub);
1924 }
1925 });
1926};
1927/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
1928/*global easyXDM, window, escape, unescape, getLocation, appendQueryParameters, createFrame, debug, apply, query, whenReady, IFRAME_PREFIX*/
1929//
1930// easyXDM
1931// http://easyxdm.net/
1932// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
1933//
1934// Permission is hereby granted, free of charge, to any person obtaining a copy
1935// of this software and associated documentation files (the "Software"), to deal
1936// in the Software without restriction, including without limitation the rights
1937// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1938// copies of the Software, and to permit persons to whom the Software is
1939// furnished to do so, subject to the following conditions:
1940//
1941// The above copyright notice and this permission notice shall be included in
1942// all copies or substantial portions of the Software.
1943//
1944// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1945// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1946// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1947// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1948// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1949// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
1950// THE SOFTWARE.
1951//
1952
1953/**
1954 * @class easyXDM.stack.FrameElementTransport
1955 * FrameElementTransport is a transport class that can be used with Gecko-browser as these allow passing variables using the frameElement property.<br/>
1956 * Security is maintained as Gecho uses Lexical Authorization to determine under which scope a function is running.
1957 * @namespace easyXDM.stack
1958 * @constructor
1959 * @param {Object} config The transports configuration.
1960 * @cfg {String} remote The remote document to communicate with.
1961 */
1962easyXDM.stack.FrameElementTransport = function(config){
1963 var trace = debug.getTracer("easyXDM.stack.FrameElementTransport");
1964 trace("constructor");
1965 var pub, frame, send, targetOrigin;
1966
1967 return (pub = {
1968 outgoing: function(message, domain, fn){
1969 send.call(this, message);
1970 if (fn) {
1971 fn();
1972 }
1973 },
1974 destroy: function(){
1975 trace("destroy");
1976 if (frame) {
1977 frame.parentNode.removeChild(frame);
1978 frame = null;
1979 }
1980 },
1981 onDOMReady: function(){
1982 trace("init");
1983 targetOrigin = getLocation(config.remote);
1984
1985 if (config.isHost) {
1986 // set up the iframe
1987 apply(config.props, {
1988 src: appendQueryParameters(config.remote, {
1989 xdm_e: getLocation(location.href),
1990 xdm_c: config.channel,
1991 xdm_p: 5 // 5 = FrameElementTransport
1992 }),
1993 name: IFRAME_PREFIX + config.channel + "_provider"
1994 });
1995 frame = createFrame(config);
1996 frame.fn = function(sendFn){
1997 delete frame.fn;
1998 send = sendFn;
1999 setTimeout(function(){
2000 pub.up.callback(true);
2001 }, 0);
2002 // remove the function so that it cannot be used to overwrite the send function later on
2003 return function(msg){
2004 pub.up.incoming(msg, targetOrigin);
2005 };
2006 };
2007 }
2008 else {
2009 // This is to mitigate origin-spoofing
2010 if (document.referrer && getLocation(document.referrer) != query.xdm_e) {
2011 window.top.location = query.xdm_e;
2012 }
2013 send = window.frameElement.fn(function(msg){
2014 pub.up.incoming(msg, targetOrigin);
2015 });
2016 pub.up.callback(true);
2017 }
2018 },
2019 init: function(){
2020 whenReady(pub.onDOMReady, pub);
2021 }
2022 });
2023};
2024/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
2025/*global easyXDM, window, escape, unescape, undef, getLocation, appendQueryParameters, resolveUrl, createFrame, debug, un, apply, whenReady, IFRAME_PREFIX*/
2026//
2027// easyXDM
2028// http://easyxdm.net/
2029// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
2030//
2031// Permission is hereby granted, free of charge, to any person obtaining a copy
2032// of this software and associated documentation files (the "Software"), to deal
2033// in the Software without restriction, including without limitation the rights
2034// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2035// copies of the Software, and to permit persons to whom the Software is
2036// furnished to do so, subject to the following conditions:
2037//
2038// The above copyright notice and this permission notice shall be included in
2039// all copies or substantial portions of the Software.
2040//
2041// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2042// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2043// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2044// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2045// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2046// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2047// THE SOFTWARE.
2048//
2049
2050/**
2051 * @class easyXDM.stack.NameTransport
2052 * NameTransport uses the window.name property to relay data.
2053 * The <code>local</code> parameter needs to be set on both the consumer and provider,<br/>
2054 * and the <code>remoteHelper</code> parameter needs to be set on the consumer.
2055 * @constructor
2056 * @param {Object} config The transports configuration.
2057 * @cfg {String} remoteHelper The url to the remote instance of hash.html - this is only needed for the host.
2058 * @namespace easyXDM.stack
2059 */
2060easyXDM.stack.NameTransport = function(config){
2061 var trace = debug.getTracer("easyXDM.stack.NameTransport");
2062 trace("constructor");
2063 if (config.isHost && undef(config.remoteHelper)) {
2064 trace("missing remoteHelper");
2065 throw new Error("missing remoteHelper");
2066 }
2067
2068 var pub; // the public interface
2069 var isHost, callerWindow, remoteWindow, readyCount, callback, remoteOrigin, remoteUrl;
2070
2071 function _sendMessage(message){
2072 var url = config.remoteHelper + (isHost ? "#_3" : "#_2") + config.channel;
2073 trace("sending message " + message);
2074 trace("navigating to '" + url + "'");
2075 callerWindow.contentWindow.sendMessage(message, url);
2076 }
2077
2078 function _onReady(){
2079 if (isHost) {
2080 if (++readyCount === 2 || !isHost) {
2081 pub.up.callback(true);
2082 }
2083 }
2084 else {
2085 _sendMessage("ready");
2086 trace("calling onReady");
2087 pub.up.callback(true);
2088 }
2089 }
2090
2091 function _onMessage(message){
2092 trace("received message " + message);
2093 pub.up.incoming(message, remoteOrigin);
2094 }
2095
2096 function _onLoad(){
2097 if (callback) {
2098 setTimeout(function(){
2099 callback(true);
2100 }, 0);
2101 }
2102 }
2103
2104 return (pub = {
2105 outgoing: function(message, domain, fn){
2106 callback = fn;
2107 _sendMessage(message);
2108 },
2109 destroy: function(){
2110 trace("destroy");
2111 callerWindow.parentNode.removeChild(callerWindow);
2112 callerWindow = null;
2113 if (isHost) {
2114 remoteWindow.parentNode.removeChild(remoteWindow);
2115 remoteWindow = null;
2116 }
2117 },
2118 onDOMReady: function(){
2119 trace("init");
2120 isHost = config.isHost;
2121 readyCount = 0;
2122 remoteOrigin = getLocation(config.remote);
2123 config.local = resolveUrl(config.local);
2124
2125 if (isHost) {
2126 // Register the callback
2127 easyXDM.Fn.set(config.channel, function(message){
2128 trace("received initial message " + message);
2129 if (isHost && message === "ready") {
2130 // Replace the handler
2131 easyXDM.Fn.set(config.channel, _onMessage);
2132 _onReady();
2133 }
2134 });
2135
2136 // Set up the frame that points to the remote instance
2137 remoteUrl = appendQueryParameters(config.remote, {
2138 xdm_e: config.local,
2139 xdm_c: config.channel,
2140 xdm_p: 2
2141 });
2142 apply(config.props, {
2143 src: remoteUrl + '#' + config.channel,
2144 name: IFRAME_PREFIX + config.channel + "_provider"
2145 });
2146 remoteWindow = createFrame(config);
2147 }
2148 else {
2149 config.remoteHelper = config.remote;
2150 easyXDM.Fn.set(config.channel, _onMessage);
2151 }
2152
2153 // Set up the iframe that will be used for the transport
2154 var onLoad = function(){
2155 // Remove the handler
2156 var w = callerWindow || this;
2157 un(w, "load", onLoad);
2158 easyXDM.Fn.set(config.channel + "_load", _onLoad);
2159 (function test(){
2160 if (typeof w.contentWindow.sendMessage == "function") {
2161 _onReady();
2162 }
2163 else {
2164 setTimeout(test, 50);
2165 }
2166 }());
2167 };
2168
2169 callerWindow = createFrame({
2170 props: {
2171 src: config.local + "#_4" + config.channel
2172 },
2173 onLoad: onLoad
2174 });
2175 },
2176 init: function(){
2177 whenReady(pub.onDOMReady, pub);
2178 }
2179 });
2180};
2181/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
2182/*global easyXDM, window, escape, unescape, getLocation, createFrame, debug, un, on, apply, whenReady, IFRAME_PREFIX*/
2183//
2184// easyXDM
2185// http://easyxdm.net/
2186// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
2187//
2188// Permission is hereby granted, free of charge, to any person obtaining a copy
2189// of this software and associated documentation files (the "Software"), to deal
2190// in the Software without restriction, including without limitation the rights
2191// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2192// copies of the Software, and to permit persons to whom the Software is
2193// furnished to do so, subject to the following conditions:
2194//
2195// The above copyright notice and this permission notice shall be included in
2196// all copies or substantial portions of the Software.
2197//
2198// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2199// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2200// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2201// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2202// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2203// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2204// THE SOFTWARE.
2205//
2206
2207/**
2208 * @class easyXDM.stack.HashTransport
2209 * HashTransport is a transport class that uses the IFrame URL Technique for communication.<br/>
2210 * <a href="http://msdn.microsoft.com/en-us/library/bb735305.aspx">http://msdn.microsoft.com/en-us/library/bb735305.aspx</a><br/>
2211 * @namespace easyXDM.stack
2212 * @constructor
2213 * @param {Object} config The transports configuration.
2214 * @cfg {String/Window} local The url to the local file used for proxying messages, or the local window.
2215 * @cfg {Number} delay The number of milliseconds easyXDM should try to get a reference to the local window.
2216 * @cfg {Number} interval The interval used when polling for messages.
2217 */
2218easyXDM.stack.HashTransport = function(config){
2219 var trace = debug.getTracer("easyXDM.stack.HashTransport");
2220 trace("constructor");
2221 var pub;
2222 var me = this, isHost, _timer, pollInterval, _lastMsg, _msgNr, _listenerWindow, _callerWindow;
2223 var useParent, _remoteOrigin;
2224
2225 function _sendMessage(message){
2226 trace("sending message '" + (_msgNr + 1) + " " + message + "' to " + _remoteOrigin);
2227 if (!_callerWindow) {
2228 trace("no caller window");
2229 return;
2230 }
2231 var url = config.remote + "#" + (_msgNr++) + "_" + message;
2232 ((isHost || !useParent) ? _callerWindow.contentWindow : _callerWindow).location = url;
2233 }
2234
2235 function _handleHash(hash){
2236 _lastMsg = hash;
2237 trace("received message '" + _lastMsg + "' from " + _remoteOrigin);
2238 pub.up.incoming(_lastMsg.substring(_lastMsg.indexOf("_") + 1), _remoteOrigin);
2239 }
2240
2241 /**
2242 * Checks location.hash for a new message and relays this to the receiver.
2243 * @private
2244 */
2245 function _pollHash(){
2246 if (!_listenerWindow) {
2247 return;
2248 }
2249 var href = _listenerWindow.location.href, hash = "", indexOf = href.indexOf("#");
2250 if (indexOf != -1) {
2251 hash = href.substring(indexOf);
2252 }
2253 if (hash && hash != _lastMsg) {
2254 trace("poll: new message");
2255 _handleHash(hash);
2256 }
2257 }
2258
2259 function _attachListeners(){
2260 trace("starting polling");
2261 _timer = setInterval(_pollHash, pollInterval);
2262 }
2263
2264 return (pub = {
2265 outgoing: function(message, domain){
2266 _sendMessage(message);
2267 },
2268 destroy: function(){
2269 window.clearInterval(_timer);
2270 if (isHost || !useParent) {
2271 _callerWindow.parentNode.removeChild(_callerWindow);
2272 }
2273 _callerWindow = null;
2274 },
2275 onDOMReady: function(){
2276 isHost = config.isHost;
2277 pollInterval = config.interval;
2278 _lastMsg = "#" + config.channel;
2279 _msgNr = 0;
2280 useParent = config.useParent;
2281 _remoteOrigin = getLocation(config.remote);
2282 if (isHost) {
2283 apply(config.props, {
2284 src: config.remote,
2285 name: IFRAME_PREFIX + config.channel + "_provider"
2286 });
2287 if (useParent) {
2288 config.onLoad = function(){
2289 _listenerWindow = window;
2290 _attachListeners();
2291 pub.up.callback(true);
2292 };
2293 }
2294 else {
2295 var tries = 0, max = config.delay / 50;
2296 (function getRef(){
2297 if (++tries > max) {
2298 trace("unable to get reference to _listenerWindow, giving up");
2299 throw new Error("Unable to reference listenerwindow");
2300 }
2301 try {
2302 _listenerWindow = _callerWindow.contentWindow.frames[IFRAME_PREFIX + config.channel + "_consumer"];
2303 }
2304 catch (ex) {
2305 }
2306 if (_listenerWindow) {
2307 _attachListeners();
2308 trace("got a reference to _listenerWindow");
2309 pub.up.callback(true);
2310 }
2311 else {
2312 setTimeout(getRef, 50);
2313 }
2314 }());
2315 }
2316 _callerWindow = createFrame(config);
2317 }
2318 else {
2319 _listenerWindow = window;
2320 _attachListeners();
2321 if (useParent) {
2322 _callerWindow = parent;
2323 pub.up.callback(true);
2324 }
2325 else {
2326 apply(config, {
2327 props: {
2328 src: config.remote + "#" + config.channel + new Date(),
2329 name: IFRAME_PREFIX + config.channel + "_consumer"
2330 },
2331 onLoad: function(){
2332 pub.up.callback(true);
2333 }
2334 });
2335 _callerWindow = createFrame(config);
2336 }
2337 }
2338 },
2339 init: function(){
2340 whenReady(pub.onDOMReady, pub);
2341 }
2342 });
2343};
2344/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
2345/*global easyXDM, window, escape, unescape, debug */
2346//
2347// easyXDM
2348// http://easyxdm.net/
2349// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
2350//
2351// Permission is hereby granted, free of charge, to any person obtaining a copy
2352// of this software and associated documentation files (the "Software"), to deal
2353// in the Software without restriction, including without limitation the rights
2354// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2355// copies of the Software, and to permit persons to whom the Software is
2356// furnished to do so, subject to the following conditions:
2357//
2358// The above copyright notice and this permission notice shall be included in
2359// all copies or substantial portions of the Software.
2360//
2361// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2362// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2363// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2364// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2365// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2366// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2367// THE SOFTWARE.
2368//
2369
2370/**
2371 * @class easyXDM.stack.ReliableBehavior
2372 * This is a behavior that tries to make the underlying transport reliable by using acknowledgements.
2373 * @namespace easyXDM.stack
2374 * @constructor
2375 * @param {Object} config The behaviors configuration.
2376 */
2377easyXDM.stack.ReliableBehavior = function(config){
2378 var trace = debug.getTracer("easyXDM.stack.ReliableBehavior");
2379 trace("constructor");
2380 var pub, // the public interface
2381 callback; // the callback to execute when we have a confirmed success/failure
2382 var idOut = 0, idIn = 0, currentMessage = "";
2383
2384 return (pub = {
2385 incoming: function(message, origin){
2386 trace("incoming: " + message);
2387 var indexOf = message.indexOf("_"), ack = message.substring(0, indexOf).split(",");
2388 message = message.substring(indexOf + 1);
2389
2390 if (ack[0] == idOut) {
2391 trace("message delivered");
2392 currentMessage = "";
2393 if (callback) {
2394 callback(true);
2395 callback = null;
2396 }
2397 }
2398 if (message.length > 0) {
2399 trace("sending ack, and passing on " + message);
2400 pub.down.outgoing(ack[1] + "," + idOut + "_" + currentMessage, origin);
2401 if (idIn != ack[1]) {
2402 idIn = ack[1];
2403 pub.up.incoming(message, origin);
2404 }
2405 }
2406
2407 },
2408 outgoing: function(message, origin, fn){
2409 currentMessage = message;
2410 callback = fn;
2411 pub.down.outgoing(idIn + "," + (++idOut) + "_" + message, origin);
2412 }
2413 });
2414};
2415/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
2416/*global easyXDM, window, escape, unescape, debug, undef, removeFromStack*/
2417//
2418// easyXDM
2419// http://easyxdm.net/
2420// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
2421//
2422// Permission is hereby granted, free of charge, to any person obtaining a copy
2423// of this software and associated documentation files (the "Software"), to deal
2424// in the Software without restriction, including without limitation the rights
2425// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2426// copies of the Software, and to permit persons to whom the Software is
2427// furnished to do so, subject to the following conditions:
2428//
2429// The above copyright notice and this permission notice shall be included in
2430// all copies or substantial portions of the Software.
2431//
2432// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2433// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2434// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2435// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2436// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2437// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2438// THE SOFTWARE.
2439//
2440
2441/**
2442 * @class easyXDM.stack.QueueBehavior
2443 * This is a behavior that enables queueing of messages. <br/>
2444 * It will buffer incoming messages and dispach these as fast as the underlying transport allows.
2445 * This will also fragment/defragment messages so that the outgoing message is never bigger than the
2446 * set length.
2447 * @namespace easyXDM.stack
2448 * @constructor
2449 * @param {Object} config The behaviors configuration. Optional.
2450 * @cfg {Number} maxLength The maximum length of each outgoing message. Set this to enable fragmentation.
2451 */
2452easyXDM.stack.QueueBehavior = function(config){
2453 var trace = debug.getTracer("easyXDM.stack.QueueBehavior");
2454 trace("constructor");
2455 var pub, queue = [], waiting = true, incoming = "", destroying, maxLength = 0, lazy = false, doFragment = false;
2456
2457 function dispatch(){
2458 if (config.remove && queue.length === 0) {
2459 trace("removing myself from the stack");
2460 removeFromStack(pub);
2461 return;
2462 }
2463 if (waiting || queue.length === 0 || destroying) {
2464 return;
2465 }
2466 trace("dispatching from queue");
2467 waiting = true;
2468 var message = queue.shift();
2469
2470 pub.down.outgoing(message.data, message.origin, function(success){
2471 waiting = false;
2472 if (message.callback) {
2473 setTimeout(function(){
2474 message.callback(success);
2475 }, 0);
2476 }
2477 dispatch();
2478 });
2479 }
2480 return (pub = {
2481 init: function(){
2482 if (undef(config)) {
2483 config = {};
2484 }
2485 if (config.maxLength) {
2486 maxLength = config.maxLength;
2487 doFragment = true;
2488 }
2489 if (config.lazy) {
2490 lazy = true;
2491 }
2492 else {
2493 pub.down.init();
2494 }
2495 },
2496 callback: function(success){
2497 waiting = false;
2498 var up = pub.up; // in case dispatch calls removeFromStack
2499 dispatch();
2500 up.callback(success);
2501 },
2502 incoming: function(message, origin){
2503 if (doFragment) {
2504 var indexOf = message.indexOf("_"), seq = parseInt(message.substring(0, indexOf), 10);
2505 incoming += message.substring(indexOf + 1);
2506 if (seq === 0) {
2507 trace("received the last fragment");
2508 if (config.encode) {
2509 incoming = decodeURIComponent(incoming);
2510 }
2511 pub.up.incoming(incoming, origin);
2512 incoming = "";
2513 }
2514 else {
2515 trace("waiting for more fragments, seq=" + message);
2516 }
2517 }
2518 else {
2519 pub.up.incoming(message, origin);
2520 }
2521 },
2522 outgoing: function(message, origin, fn){
2523 if (config.encode) {
2524 message = encodeURIComponent(message);
2525 }
2526 var fragments = [], fragment;
2527 if (doFragment) {
2528 // fragment into chunks
2529 while (message.length !== 0) {
2530 fragment = message.substring(0, maxLength);
2531 message = message.substring(fragment.length);
2532 fragments.push(fragment);
2533 }
2534 // enqueue the chunks
2535 while ((fragment = fragments.shift())) {
2536 trace("enqueuing");
2537 queue.push({
2538 data: fragments.length + "_" + fragment,
2539 origin: origin,
2540 callback: fragments.length === 0 ? fn : null
2541 });
2542 }
2543 }
2544 else {
2545 queue.push({
2546 data: message,
2547 origin: origin,
2548 callback: fn
2549 });
2550 }
2551 if (lazy) {
2552 pub.down.init();
2553 }
2554 else {
2555 dispatch();
2556 }
2557 },
2558 destroy: function(){
2559 trace("destroy");
2560 destroying = true;
2561 pub.down.destroy();
2562 }
2563 });
2564};
2565/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
2566/*global easyXDM, window, escape, unescape, undef, debug */
2567//
2568// easyXDM
2569// http://easyxdm.net/
2570// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
2571//
2572// Permission is hereby granted, free of charge, to any person obtaining a copy
2573// of this software and associated documentation files (the "Software"), to deal
2574// in the Software without restriction, including without limitation the rights
2575// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2576// copies of the Software, and to permit persons to whom the Software is
2577// furnished to do so, subject to the following conditions:
2578//
2579// The above copyright notice and this permission notice shall be included in
2580// all copies or substantial portions of the Software.
2581//
2582// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2583// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2584// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2585// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2586// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2587// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2588// THE SOFTWARE.
2589//
2590
2591/**
2592 * @class easyXDM.stack.VerifyBehavior
2593 * This behavior will verify that communication with the remote end is possible, and will also sign all outgoing,
2594 * and verify all incoming messages. This removes the risk of someone hijacking the iframe to send malicious messages.
2595 * @namespace easyXDM.stack
2596 * @constructor
2597 * @param {Object} config The behaviors configuration.
2598 * @cfg {Boolean} initiate If the verification should be initiated from this end.
2599 */
2600easyXDM.stack.VerifyBehavior = function(config){
2601 var trace = debug.getTracer("easyXDM.stack.VerifyBehavior");
2602 trace("constructor");
2603 if (undef(config.initiate)) {
2604 throw new Error("settings.initiate is not set");
2605 }
2606 var pub, mySecret, theirSecret, verified = false;
2607
2608 function startVerification(){
2609 trace("requesting verification");
2610 mySecret = Math.random().toString(16).substring(2);
2611 pub.down.outgoing(mySecret);
2612 }
2613
2614 return (pub = {
2615 incoming: function(message, origin){
2616 var indexOf = message.indexOf("_");
2617 if (indexOf === -1) {
2618 if (message === mySecret) {
2619 trace("verified, calling callback");
2620 pub.up.callback(true);
2621 }
2622 else if (!theirSecret) {
2623 trace("returning secret");
2624 theirSecret = message;
2625 if (!config.initiate) {
2626 startVerification();
2627 }
2628 pub.down.outgoing(message);
2629 }
2630 }
2631 else {
2632 if (message.substring(0, indexOf) === theirSecret) {
2633 pub.up.incoming(message.substring(indexOf + 1), origin);
2634 }
2635 }
2636 },
2637 outgoing: function(message, origin, fn){
2638 pub.down.outgoing(mySecret + "_" + message, origin, fn);
2639 },
2640 callback: function(success){
2641 if (config.initiate) {
2642 startVerification();
2643 }
2644 }
2645 });
2646};
2647/*jslint evil: true, browser: true, immed: true, passfail: true, undef: true, newcap: true*/
2648/*global easyXDM, window, escape, unescape, undef, getJSON, debug, emptyFn, isArray */
2649//
2650// easyXDM
2651// http://easyxdm.net/
2652// Copyright(c) 2009-2011, Øyvind Sean Kinsey, oyvind@kinsey.no.
2653//
2654// Permission is hereby granted, free of charge, to any person obtaining a copy
2655// of this software and associated documentation files (the "Software"), to deal
2656// in the Software without restriction, including without limitation the rights
2657// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2658// copies of the Software, and to permit persons to whom the Software is
2659// furnished to do so, subject to the following conditions:
2660//
2661// The above copyright notice and this permission notice shall be included in
2662// all copies or substantial portions of the Software.
2663//
2664// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2665// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2666// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2667// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2668// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2669// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2670// THE SOFTWARE.
2671//
2672
2673/**
2674 * @class easyXDM.stack.RpcBehavior
2675 * This uses JSON-RPC 2.0 to expose local methods and to invoke remote methods and have responses returned over the the string based transport stack.<br/>
2676 * Exposed methods can return values synchronous, asyncronous, or bet set up to not return anything.
2677 * @namespace easyXDM.stack
2678 * @constructor
2679 * @param {Object} proxy The object to apply the methods to.
2680 * @param {Object} config The definition of the local and remote interface to implement.
2681 * @cfg {Object} local The local interface to expose.
2682 * @cfg {Object} remote The remote methods to expose through the proxy.
2683 * @cfg {Object} serializer The serializer to use for serializing and deserializing the JSON. Should be compatible with the HTML5 JSON object. Optional, will default to JSON.
2684 */
2685easyXDM.stack.RpcBehavior = function(proxy, config){
2686 var trace = debug.getTracer("easyXDM.stack.RpcBehavior");
2687 var pub, serializer = config.serializer || getJSON();
2688 var _callbackCounter = 0, _callbacks = {};
2689
2690 /**
2691 * Serializes and sends the message
2692 * @private
2693 * @param {Object} data The JSON-RPC message to be sent. The jsonrpc property will be added.
2694 */
2695 function _send(data){
2696 data.jsonrpc = "2.0";
2697 pub.down.outgoing(serializer.stringify(data));
2698 }
2699
2700 /**
2701 * Creates a method that implements the given definition
2702 * @private
2703 * @param {Object} The method configuration
2704 * @param {String} method The name of the method
2705 * @return {Function} A stub capable of proxying the requested method call
2706 */
2707 function _createMethod(definition, method){
2708 var slice = Array.prototype.slice;
2709
2710 trace("creating method " + method);
2711 return function(){
2712 trace("executing method " + method);
2713 var l = arguments.length, callback, message = {
2714 method: method
2715 };
2716
2717 if (l > 0 && typeof arguments[l - 1] === "function") {
2718 //with callback, procedure
2719 if (l > 1 && typeof arguments[l - 2] === "function") {
2720 // two callbacks, success and error
2721 callback = {
2722 success: arguments[l - 2],
2723 error: arguments[l - 1]
2724 };
2725 message.params = slice.call(arguments, 0, l - 2);
2726 }
2727 else {
2728 // single callback, success
2729 callback = {
2730 success: arguments[l - 1]
2731 };
2732 message.params = slice.call(arguments, 0, l - 1);
2733 }
2734 _callbacks["" + (++_callbackCounter)] = callback;
2735 message.id = _callbackCounter;
2736 }
2737 else {
2738 // no callbacks, a notification
2739 message.params = slice.call(arguments, 0);
2740 }
2741 if (definition.namedParams && message.params.length === 1) {
2742 message.params = message.params[0];
2743 }
2744 // Send the method request
2745 _send(message);
2746 };
2747 }
2748
2749 /**
2750 * Executes the exposed method
2751 * @private
2752 * @param {String} method The name of the method
2753 * @param {Number} id The callback id to use
2754 * @param {Function} method The exposed implementation
2755 * @param {Array} params The parameters supplied by the remote end
2756 */
2757 function _executeMethod(method, id, fn, params){
2758 if (!fn) {
2759 trace("requested to execute non-existent procedure " + method);
2760 if (id) {
2761 _send({
2762 id: id,
2763 error: {
2764 code: -32601,
2765 message: "Procedure not found."
2766 }
2767 });
2768 }
2769 return;
2770 }
2771
2772 trace("requested to execute procedure " + method);
2773 var success, error;
2774 if (id) {
2775 success = function(result){
2776 success = emptyFn;
2777 _send({
2778 id: id,
2779 result: result
2780 });
2781 };
2782 error = function(message, data){
2783 error = emptyFn;
2784 var msg = {
2785 id: id,
2786 error: {
2787 code: -32099,
2788 message: message
2789 }
2790 };
2791 if (data) {
2792 msg.error.data = data;
2793 }
2794 _send(msg);
2795 };
2796 }
2797 else {
2798 success = error = emptyFn;
2799 }
2800 // Call local method
2801 if (!isArray(params)) {
2802 params = [params];
2803 }
2804 try {
2805 var result = fn.method.apply(fn.scope, params.concat([success, error]));
2806 if (!undef(result)) {
2807 success(result);
2808 }
2809 }
2810 catch (ex1) {
2811 error(ex1.message);
2812 }
2813 }
2814
2815 return (pub = {
2816 incoming: function(message, origin){
2817 var data = serializer.parse(message);
2818 if (data.method) {
2819 trace("received request to execute method " + data.method + (data.id ? (" using callback id " + data.id) : ""));
2820 // A method call from the remote end
2821 if (config.handle) {
2822 config.handle(data, _send);
2823 }
2824 else {
2825 _executeMethod(data.method, data.id, config.local[data.method], data.params);
2826 }
2827 }
2828 else {
2829 trace("received return value destined to callback with id " + data.id);
2830 // A method response from the other end
2831 var callback = _callbacks[data.id];
2832 if (data.error) {
2833 if (callback.error) {
2834 callback.error(data.error);
2835 }
2836 else {
2837 trace("unhandled error returned.");
2838 }
2839 }
2840 else if (callback.success) {
2841 callback.success(data.result);
2842 }
2843 delete _callbacks[data.id];
2844 }
2845 },
2846 init: function(){
2847 trace("init");
2848 if (config.remote) {
2849 trace("creating stubs");
2850 // Implement the remote sides exposed methods
2851 for (var method in config.remote) {
2852 if (config.remote.hasOwnProperty(method)) {
2853 proxy[method] = _createMethod(config.remote[method], method);
2854 }
2855 }
2856 }
2857 pub.down.init();
2858 },
2859 destroy: function(){
2860 trace("destroy");
2861 for (var method in config.remote) {
2862 if (config.remote.hasOwnProperty(method) && proxy.hasOwnProperty(method)) {
2863 delete proxy[method];
2864 }
2865 }
2866 pub.down.destroy();
2867 }
2868 });
2869};
2870global.easyXDM = easyXDM;
2871})(window, document, location, window.setTimeout, decodeURIComponent, encodeURIComponent);