Mercurial > repos > saskia-hiltemann > ireport_devel
comparison iframe-resizer/src/iframeResizer.contentWindow.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: iframeSizer.contentWindow.js | |
| 3 * Desc: Include this file in any page being loaded into an iframe | |
| 4 * to force the iframe to resize to the content size. | |
| 5 * Requires: iframeResizer.js on host page. | |
| 6 * Author: David J. Bradshaw - dave@bradshaw.net | |
| 7 * Contributor: Jure Mav - jure.mav@gmail.com | |
| 8 */ | |
| 9 | |
| 10 ;(function() { | |
| 11 'use strict'; | |
| 12 | |
| 13 var | |
| 14 autoResize = true, | |
| 15 base = 10, | |
| 16 bodyBackground = '', | |
| 17 bodyMargin = 0, | |
| 18 bodyMarginStr = '', | |
| 19 bodyPadding = '', | |
| 20 calculateWidth = false, | |
| 21 doubleEventList = {'resize':1,'click':1}, | |
| 22 eventCancelTimer = 64, | |
| 23 height = 1, | |
| 24 firstRun = true, | |
| 25 heightCalcModeDefault = 'offset', | |
| 26 heightCalcMode = heightCalcModeDefault, | |
| 27 initLock = true, | |
| 28 initMsg = '', | |
| 29 interval = 32, | |
| 30 logging = false, | |
| 31 msgID = '[iFrameSizer]', //Must match host page msg ID | |
| 32 msgIdLen = msgID.length, | |
| 33 myID = '', | |
| 34 publicMethods = false, | |
| 35 resetRequiredMethods = {max:1,scroll:1,bodyScroll:1,documentElementScroll:1}, | |
| 36 targetOriginDefault = '*', | |
| 37 target = window.parent, | |
| 38 tolerance = 0, | |
| 39 triggerLocked = false, | |
| 40 triggerLockedTimer = null, | |
| 41 width = 1; | |
| 42 | |
| 43 | |
| 44 function addEventListener(el,evt,func){ | |
| 45 if ('addEventListener' in window){ | |
| 46 el.addEventListener(evt,func, false); | |
| 47 } else if ('attachEvent' in window){ //IE | |
| 48 el.attachEvent('on'+evt,func); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 function formatLogMsg(msg){ | |
| 53 return msgID + '[' + myID + ']' + ' ' + msg; | |
| 54 } | |
| 55 | |
| 56 function log(msg){ | |
| 57 if (logging && ('object' === typeof window.console)){ | |
| 58 console.log(formatLogMsg(msg)); | |
| 59 } | |
| 60 } | |
| 61 | |
| 62 function warn(msg){ | |
| 63 if ('object' === typeof window.console){ | |
| 64 console.warn(formatLogMsg(msg)); | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 | |
| 69 function init(){ | |
| 70 log('Initialising iFrame'); | |
| 71 readData(); | |
| 72 setMargin(); | |
| 73 setBodyStyle('background',bodyBackground); | |
| 74 setBodyStyle('padding',bodyPadding); | |
| 75 injectClearFixIntoBodyElement(); | |
| 76 checkHeightMode(); | |
| 77 stopInfiniteResizingOfIFrame(); | |
| 78 setupPublicMethods(); | |
| 79 startEventListeners(); | |
| 80 sendSize('init','Init message from host page'); | |
| 81 } | |
| 82 | |
| 83 function readData(){ | |
| 84 | |
| 85 var data = initMsg.substr(msgIdLen).split(':'); | |
| 86 | |
| 87 function strBool(str){ | |
| 88 return 'true' === str ? true : false; | |
| 89 } | |
| 90 | |
| 91 myID = data[0]; | |
| 92 bodyMargin = (undefined !== data[1]) ? Number(data[1]) : bodyMargin; //For V1 compatibility | |
| 93 calculateWidth = (undefined !== data[2]) ? strBool(data[2]) : calculateWidth; | |
| 94 logging = (undefined !== data[3]) ? strBool(data[3]) : logging; | |
| 95 interval = (undefined !== data[4]) ? Number(data[4]) : interval; | |
| 96 publicMethods = (undefined !== data[5]) ? strBool(data[5]) : publicMethods; | |
| 97 autoResize = (undefined !== data[6]) ? strBool(data[6]) : autoResize; | |
| 98 bodyMarginStr = data[7]; | |
| 99 heightCalcMode = (undefined !== data[8]) ? data[8] : heightCalcMode; | |
| 100 bodyBackground = data[9]; | |
| 101 bodyPadding = data[10]; | |
| 102 tolerance = (undefined !== data[11]) ? Number(data[11]) : tolerance; | |
| 103 } | |
| 104 | |
| 105 function chkCSS(attr,value){ | |
| 106 if (-1 !== value.indexOf('-')){ | |
| 107 warn('Negative CSS value ignored for '+attr); | |
| 108 value=''; | |
| 109 } | |
| 110 return value; | |
| 111 } | |
| 112 | |
| 113 function setBodyStyle(attr,value){ | |
| 114 if ((undefined !== value) && ('' !== value) && ('null' !== value)){ | |
| 115 document.body.style[attr] = value; | |
| 116 log('Body '+attr+' set to "'+value+'"'); | |
| 117 } | |
| 118 } | |
| 119 | |
| 120 function setMargin(){ | |
| 121 //If called via V1 script, convert bodyMargin from int to str | |
| 122 if (undefined === bodyMarginStr){ | |
| 123 bodyMarginStr = bodyMargin+'px'; | |
| 124 } | |
| 125 chkCSS('margin',bodyMarginStr); | |
| 126 setBodyStyle('margin',bodyMarginStr); | |
| 127 } | |
| 128 | |
| 129 function stopInfiniteResizingOfIFrame(){ | |
| 130 document.documentElement.style.height = ''; | |
| 131 document.body.style.height = ''; | |
| 132 log('HTML & body height set to "auto"'); | |
| 133 } | |
| 134 | |
| 135 function initWindowResizeListener(){ | |
| 136 addEventListener(window,'resize', function(){ | |
| 137 sendSize('resize','Window resized'); | |
| 138 }); | |
| 139 } | |
| 140 | |
| 141 function initWindowClickListener(){ | |
| 142 addEventListener(window,'click', function(){ | |
| 143 sendSize('click','Window clicked'); | |
| 144 }); | |
| 145 } | |
| 146 | |
| 147 function checkHeightMode(){ | |
| 148 if (heightCalcModeDefault !== heightCalcMode){ | |
| 149 if (!(heightCalcMode in getHeight)){ | |
| 150 warn(heightCalcMode + ' is not a valid option for heightCalculationMethod.'); | |
| 151 heightCalcMode='bodyScroll'; | |
| 152 } | |
| 153 log('Height calculation method set to "'+heightCalcMode+'"'); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 function startEventListeners(){ | |
| 158 if ( true === autoResize ) { | |
| 159 initWindowResizeListener(); | |
| 160 initWindowClickListener(); | |
| 161 setupMutationObserver(); | |
| 162 } | |
| 163 else { | |
| 164 log('Auto Resize disabled'); | |
| 165 } | |
| 166 } | |
| 167 | |
| 168 function injectClearFixIntoBodyElement(){ | |
| 169 var clearFix = document.createElement('div'); | |
| 170 clearFix.style.clear = 'both'; | |
| 171 clearFix.style.display = 'block'; //Guard against this having been globally redefined in CSS. | |
| 172 document.body.appendChild(clearFix); | |
| 173 } | |
| 174 | |
| 175 function setupPublicMethods(){ | |
| 176 if (publicMethods) { | |
| 177 log('Enable public methods'); | |
| 178 | |
| 179 window.parentIFrame = { | |
| 180 close: function closeF(){ | |
| 181 sendSize('close','parentIFrame.close()', 0, 0); | |
| 182 }, | |
| 183 getId: function getIdF(){ | |
| 184 return myID; | |
| 185 }, | |
| 186 reset: function resetF(){ | |
| 187 resetIFrame('parentIFrame.size'); | |
| 188 }, | |
| 189 sendMessage: function sendMessageF(msg,targetOrigin){ | |
| 190 sendMsg(0,0,'message',msg,targetOrigin); | |
| 191 }, | |
| 192 setHeightCalculationMethod: function setHeightCalculationMethodF(heightCalculationMethod){ | |
| 193 heightCalcMode = heightCalculationMethod; | |
| 194 checkHeightMode(); | |
| 195 }, | |
| 196 setTargetOrigin: function setTargetOriginF(targetOrigin){ | |
| 197 log('Set targetOrigin: '+targetOrigin); | |
| 198 targetOriginDefault = targetOrigin; | |
| 199 }, | |
| 200 size: function sizeF(customHeight, customWidth){ | |
| 201 var valString = ''+(customHeight?customHeight:'')+(customWidth?','+customWidth:''); | |
| 202 lockTrigger(); | |
| 203 sendSize('size','parentIFrame.size('+valString+')', customHeight, customWidth); | |
| 204 } | |
| 205 }; | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 function initInterval(){ | |
| 210 if ( 0 !== interval ){ | |
| 211 log('setInterval: '+interval+'ms'); | |
| 212 setInterval(function(){ | |
| 213 sendSize('interval','setInterval: '+interval); | |
| 214 },Math.abs(interval)); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 function setupInjectElementLoadListners(mutations){ | |
| 219 function addLoadListener(element){ | |
| 220 if (element.height === undefined || element.width === undefined || 0 === element.height || 0 === element.width){ | |
| 221 log('Attach listerner to '+element.src); | |
| 222 addEventListener(element,'load', function imageLoaded(){ | |
| 223 sendSize('imageLoad','Image loaded'); | |
| 224 }); | |
| 225 } | |
| 226 } | |
| 227 | |
| 228 mutations.forEach(function (mutation) { | |
| 229 if (mutation.type === 'attributes' && mutation.attributeName === 'src'){ | |
| 230 addLoadListener(mutation.target); | |
| 231 } else if (mutation.type === 'childList'){ | |
| 232 var images = mutation.target.querySelectorAll('img'); | |
| 233 Array.prototype.forEach.call(images,function (image) { | |
| 234 addLoadListener(image); | |
| 235 }); | |
| 236 } | |
| 237 }); | |
| 238 } | |
| 239 | |
| 240 function setupMutationObserver(){ | |
| 241 | |
| 242 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; | |
| 243 | |
| 244 function createMutationObserver(){ | |
| 245 var | |
| 246 target = document.querySelector('body'), | |
| 247 | |
| 248 config = { | |
| 249 attributes : true, | |
| 250 attributeOldValue : false, | |
| 251 characterData : true, | |
| 252 characterDataOldValue : false, | |
| 253 childList : true, | |
| 254 subtree : true | |
| 255 }, | |
| 256 | |
| 257 observer = new MutationObserver(function(mutations) { | |
| 258 sendSize('mutationObserver','mutationObserver: ' + mutations[0].target + ' ' + mutations[0].type); | |
| 259 setupInjectElementLoadListners(mutations); //Deal with WebKit asyncing image loading when tags are injected into the page | |
| 260 }); | |
| 261 | |
| 262 log('Enable MutationObserver'); | |
| 263 observer.observe(target, config); | |
| 264 } | |
| 265 | |
| 266 if (MutationObserver){ | |
| 267 if (0 > interval) { | |
| 268 initInterval(); | |
| 269 } else { | |
| 270 createMutationObserver(); | |
| 271 } | |
| 272 } | |
| 273 else { | |
| 274 warn('MutationObserver not supported in this browser!'); | |
| 275 initInterval(); | |
| 276 } | |
| 277 } | |
| 278 | |
| 279 | |
| 280 // document.documentElement.offsetHeight is not reliable, so | |
| 281 // we have to jump through hoops to get a better value. | |
| 282 function getBodyOffsetHeight(){ | |
| 283 function getComputedBodyStyle(prop) { | |
| 284 function convertUnitsToPxForIE8(value) { | |
| 285 var PIXEL = /^\d+(px)?$/i; | |
| 286 | |
| 287 if (PIXEL.test(value)) { | |
| 288 return parseInt(value,base); | |
| 289 } | |
| 290 | |
| 291 var | |
| 292 style = el.style.left, | |
| 293 runtimeStyle = el.runtimeStyle.left; | |
| 294 | |
| 295 el.runtimeStyle.left = el.currentStyle.left; | |
| 296 el.style.left = value || 0; | |
| 297 value = el.style.pixelLeft; | |
| 298 el.style.left = style; | |
| 299 el.runtimeStyle.left = runtimeStyle; | |
| 300 | |
| 301 return value; | |
| 302 } | |
| 303 | |
| 304 var | |
| 305 el = document.body, | |
| 306 retVal = 0; | |
| 307 | |
| 308 if (('defaultView' in document) && ('getComputedStyle' in document.defaultView)) { | |
| 309 retVal = document.defaultView.getComputedStyle(el, null); | |
| 310 retVal = (null !== retVal) ? retVal[prop] : 0; | |
| 311 } else {//IE8 | |
| 312 retVal = convertUnitsToPxForIE8(el.currentStyle[prop]); | |
| 313 } | |
| 314 | |
| 315 return parseInt(retVal,base); | |
| 316 } | |
| 317 | |
| 318 return document.body.offsetHeight + | |
| 319 getComputedBodyStyle('marginTop') + | |
| 320 getComputedBodyStyle('marginBottom'); | |
| 321 } | |
| 322 | |
| 323 function getBodyScrollHeight(){ | |
| 324 return document.body.scrollHeight; | |
| 325 } | |
| 326 | |
| 327 function getDEOffsetHeight(){ | |
| 328 return document.documentElement.offsetHeight; | |
| 329 } | |
| 330 | |
| 331 function getDEScrollHeight(){ | |
| 332 return document.documentElement.scrollHeight; | |
| 333 } | |
| 334 | |
| 335 //From https://github.com/guardian/iframe-messenger | |
| 336 function getLowestElementHeight() { | |
| 337 var | |
| 338 allElements = document.querySelectorAll('body *'), | |
| 339 allElementsLength = allElements.length, | |
| 340 maxBottomVal = 0, | |
| 341 timer = new Date().getTime(); | |
| 342 | |
| 343 for (var i = 0; i < allElementsLength; i++) { | |
| 344 if (allElements[i].getBoundingClientRect().bottom > maxBottomVal) { | |
| 345 maxBottomVal = allElements[i].getBoundingClientRect().bottom; | |
| 346 } | |
| 347 } | |
| 348 | |
| 349 timer = new Date().getTime() - timer; | |
| 350 | |
| 351 log('Parsed '+allElementsLength+' HTML elements'); | |
| 352 log('LowestElement bottom position calculated in ' + timer + 'ms'); | |
| 353 | |
| 354 return maxBottomVal; | |
| 355 } | |
| 356 | |
| 357 function getAllHeights(){ | |
| 358 return [ | |
| 359 getBodyOffsetHeight(), | |
| 360 getBodyScrollHeight(), | |
| 361 getDEOffsetHeight(), | |
| 362 getDEScrollHeight() | |
| 363 ]; | |
| 364 } | |
| 365 | |
| 366 function getMaxHeight(){ | |
| 367 return Math.max.apply(null,getAllHeights()); | |
| 368 } | |
| 369 | |
| 370 function getMinHeight(){ | |
| 371 return Math.min.apply(null,getAllHeights()); | |
| 372 } | |
| 373 | |
| 374 function getBestHeight(){ | |
| 375 return Math.max(getBodyOffsetHeight(),getLowestElementHeight()); | |
| 376 } | |
| 377 | |
| 378 var getHeight = { | |
| 379 offset : getBodyOffsetHeight, //Backward compatability | |
| 380 bodyOffset : getBodyOffsetHeight, | |
| 381 bodyScroll : getBodyScrollHeight, | |
| 382 documentElementOffset : getDEOffsetHeight, | |
| 383 scroll : getDEScrollHeight, //Backward compatability | |
| 384 documentElementScroll : getDEScrollHeight, | |
| 385 max : getMaxHeight, | |
| 386 min : getMinHeight, | |
| 387 grow : getMaxHeight, | |
| 388 lowestElement : getBestHeight, | |
| 389 }; | |
| 390 | |
| 391 function getWidth(){ | |
| 392 return Math.max( | |
| 393 document.documentElement.scrollWidth, | |
| 394 document.body.scrollWidth | |
| 395 ); | |
| 396 } | |
| 397 | |
| 398 function sendSize(triggerEvent, triggerEventDesc, customHeight, customWidth){ | |
| 399 | |
| 400 var currentHeight,currentWidth; | |
| 401 | |
| 402 function recordTrigger(){ | |
| 403 if (!(triggerEvent in {'reset':1,'resetPage':1,'init':1})){ | |
| 404 log( 'Trigger event: ' + triggerEventDesc ); | |
| 405 } | |
| 406 } | |
| 407 | |
| 408 function resizeIFrame(){ | |
| 409 height = currentHeight; | |
| 410 width = currentWidth; | |
| 411 | |
| 412 sendMsg(height,width,triggerEvent); | |
| 413 } | |
| 414 | |
| 415 function isDoubleFiredEvent(){ | |
| 416 return triggerLocked && (triggerEvent in doubleEventList); | |
| 417 } | |
| 418 | |
| 419 function isSizeChangeDetected(){ | |
| 420 function checkTolarance(a,b){ | |
| 421 var retVal = Math.abs(a-b) <= tolerance; | |
| 422 return !retVal; | |
| 423 } | |
| 424 | |
| 425 currentHeight = (undefined !== customHeight) ? customHeight : getHeight[heightCalcMode](); | |
| 426 currentWidth = (undefined !== customWidth ) ? customWidth : getWidth(); | |
| 427 | |
| 428 return checkTolarance(height,currentHeight) || | |
| 429 (calculateWidth && checkTolarance(width,currentWidth)); | |
| 430 | |
| 431 //return (height !== currentHeight) || | |
| 432 // (calculateWidth && width !== currentWidth); | |
| 433 } | |
| 434 | |
| 435 function isForceResizableEvent(){ | |
| 436 return !(triggerEvent in {'init':1,'interval':1,'size':1}); | |
| 437 } | |
| 438 | |
| 439 function isForceResizableHeightCalcMode(){ | |
| 440 return (heightCalcMode in resetRequiredMethods); | |
| 441 } | |
| 442 | |
| 443 function logIgnored(){ | |
| 444 log('No change in size detected'); | |
| 445 } | |
| 446 | |
| 447 function checkDownSizing(){ | |
| 448 if (isForceResizableEvent() && isForceResizableHeightCalcMode()){ | |
| 449 resetIFrame(triggerEventDesc); | |
| 450 } else if (!(triggerEvent in {'interval':1})){ | |
| 451 recordTrigger(); | |
| 452 logIgnored(); | |
| 453 } | |
| 454 } | |
| 455 | |
| 456 if (!isDoubleFiredEvent()){ | |
| 457 if (isSizeChangeDetected()){ | |
| 458 recordTrigger(); | |
| 459 lockTrigger(); | |
| 460 resizeIFrame(); | |
| 461 } else { | |
| 462 checkDownSizing(); | |
| 463 } | |
| 464 } else { | |
| 465 log('Trigger event cancelled: '+triggerEvent); | |
| 466 } | |
| 467 } | |
| 468 | |
| 469 function lockTrigger(){ | |
| 470 if (!triggerLocked){ | |
| 471 triggerLocked = true; | |
| 472 log('Trigger event lock on'); | |
| 473 } | |
| 474 clearTimeout(triggerLockedTimer); | |
| 475 triggerLockedTimer = setTimeout(function(){ | |
| 476 triggerLocked = false; | |
| 477 log('Trigger event lock off'); | |
| 478 log('--'); | |
| 479 },eventCancelTimer); | |
| 480 } | |
| 481 | |
| 482 function triggerReset(triggerEvent){ | |
| 483 height = getHeight[heightCalcMode](); | |
| 484 width = getWidth(); | |
| 485 | |
| 486 sendMsg(height,width,triggerEvent); | |
| 487 } | |
| 488 | |
| 489 function resetIFrame(triggerEventDesc){ | |
| 490 var hcm = heightCalcMode; | |
| 491 heightCalcMode = heightCalcModeDefault; | |
| 492 | |
| 493 log('Reset trigger event: ' + triggerEventDesc); | |
| 494 lockTrigger(); | |
| 495 triggerReset('reset'); | |
| 496 | |
| 497 heightCalcMode = hcm; | |
| 498 } | |
| 499 | |
| 500 function sendMsg(height,width,triggerEvent,msg,targetOrigin){ | |
| 501 function setTargetOrigin(){ | |
| 502 if (undefined === targetOrigin){ | |
| 503 targetOrigin = targetOriginDefault; | |
| 504 } else { | |
| 505 log('Message targetOrigin: '+targetOrigin); | |
| 506 } | |
| 507 } | |
| 508 | |
| 509 function sendToParent(){ | |
| 510 var | |
| 511 size = height + ':' + width, | |
| 512 message = myID + ':' + size + ':' + triggerEvent + (undefined !== msg ? ':' + msg : ''); | |
| 513 | |
| 514 log('Sending message to host page (' + message + ')'); | |
| 515 target.postMessage( msgID + message, targetOrigin); | |
| 516 } | |
| 517 | |
| 518 setTargetOrigin(); | |
| 519 sendToParent(); | |
| 520 } | |
| 521 | |
| 522 function receiver(event) { | |
| 523 function isMessageForUs(){ | |
| 524 return msgID === (''+event.data).substr(0,msgIdLen); //''+ Protects against non-string messages | |
| 525 } | |
| 526 | |
| 527 function initFromParent(){ | |
| 528 initMsg = event.data; | |
| 529 init(); | |
| 530 firstRun = false; | |
| 531 setTimeout(function(){ initLock = false;},eventCancelTimer); | |
| 532 } | |
| 533 | |
| 534 function resetFromParent(){ | |
| 535 if (!initLock){ | |
| 536 log('Page size reset by host page'); | |
| 537 triggerReset('resetPage'); | |
| 538 } else { | |
| 539 log('Page reset ignored by init'); | |
| 540 } | |
| 541 } | |
| 542 | |
| 543 function getMessageType(){ | |
| 544 return event.data.split(']')[1]; | |
| 545 } | |
| 546 | |
| 547 function isMiddleTier(){ | |
| 548 return ('iFrameResize' in window); | |
| 549 } | |
| 550 | |
| 551 if (isMessageForUs()){ | |
| 552 if (firstRun){ //Check msg ID | |
| 553 initFromParent(); | |
| 554 } else if ('reset' === getMessageType()){ | |
| 555 resetFromParent(); | |
| 556 } else if (event.data !== initMsg && !isMiddleTier()){ | |
| 557 warn('Unexpected message ('+event.data+')'); | |
| 558 } | |
| 559 } | |
| 560 } | |
| 561 | |
| 562 addEventListener(window, 'message', receiver); | |
| 563 | |
| 564 })(); |
