(function(w) {
if (w.fastXDM) return;
var handlers = {};
var onEnvLoad = [];
var env = {};
// Key generation
function genKey() {
var key = '';
for (var i = 0; i < 5; i++) {
key += Math.ceil(Math.random() * 15).toString(16);
}
return key;
}
function waitFor(obj, prop, func, self, count) {
if (obj[prop]) {
func.apply(self);
} else {
count = count || 0;
if (count < 1000) {
setTimeout(function() {
waitFor(obj, prop, func, self, count + 1);
}, 0);
}
}
}
function attachScript(url) {
setTimeout(function() {
var newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = url || w.fastXDM.helperUrl;
waitFor(document, 'body', function() {
document.getElementsByTagName('HEAD')[0].appendChild(newScript);
});
}, 0);
}
function walkVar(value, clean) {
var newValue;
switch (typeof value) {
case 'string':
if (clean) {
newValue = value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
} else {
newValue = value.replace(/'/g, '\'').replace(/"/g, '"').replace(/>/g, '>').replace(/</g, '<').replace(/&/g, '&');
}
break;
case 'object':
if (Object.prototype.toString.apply(value) === '[object Array]') {
newValue = [];
for (var i = 0, len = value.length; i < len; i++) {
newValue[i] = walkVar(value[i], clean);
}
} else {
newValue = {};
for (var k in value) {
if (Object.hasOwnProperty.call(value, k)) {
newValue[k] = walkVar(value[k], clean);
}
}
}
break;
default:
newValue = value;
break;
}
return newValue;
}
// Env functions
function getEnv(callback, self) {
if (env.loaded) {
callback.apply(self, [env]);
} else {
onEnvLoad.push([self, callback]);
}
}
function envLoaded() {
env.loaded = true;
for (var i = 0, len = onEnvLoad.length; i < len; i++) {
onEnvLoad[i][1].apply(onEnvLoad[i][0], [env]);
}
}
function applyMethod(strData, self) {
getEnv(function(env) {
var data = env.json.parse(strData);
if (data[0]) {
if (!data[1]) data[1] = [];
for (var i = 0, len = data[1].length; i < len; i++) {
if (data[1][i] && data[1][i]._func) {
var funcNum = data[1][i]._func;
data[1][i] = function() {
var args = Array.prototype.slice.call(arguments);
args.unshift('_func' + funcNum);
self.callMethod.apply(self, args);
}
} else if (self.options.safe) {
data[1][i] = walkVar(data[1][i], true);
}
}
setTimeout(function() {
if (!self.methods[data[0]]) {
throw Error('fastXDM: Method ' + data[0] + ' is undefined');
}
self.methods[data[0]].apply(self, data[1]);
}, 0);
}
});
}
function extend(obj1, obj2) {
for (var i in obj2) {
if (obj1[i] && typeof(obj1[i]) === 'object') {
extend(obj1[i], obj2[i])
} else {
obj1[i] = obj2[i];
}
}
}
// XDM object
w.fastXDM = {
_id: 0,
helperUrl: 'https://vk.com/js/api/xdmHelper.js',
Server: function(methods, filter, options) {
this.methods = methods || {};
this.filter = filter;
this.options = options || {};
this.id = w.fastXDM._id++;
this.key = genKey();
this.frameName = 'fXD' + this.key;
this.server = true;
this.methods['%init%'] = this.methods.__fxdm_i = function() {
w.fastXDM.run(this.id);
if (this.methods.onInit) {
this.methods.onInit();
}
};
handlers[this.key] = [applyMethod, this];
},
Client: function(methods, options) {
this.methods = methods || {};
this.options = options || {};
this.id = w.fastXDM._id++;
this.client = true;
w.fastXDM.run(this.id);
if (window.name.indexOf('fXD') === 0) {
this.key = window.name.substr(3);
} else {
throw Error('Wrong window.name property.');
}
this.caller = window.parent;
handlers[this.key] = [applyMethod, this];
w.fastXDM.on('helper', function() {
w.fastXDM.onClientStart(this);
}, this);
getEnv(function(env) {
env.send(this, env.json.stringify(['%init%']));
var methods = this.methods;
setTimeout(function() {
if (methods.onInit) {
methods.onInit();
}
}, 0);
}, this);
},
onMessage: function(e) {
var data = e.data;
if (!data) {
return false;
}
if (typeof data !== 'string' && !(data instanceof String)) {
return false;
}
var key = data.substr(0, 5);
if (handlers[key]) {
var self = handlers[key][1];
if (self && (!self.filter || self.filter(e.origin))) {
handlers[key][0](data.substr(6), self);
}
}
},
setJSON: function(json) {
env.json = json;
},
getJSON: function(callback) {
if (!callback) {
return env.json;
}
getEnv(function(env) {
callback(env.json);
});
},
setEnv: function(exEnv) {
for (var i in exEnv) {
env[i] = exEnv[i];
}
envLoaded();
},
_q: {},
on: function(key, act, self) {
if (!this._q[key]) this._q[key] = [];
if (this._q[key] == -1) {
act.apply(self);
} else {
this._q[key].push([act, self]);
}
},
run: function(key) {
var len = (this._q[key] || []).length;
for (var i = 0; i < len; i++) {
this._q[key][i][0].apply(this._q[key][i][1]);
}
this._q[key] = -1;
},
waitFor: waitFor
}
w.fastXDM.Server.prototype.start = function(obj, count) {
if (obj.contentWindow) {
this.caller = obj.contentWindow;
this.frame = obj;
w.fastXDM.on('helper', function() {
w.fastXDM.onServerStart(this);
}, this);
} else { // Opera old versions
var self = this;
count = count || 0;
if (count < 50) {
setTimeout(function() {
self.start.apply(self, [obj, count + 1]);
}, 100);
}
}
}
w.fastXDM.Server.prototype.destroy = function() {
delete handlers[this.key];
}
w.fastXDM.Server.prototype.append = function(obj, options, attrs) {
var div = document.createElement('DIV');
div.innerHTML = '<iframe name="' + this.frameName + '" ' + (attrs || '') + '></iframe>';
var frame = div.firstChild;
var self = this;
setTimeout(function() {
frame.frameBorder = '0';
if (options) extend(frame, options);
obj.insertBefore(frame, obj.firstChild);
self.start(frame);
}, 0);
return frame;
}
w.fastXDM.Client.prototype.callMethod = w.fastXDM.Server.prototype.callMethod = function() {
var args = Array.prototype.slice.call(arguments);
var method = args.shift();
for (var i = 0, len = args.length; i < len; i++) {
if (typeof(args[i]) === 'function') {
this.funcsCount = (this.funcsCount || 0) + 1;
var func = args[i];
var funcName = '_func' + this.funcsCount;
this.methods[funcName] = function() {
func.apply(this, arguments);
delete this.methods[funcName];
}
args[i] = {_func: this.funcsCount};
} else if (this.options.safe) {
args[i] = walkVar(args[i], false);
}
}
waitFor(this, 'caller', function() {
w.fastXDM.on(this.id, function() {
getEnv(function(env) {
env.send(this, env.json.stringify([method, args]));
}, this);
}, this);
}, this);
}
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]}') {
env.json = {parse: w.JSON.parse, stringify: w.JSON.stringify};
} else {
w.fastXDM._needJSON = true;
}
// PostMessage cover
if (w.postMessage) {
env.protocol = 'p';
env.send = function(xdm, strData) {
var win = (xdm.frame ? xdm.frame.contentWindow : xdm.caller);
if (win) {
try {
win.postMessage(xdm.key + ':' + strData, "*");
} catch(e) {
window.postMessage.call(win, xdm.key + ':' + strData, "*");
}
}
}
if (w.addEventListener) {
w.addEventListener("message", w.fastXDM.onMessage, false);
} else {
w.attachEvent("onmessage", w.fastXDM.onMessage);
}
if (w.fastXDM._needJSON) {
w.fastXDM._onlyJSON = true;
attachScript();
} else {
envLoaded();
}
} else {
attachScript();
}
})(window);
try{stManager.done('api/xdm.js');}catch(e){}