Skip to content

Instantly share code, notes, and snippets.

@Bitaru
Created August 20, 2015 11:33
Show Gist options
  • Select an option

  • Save Bitaru/ddb4d970cbec5b34452e to your computer and use it in GitHub Desktop.

Select an option

Save Bitaru/ddb4d970cbec5b34452e to your computer and use it in GitHub Desktop.
Iframe widgets initializer
require('currentscript')
var EX = function(config){
this.config = this.prepareConfig(config);
this.cache_bust = 1;
this.iframe = this.create(this.config);
this.subscribe(config.debug ? true : false);
/**
* Attach iframe node to window object in production mode
* for easy access to those styles
* And set excu object globaly for get/set events in debug
*/
if(typeof config.debug !== 'undefined'){
this.src = this.getCurrent().src
return this
}
return this.iframe
};
/**
* Basic iframe constructor
* @return {Object} Iframe NODE
*/
EX.prototype.create = function(opt){
// Create a buddy to get width
var buddy, parent,
script = this.getCurrent(),
dimension = this.getDimensions(this.config);
if(opt && opt.parent){
parent = typeof opt.parent == 'string' ? document.getElementById(opt.parent.replace('#', '')) : opt.parent;
}else{
parent = script.parentElement;
}
if(typeof opt === 'undefined'){
buddy = document.createElement("div");
buddy.style.width = "100%";
buddy.style.height = "auto";
parent.insertBefore(buddy, script.nextSibling);
}else{
buddy = parent;
}
var origin = this.getOrigin( opt && opt.src ? opt.src : script.src ),
frameUrl = this.getFramePath(origin),
iframe = document.createElement('iframe');
iframe.width = dimension.width;
iframe.src = frameUrl+'#'+encodeURIComponent(document.location.href);
iframe.frameBorder = 0;
iframe.frameborder = 0;
iframe.style.border = 'none';
iframe.marginheight = 0;
iframe.marginwidth = 0;
iframe.height = dimension.height;
iframe.scrolling = 'no';
buddy.appendChild(iframe);
return iframe;
};
/**
* Prepare config for support old version
* @param {Object} Config object
* @global {String} set excu.configString -
* @return {Object}
*/
EX.prototype.prepareConfig = function prepare(config){
var out = {}, tmp = [], styles = [];
for(var i in config){
var clean = i.replace('exc_', '');
if(typeof config[i] === 'string' && config[i].indexOf('%') !== -1){
continue
}
if(['query', 'q'].indexOf(clean) > -1 && config[i] === ''){
config[i] = document.title;
}
if(i === 'box_params' || i === 'style' || i === 'params'){
for(var p in config[i]){
if (config[i].hasOwnProperty(p)) {
if(p === 'title' || p === 'query' || p === 'q'){
if(['query', 'q'].indexOf(p) > -1 && config[i][p] === ''){
config[i][p] = document.title;
}
tmp.push(p + '=' + this.encodeParamValue(config[i][p]));
}else{
styles.push( p +'='+config[i][p]);
}
}
}
}else{
tmp.push(clean+'='+this.encodeParamValue(config[i]));
}
out[clean] = config[i];
}
this.configString = '?'+tmp.join('&') + (styles.length > 0 ? '&style='+this.encodeParamValue(styles.join(';')) : '');
return out
};
/**
* Get origin of script/iframe
* @params {String} Excusriopedia domain
* @return {String} Origin
*/
EX.prototype.getOrigin = function(src){
var regex = /^(https?\:\/\/[^\/?#]+)(?:[\/?#]|$)/i, parts;
if(src.indexOf(':3') == -1){
src = src.replace(/^http:\/\//i, 'https://');
}
parts = src.match(regex);
return parts && parts[1]
};
/**
* Return object of computed width and height for boxes
* @params {Object} Widget config
* @return {Object} Height|Width
*/
EX.prototype.getDimensions = function(config){
var isHeight = function () { return typeof config.height !== 'undefined'},
isWidth = function () { return typeof config.width !== 'undefined'},
compute;
compute = function (height, width){
return {
height: !isHeight() ? height : config.height,
width: !isWidth() ? width : config.width
}
};
switch (config.type){
case 'box2':
return compute('145px', '100%');
case 'box1':
return compute('400px', '242px');
case 'category':
return compute('690px', '900px');
case 'boxcustom':
var params = config.box_params ? config.box_params : config.params
return compute(params.height + 'px', params.width + 'px');
default:
return compute('100%', '100%');
};
};
/**
* Default iframe response aggregator for production mode
* @params {Object} Response from iframe
* @param {String} Additional option(default iframe hostname)
*/
EX.prototype.resizer = function(e){
var frame = (window.EX.iframe || window.EX);
if (typeof e === 'object') {
if(e.width){
frame.setAttribute('width', e.width + 'px');
}
if(e.height){
frame.setAttribute('height', e.height + 'px');
}
}
};
/**
* Return current script NODE
* @return {Object}
*/
EX.prototype.getCurrent = function(){
var script;
try {
script = document.currentScript;
script.src;
}catch (e){
var stack = e.stack,
scripts = document.getElementsByTagName("script"),
src;
if (!stack)
return scripts[scripts.length - 1];
e = stack.indexOf(' at ') !== -1 ? ' at ' : '@';
while (stack.indexOf(e) !== -1)
stack = stack.substring(stack.indexOf(e) + e.length);
src = stack.substring(0, stack.indexOf(':', stack.indexOf(':')+1));
for(var i = 0; i < scripts.length; i++){
if(scripts[i].src === src){
script = scripts[i];
}
}
}
return script;
};
/**
* Subscribe callbacks to message from iframe
* @param {bool}
*/
EX.prototype.subscribe = function(debug){
// Attach event listener
if(debug){
EX.prototype.set = function(data){
this.postMessage(data);
if(typeof data.width !== 'undefined' || typeof data.height !== 'undefined'){
this.resizer(data);
}
return this
};
EX.prototype.ready = function(callback){
this.getMessage(function(message){
if(message.ready && callback){
callback(this);
}
});
};
EX.prototype.reload = function(data){
var parent = this.iframe.parentNode,
src = this.iframe.src;
this.config = data;
this.prepareConfig(data);
removeElement(this.iframe);
// Create new frame
this.iframe = this.create({parent: parent, src: src});
return this
};
}else{
var _self = this;
this.getMessage(function(data){
_self.resizer.call(_self, data);
});
}
return this
};
/**
* Return path to widget
* @param {string} Excurisopedia widget root
*/
EX.prototype.getFramePath = function(origin){
return origin + '/' + (this.config.lang || 'ru') + '/box/'+this.config.type + '/'+ this.configString;
}
/**
* Excronize data for get Request
* @param {string} Excurisopedia widget root
*/
EX.prototype.encodeParamValue = function(value) {
if (typeof encodeURIComponent == "function") return encodeURIComponent(value);
return escape(value);
}
/**
* Send message to iframe
* @param {Object}
*/
EX.prototype.postMessage = function(message) {
var target = this.iframe || window.EX,
target_url = target.src;
if (window['postMessage']) {
target.contentWindow['postMessage'](encodeJson(message), '*');
} else {
target.location = target_url.replace(/#.*$/, '') + '#' + (+new Date) + (this.cache_bust++) + '&' + encodeJson(message);
}
};
/**
* Get message from iframe
* @return {Function}
*/
EX.prototype.getMessage = function(callback) {
if (window['postMessage']) {
if (callback) {
var source_origin = this.getOrigin(this.iframe.src || EX.src)
var attached_callback = function(e) {
if ((typeof source_origin === 'string' && e.origin !== source_origin)
|| (Object.prototype.toString.call(source_origin) === "[object Function]" && source_origin(e.origin) === false)) {
return !1;
}
callback(decodeJson(e.data));
};
}
if (window['addEventListener']) {
window[ callback ? 'addEventListener' : 'removeEventListener']('message', attached_callback, false);
} else {
window[callback ? 'attachEvent' : 'detachEvent']('onmessage', attached_callback);
}
} else {
this.interval_id && clearInterval(this.interval_id);
this.interval_id = null;
var last_hash;
if (callback) {
last_hash = void 0;
this.interval_id = setInterval((function(_this) {
return function() {
var hash, re;
hash = document.location.hash;
re = /^#?\d+&/;
if (hash !== last_hash && re.test(hash)) {
_this.last_hash = hash;
callback({
data: decodeJson(encodeURIComponent(hash.replace(re, "")))
});
}
};
})(this), 100);
}
}
};
// Helpers
/**
* JSON.parse
* @return {Object}
*/
var decodeJson = function (string){
var out = {},
array = string.split('&');
for(var i = 0; i < array.length; i++){
var match = array[i].split('=');
out[match[0]] = match[1];
}
return out;
};
/**
* JSON.stringify
* @return {String}
*/
var encodeJson = function (obj){
var out = [];
for( var i in obj ){
obj.hasOwnProperty(i) && out.push(i+'='+obj[i]);
}
return out.join('&');
};
/**
* Crossbrowser remove element
*/
var removeElement = function(element){
element && element.parentNode && element.parentNode.removeChild(element);
};
/**
* indexOf for IE < 9
* @return {bool}
*/
if (!Array.prototype.indexOf){
Array.prototype.indexOf = function(elt /*, from*/){
var len = this.length >>> 0;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
}
if(typeof exc_config === 'object'){
window.EX = new EX(exc_config);
}else{
window.EX = EX;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment