428 lines
9.7 KiB
JavaScript
428 lines
9.7 KiB
JavaScript
/*
|
|
* IPS embed handling
|
|
* http://www.invisionpower.com
|
|
*/
|
|
( function () {
|
|
"use strict";
|
|
|
|
var _origin;
|
|
var _div;
|
|
var _posting = false;
|
|
var _timer;
|
|
var _timeout = 200; //ms
|
|
var _embedId = '';
|
|
|
|
/**
|
|
* Init method, called when the document is ready
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function init() {
|
|
// Check for postMessage and JSON support
|
|
if( !window.postMessage || !window.JSON.parse ){
|
|
return;
|
|
}
|
|
|
|
// Work out our URL
|
|
_div = utils.get('ipsEmbed');
|
|
var url = utils.parseURL( window.location );
|
|
|
|
if( url.protocol == '' || url.protocol == ':' ){
|
|
utils.log( url );
|
|
url.protocol = window.location.protocol;
|
|
}
|
|
|
|
// Set our origin
|
|
_origin = url.protocol + '//' + url.host;
|
|
utils.log( "Origin in loader is " + _origin );
|
|
|
|
// Hide the content
|
|
_div.style.opacity = '0.00001';
|
|
};
|
|
|
|
/**
|
|
* Starts our main loop, which posts messages to the parent frame as needed
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function startTimeout() {
|
|
|
|
var currentSize = 0;
|
|
var repeats = 0;
|
|
|
|
utils.log("Starting timeout...");
|
|
|
|
// Main loop
|
|
_timer = setInterval( function () {
|
|
// What we want to do here is make the loading process more pleasant and less jumpy.
|
|
// To do that, we'll make the embed invisible until we've fetched the same height 6 times
|
|
// in a row (approx 1 second). If that happens we'll assume it has finished loading, and then
|
|
// we'll fade it in and start sending the recorded height to the parent for resizing.
|
|
|
|
var height = utils.getObjHeight( _div );
|
|
|
|
// If we HAVEN'T started posting our size
|
|
if( !_posting ){
|
|
if( height == currentSize ){
|
|
repeats++;
|
|
} else {
|
|
// The height has changed, so reset our repeat counter
|
|
repeats = 0;
|
|
}
|
|
|
|
if( repeats == 6 ){
|
|
_posting = true;
|
|
utils.fadeIn( _div );
|
|
}
|
|
}
|
|
|
|
currentSize = height;
|
|
|
|
if( _posting ){
|
|
_div.parentNode.className = '';
|
|
|
|
_postMessage('height', {
|
|
height: height
|
|
});
|
|
}
|
|
}, _timeout);
|
|
};
|
|
|
|
/**
|
|
* Posts a message to the iframe
|
|
*
|
|
* @param {number} [pageNo] Page number to load
|
|
* @returns {void}
|
|
*/
|
|
var _postMessage = function (method, obj) {
|
|
// Send to parent window
|
|
window.top.postMessage( JSON.stringify( utils.extendObj( obj || {}, {
|
|
method: method,
|
|
embedId: _embedId
|
|
} ) ), _origin );
|
|
};
|
|
|
|
/**
|
|
* Events sent to the iframe
|
|
*/
|
|
var messageEvents = {
|
|
/**
|
|
* The parent is ready for messages
|
|
*
|
|
* @param {object} data Data from the iframe
|
|
* @returns {void}
|
|
*/
|
|
ready: function (data) {
|
|
_embedId = data.embedId;
|
|
_postMessage('ok');
|
|
|
|
startTimeout();
|
|
},
|
|
|
|
stop: function (data) {
|
|
clearInterval( _timer );
|
|
eventHandler.off( window, 'message', windowMessage );
|
|
}
|
|
};
|
|
|
|
/*******************************************************************************************/
|
|
/* Boring stuff below */
|
|
|
|
|
|
/**
|
|
* Event handling
|
|
*/
|
|
// http://www.anujgakhar.com/2013/05/22/cross-browser-event-handling-in-javascript/
|
|
var eventHandler = {
|
|
on: function (el, ev, fn) {
|
|
if( window.addEventListener ){
|
|
el.addEventListener( ev, fn, false );
|
|
} else if( window.attachEvent ){
|
|
el.attachEvent( 'on' + ev, fn );
|
|
} else {
|
|
el[ 'on' + ev ] = fn;
|
|
}
|
|
},
|
|
|
|
off: function (el, ev, fn) {
|
|
if( window.removeEventListener ){
|
|
el.removeEventListener( ev, fn, false );
|
|
} else if( window.detachEvent ) {
|
|
el.detachEvent( 'on' + ev, fn );
|
|
} else {
|
|
elem[ 'on' + ev ] = null;
|
|
}
|
|
},
|
|
|
|
stop: function (ev) {
|
|
var e = ev || window.event;
|
|
e.cancelBubble = true;
|
|
if( e.stopPropagation ){
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
};
|
|
|
|
// Main message handler
|
|
eventHandler.on( window, 'message', windowMessage );
|
|
|
|
function windowMessage (e) {
|
|
if( e.origin !== _origin ){
|
|
utils.log( e.origin + 'does not equal' + _origin );
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var pmData = JSON.parse( e.data );
|
|
var method = pmData.method;
|
|
} catch (err) {
|
|
utils.error("iframe: invalid data.");
|
|
return;
|
|
}
|
|
|
|
if( method && typeof messageEvents[ method ] != 'undefined' ){
|
|
utils.log("Called " + method );
|
|
messageEvents[ method ].call( this, pmData );
|
|
} else {
|
|
utils.log("Method " + method + " doesn't exist");
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Utilities
|
|
*/
|
|
var utils = {
|
|
|
|
/**
|
|
* Log to console if supported
|
|
*
|
|
* @param {string} message Message to log
|
|
* @returns {void}
|
|
*/
|
|
log: function (message) {
|
|
if( window.console ){
|
|
console.log( "(EMBED): " + message );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Error to console if supported
|
|
*
|
|
* @param {string} message Message to log
|
|
* @returns {void}
|
|
*/
|
|
error: function (message) {
|
|
if( window.console ){
|
|
console.error( message );
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Gets an element based on ID
|
|
*
|
|
* @param {string} id Element ID
|
|
* @returns {element}
|
|
*/
|
|
get: function (id) {
|
|
return document.getElementById( id );
|
|
},
|
|
|
|
/**
|
|
* Extend an object with another object
|
|
*
|
|
* @param {object} originalObj Original object
|
|
* @param {object} newObj New object
|
|
* @returns {void}
|
|
*/
|
|
extendObj: function (originalObj, newObj) {
|
|
for( var i in newObj ){
|
|
if( newObj.hasOwnProperty(i) ){
|
|
originalObj[i] = newObj[i];
|
|
}
|
|
}
|
|
|
|
return originalObj;
|
|
},
|
|
|
|
/**
|
|
* Returns parsed information about a URL
|
|
*
|
|
* @param {string} url URL to parse
|
|
* @returns {object}
|
|
*/
|
|
parseURL: function (url) {
|
|
var elem = document.createElement('a');
|
|
utils.insertBefore( elem, _div );
|
|
elem.href = url;
|
|
|
|
var data = {
|
|
protocol: elem.protocol,
|
|
hostname: elem.hostname,
|
|
port: elem.port,
|
|
pathname: elem.pathname,
|
|
search: elem.search,
|
|
hash: elem.hash,
|
|
host: elem.host
|
|
};
|
|
|
|
elem.parentNode.removeChild( elem );
|
|
return data;
|
|
},
|
|
|
|
/**
|
|
* Returns the document scroll offset
|
|
*
|
|
* @returns {object}
|
|
*/
|
|
getScrollOffset: function () {
|
|
var doc = document.documentElement;
|
|
|
|
return {
|
|
left: (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0),
|
|
top: (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0)
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Returns the offset relative to the document for an element
|
|
*
|
|
* @param {element} element Element to get the offset for
|
|
* @returns {object}
|
|
*/
|
|
getOffset: function (element) {
|
|
var p = {
|
|
left: element.offsetLeft || 0,
|
|
top: element.offsetTop || 0
|
|
};
|
|
|
|
while (element = element.offsetParent) {
|
|
p.left += element.offsetLeft;
|
|
p.top += element.offsetTop;
|
|
}
|
|
|
|
return p;
|
|
},
|
|
|
|
/**
|
|
* Returns the outer height of the element
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
getObjHeight: function (elem) {
|
|
return elem.offsetHeight || 0;
|
|
},
|
|
|
|
/**
|
|
* Fade the element in
|
|
*
|
|
* @returns void
|
|
*/
|
|
fadeIn: function (elem) {
|
|
elem.style.opacity = 0;
|
|
|
|
var last = +new Date();
|
|
var tick = function() {
|
|
elem.style.opacity = +elem.style.opacity + (new Date() - last) / 400;
|
|
last = +new Date();
|
|
|
|
if( +elem.style.opacity < 1 ){
|
|
( window.requestAnimationFrame && requestAnimationFrame(tick) ) || setTimeout(tick, 16);
|
|
}
|
|
};
|
|
|
|
tick();
|
|
},
|
|
|
|
/**
|
|
* Returns the document height
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
getDocHeight: function () {
|
|
var D = document;
|
|
|
|
return Math.max(
|
|
D.body.scrollHeight, D.documentElement.scrollHeight,
|
|
D.body.offsetHeight, D.documentElement.offsetHeight,
|
|
D.body.clientHeight, D.documentElement.clientHeight
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Returns the viewport height
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
getViewportHeight: function () {
|
|
return Math.max( document.documentElement.clientHeight, window.innerHeight || 0 );
|
|
},
|
|
|
|
/**
|
|
* Returns attributes for an element
|
|
*
|
|
* @param {element} element DOM element
|
|
* @returns {object}
|
|
*/
|
|
getAttributes: function (element) {
|
|
if( typeof element == 'undefined' ){
|
|
return;
|
|
}
|
|
|
|
var attributes = {};
|
|
|
|
if( element.hasAttributes() ){
|
|
var _attr = element.attributes;
|
|
|
|
for( var i = 0; i < _attr.length; i++ ){
|
|
attributes[ _attr[ i ].name.toLowerCase() ] = _attr[ i ].value;
|
|
}
|
|
}
|
|
|
|
return attributes;
|
|
},
|
|
|
|
/**
|
|
* A simple method to insert an element after another element
|
|
*
|
|
* @param {element} elem The new element
|
|
* @param {element} existing The existing element, after which the new will be inserted
|
|
* @returns {void}
|
|
*/
|
|
insertAfter: function (elem, existing) {
|
|
existing.parentNode.insertBefore( elem, existing.nextSibling );
|
|
},
|
|
|
|
/**
|
|
* A simple method to insert an element before another element
|
|
*
|
|
* @param {element} elem The new element
|
|
* @param {element} existing The existing element, before which the new will be inserted
|
|
* @returns {void}
|
|
*/
|
|
insertBefore: function (elem, existing) {
|
|
existing.parentNode.insertBefore( elem, existing );
|
|
},
|
|
|
|
/**
|
|
* Returns a style property value for the given element and style
|
|
*
|
|
* @param {element} elem Element whose style is to be fetched
|
|
* @param {string} style Property to fetch
|
|
* @returns {mixed}
|
|
*/
|
|
getStyle: function (elem, style) {
|
|
return window.getComputedStyle( elem, null ).getPropertyValue( style );
|
|
},
|
|
|
|
/*
|
|
* Cross-browser 'dom ready' event handler to init the page
|
|
*/
|
|
/*! contentloaded.min.js - https://github.com/dperini/ContentLoaded - Author: Diego Perini - License: MIT */
|
|
contentLoaded: function (b,i) {
|
|
var j=false,k=true,a=b.document,l=a.documentElement,f=a.addEventListener,h=f?'addEventListener':'attachEvent',n=f?'removeEventListener':'detachEvent',g=f?'':'on',c=function(d){if(d.type=='readystatechange'&&a.readyState!='complete')return;(d.type=='load'?b:a)[n](g+d.type,c,false);if(!j&&(j=true))i.call(b,d.type||d)},m=function(){try{l.doScroll('left')}catch(e){setTimeout(m,50);return}c('poll')};if(a.readyState=='complete')i.call(b,'lazy');else{if(!f&&l.doScroll){try{k=!b.frameElement}catch(e){}if(k)m()}a[h](g+'DOMContentLoaded',c,false);a[h](g+'readystatechange',c,false);b[h](g+'load',c,false)}
|
|
}
|
|
};
|
|
utils.contentLoaded( window, function () {
|
|
init();
|
|
});
|
|
})(); |