diff --git a/examples/assets/library/iframe-content.js b/examples/assets/library/iframe-content.js new file mode 100644 index 000000000..7e57d79a5 --- /dev/null +++ b/examples/assets/library/iframe-content.js @@ -0,0 +1,707 @@ +/* + * 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 + * Contributor: Ian Caunce - ian@hallnet.co.uk + */ + +;(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}, + resizeFrom = 'parent', + 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; + resizeFrom = data[13]; + } + + 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' }); + if('child' === resizeFrom){ + addTriggerEvent({ eventType: 'IFrame 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(){ + sendMsg(0,0,'close'); + }, + 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 resizeFromParent(){ + sendSize('resizeParent','Parent window resized'); + } + + 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 === false) { + if ('reset' === getMessageType()){ + resetFromParent(); + } else if ('resize' === getMessageType()){ + resizeFromParent(); + } else if (event.data !== initMsg && !isMiddleTier()){ + warn('Unexpected message ('+event.data+')'); + } + } else if (isInitMsg()) { + initFromParent(); + } else { + warn('Received message of type ('+getMessageType()+') before initialization.'); + } + } + } + + addEventListener(window, 'message', receiver); + +})(); \ No newline at end of file diff --git a/examples/assets/library/iframe.js b/examples/assets/library/iframe.js new file mode 100644 index 000000000..7f612ff55 --- /dev/null +++ b/examples/assets/library/iframe.js @@ -0,0 +1,642 @@ +/* + * File: iframeResizer.js + * Desc: Force iframes to size to content. + * Requires: iframeResizer.contentWindow.js to be loaded into the target frame. + * Author: David J. Bradshaw - dave@bradshaw.net + * Contributor: Jure Mav - jure.mav@gmail.com + * Contributor: Reed Dadoune - reed@dadoune.com + */ +;(function() { + 'use strict'; + + var + count = 0, + firstRun = true, + logEnabled = false, + msgHeader = 'message', + msgHeaderLen = msgHeader.length, + msgId = '[iFrameSizer]', //Must match iframe msg ID + msgIdLen = msgId.length, + pagePosition = null, + requestAnimationFrame = window.requestAnimationFrame, + resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1}, + settings = {}, + timer = null, + + defaults = { + autoResize : true, + bodyBackground : null, + bodyMargin : null, + bodyMarginV1 : 8, + bodyPadding : null, + checkOrigin : true, + enableInPageLinks : false, + enablePublicMethods : false, + heightCalculationMethod : 'offset', + interval : 32, + log : false, + maxHeight : Infinity, + maxWidth : Infinity, + minHeight : 0, + minWidth : 0, + resizeFrom : 'parent', + scrolling : false, + sizeHeight : true, + sizeWidth : false, + tolerance : 0, + closedCallback : function(){}, + initCallback : function(){}, + messageCallback : function(){}, + resizedCallback : function(){}, + scrollCallback : function(){return true;} + }; + + function addEventListener(obj,evt,func){ + if ('addEventListener' in window){ + obj.addEventListener(evt,func, false); + } else if ('attachEvent' in window){//IE + obj.attachEvent('on'+evt,func); + } + } + + function setupRequestAnimationFrame(){ + var + vendors = ['moz', 'webkit', 'o', 'ms'], + x; + + // Remove vendor prefixing if prefixed and break early if not + for (x = 0; x < vendors.length && !requestAnimationFrame; x += 1) { + requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + } + + if (!(requestAnimationFrame)){ + log(' RequestAnimationFrame not supported'); + } + } + + function getMyID(){ + var retStr = 'Host page'; + + if (window.top!==window.self){ + if (window.parentIFrame){ + retStr = window.parentIFrame.getId(); + } else { + retStr = 'Nested host page'; + } + } + + return retStr; + } + + function formatLogMsg(msg){ + return msgId + '[' + getMyID() + ']' + msg; + } + + function log(msg){ + if (logEnabled && ('object' === typeof window.console)){ + console.log(formatLogMsg(msg)); + } + } + + function warn(msg){ + if ('object' === typeof window.console){ + console.warn(formatLogMsg(msg)); + } + } + + function iFrameListener(event){ + function resizeIFrame(){ + function resize(){ + setSize(messageData); + setPagePosition(); + settings[iframeID].resizedCallback(messageData); + } + + ensureInRange('Height'); + ensureInRange('Width'); + + syncResize(resize,messageData,'resetPage'); + } + + function closeIFrame(iframe){ + var iframeID = iframe.id; + + log(' Removing iFrame: '+iframeID); + iframe.parentNode.removeChild(iframe); + settings[iframeID].closedCallback(iframeID); + delete settings[iframeID]; + log(' --'); + } + + function processMsg(){ + var data = msg.substr(msgIdLen).split(':'); + + return { + iframe: document.getElementById(data[0]), + id: data[0], + height: data[1], + width: data[2], + type: data[3] + }; + } + + function ensureInRange(Dimension){ + var + max = Number(settings[iframeID]['max'+Dimension]), + min = Number(settings[iframeID]['min'+Dimension]), + dimension = Dimension.toLowerCase(), + size = Number(messageData[dimension]); + + if (min>max){ + throw new Error('Value for min'+Dimension+' can not be greater than max'+Dimension); + } + + log(' Checking '+dimension+' is in range '+min+'-'+max); + + if (sizemax) { + size=max; + log(' Set '+dimension+' to max value'); + } + + messageData[dimension]=''+size; + } + + + function isMessageFromIFrame(){ + function checkAllowedOrigin(){ + function checkList(){ + log(' Checking connection is from allowed list of origins: ' + checkOrigin); + var i; + for (i = 0; i < checkOrigin.length; i++) { + if (checkOrigin[i] === origin) { + return true; + } + } + return false; + } + + function checkSingle(){ + log(' Checking connection is from: '+remoteHost); + return origin == remoteHost; + } + + return checkOrigin.constructor === Array ? checkList() : checkSingle(); + } + + var + origin = event.origin, + checkOrigin = settings[iframeID].checkOrigin, + remoteHost = messageData.iframe.src.split('/').slice(0,3).join('/'); + + if (checkOrigin) { + if ((''+origin !== 'null') && !checkAllowedOrigin()) { + throw new Error( + 'Unexpected message received from: ' + origin + + ' for ' + messageData.iframe.id + + '. Message was: ' + event.data + + '. This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.' + ); + } + } + + return true; + } + + function isMessageForUs(){ + return msgId === ('' + msg).substr(0,msgIdLen); //''+Protects against non-string msg + } + + function isMessageFromMetaParent(){ + //test if this message is from a parent above us. This is an ugly test, however, updating + //the message format would break backwards compatibity. + var retCode = messageData.type in {'true':1,'false':1,'undefined':1}; + + if (retCode){ + log(' Ignoring init message from meta parent page'); + } + + return retCode; + } + + function getMsgBody(offset){ + return msg.substr(msg.indexOf(':')+msgHeaderLen+offset); + } + + function forwardMsgFromIFrame(msgBody){ + log(' MessageCallback passed: {iframe: '+ messageData.iframe.id + ', message: ' + msgBody + '}'); + settings[iframeID].messageCallback({ + iframe: messageData.iframe, + message: JSON.parse(msgBody) + }); + log(' --'); + } + + function checkIFrameExists(){ + if (null === messageData.iframe) { + warn(' IFrame ('+messageData.id+') not found'); + return false; + } + return true; + } + + function getElementPosition(target){ + var + iFramePosition = target.getBoundingClientRect(); + + getPagePosition(); + + return { + x: parseInt(iFramePosition.left, 10) + parseInt(pagePosition.x, 10), + y: parseInt(iFramePosition.top, 10) + parseInt(pagePosition.y, 10) + }; + } + + function scrollRequestFromChild(addOffset){ + function reposition(){ + pagePosition = newPosition; + + scrollTo(); + + log(' --'); + } + + function calcOffset(){ + return { + x: Number(messageData.width) + offset.x, + y: Number(messageData.height) + offset.y + }; + } + + var + offset = addOffset ? getElementPosition(messageData.iframe) : {x:0,y:0}, + newPosition = calcOffset(); + + log(' Reposition requested from iFrame (offset x:'+offset.x+' y:'+offset.y+')'); + + if(window.top!==window.self){ + if (window.parentIFrame){ + if (addOffset){ + parentIFrame.scrollToOffset(newPosition.x,newPosition.y); + } else { + parentIFrame.scrollTo(messageData.width,messageData.height); + } + } else { + warn(' Unable to scroll to requested position, window.parentIFrame not found'); + } + } else { + reposition(); + } + + } + + function scrollTo(){ + if (false !== settings[iframeID].scrollCallback(pagePosition)){ + setPagePosition(); + } + } + + 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); + pagePosition = { + x: jumpPosition.x, + y: jumpPosition.y + }; + + scrollTo(); + log(' --'); + } + + var target = document.getElementById(hashData) || document.getElementsByName(hashData)[0]; + + if(window.top!==window.self){ + if (window.parentIFrame){ + parentIFrame.moveToAnchor(hash); + } else { + log(' In page link #'+hash+' not found and window.parentIFrame not found'); + } + } else if (target){ + jumpToTarget(target); + } else { + log(' In page link #'+hash+' not found'); + } + } + + function actionMsg(){ + switch(messageData.type){ + case 'close': + closeIFrame(messageData.iframe); + break; + case 'message': + forwardMsgFromIFrame(getMsgBody(6)); + break; + case 'scrollTo': + scrollRequestFromChild(false); + break; + case 'scrollToOffset': + scrollRequestFromChild(true); + break; + case 'inPageLink': + findTarget(getMsgBody(9)); + break; + case 'reset': + resetIFrame(messageData); + break; + case 'init': + resizeIFrame(); + settings[iframeID].initCallback(messageData.iframe); + break; + default: + resizeIFrame(); + } + } + + function hasSettings(iframeID){ + var retBool = true; + + if (!settings[iframeID]){ + retBool = false; + warn(messageData.type + ' No settings for ' + iframeID + '. Message was: ' + msg); + } + + return retBool; + } + + var + msg = event.data, + messageData = {}, + iframeID = null; + + if (isMessageForUs()){ + messageData = processMsg(); + iframeID = messageData.id; + + if (!isMessageFromMetaParent() && hasSettings(iframeID)){ + logEnabled = settings[iframeID].log; + log(' Received: '+msg); + + if ( checkIFrameExists() && isMessageFromIFrame() ){ + actionMsg(); + firstRun = false; + } + } + } + } + + + function getPagePosition (){ + if(null === pagePosition){ + pagePosition = { + x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft, + y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop + }; + log(' Get page position: '+pagePosition.x+','+pagePosition.y); + } + } + + function setPagePosition(){ + if(null !== pagePosition){ + window.scrollTo(pagePosition.x,pagePosition.y); + log(' Set page position: '+pagePosition.x+','+pagePosition.y); + pagePosition = null; + } + } + + function resetIFrame(messageData){ + function reset(){ + setSize(messageData); + trigger('reset','reset',messageData.iframe,messageData.id); + } + + log(' Size reset requested by '+('init'===messageData.type?'host page':'iFrame')); + getPagePosition(); + syncResize(reset,messageData,'init'); + } + + function setSize(messageData){ + function setDimension(dimension){ + messageData.iframe.style[dimension] = messageData[dimension] + 'px'; + log( + ' IFrame (' + iframeID + + ') ' + dimension + + ' set to ' + messageData[dimension] + 'px' + ); + } + var iframeID = messageData.iframe.id; + if( settings[iframeID].sizeHeight) { setDimension('height'); } + if( settings[iframeID].sizeWidth ) { setDimension('width'); } + } + + function syncResize(func,messageData,doNotSync){ + if(doNotSync!==messageData.type && requestAnimationFrame){ + log(' Requesting animation frame'); + requestAnimationFrame(func); + } else { + func(); + } + } + + function trigger(calleeMsg,msg,iframe,id){ + if(iframe && iframe.contentWindow){ + log('[' + calleeMsg + '] Sending msg to iframe ('+msg+')'); + iframe.contentWindow.postMessage( msgId + msg, '*' ); + } else { + warn('[' + calleeMsg + '] IFrame not found'); + if(settings[id]) delete settings[id]; + } + } + + + function setupIFrame(options){ + function setLimits(){ + function addStyle(style){ + if ((Infinity !== settings[iframeID][style]) && (0 !== settings[iframeID][style])){ + iframe.style[style] = settings[iframeID][style] + 'px'; + log(' Set '+style+' = '+settings[iframeID][style]+'px'); + } + } + + addStyle('maxHeight'); + addStyle('minHeight'); + addStyle('maxWidth'); + addStyle('minWidth'); + } + + function ensureHasId(iframeID){ + if (''===iframeID){ + iframe.id = iframeID = 'iFrameResizer' + count++; + logEnabled = (options || {}).log; + log(' Added missing iframe ID: '+ iframeID +' (' + iframe.src + ')'); + } + + return iframeID; + } + + function setScrolling(){ + log(' IFrame scrolling ' + (settings[iframeID].scrolling ? 'enabled' : 'disabled') + ' for ' + iframeID); + iframe.style.overflow = false === settings[iframeID].scrolling ? 'hidden' : 'auto'; + iframe.scrolling = false === settings[iframeID].scrolling ? 'no' : 'yes'; + } + + //The V1 iFrame script expects an int, where as in V2 expects a CSS + //string value such as '1px 3em', so if we have an int for V2, set V1=V2 + //and then convert V2 to a string PX value. + function setupBodyMarginValues(){ + if (('number'===typeof(settings[iframeID].bodyMargin)) || ('0'===settings[iframeID].bodyMargin)){ + settings[iframeID].bodyMarginV1 = settings[iframeID].bodyMargin; + settings[iframeID].bodyMargin = '' + settings[iframeID].bodyMargin + 'px'; + } + } + + function createOutgoingMsg(){ + return iframeID + + ':' + settings[iframeID].bodyMarginV1 + + ':' + settings[iframeID].sizeWidth + + ':' + settings[iframeID].log + + ':' + settings[iframeID].interval + + ':' + settings[iframeID].enablePublicMethods + + ':' + settings[iframeID].autoResize + + ':' + settings[iframeID].bodyMargin + + ':' + settings[iframeID].heightCalculationMethod + + ':' + settings[iframeID].bodyBackground + + ':' + settings[iframeID].bodyPadding + + ':' + settings[iframeID].tolerance + + ':' + settings[iframeID].enableInPageLinks + + ':' + settings[iframeID].resizeFrom; + } + + function init(msg){ + //We have to call trigger twice, as we can not be sure if all + //iframes have completed loading when this code runs. The + //event listener also catches the page changing in the iFrame. + addEventListener(iframe,'load',function(){ + var fr = firstRun; // Reduce scope of var to function, because IE8's JS execution + // context stack is borked and this value gets externally + // changed midway through running this function. + trigger('iFrame.onload',msg,iframe); + if (!fr && settings[iframeID].heightCalculationMethod in resetRequiredMethods){ + resetIFrame({ + iframe:iframe, + height:0, + width:0, + type:'init' + }); + } + }); + trigger('init',msg,iframe); + } + + function checkOptions(options){ + if ('object' !== typeof options){ + throw new TypeError('Options is not an object.'); + } + } + + function processOptions(options){ + options = options || {}; + settings[iframeID] = {}; + + checkOptions(options); + + for (var option in defaults) { + if (defaults.hasOwnProperty(option)){ + settings[iframeID][option] = options.hasOwnProperty(option) ? options[option] : defaults[option]; + } + } + + logEnabled = settings[iframeID].log; + } + + var + /*jshint validthis:true */ + iframe = this, + iframeID = ensureHasId(iframe.id); + + processOptions(options); + setScrolling(); + setLimits(); + setupBodyMarginValues(); + init(createOutgoingMsg()); + } + + function throttle(fn,time){ + if (null === timer){ + timer = setTimeout(function(){ + timer = null; + fn(); + }, time); + } + } + + function winResize(){ + throttle(function(){ + for (var iframeId in settings){ + if('parent' === settings[iframeId].resizeFrom){ + trigger('Window resize','resize',document.getElementById(iframeId),iframeId); + } + } + },66); + } + + function factory(){ + + setupRequestAnimationFrame(); + addEventListener(window,'message',iFrameListener); + addEventListener(window,'resize', winResize); + + function init(element, options){ + if(!element.tagName) { + throw new TypeError('Object is not a valid DOM element'); + } else if ('IFRAME' !== element.tagName.toUpperCase()) { + throw new TypeError('Expected +

Menu

- +

Buttons

- +

Table

- +

Input

- +

Card

- + + +