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