cobot_client 0.0.1 → 0.1.0

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