easyxdm-rails 0.0.4 → 0.0.5

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