Mercurial > repos > saskia-hiltemann > ireport_devel
comparison iframe-resizer/src/iframeResizer.js @ 0:3c160414da2e
initial upload
| author | shiltemann |
|---|---|
| date | Thu, 26 Feb 2015 14:05:23 +0100 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:3c160414da2e |
|---|---|
| 1 /* | |
| 2 * File: iframeReizer.js | |
| 3 * Desc: Force iframes to size to content. | |
| 4 * Requires: iframeResizer.contentWindow.js to be loaded into the target frame. | |
| 5 * Author: David J. Bradshaw - dave@bradshaw.net | |
| 6 * Contributor: Jure Mav - jure.mav@gmail.com | |
| 7 */ | |
| 8 ;( function() { | |
| 9 'use strict'; | |
| 10 | |
| 11 var | |
| 12 count = 0, | |
| 13 firstRun = true, | |
| 14 msgHeader = 'message', | |
| 15 msgHeaderLen = msgHeader.length, | |
| 16 msgId = '[iFrameSizer]', //Must match iframe msg ID | |
| 17 msgIdLen = msgId.length, | |
| 18 page = '', //:'+location.href, //Uncoment to debug nested iFrames | |
| 19 pagePosition = null, | |
| 20 requestAnimationFrame = window.requestAnimationFrame, | |
| 21 resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1}, | |
| 22 settings = {}, | |
| 23 | |
| 24 defaults = { | |
| 25 autoResize : true, | |
| 26 bodyBackground : null, | |
| 27 bodyMargin : null, | |
| 28 bodyMarginV1 : 8, | |
| 29 bodyPadding : null, | |
| 30 checkOrigin : true, | |
| 31 enablePublicMethods : false, | |
| 32 heightCalculationMethod : 'offset', | |
| 33 interval : 32, | |
| 34 log : false, | |
| 35 maxHeight : Infinity, | |
| 36 maxWidth : Infinity, | |
| 37 minHeight : 0, | |
| 38 minWidth : 0, | |
| 39 scrolling : false, | |
| 40 sizeHeight : true, | |
| 41 sizeWidth : false, | |
| 42 tolerance : 0, | |
| 43 closedCallback : function(){}, | |
| 44 initCallback : function(){}, | |
| 45 messageCallback : function(){}, | |
| 46 resizedCallback : function(){} | |
| 47 }; | |
| 48 | |
| 49 function addEventListener(obj,evt,func){ | |
| 50 if ('addEventListener' in window){ | |
| 51 obj.addEventListener(evt,func, false); | |
| 52 } else if ('attachEvent' in window){//IE | |
| 53 obj.attachEvent('on'+evt,func); | |
| 54 } | |
| 55 } | |
| 56 | |
| 57 function setupRequestAnimationFrame(){ | |
| 58 var | |
| 59 vendors = ['moz', 'webkit', 'o', 'ms'], | |
| 60 x; | |
| 61 | |
| 62 // Remove vendor prefixing if prefixed and break early if not | |
| 63 for (x = 0; x < vendors.length && !requestAnimationFrame; x += 1) { | |
| 64 requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; | |
| 65 } | |
| 66 | |
| 67 if (!(requestAnimationFrame)){ | |
| 68 log(' RequestAnimationFrame not supported'); | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 function log(msg){ | |
| 73 if (settings.log && (typeof console === 'object')){ | |
| 74 console.log(msgId + '[Host page'+page+']' + msg); | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 | |
| 79 function iFrameListener(event){ | |
| 80 function resizeIFrame(){ | |
| 81 function resize(){ | |
| 82 setSize(messageData); | |
| 83 setPagePosition(); | |
| 84 settings.resizedCallback(messageData); | |
| 85 } | |
| 86 | |
| 87 syncResize(resize,messageData,'resetPage'); | |
| 88 } | |
| 89 | |
| 90 function closeIFrame(iframe){ | |
| 91 var iframeID = iframe.id; | |
| 92 | |
| 93 log(' Removing iFrame: '+iframeID); | |
| 94 iframe.parentNode.removeChild(iframe); | |
| 95 settings.closedCallback(iframeID); | |
| 96 log(' --'); | |
| 97 } | |
| 98 | |
| 99 function processMsg(){ | |
| 100 var data = msg.substr(msgIdLen).split(':'); | |
| 101 | |
| 102 return { | |
| 103 iframe: document.getElementById(data[0]), | |
| 104 id: data[0], | |
| 105 height: data[1], | |
| 106 width: data[2], | |
| 107 type: data[3] | |
| 108 }; | |
| 109 } | |
| 110 | |
| 111 function ensureInRange(Dimension){ | |
| 112 var | |
| 113 max = Number(settings['max'+Dimension]), | |
| 114 min = Number(settings['min'+Dimension]), | |
| 115 dimension = Dimension.toLowerCase(), | |
| 116 size = Number(messageData[dimension]); | |
| 117 | |
| 118 if (min>max){ | |
| 119 throw new Error('Value for min'+Dimension+' can not be greater than max'+Dimension); | |
| 120 } | |
| 121 | |
| 122 log(' Checking '+dimension+' is in range '+min+'-'+max); | |
| 123 | |
| 124 if (size<min) { | |
| 125 size=min; | |
| 126 log(' Set '+dimension+' to min value'); | |
| 127 } | |
| 128 | |
| 129 if (size>max) { | |
| 130 size=max; | |
| 131 log(' Set '+dimension+' to max value'); | |
| 132 } | |
| 133 | |
| 134 messageData[dimension]=''+size; | |
| 135 } | |
| 136 | |
| 137 function isMessageFromIFrame(){ | |
| 138 | |
| 139 var | |
| 140 origin = event.origin, | |
| 141 remoteHost = messageData.iframe.src.split('/').slice(0,3).join('/'); | |
| 142 | |
| 143 if (settings.checkOrigin) { | |
| 144 log(' Checking connection is from: '+remoteHost); | |
| 145 | |
| 146 if ((''+origin !== 'null') && (origin !== remoteHost)) { | |
| 147 throw new Error( | |
| 148 'Unexpected message received from: ' + origin + | |
| 149 ' for ' + messageData.iframe.id + | |
| 150 '. Message was: ' + event.data + | |
| 151 '. This error can be disabled by adding the checkOrigin: false option.' | |
| 152 ); | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 return true; | |
| 157 } | |
| 158 | |
| 159 function isMessageForUs(){ | |
| 160 return msgId === ('' + msg).substr(0,msgIdLen); //''+Protects against non-string msg | |
| 161 } | |
| 162 | |
| 163 function isMessageFromMetaParent(){ | |
| 164 //test if this message is from a parent above us. This is an ugly test, however, updating | |
| 165 //the message format would break backwards compatibity. | |
| 166 var retCode = messageData.type in {'true':1,'false':1}; | |
| 167 | |
| 168 if (retCode){ | |
| 169 log(' Ignoring init message from meta parent page'); | |
| 170 } | |
| 171 | |
| 172 return retCode; | |
| 173 } | |
| 174 | |
| 175 function forwardMsgFromIFrame(){ | |
| 176 var msgBody = msg.substr(msg.indexOf(':')+msgHeaderLen+6); //6 === ':0:0:' + ':' (Ideas to name this magic number most welcome) | |
| 177 | |
| 178 log(' MessageCallback passed: {iframe: '+ messageData.iframe.id + ', message: ' + msgBody + '}'); | |
| 179 settings.messageCallback({ | |
| 180 iframe: messageData.iframe, | |
| 181 message: msgBody | |
| 182 }); | |
| 183 log(' --'); | |
| 184 } | |
| 185 | |
| 186 function checkIFrameExists(){ | |
| 187 if (null === messageData.iframe) { | |
| 188 throw new Error('iFrame ('+messageData.id+') does not exist on ' + page); | |
| 189 } | |
| 190 return true; | |
| 191 } | |
| 192 | |
| 193 function actionMsg(){ | |
| 194 switch(messageData.type){ | |
| 195 case 'close': | |
| 196 closeIFrame(messageData.iframe); | |
| 197 settings.resizedCallback(messageData); //To be removed. | |
| 198 break; | |
| 199 case 'message': | |
| 200 forwardMsgFromIFrame(); | |
| 201 break; | |
| 202 case 'reset': | |
| 203 resetIFrame(messageData); | |
| 204 break; | |
| 205 case 'init': | |
| 206 resizeIFrame(); | |
| 207 settings.initCallback(messageData.iframe); | |
| 208 break; | |
| 209 default: | |
| 210 resizeIFrame(); | |
| 211 } | |
| 212 } | |
| 213 | |
| 214 var | |
| 215 msg = event.data, | |
| 216 messageData = {}; | |
| 217 | |
| 218 if (isMessageForUs()){ | |
| 219 log(' Received: '+msg); | |
| 220 messageData = processMsg(); | |
| 221 ensureInRange('Height'); | |
| 222 ensureInRange('Width'); | |
| 223 | |
| 224 if ( !isMessageFromMetaParent() && checkIFrameExists() && isMessageFromIFrame() ){ | |
| 225 actionMsg(); | |
| 226 firstRun = false; | |
| 227 } | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 | |
| 232 function getPagePosition (){ | |
| 233 if(null === pagePosition){ | |
| 234 pagePosition = { | |
| 235 x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft, | |
| 236 y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop | |
| 237 }; | |
| 238 log(' Get position: '+pagePosition.x+','+pagePosition.y); | |
| 239 } | |
| 240 } | |
| 241 | |
| 242 function setPagePosition(){ | |
| 243 if(null !== pagePosition){ | |
| 244 window.scrollTo(pagePosition.x,pagePosition.y); | |
| 245 log(' Set position: '+pagePosition.x+','+pagePosition.y); | |
| 246 pagePosition = null; | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 function resetIFrame(messageData){ | |
| 251 function reset(){ | |
| 252 setSize(messageData); | |
| 253 trigger('reset','reset',messageData.iframe); | |
| 254 } | |
| 255 | |
| 256 log(' Size reset requested by '+('init'===messageData.type?'host page':'iFrame')); | |
| 257 getPagePosition(); | |
| 258 syncResize(reset,messageData,'init'); | |
| 259 } | |
| 260 | |
| 261 function setSize(messageData){ | |
| 262 function setDimension(dimension,min,max){ | |
| 263 messageData.iframe.style[dimension] = messageData[dimension] + 'px'; | |
| 264 log( | |
| 265 ' IFrame (' + messageData.iframe.id + | |
| 266 ') ' + dimension + | |
| 267 ' set to ' + messageData[dimension] + 'px' | |
| 268 ); | |
| 269 } | |
| 270 | |
| 271 if( settings.sizeHeight) { setDimension('height'); } | |
| 272 if( settings.sizeWidth ) { setDimension('width'); } | |
| 273 } | |
| 274 | |
| 275 function syncResize(func,messageData,doNotSync){ | |
| 276 if(doNotSync!==messageData.type && requestAnimationFrame){ | |
| 277 log(' Requesting animation frame'); | |
| 278 requestAnimationFrame(func); | |
| 279 } else { | |
| 280 func(); | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 function trigger(calleeMsg,msg,iframe){ | |
| 285 log('[' + calleeMsg + '] Sending msg to iframe ('+msg+')'); | |
| 286 iframe.contentWindow.postMessage( msgId + msg, '*' ); | |
| 287 } | |
| 288 | |
| 289 | |
| 290 function setupIFrame(){ | |
| 291 function setLimits(){ | |
| 292 function addStyle(style){ | |
| 293 if ((Infinity !== settings[style]) && (0 !== settings[style])){ | |
| 294 iframe.style[style] = settings[style] + 'px'; | |
| 295 log(' Set '+style+' = '+settings[style]+'px'); | |
| 296 } | |
| 297 } | |
| 298 | |
| 299 addStyle('maxHeight'); | |
| 300 addStyle('minHeight'); | |
| 301 addStyle('maxWidth'); | |
| 302 addStyle('minWidth'); | |
| 303 } | |
| 304 | |
| 305 function ensureHasId(iframeID){ | |
| 306 if (''===iframeID){ | |
| 307 iframe.id = iframeID = 'iFrameResizer' + count++; | |
| 308 log(' Added missing iframe ID: '+ iframeID); | |
| 309 } | |
| 310 | |
| 311 return iframeID; | |
| 312 } | |
| 313 | |
| 314 function setScrolling(){ | |
| 315 log(' IFrame scrolling ' + (settings.scrolling ? 'enabled' : 'disabled') + ' for ' + iframeID); | |
| 316 iframe.style.overflow = false === settings.scrolling ? 'hidden' : 'auto'; | |
| 317 iframe.scrolling = false === settings.scrolling ? 'no' : 'yes'; | |
| 318 } | |
| 319 | |
| 320 //The V1 iFrame script expects an int, where as in V2 expects a CSS | |
| 321 //string value such as '1px 3em', so if we have an int for V2, set V1=V2 | |
| 322 //and then convert V2 to a string PX value. | |
| 323 function setupBodyMarginValues(){ | |
| 324 if (('number'===typeof(settings.bodyMargin)) || ('0'===settings.bodyMargin)){ | |
| 325 settings.bodyMarginV1 = settings.bodyMargin; | |
| 326 settings.bodyMargin = '' + settings.bodyMargin + 'px'; | |
| 327 } | |
| 328 } | |
| 329 | |
| 330 function createOutgoingMsg(){ | |
| 331 return iframeID + | |
| 332 ':' + settings.bodyMarginV1 + | |
| 333 ':' + settings.sizeWidth + | |
| 334 ':' + settings.log + | |
| 335 ':' + settings.interval + | |
| 336 ':' + settings.enablePublicMethods + | |
| 337 ':' + settings.autoResize + | |
| 338 ':' + settings.bodyMargin + | |
| 339 ':' + settings.heightCalculationMethod + | |
| 340 ':' + settings.bodyBackground + | |
| 341 ':' + settings.bodyPadding + | |
| 342 ':' + settings.tolerance; | |
| 343 } | |
| 344 | |
| 345 function init(msg){ | |
| 346 //We have to call trigger twice, as we can not be sure if all | |
| 347 //iframes have completed loading when this code runs. The | |
| 348 //event listener also catches the page changing in the iFrame. | |
| 349 addEventListener(iframe,'load',function(){ | |
| 350 var fr = firstRun; // Reduce scope of var to function, because IE8's JS execution | |
| 351 // context stack is borked and this value gets externally | |
| 352 // changed midway through running this function. | |
| 353 trigger('iFrame.onload',msg,iframe); | |
| 354 if (!fr && settings.heightCalculationMethod in resetRequiredMethods){ | |
| 355 resetIFrame({ | |
| 356 iframe:iframe, | |
| 357 height:0, | |
| 358 width:0, | |
| 359 type:'init' | |
| 360 }); | |
| 361 } | |
| 362 }); | |
| 363 trigger('init',msg,iframe); | |
| 364 } | |
| 365 | |
| 366 var | |
| 367 /*jshint validthis:true */ | |
| 368 iframe = this, | |
| 369 iframeID = ensureHasId(iframe.id); | |
| 370 | |
| 371 setScrolling(); | |
| 372 setLimits(); | |
| 373 setupBodyMarginValues(); | |
| 374 init(createOutgoingMsg()); | |
| 375 } | |
| 376 | |
| 377 function checkOptions(options){ | |
| 378 if ('object' !== typeof options){ | |
| 379 throw new TypeError('Options is not an object.'); | |
| 380 } | |
| 381 } | |
| 382 | |
| 383 function createNativePublicFunction(){ | |
| 384 function init(element){ | |
| 385 if('IFRAME' !== element.tagName) { | |
| 386 throw new TypeError('Expected <IFRAME> tag, found <'+element.tagName+'>.'); | |
| 387 } else { | |
| 388 setupIFrame.call(element); | |
| 389 } | |
| 390 } | |
| 391 | |
| 392 function processOptions(options){ | |
| 393 options = options || {}; | |
| 394 | |
| 395 checkOptions(options); | |
| 396 | |
| 397 for (var option in defaults) { | |
| 398 if (defaults.hasOwnProperty(option)){ | |
| 399 settings[option] = options.hasOwnProperty(option) ? options[option] : defaults[option]; | |
| 400 } | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 return function iFrameResizeF(options,selecter){ | |
| 405 processOptions(options); | |
| 406 Array.prototype.forEach.call( document.querySelectorAll( selecter || 'iframe' ), init ); | |
| 407 }; | |
| 408 } | |
| 409 | |
| 410 function createJQueryPublicMethod($){ | |
| 411 $.fn.iFrameResize = function $iFrameResizeF(options) { | |
| 412 checkOptions(options); | |
| 413 settings = $.extend( {}, defaults, options ); | |
| 414 return this.filter('iframe').each( setupIFrame ).end(); | |
| 415 }; | |
| 416 } | |
| 417 | |
| 418 setupRequestAnimationFrame(); | |
| 419 addEventListener(window,'message',iFrameListener); | |
| 420 | |
| 421 if ('jQuery' in window) { createJQueryPublicMethod(jQuery); } | |
| 422 | |
| 423 if (typeof define === 'function' && define.amd) { | |
| 424 define(function (){ return createNativePublicFunction(); }); | |
| 425 } else { | |
| 426 window.iFrameResize = createNativePublicFunction(); | |
| 427 } | |
| 428 | |
| 429 })(); |
