├── README
├── xdm.js
└── xdmHelper.js
/README:
--------------------------------------------------------------------------------
 1 | based on easyXDM 
 2 | 
 3 | 
 4 | Copyright (c) 2010 vkontakte
 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 | 
--------------------------------------------------------------------------------
/xdm.js:
--------------------------------------------------------------------------------
  1 | (function(w) {
  2 | if (w.fastXDM) return;
  3 | 
  4 | var handlers = {};
  5 | var onEnvLoad = [];
  6 | var env = {};
  7 | 
  8 | // Key generation
  9 | function genKey() {
 10 |   var key = '';
 11 |   for (i=0;i<5;i++) key += Math.ceil(Math.random()*15).toString(16);
 12 |   return key;
 13 | }
 14 | 
 15 | function waitFor(obj, prop, func, self,  count) {
 16 |   if (obj[prop]) {
 17 |      func.apply(self);
 18 |   } else {
 19 |     count = count || 0;
 20 |     if (count < 1000) setTimeout(function() {
 21 |       waitFor(obj, prop, func, self, count + 1)
 22 |     }, 0);
 23 |   }
 24 | }
 25 | 
 26 | function attachScript(url) {
 27 |   setTimeout(function() {
 28 |     var newScript = document.createElement('script');
 29 |     newScript.type = 'text/javascript';
 30 |     newScript.src = url || w.fastXDM.helperUrl;
 31 |     waitFor(document, 'body', function() {      
 32 |       document.getElementsByTagName('HEAD')[0].appendChild(newScript);
 33 |     });
 34 |   }, 0);
 35 | }
 36 | 
 37 | // Env functions
 38 | function getEnv(callback, self) {
 39 |   if (env.loaded) {
 40 |     callback.apply(self, [env]);
 41 |   } else {
 42 |     onEnvLoad.push([self, callback]);
 43 |   }
 44 | }
 45 | 
 46 | function envLoaded() {
 47 |   env.loaded = true;
 48 |   if (onEnvLoad.length > 0) {
 49 |     for (callback in onEnvLoad) onEnvLoad[callback][1].apply(onEnvLoad[callback][0], [env]);
 50 |   }
 51 | }
 52 | 
 53 | function applyMethod(strData, self) {
 54 |   getEnv(function(env) {
 55 |     var data = env.json.parse(strData);
 56 |     if (data[0]) {
 57 |       if (!data[1]) data[1] = [];
 58 |       for (i in data[1]) {
 59 |         if (data[1][i]._func) {
 60 |           var funcNum = data[1][i]._func;
 61 |           data[1][i] = function() {
 62 |             var args = Array.prototype.slice.call(arguments);
 63 |             args.unshift('_func'+funcNum);
 64 |             self.callMethod.apply(self, args);
 65 |           }
 66 |         }
 67 |       }
 68 |       setTimeout(function() {
 69 |         if (!self.methods[data[0]]) {
 70 |           throw Error('fastXDM: Method ' + data[0] + ' is undefined');
 71 |         }
 72 |         self.methods[data[0]].apply(self, data[1]);
 73 |       }, 0);
 74 |     }
 75 |   });
 76 | }
 77 | 
 78 | // XDM object
 79 | w.fastXDM = {
 80 |   _id: 0,
 81 |   helperUrl: 'http://userapi.com/js/api/xdmHelper.js',
 82 | 
 83 |   Server: function(methods, filter) {
 84 |     this.methods = methods || {};
 85 |     this.id = w.fastXDM._id++;
 86 |     this.filter = filter;
 87 |     this.key = genKey();
 88 |     this.methods['%init%'] = this.methods['__fxdm_i'] = function() {
 89 |       w.fastXDM.run(this.id);
 90 |       if (this.methods['onInit']) this.methods['onInit']();
 91 |     };
 92 |     this.frameName = 'fXD'+this.key;
 93 |     this.server = true;
 94 |     handlers[this.key] = [applyMethod, this];
 95 |   },
 96 |   
 97 |   Client: function(methods) {
 98 |     this.methods = methods || {};
 99 |     this.id = w.fastXDM._id++;
100 |     w.fastXDM.run(this.id);
101 |     if (window.name.indexOf('fXD') == 0) {
102 |       this.key = window.name.substr(3);
103 |     } else {
104 |       throw Error('Wrong window.name property.');
105 |     }
106 |     this.caller = window.parent;
107 |     handlers[this.key] = [applyMethod, this];
108 |     this.client = true;
109 |     
110 |     w.fastXDM.on('helper', function() {
111 |       w.fastXDM.onClientStart(this);
112 |     }, this);
113 |     
114 |     getEnv(function(env) {
115 |       if (w.location.toString().indexOf('chaskor.ru') != -1) {
116 |         env.send(this, env.json.stringify(['__fxdm_i']));
117 |       } else {
118 |         env.send(this, env.json.stringify(['%init%']));
119 |       }
120 |       var methods = this.methods;
121 |       setTimeout(function() {
122 |         if (methods['onInit']) methods['onInit']();
123 |       }, 0);
124 |     }, this);
125 |   },
126 |   
127 |   onMessage: function(e) {
128 |     if (!e.data) return false;
129 |     var key = e.data.substr(0, 5);
130 |     if (handlers[key]) {
131 |       var self = handlers[key][1];
132 |       if (self && (!self.filter || self.filter(e.origin))) {
133 |         handlers[key][0](e.data.substr(6), self);
134 |       }
135 |     }
136 |   },
137 |   
138 |   setJSON: function(json) {
139 |     env.json = json;
140 |   },
141 |   
142 |   getJSON: function(callback) {
143 |     if (!callback) return env.json;
144 |     getEnv(function(env) {
145 |       callback(env.json);
146 |     });
147 |   },
148 |   
149 |   setEnv: function(exEnv) {
150 |     for (i in exEnv) {
151 |       env[i] = exEnv[i];
152 |     }
153 |     envLoaded();
154 |   },
155 |   
156 |   _q: {},
157 |   
158 |   on: function(key, act, self) {
159 |     if (!this._q[key]) this._q[key] = [];
160 |     if (this._q[key] == -1) {
161 |       act.apply(self);
162 |     } else {
163 |       this._q[key].push([act, self]);
164 |     }
165 |   },
166 |   
167 |   run: function(key) {
168 |     if (this._q[key] && this._q[key].length > 0) {
169 |       for (i = 0; i < this._q[key].length; i++) this._q[key][i][0].apply(this._q[key][i][1]);
170 |     }
171 |     this._q[key] = -1;
172 |   },
173 |   
174 |   waitFor: waitFor
175 | }
176 | 
177 | 
178 | w.fastXDM.Server.prototype.start = function(obj, count) {
179 |   if (obj.contentWindow) {
180 |     this.caller = obj.contentWindow;
181 |     this.frame = obj;
182 |     
183 |     w.fastXDM.on('helper', function() {
184 |       w.fastXDM.onServerStart(this);
185 |     }, this);
186 | 
187 |   } else { // Opera old versions
188 |     var self = this;
189 |     count = count || 0;
190 |     if (count < 50) setTimeout(function() {
191 |       self.start.apply(self, [obj, count+1]);
192 |     }, 100);
193 |   }
194 | }
195 | 
196 | w.fastXDM.Server.prototype.destroy = function() {
197 |   handlers.splice(handlers.indexOf(this.key), 1);
198 | }
199 | 
200 | function extend(obj1, obj2){
201 |   for (var i in obj2) {
202 |     if (obj1[i] && typeof(obj1[i]) == 'object') {
203 |       extend(obj1[i], obj2[i])
204 |     } else {
205 |       obj1[i] = obj2[i];
206 |     }
207 |   }
208 | }
209 | 
210 | w.fastXDM.Server.prototype.append = function(obj, options) {
211 |   var div = document.createElement('DIV');
212 |   div.innerHTML = '';
213 |   var frame = div.firstChild;
214 |   var self = this;
215 |   setTimeout(function() {
216 |     frame.frameBorder = '0';
217 |     if (options) extend(frame, options);
218 |     obj.insertBefore(frame, obj.firstChild);
219 |     self.start(frame);
220 |   }, 0);
221 |   return frame;
222 | }
223 | 
224 | w.fastXDM.Client.prototype.callMethod = w.fastXDM.Server.prototype.callMethod = function() {
225 |   var args = Array.prototype.slice.call(arguments);
226 |   var method = args.shift();
227 |   for (i in args) {
228 |     if (typeof(args[i]) == 'function') {
229 |       this.funcsCount = (this.funcsCount || 0) + 1;
230 |       var func = args[i];
231 |       var funcName = '_func' + this.funcsCount;
232 |       this.methods[funcName] = function() {
233 |         func.apply(this, arguments);
234 |         delete this.methods[funcName];
235 |       }
236 |       args[i] = {_func: this.funcsCount};
237 |     }
238 |   }
239 |   waitFor(this, 'caller', function() {
240 |     w.fastXDM.on(this.id, function() {
241 |       getEnv(function(env) {
242 |         env.send(this, env.json.stringify([method, args]));
243 |       }, this);
244 |     }, this);
245 |   }, this);
246 | }
247 | 
248 | if (w.JSON && typeof(w.JSON) == 'object' && w.JSON.parse && w.JSON.stringify && w.JSON.stringify({a:[1,2,3]}).replace(/ /g, '') == '{"a":[1,2,3]}') {
249 |   env.json = {parse: w.JSON.parse, stringify: w.JSON.stringify};
250 | } else {
251 |   w.fastXDM._needJSON = true;
252 | }
253 | 
254 | // PostMessage cover
255 | if (w.postMessage) {
256 |   env.protocol = 'p';
257 |   env.send = function(xdm, strData) {
258 |     // alert(key+':'+strData);
259 |     xdm.caller.postMessage(xdm.key+':'+strData, "*");
260 |   }
261 |   if (w.addEventListener) {
262 |     w.addEventListener("message", w.fastXDM.onMessage, false);
263 |   } else {
264 |     w.attachEvent("onmessage", w.fastXDM.onMessage);
265 |   }
266 |   
267 |   if (w.fastXDM._needJSON) {
268 |     w.fastXDM._onlyJSON = true;
269 |     attachScript(); 
270 |   } else {
271 |     envLoaded();
272 |   }
273 | } else {
274 |   attachScript();  
275 | }
276 | 
277 | })(window);
278 | 
--------------------------------------------------------------------------------
/xdmHelper.js:
--------------------------------------------------------------------------------
  1 | (function(w) {
  2 |   function getJSON() {
  3 |     var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  4 |         escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
  5 |         gap,
  6 |         indent,
  7 |         meta = {    // table of character substitutions
  8 |             '\b': '\\b',
  9 |             '\t': '\\t',
 10 |             '\n': '\\n',
 11 |             '\f': '\\f',
 12 |             '\r': '\\r',
 13 |             '"' : '\\"',
 14 |             '\\': '\\\\'
 15 |         },
 16 |         rep;
 17 |         
 18 |     function f(n) {
 19 |       // Format integers to have at least two digits.
 20 |       return n < 10 ? '0' + n : n;
 21 |     }
 22 | 
 23 |     function quote(string) {
 24 |       escapable.lastIndex = 0;
 25 |       return escapable.test(string) ?
 26 |           '"' + string.replace(escapable, function (a) {
 27 |               var c = meta[a];
 28 |               return typeof c === 'string' ? c :
 29 |                   '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
 30 |           }) + '"' :
 31 |           '"' + string + '"';
 32 |     }
 33 |     
 34 |     Date.prototype._fxdmToJSON = function (key) {
 35 |       return isFinite(this.valueOf()) ?
 36 |              this.getUTCFullYear()   + '-' +
 37 |            f(this.getUTCMonth() + 1) + '-' +
 38 |            f(this.getUTCDate())      + 'T' +
 39 |            f(this.getUTCHours())     + ':' +
 40 |            f(this.getUTCMinutes())   + ':' +
 41 |            f(this.getUTCSeconds())   + 'Z' : null;
 42 |     };
 43 |     
 44 |     String.prototype._fxdmToJSON =
 45 |     Number.prototype._fxdmToJSON =
 46 |     Boolean.prototype._fxdmToJSON = function (key) {
 47 |         return this.valueOf();
 48 |     };
 49 | 
 50 |     function str(key, holder) {
 51 |       var i,          // The loop counter.
 52 |           k,          // The member key.
 53 |           v,          // The member value.
 54 |           length,
 55 |           mind = gap,
 56 |           partial,
 57 |           value = holder[key];
 58 |           
 59 |       if (value && typeof value === 'object' && typeof value._fxdmToJSON === 'function') {
 60 |         value = value._fxdmToJSON(key);
 61 |       }
 62 | 
 63 |       if (typeof rep === 'function') {
 64 |         value = rep.call(holder, key, value);
 65 |       }
 66 | 
 67 |       switch (typeof value) {
 68 |       case 'string':
 69 |         return quote(value);
 70 | 
 71 |       case 'number':
 72 |         return isFinite(value) ? String(value) : 'null';
 73 | 
 74 |       case 'boolean':
 75 |       case 'null':
 76 |         return String(value);
 77 |           
 78 |       case 'object':
 79 |         if (!value) {
 80 |           return 'null';
 81 |         }
 82 | 
 83 |         gap += indent;
 84 |         partial = [];
 85 | 
 86 |         if (Object.prototype.toString.apply(value) === '[object Array]') {
 87 |             length = value.length;
 88 |             for (i = 0; i < length; i += 1) {
 89 |               partial[i] = str(i, value) || 'null';
 90 |             }
 91 | 
 92 |             v = partial.length === 0 ? '[]' :
 93 |               gap ? '[\n' + gap +
 94 |                       partial.join(',\n' + gap) + '\n' +
 95 |                           mind + ']' :
 96 |                     '[' + partial.join(',') + ']';
 97 |             gap = mind;
 98 |             return v;
 99 |         }
100 | 
101 |         if (rep && typeof rep === 'object') {
102 |             length = rep.length;
103 |             for (i = 0; i < length; i += 1) {
104 |               k = rep[i];
105 |               if (typeof k === 'string') {
106 |                 v = str(k, value);
107 |                 if (v) {
108 |                   partial.push(quote(k) + (gap ? ': ' : ':') + v);
109 |                 }
110 |               }
111 |             }
112 |         } else {
113 |           for (k in value) {
114 |             if (Object.hasOwnProperty.call(value, k)) {
115 |               v = str(k, value);
116 |               if (v) {
117 |                 partial.push(quote(k) + (gap ? ': ' : ':') + v);
118 |               }
119 |             }
120 |           }
121 |         }
122 |         v = partial.length === 0 ? '{}' :
123 |           gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
124 |                   mind + '}' : '{' + partial.join(',') + '}';
125 |         gap = mind;
126 |         return v;
127 |       }
128 |     }
129 | 
130 |     return {
131 |       stringify: function (value, replacer, space) {
132 |         var i;
133 |         gap = '';
134 |         indent = '';
135 | 
136 |         if (typeof space === 'number') {
137 |           for (i = 0; i < space; i += 1) {
138 |             indent += ' ';
139 |           }
140 | 
141 |         } else if (typeof space === 'string') {
142 |           indent = space;
143 |         }
144 | 
145 |         rep = replacer;
146 |         if (replacer && typeof replacer !== 'function' &&
147 |               (typeof replacer !== 'object' ||
148 |                typeof replacer.length !== 'number')) {
149 |           throw new Error('JSON.stringify');
150 |         }
151 | 
152 |         return str('', {'': value});
153 |       },
154 |       parse: function (text, reviver) {
155 |         var j;
156 |         function walk(holder, key) {
157 |             var k, v, value = holder[key];
158 |             if (value && typeof value === 'object') {
159 |                 for (k in value) {
160 |                     if (Object.hasOwnProperty.call(value, k)) {
161 |                         v = walk(value, k);
162 |                         if (v !== undefined) {
163 |                             value[k] = v;
164 |                         } else {
165 |                             delete value[k];
166 |                         }
167 |                     }
168 |                 }
169 |             }
170 |             return reviver.call(holder, key, value);
171 |         }
172 | 
173 |         text = String(text);
174 |         cx.lastIndex = 0;
175 |         if (cx.test(text)) {
176 |             text = text.replace(cx, function (a) {
177 |                 return '\\u' +
178 |                     ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
179 |             });
180 |         }
181 | 
182 |         if (/^[\],:{}\s]*$/
183 |       .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
184 |       .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
185 |       .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
186 | 
187 |           j = eval('(' + text + ')');
188 | 
189 |           return typeof reviver === 'function' ?
190 |             walk({'': j}, '') : j;
191 |         }
192 | 
193 |         throw new SyntaxError('JSON.parse');
194 |       }
195 |     }
196 |     
197 |   };
198 |   
199 | 
200 |   if (w.fastXDM._needJSON) {
201 |     w.fastXDM.setJSON(getJSON());
202 |   }
203 |   
204 |   if (w.fastXDM._onlyJSON) {
205 |     return w.fastXDM.setEnv();
206 |   }
207 | 
208 |   if ("ActiveXObject" in w && "execScript" in w) {
209 |     
210 |     w.fastXDM.onServerStart = function(xdm) {
211 |       if (!("getNixProxy" in w)) {
212 |         window.execScript('Class NixProxy\n' +
213 |                           '    Private m_parent, m_child\n' +
214 |                           '\n' +
215 |                           '    Public Sub SetParent(obj)\n' +
216 |                           '        SET m_parent = obj\n' +
217 |                           '    End Sub\n' +
218 |                           '    Public Sub SetChild(obj)\n' +
219 |                           '        SET m_child = obj\n' +
220 |                           '    End Sub\n' +
221 |                           '\n' +
222 |                           '    Public Sub SendToParent(data)\n' +
223 |                           '        m_parent.send(CStr(data))\n' +
224 |                           '    End Sub\n' +
225 |                           '    Public Sub SendToChild(data)\n' +
226 |                           '        m_child.send(CStr(data))\n' +
227 |                           '    End Sub\n' +
228 |                           'End Class\n' +
229 |                           'Function getNixProxy()\n' +
230 |                           '    Set GetNixProxy = New NixProxy\n' +
231 |                           'End Function\n', 'vbscript');
232 |       }
233 |       xdm.proxy = getNixProxy();
234 |       xdm.proxy.SetParent({
235 |         send: function(msg){
236 |           w.fastXDM.onMessage({data: msg});
237 |         }
238 |       });
239 |       xdm.caller.opener = xdm.proxy;
240 |     }
241 |     
242 |     w.fastXDM.onClientStart = function(xdm) {
243 |       w.fastXDM.waitFor(w, 'opener', function() {
244 |         if (w.opener) {
245 |           xdm.proxy = w.opener;
246 |           xdm.proxy.SetChild({
247 |             send: function(msg){
248 |               setTimeout(function(){
249 |                   w.fastXDM.onMessage({data: msg});
250 |               }, 0);
251 |             }
252 |           });
253 |         }
254 |       });
255 |     }
256 |       
257 |     w.fastXDM.run('helper')
258 |     
259 |     return w.fastXDM.setEnv({
260 |       send: function(xdm, strData) {
261 |         if (xdm.server) {
262 |           xdm.proxy.SendToChild(xdm.key+':'+strData);
263 |         } else {
264 |           w.fastXDM.waitFor(xdm, 'proxy', function() {
265 |             xdm.proxy.SendToParent(xdm.key+':'+strData);
266 |           });
267 |         }
268 |       }
269 |     });
270 |   }
271 |   if (navigator.product === "Gecko" && "frameElement" in window && navigator.userAgent.indexOf('WebKit') == -1) {
272 |     w.fastXDM.onServerStart = function(xdm) {
273 |       //setTimeout(function() {
274 |       xdm.frame._fn = function(sendFn) {
275 |         delete xdm.frame._fn;
276 |         xdm.send = sendFn;
277 |         // remove the function so that it cannot be used to overwrite the send function later on
278 |         return function(msg) {
279 |           w.fastXDM.onMessage({data: msg});
280 |         };
281 |       };
282 |       //}, 3000);
283 |     }
284 |     
285 |     w.fastXDM.onClientStart = function(xdm) {
286 |       xdm.send = window.frameElement._fn(function(msg) {
287 |         w.fastXDM.onMessage({data: msg});
288 |       });
289 |     }
290 |     
291 |     w.fastXDM.run('helper');
292 |     
293 |     return w.fastXDM.setEnv({
294 |       send: function(xdm, strData) {
295 |         //setTimeout(function() {
296 |         w.fastXDM.waitFor(xdm, 'send', function() {
297 |           xdm.send(xdm.key+':'+strData);
298 |         });
299 |         //}, 5000);
300 |       }
301 |     });
302 |   
303 |   }
304 |   
305 | })(window);
306 | 
--------------------------------------------------------------------------------