123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692 |
- /*
- * File: iframeResizer.contentWindow.js
- * Desc: Include this file in any page being loaded into an iframe
- * to force the iframe to resize to the content size.
- * Requires: iframeResizer.js on host page.
- * Author: David J. Bradshaw - dave@bradshaw.net
- * Contributor: Jure Mav - jure.mav@gmail.com
- */
- ;(function() {
- 'use strict';
- var
- autoResize = true,
- base = 10,
- bodyBackground = '',
- bodyMargin = 0,
- bodyMarginStr = '',
- bodyPadding = '',
- calculateWidth = false,
- doubleEventList = {'resize':1,'click':1},
- eventCancelTimer = 128,
- height = 1,
- firstRun = true,
- heightCalcModeDefault = 'offset',
- heightCalcMode = heightCalcModeDefault,
- initLock = true,
- initMsg = '',
- inPageLinks = {},
- interval = 32,
- logging = false,
- msgID = '[iFrameSizer]', //Must match host page msg ID
- msgIdLen = msgID.length,
- myID = '',
- publicMethods = false,
- resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1},
- targetOriginDefault = '*',
- target = window.parent,
- tolerance = 0,
- triggerLocked = false,
- triggerLockedTimer = null,
- width = 1;
- function addEventListener(el,evt,func){
- if ('addEventListener' in window){
- el.addEventListener(evt,func, false);
- } else if ('attachEvent' in window){ //IE
- el.attachEvent('on'+evt,func);
- }
- }
- function formatLogMsg(msg){
- return msgID + '[' + myID + ']' + ' ' + msg;
- }
- function log(msg){
- if (logging && ('object' === typeof window.console)){
- console.log(formatLogMsg(msg));
- }
- }
- function warn(msg){
- if ('object' === typeof window.console){
- console.warn(formatLogMsg(msg));
- }
- }
- function init(){
- log('Initialising iFrame');
- readData();
- setMargin();
- setBodyStyle('background',bodyBackground);
- setBodyStyle('padding',bodyPadding);
- injectClearFixIntoBodyElement();
- checkHeightMode();
- stopInfiniteResizingOfIFrame();
- setupPublicMethods();
- startEventListeners();
- inPageLinks = setupInPageLinks();
- sendSize('init','Init message from host page');
- }
- function readData(){
- var data = initMsg.substr(msgIdLen).split(':');
- function strBool(str){
- return 'true' === str ? true : false;
- }
- myID = data[0];
- bodyMargin = (undefined !== data[1]) ? Number(data[1]) : bodyMargin; //For V1 compatibility
- calculateWidth = (undefined !== data[2]) ? strBool(data[2]) : calculateWidth;
- logging = (undefined !== data[3]) ? strBool(data[3]) : logging;
- interval = (undefined !== data[4]) ? Number(data[4]) : interval;
- publicMethods = (undefined !== data[5]) ? strBool(data[5]) : publicMethods;
- autoResize = (undefined !== data[6]) ? strBool(data[6]) : autoResize;
- bodyMarginStr = data[7];
- heightCalcMode = (undefined !== data[8]) ? data[8] : heightCalcMode;
- bodyBackground = data[9];
- bodyPadding = data[10];
- tolerance = (undefined !== data[11]) ? Number(data[11]) : tolerance;
- inPageLinks.enable = (undefined !== data[12]) ? strBool(data[12]): false;
- }
- function chkCSS(attr,value){
- if (-1 !== value.indexOf('-')){
- warn('Negative CSS value ignored for '+attr);
- value='';
- }
- return value;
- }
- function setBodyStyle(attr,value){
- if ((undefined !== value) && ('' !== value) && ('null' !== value)){
- document.body.style[attr] = value;
- log('Body '+attr+' set to "'+value+'"');
- }
- }
- function setMargin(){
- //If called via V1 script, convert bodyMargin from int to str
- if (undefined === bodyMarginStr){
- bodyMarginStr = bodyMargin+'px';
- }
- chkCSS('margin',bodyMarginStr);
- setBodyStyle('margin',bodyMarginStr);
- }
- function stopInfiniteResizingOfIFrame(){
- document.documentElement.style.height = '';
- document.body.style.height = '';
- log('HTML & body height set to "auto"');
- }
- function addTriggerEvent(options){
- function addListener(eventName){
- addEventListener(window,eventName,function(e){
- sendSize(options.eventName,options.eventType);
- });
- }
- if(options.eventNames && Array.prototype.map){
- options.eventName = options.eventNames[0];
- options.eventNames.map(addListener);
- } else {
- addListener(options.eventName);
- }
- log('Added event listener: ' + options.eventType);
- }
- function initEventListeners(){
- addTriggerEvent({ eventType: 'Animation Start', eventNames: ['animationstart','webkitAnimationStart'] });
- addTriggerEvent({ eventType: 'Animation Iteration', eventNames: ['animationiteration','webkitAnimationIteration'] });
- addTriggerEvent({ eventType: 'Animation End', eventNames: ['animationend','webkitAnimationEnd'] });
- addTriggerEvent({ eventType: 'Device Orientation Change', eventName: 'deviceorientation' });
- addTriggerEvent({ eventType: 'Transition End', eventNames: ['transitionend','webkitTransitionEnd','MSTransitionEnd','oTransitionEnd','otransitionend'] });
- addTriggerEvent({ eventType: 'Window Clicked', eventName: 'click' });
- //addTriggerEvent({ eventType: 'Window Mouse Down', eventName: 'mousedown' });
- //addTriggerEvent({ eventType: 'Window Mouse Up', eventName: 'mouseup' });
- addTriggerEvent({ eventType: 'Window Resized', eventName: 'resize' });
- }
- function checkHeightMode(){
- if (heightCalcModeDefault !== heightCalcMode){
- if (!(heightCalcMode in getHeight)){
- warn(heightCalcMode + ' is not a valid option for heightCalculationMethod.');
- heightCalcMode='bodyScroll';
- }
- log('Height calculation method set to "'+heightCalcMode+'"');
- }
- }
- function startEventListeners(){
- if ( true === autoResize ) {
- initEventListeners();
- setupMutationObserver();
- }
- else {
- log('Auto Resize disabled');
- }
- }
- function injectClearFixIntoBodyElement(){
- var clearFix = document.createElement('div');
- clearFix.style.clear = 'both';
- clearFix.style.display = 'block'; //Guard against this having been globally redefined in CSS.
- document.body.appendChild(clearFix);
- }
- function setupInPageLinks(){
- function getPagePosition (){
- return {
- x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft,
- y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop
- };
- }
- function getElementPosition(el){
- var
- elPosition = el.getBoundingClientRect(),
- pagePosition = getPagePosition();
- return {
- x: parseInt(elPosition.left,10) + parseInt(pagePosition.x,10),
- y: parseInt(elPosition.top,10) + parseInt(pagePosition.y,10)
- };
- }
- function findTarget(location){
- var hash = location.split("#")[1] || "";
- var hashData = decodeURIComponent(hash);
- function jumpToTarget(target){
- var jumpPosition = getElementPosition(target);
- log('Moving to in page link (#'+hash+') at x: '+jumpPosition.x+' y: '+jumpPosition.y);
- sendMsg(jumpPosition.y, jumpPosition.x, 'scrollToOffset'); // X&Y reversed at sendMsg uses height/width
- }
- var target = document.getElementById(hashData) || document.getElementsByName(hashData)[0];
- if (target){
- jumpToTarget(target);
- } else {
- log('In page link (#' + hash + ') not found in iFrame, so sending to parent');
- sendMsg(0,0,'inPageLink','#'+hash);
- }
- }
- function checkLocationHash(){
- if ('' !== location.hash && '#' !== location.hash){
- findTarget(location.href);
- }
- }
- function bindAnchors(){
- function setupLink(el){
- function linkClicked(e){
- e.preventDefault();
- /*jshint validthis:true */
- findTarget(this.getAttribute('href'));
- }
- if ('#' !== el.getAttribute('href')){
- addEventListener(el,'click',linkClicked);
- }
- }
- Array.prototype.forEach.call( document.querySelectorAll( 'a[href^="#"]' ), setupLink );
- }
- function bindLocationHash(){
- addEventListener(window,'hashchange',checkLocationHash);
- }
- function initCheck(){ //check if page loaded with location hash after init resize
- setTimeout(checkLocationHash,eventCancelTimer);
- }
- function enableInPageLinks(){
- if(Array.prototype.forEach && document.querySelectorAll){
- log('Setting up location.hash handlers');
- bindAnchors();
- bindLocationHash();
- initCheck();
- } else {
- warn('In page linking not fully supported in this browser! (See README.md for IE8 workaround)');
- }
- }
- if(inPageLinks.enable){
- enableInPageLinks();
- } else {
- log('In page linking not enabled');
- }
- return {
- findTarget:findTarget
- };
- }
- function setupPublicMethods(){
- if (publicMethods) {
- log('Enable public methods');
- window.parentIFrame = {
- close: function closeF(){
- sendSize('close','parentIFrame.close()', 0, 0);
- },
- getId: function getIdF(){
- return myID;
- },
- moveToAnchor: function moveToAnchorF(hash){
- inPageLinks.findTarget(hash);
- },
- reset: function resetF(){
- resetIFrame('parentIFrame.reset');
- },
- scrollTo: function scrollToF(x,y){
- sendMsg(y,x,'scrollTo'); // X&Y reversed at sendMsg uses height/width
- },
- scrollToOffset: function scrollToF(x,y){
- sendMsg(y,x,'scrollToOffset'); // X&Y reversed at sendMsg uses height/width
- },
- sendMessage: function sendMessageF(msg,targetOrigin){
- sendMsg(0,0,'message',JSON.stringify(msg),targetOrigin);
- },
- setHeightCalculationMethod: function setHeightCalculationMethodF(heightCalculationMethod){
- heightCalcMode = heightCalculationMethod;
- checkHeightMode();
- },
- setTargetOrigin: function setTargetOriginF(targetOrigin){
- log('Set targetOrigin: '+targetOrigin);
- targetOriginDefault = targetOrigin;
- },
- size: function sizeF(customHeight, customWidth){
- var valString = ''+(customHeight?customHeight:'')+(customWidth?','+customWidth:'');
- lockTrigger();
- sendSize('size','parentIFrame.size('+valString+')', customHeight, customWidth);
- }
- };
- }
- }
- function initInterval(){
- if ( 0 !== interval ){
- log('setInterval: '+interval+'ms');
- setInterval(function(){
- sendSize('interval','setInterval: '+interval);
- },Math.abs(interval));
- }
- }
- function setupInjectElementLoadListners(mutations){
- function addLoadListener(element){
- if (element.height === undefined || element.width === undefined || 0 === element.height || 0 === element.width){
- log('Attach listerner to '+element.src);
- addEventListener(element,'load', function imageLoaded(){
- sendSize('imageLoad','Image loaded');
- });
- }
- }
- mutations.forEach(function (mutation) {
- if (mutation.type === 'attributes' && mutation.attributeName === 'src'){
- addLoadListener(mutation.target);
- } else if (mutation.type === 'childList'){
- var images = mutation.target.querySelectorAll('img');
- Array.prototype.forEach.call(images,function (image) {
- addLoadListener(image);
- });
- }
- });
- }
- function setupMutationObserver(){
- var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
- function createMutationObserver(){
- var
- target = document.querySelector('body'),
- config = {
- attributes : true,
- attributeOldValue : false,
- characterData : true,
- characterDataOldValue : false,
- childList : true,
- subtree : true
- },
- observer = new MutationObserver(function(mutations) {
- sendSize('mutationObserver','mutationObserver: ' + mutations[0].target + ' ' + mutations[0].type);
- setupInjectElementLoadListners(mutations); //Deal with WebKit asyncing image loading when tags are injected into the page
- });
- log('Enable MutationObserver');
- observer.observe(target, config);
- }
- if (MutationObserver){
- if (0 > interval) {
- initInterval();
- } else {
- createMutationObserver();
- }
- }
- else {
- warn('MutationObserver not supported in this browser!');
- initInterval();
- }
- }
- // document.documentElement.offsetHeight is not reliable, so
- // we have to jump through hoops to get a better value.
- function getBodyOffsetHeight(){
- function getComputedBodyStyle(prop) {
- function convertUnitsToPxForIE8(value) {
- var PIXEL = /^\d+(px)?$/i;
- if (PIXEL.test(value)) {
- return parseInt(value,base);
- }
- var
- style = el.style.left,
- runtimeStyle = el.runtimeStyle.left;
- el.runtimeStyle.left = el.currentStyle.left;
- el.style.left = value || 0;
- value = el.style.pixelLeft;
- el.style.left = style;
- el.runtimeStyle.left = runtimeStyle;
- return value;
- }
- var
- el = document.body,
- retVal = 0;
- if (('defaultView' in document) && ('getComputedStyle' in document.defaultView)) {
- retVal = document.defaultView.getComputedStyle(el, null);
- retVal = (null !== retVal) ? retVal[prop] : 0;
- } else {//IE8
- retVal = convertUnitsToPxForIE8(el.currentStyle[prop]);
- }
- return parseInt(retVal,base);
- }
- return document.body.offsetHeight +
- getComputedBodyStyle('marginTop') +
- getComputedBodyStyle('marginBottom');
- }
- function getBodyScrollHeight(){
- return document.body.scrollHeight;
- }
- function getDEOffsetHeight(){
- return document.documentElement.offsetHeight;
- }
- function getDEScrollHeight(){
- return document.documentElement.scrollHeight;
- }
- //From https://github.com/guardian/iframe-messenger
- function getLowestElementHeight() {
- var
- allElements = document.querySelectorAll('body *'),
- allElementsLength = allElements.length,
- maxBottomVal = 0,
- timer = new Date().getTime();
- for (var i = 0; i < allElementsLength; i++) {
- if (allElements[i].getBoundingClientRect().bottom > maxBottomVal) {
- maxBottomVal = allElements[i].getBoundingClientRect().bottom;
- }
- }
- timer = new Date().getTime() - timer;
- log('Parsed '+allElementsLength+' HTML elements');
- log('LowestElement bottom position calculated in ' + timer + 'ms');
- return maxBottomVal;
- }
- function getAllHeights(){
- return [
- getBodyOffsetHeight(),
- getBodyScrollHeight(),
- getDEOffsetHeight(),
- getDEScrollHeight()
- ];
- }
- function getMaxHeight(){
- return Math.max.apply(null,getAllHeights());
- }
- function getMinHeight(){
- return Math.min.apply(null,getAllHeights());
- }
- function getBestHeight(){
- return Math.max(getBodyOffsetHeight(),getLowestElementHeight());
- }
- var getHeight = {
- offset : getBodyOffsetHeight, //Backward compatability
- bodyOffset : getBodyOffsetHeight,
- bodyScroll : getBodyScrollHeight,
- documentElementOffset : getDEOffsetHeight,
- scroll : getDEScrollHeight, //Backward compatability
- documentElementScroll : getDEScrollHeight,
- max : getMaxHeight,
- min : getMinHeight,
- grow : getMaxHeight,
- lowestElement : getBestHeight
- };
- function getWidth(){
- return Math.max(
- document.documentElement.scrollWidth,
- document.body.scrollWidth
- );
- }
- function sendSize(triggerEvent, triggerEventDesc, customHeight, customWidth){
- var currentHeight,currentWidth;
- function recordTrigger(){
- if (!(triggerEvent in {'reset':1,'resetPage':1,'init':1})){
- log( 'Trigger event: ' + triggerEventDesc );
- }
- }
- function resizeIFrame(){
- height = currentHeight;
- width = currentWidth;
- sendMsg(height,width,triggerEvent);
- }
- function isDoubleFiredEvent(){
- return triggerLocked && (triggerEvent in doubleEventList);
- }
- function isSizeChangeDetected(){
- function checkTolarance(a,b){
- var retVal = Math.abs(a-b) <= tolerance;
- return !retVal;
- }
- currentHeight = (undefined !== customHeight) ? customHeight : getHeight[heightCalcMode]();
- currentWidth = (undefined !== customWidth ) ? customWidth : getWidth();
- return checkTolarance(height,currentHeight) ||
- (calculateWidth && checkTolarance(width,currentWidth));
- }
- function isForceResizableEvent(){
- return !(triggerEvent in {'init':1,'interval':1,'size':1});
- }
- function isForceResizableHeightCalcMode(){
- return (heightCalcMode in resetRequiredMethods);
- }
- function logIgnored(){
- log('No change in size detected');
- }
- function checkDownSizing(){
- if (isForceResizableEvent() && isForceResizableHeightCalcMode()){
- resetIFrame(triggerEventDesc);
- } else if (!(triggerEvent in {'interval':1})){
- recordTrigger();
- logIgnored();
- }
- }
- if (!isDoubleFiredEvent()){
- if (isSizeChangeDetected()){
- recordTrigger();
- lockTrigger();
- resizeIFrame();
- } else {
- checkDownSizing();
- }
- } else {
- log('Trigger event cancelled: '+triggerEvent);
- }
- }
- function lockTrigger(){
- if (!triggerLocked){
- triggerLocked = true;
- log('Trigger event lock on');
- }
- clearTimeout(triggerLockedTimer);
- triggerLockedTimer = setTimeout(function(){
- triggerLocked = false;
- log('Trigger event lock off');
- log('--');
- },eventCancelTimer);
- }
- function triggerReset(triggerEvent){
- height = getHeight[heightCalcMode]();
- width = getWidth();
- sendMsg(height,width,triggerEvent);
- }
- function resetIFrame(triggerEventDesc){
- var hcm = heightCalcMode;
- heightCalcMode = heightCalcModeDefault;
- log('Reset trigger event: ' + triggerEventDesc);
- lockTrigger();
- triggerReset('reset');
- heightCalcMode = hcm;
- }
- function sendMsg(height,width,triggerEvent,msg,targetOrigin){
- function setTargetOrigin(){
- if (undefined === targetOrigin){
- targetOrigin = targetOriginDefault;
- } else {
- log('Message targetOrigin: '+targetOrigin);
- }
- }
- function sendToParent(){
- var
- size = height + ':' + width,
- message = myID + ':' + size + ':' + triggerEvent + (undefined !== msg ? ':' + msg : '');
- log('Sending message to host page (' + message + ')');
- target.postMessage( msgID + message, targetOrigin);
- }
- setTargetOrigin();
- sendToParent();
- }
- function receiver(event) {
- function isMessageForUs(){
- return msgID === (''+event.data).substr(0,msgIdLen); //''+ Protects against non-string messages
- }
- function initFromParent(){
- initMsg = event.data;
- target = event.source;
- init();
- firstRun = false;
- setTimeout(function(){ initLock = false;},eventCancelTimer);
- }
- function resetFromParent(){
- if (!initLock){
- log('Page size reset by host page');
- triggerReset('resetPage');
- } else {
- log('Page reset ignored by init');
- }
- }
- function getMessageType(){
- return event.data.split(']')[1];
- }
- function isMiddleTier(){
- return ('iFrameResize' in window);
- }
- function isInitMsg(){
- //test if this message is from a child below us. This is an ugly test, however, updating
- //the message format would break backwards compatibity.
- return event.data.split(':')[2] in {'true':1,'false':1};
- }
- if (isMessageForUs()){
- if (firstRun && isInitMsg()){ //Check msg ID
- initFromParent();
- } else if ('reset' === getMessageType()){
- resetFromParent();
- } else if (event.data !== initMsg && !isMiddleTier()){
- warn('Unexpected message ('+event.data+')');
- }
- }
- }
- addEventListener(window, 'message', receiver);
- })();
|