Mercurial > repos > saskia-hiltemann > ireport
view iframe-resizer/src/iframeResizer.js @ 0:ac5f9272033b draft
first upload
author | saskia-hiltemann |
---|---|
date | Tue, 01 Jul 2014 11:42:23 -0400 |
parents | |
children |
line wrap: on
line source
/* * File: iframeReizer.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 */ ;( function() { 'use strict'; var count = 0, firstRun = true, msgHeader = 'message', msgHeaderLen = msgHeader.length, msgId = '[iFrameSizer]', //Must match iframe msg ID msgIdLen = msgId.length, page = '', //:'+location.href, //Uncoment to debug nested iFrames pagePosition = null, requestAnimationFrame = window.requestAnimationFrame, resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1}, settings = {}, defaults = { autoResize : true, bodyBackground : null, bodyMargin : null, bodyMarginV1 : 8, bodyPadding : null, checkOrigin : true, enablePublicMethods : false, heightCalculationMethod : 'offset', interval : 32, log : false, maxHeight : Infinity, maxWidth : Infinity, minHeight : 0, minWidth : 0, scrolling : false, sizeHeight : true, sizeWidth : false, tolerance : 0, closedCallback : function(){}, initCallback : function(){}, messageCallback : function(){}, resizedCallback : function(){} }; 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 log(msg){ if (settings.log && (typeof console === 'object')){ console.log(msgId + '[Host page'+page+']' + msg); } } function iFrameListener(event){ function resizeIFrame(){ function resize(){ setSize(messageData); setPagePosition(); settings.resizedCallback(messageData); } syncResize(resize,messageData,'resetPage'); } function closeIFrame(iframe){ var iframeID = iframe.id; log(' Removing iFrame: '+iframeID); iframe.parentNode.removeChild(iframe); settings.closedCallback(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['max'+Dimension]), min = Number(settings['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 (size<min) { size=min; log(' Set '+dimension+' to min value'); } if (size>max) { size=max; log(' Set '+dimension+' to max value'); } messageData[dimension]=''+size; } function isMessageFromIFrame(){ var origin = event.origin, remoteHost = messageData.iframe.src.split('/').slice(0,3).join('/'); if (settings.checkOrigin) { log(' Checking connection is from: '+remoteHost); if ((''+origin !== 'null') && (origin !== remoteHost)) { throw new Error( 'Unexpected message received from: ' + origin + ' for ' + messageData.iframe.id + '. Message was: ' + event.data + '. This error can be disabled by adding the checkOrigin: false option.' ); } } 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}; if (retCode){ log(' Ignoring init message from meta parent page'); } return retCode; } function forwardMsgFromIFrame(){ var msgBody = msg.substr(msg.indexOf(':')+msgHeaderLen+6); //6 === ':0:0:' + ':' (Ideas to name this magic number most welcome) log(' MessageCallback passed: {iframe: '+ messageData.iframe.id + ', message: ' + msgBody + '}'); settings.messageCallback({ iframe: messageData.iframe, message: msgBody }); log(' --'); } function checkIFrameExists(){ if (null === messageData.iframe) { throw new Error('iFrame ('+messageData.id+') does not exist on ' + page); } return true; } function actionMsg(){ switch(messageData.type){ case 'close': closeIFrame(messageData.iframe); settings.resizedCallback(messageData); //To be removed. break; case 'message': forwardMsgFromIFrame(); break; case 'reset': resetIFrame(messageData); break; case 'init': resizeIFrame(); settings.initCallback(messageData.iframe); break; default: resizeIFrame(); } } var msg = event.data, messageData = {}; if (isMessageForUs()){ log(' Received: '+msg); messageData = processMsg(); ensureInRange('Height'); ensureInRange('Width'); if ( !isMessageFromMetaParent() && 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 position: '+pagePosition.x+','+pagePosition.y); } } function setPagePosition(){ if(null !== pagePosition){ window.scrollTo(pagePosition.x,pagePosition.y); log(' Set position: '+pagePosition.x+','+pagePosition.y); pagePosition = null; } } function resetIFrame(messageData){ function reset(){ setSize(messageData); trigger('reset','reset',messageData.iframe); } log(' Size reset requested by '+('init'===messageData.type?'host page':'iFrame')); getPagePosition(); syncResize(reset,messageData,'init'); } function setSize(messageData){ function setDimension(dimension,min,max){ messageData.iframe.style[dimension] = messageData[dimension] + 'px'; log( ' IFrame (' + messageData.iframe.id + ') ' + dimension + ' set to ' + messageData[dimension] + 'px' ); } if( settings.sizeHeight) { setDimension('height'); } if( settings.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){ log('[' + calleeMsg + '] Sending msg to iframe ('+msg+')'); iframe.contentWindow.postMessage( msgId + msg, '*' ); } function setupIFrame(){ function setLimits(){ function addStyle(style){ if ((Infinity !== settings[style]) && (0 !== settings[style])){ iframe.style[style] = settings[style] + 'px'; log(' Set '+style+' = '+settings[style]+'px'); } } addStyle('maxHeight'); addStyle('minHeight'); addStyle('maxWidth'); addStyle('minWidth'); } function ensureHasId(iframeID){ if (''===iframeID){ iframe.id = iframeID = 'iFrameResizer' + count++; log(' Added missing iframe ID: '+ iframeID); } return iframeID; } function setScrolling(){ log(' IFrame scrolling ' + (settings.scrolling ? 'enabled' : 'disabled') + ' for ' + iframeID); iframe.style.overflow = false === settings.scrolling ? 'hidden' : 'auto'; iframe.scrolling = false === settings.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.bodyMargin)) || ('0'===settings.bodyMargin)){ settings.bodyMarginV1 = settings.bodyMargin; settings.bodyMargin = '' + settings.bodyMargin + 'px'; } } function createOutgoingMsg(){ return iframeID + ':' + settings.bodyMarginV1 + ':' + settings.sizeWidth + ':' + settings.log + ':' + settings.interval + ':' + settings.enablePublicMethods + ':' + settings.autoResize + ':' + settings.bodyMargin + ':' + settings.heightCalculationMethod + ':' + settings.bodyBackground + ':' + settings.bodyPadding + ':' + settings.tolerance; } 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.heightCalculationMethod in resetRequiredMethods){ resetIFrame({ iframe:iframe, height:0, width:0, type:'init' }); } }); trigger('init',msg,iframe); } var /*jshint validthis:true */ iframe = this, iframeID = ensureHasId(iframe.id); setScrolling(); setLimits(); setupBodyMarginValues(); init(createOutgoingMsg()); } function checkOptions(options){ if ('object' !== typeof options){ throw new TypeError('Options is not an object.'); } } function createNativePublicFunction(){ function init(element){ if('IFRAME' !== element.tagName) { throw new TypeError('Expected <IFRAME> tag, found <'+element.tagName+'>.'); } else { setupIFrame.call(element); } } function processOptions(options){ options = options || {}; checkOptions(options); for (var option in defaults) { if (defaults.hasOwnProperty(option)){ settings[option] = options.hasOwnProperty(option) ? options[option] : defaults[option]; } } } return function iFrameResizeF(options,selecter){ processOptions(options); Array.prototype.forEach.call( document.querySelectorAll( selecter || 'iframe' ), init ); }; } function createJQueryPublicMethod($){ $.fn.iFrameResize = function $iFrameResizeF(options) { checkOptions(options); settings = $.extend( {}, defaults, options ); return this.filter('iframe').each( setupIFrame ).end(); }; } setupRequestAnimationFrame(); addEventListener(window,'message',iFrameListener); if ('jQuery' in window) { createJQueryPublicMethod(jQuery); } if (typeof define === 'function' && define.amd) { define(function (){ return createNativePublicFunction(); }); } else { window.iFrameResize = createNativePublicFunction(); } })();