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