├── 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 |
--------------------------------------------------------------------------------