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