github.com/GoogleContainerTools/skaffold/v2@v2.13.2/docs-v2/static/scripts/iframeResizer.contentWindow.js (about) 1 /* 2 * File: iframeResizer.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 * Doc: https://github.com/davidjbradshaw/iframe-resizer 7 * Author: David J. Bradshaw - dave@bradshaw.net 8 * Contributor: Jure Mav - jure.mav@gmail.com 9 * Contributor: Ian Caunce - ian@hallnet.co.uk 10 */ 11 12 13 ;(function(window, undefined) { 14 'use strict'; 15 16 var 17 autoResize = true, 18 base = 10, 19 bodyBackground = '', 20 bodyMargin = 0, 21 bodyMarginStr = '', 22 bodyObserver = null, 23 bodyPadding = '', 24 calculateWidth = false, 25 doubleEventList = {'resize':1,'click':1}, 26 eventCancelTimer = 128, 27 firstRun = true, 28 height = 1, 29 heightCalcModeDefault = 'bodyOffset', 30 heightCalcMode = heightCalcModeDefault, 31 initLock = true, 32 initMsg = '', 33 inPageLinks = {}, 34 interval = 32, 35 intervalTimer = null, 36 logging = false, 37 msgID = '[iFrameSizer]', //Must match host page msg ID 38 msgIdLen = msgID.length, 39 myID = '', 40 observer = null, 41 resetRequiredMethods = {max:1,min:1,bodyScroll:1,documentElementScroll:1}, 42 resizeFrom = 'child', 43 sendPermit = true, 44 target = window.parent, 45 targetOriginDefault = '*', 46 tolerance = 0, 47 triggerLocked = false, 48 triggerLockedTimer = null, 49 throttledTimer = 16, 50 width = 1, 51 widthCalcModeDefault = 'scroll', 52 widthCalcMode = widthCalcModeDefault, 53 win = window, 54 messageCallback = function(){ warn('MessageCallback function not defined'); }, 55 readyCallback = function(){}, 56 pageInfoCallback = function(){}, 57 customCalcMethods = { 58 height: function(){ 59 warn('Custom height calculation function not defined'); 60 return document.documentElement.offsetHeight; 61 }, 62 width: function(){ 63 warn('Custom width calculation function not defined'); 64 return document.body.scrollWidth; 65 } 66 }; 67 68 69 function addEventListener(el,evt,func){ 70 /* istanbul ignore else */ // Not testable in phantonJS 71 if ('addEventListener' in window){ 72 el.addEventListener(evt,func, false); 73 } else if ('attachEvent' in window){ //IE 74 el.attachEvent('on'+evt,func); 75 } 76 } 77 78 function removeEventListener(el,evt,func){ 79 /* istanbul ignore else */ // Not testable in phantonJS 80 if ('removeEventListener' in window){ 81 el.removeEventListener(evt,func, false); 82 } else if ('detachEvent' in window){ //IE 83 el.detachEvent('on'+evt,func); 84 } 85 } 86 87 function capitalizeFirstLetter(string) { 88 return string.charAt(0).toUpperCase() + string.slice(1); 89 } 90 91 //Based on underscore.js 92 function throttle(func) { 93 var 94 context, args, result, 95 timeout = null, 96 previous = 0, 97 later = function() { 98 previous = getNow(); 99 timeout = null; 100 result = func.apply(context, args); 101 if (!timeout) { 102 context = args = null; 103 } 104 }; 105 106 return function() { 107 var now = getNow(); 108 109 if (!previous) { 110 previous = now; 111 } 112 113 var remaining = throttledTimer - (now - previous); 114 115 context = this; 116 args = arguments; 117 118 if (remaining <= 0 || remaining > throttledTimer) { 119 if (timeout) { 120 clearTimeout(timeout); 121 timeout = null; 122 } 123 124 previous = now; 125 result = func.apply(context, args); 126 127 if (!timeout) { 128 context = args = null; 129 } 130 131 } else if (!timeout) { 132 timeout = setTimeout(later, remaining); 133 } 134 135 return result; 136 }; 137 } 138 139 var getNow = Date.now || function() { 140 /* istanbul ignore next */ // Not testable in PhantonJS 141 return new Date().getTime(); 142 }; 143 144 function formatLogMsg(msg){ 145 return msgID + '[' + myID + ']' + ' ' + msg; 146 } 147 148 function log(msg){ 149 if (logging && ('object' === typeof window.console)){ 150 console.log(formatLogMsg(msg)); 151 } 152 } 153 154 function warn(msg){ 155 if ('object' === typeof window.console){ 156 console.warn(formatLogMsg(msg)); 157 } 158 } 159 160 161 function init(){ 162 readDataFromParent(); 163 log('Initialising iFrame ('+location.href+')'); 164 readDataFromPage(); 165 setMargin(); 166 setBodyStyle('background',bodyBackground); 167 setBodyStyle('padding',bodyPadding); 168 injectClearFixIntoBodyElement(); 169 checkHeightMode(); 170 checkWidthMode(); 171 stopInfiniteResizingOfIFrame(); 172 setupPublicMethods(); 173 startEventListeners(); 174 inPageLinks = setupInPageLinks(); 175 sendSize('init','Init message from host page'); 176 readyCallback(); 177 } 178 179 function readDataFromParent(){ 180 181 function strBool(str){ 182 return 'true' === str ? true : false; 183 } 184 185 var data = initMsg.substr(msgIdLen).split(':'); 186 187 myID = data[0]; 188 bodyMargin = (undefined !== data[1]) ? Number(data[1]) : bodyMargin; //For V1 compatibility 189 calculateWidth = (undefined !== data[2]) ? strBool(data[2]) : calculateWidth; 190 logging = (undefined !== data[3]) ? strBool(data[3]) : logging; 191 interval = (undefined !== data[4]) ? Number(data[4]) : interval; 192 autoResize = (undefined !== data[6]) ? strBool(data[6]) : autoResize; 193 bodyMarginStr = data[7]; 194 heightCalcMode = (undefined !== data[8]) ? data[8] : heightCalcMode; 195 bodyBackground = data[9]; 196 bodyPadding = data[10]; 197 tolerance = (undefined !== data[11]) ? Number(data[11]) : tolerance; 198 inPageLinks.enable = (undefined !== data[12]) ? strBool(data[12]): false; 199 resizeFrom = (undefined !== data[13]) ? data[13] : resizeFrom; 200 widthCalcMode = (undefined !== data[14]) ? data[14] : widthCalcMode; 201 } 202 203 function readDataFromPage(){ 204 function readData(){ 205 var data = window.iFrameResizer; 206 207 log('Reading data from page: ' + JSON.stringify(data)); 208 209 messageCallback = ('messageCallback' in data) ? data.messageCallback : messageCallback; 210 readyCallback = ('readyCallback' in data) ? data.readyCallback : readyCallback; 211 targetOriginDefault = ('targetOrigin' in data) ? data.targetOrigin : targetOriginDefault; 212 heightCalcMode = ('heightCalculationMethod' in data) ? data.heightCalculationMethod : heightCalcMode; 213 widthCalcMode = ('widthCalculationMethod' in data) ? data.widthCalculationMethod : widthCalcMode; 214 } 215 216 function setupCustomCalcMethods(calcMode, calcFunc){ 217 if ('function' === typeof calcMode) { 218 log('Setup custom ' + calcFunc + 'CalcMethod'); 219 customCalcMethods[calcFunc] = calcMode; 220 calcMode = 'custom'; 221 } 222 223 return calcMode; 224 } 225 226 if(('iFrameResizer' in window) && (Object === window.iFrameResizer.constructor)) { 227 readData(); 228 heightCalcMode = setupCustomCalcMethods(heightCalcMode, 'height'); 229 widthCalcMode = setupCustomCalcMethods(widthCalcMode, 'width'); 230 } 231 232 log('TargetOrigin for parent set to: ' + targetOriginDefault); 233 } 234 235 236 function chkCSS(attr,value){ 237 if (-1 !== value.indexOf('-')){ 238 warn('Negative CSS value ignored for '+attr); 239 value=''; 240 } 241 return value; 242 } 243 244 function setBodyStyle(attr,value){ 245 if ((undefined !== value) && ('' !== value) && ('null' !== value)){ 246 document.body.style[attr] = value; 247 log('Body '+attr+' set to "'+value+'"'); 248 } 249 } 250 251 function setMargin(){ 252 //If called via V1 script, convert bodyMargin from int to str 253 if (undefined === bodyMarginStr){ 254 bodyMarginStr = bodyMargin+'px'; 255 } 256 257 setBodyStyle('margin',chkCSS('margin',bodyMarginStr)); 258 } 259 260 function stopInfiniteResizingOfIFrame(){ 261 document.documentElement.style.height = ''; 262 document.body.style.height = ''; 263 log('HTML & body height set to "auto"'); 264 } 265 266 267 function manageTriggerEvent(options){ 268 function handleEvent(){ 269 sendSize(options.eventName,options.eventType); 270 } 271 272 var listener = { 273 add: function(eventName){ 274 addEventListener(window,eventName,handleEvent); 275 }, 276 remove: function(eventName){ 277 removeEventListener(window,eventName,handleEvent); 278 } 279 }; 280 281 if(options.eventNames && Array.prototype.map){ 282 options.eventName = options.eventNames[0]; 283 options.eventNames.map(listener[options.method]); 284 } else { 285 listener[options.method](options.eventName); 286 } 287 288 log(capitalizeFirstLetter(options.method) + ' event listener: ' + options.eventType); 289 } 290 291 function manageEventListeners(method){ 292 manageTriggerEvent({method:method, eventType: 'Animation Start', eventNames: ['animationstart','webkitAnimationStart'] }); 293 manageTriggerEvent({method:method, eventType: 'Animation Iteration', eventNames: ['animationiteration','webkitAnimationIteration'] }); 294 manageTriggerEvent({method:method, eventType: 'Animation End', eventNames: ['animationend','webkitAnimationEnd'] }); 295 manageTriggerEvent({method:method, eventType: 'Input', eventName: 'input' }); 296 manageTriggerEvent({method:method, eventType: 'Mouse Up', eventName: 'mouseup' }); 297 manageTriggerEvent({method:method, eventType: 'Mouse Down', eventName: 'mousedown' }); 298 manageTriggerEvent({method:method, eventType: 'Orientation Change', eventName: 'orientationchange' }); 299 manageTriggerEvent({method:method, eventType: 'Print', eventName: ['afterprint', 'beforeprint'] }); 300 manageTriggerEvent({method:method, eventType: 'Ready State Change', eventName: 'readystatechange' }); 301 manageTriggerEvent({method:method, eventType: 'Touch Start', eventName: 'touchstart' }); 302 manageTriggerEvent({method:method, eventType: 'Touch End', eventName: 'touchend' }); 303 manageTriggerEvent({method:method, eventType: 'Touch Cancel', eventName: 'touchcancel' }); 304 manageTriggerEvent({method:method, eventType: 'Transition Start', eventNames: ['transitionstart','webkitTransitionStart','MSTransitionStart','oTransitionStart','otransitionstart'] }); 305 manageTriggerEvent({method:method, eventType: 'Transition Iteration', eventNames: ['transitioniteration','webkitTransitionIteration','MSTransitionIteration','oTransitionIteration','otransitioniteration'] }); 306 manageTriggerEvent({method:method, eventType: 'Transition End', eventNames: ['transitionend','webkitTransitionEnd','MSTransitionEnd','oTransitionEnd','otransitionend'] }); 307 if('child' === resizeFrom){ 308 manageTriggerEvent({method:method, eventType: 'IFrame Resized', eventName: 'resize' }); 309 } 310 } 311 312 function checkCalcMode(calcMode,calcModeDefault,modes,type){ 313 if (calcModeDefault !== calcMode){ 314 if (!(calcMode in modes)){ 315 warn(calcMode + ' is not a valid option for '+type+'CalculationMethod.'); 316 calcMode=calcModeDefault; 317 } 318 log(type+' calculation method set to "'+calcMode+'"'); 319 } 320 321 return calcMode; 322 } 323 324 function checkHeightMode(){ 325 heightCalcMode = checkCalcMode(heightCalcMode,heightCalcModeDefault,getHeight,'height'); 326 } 327 328 function checkWidthMode(){ 329 widthCalcMode = checkCalcMode(widthCalcMode,widthCalcModeDefault,getWidth,'width'); 330 } 331 332 function startEventListeners(){ 333 if ( true === autoResize ) { 334 manageEventListeners('add'); 335 setupMutationObserver(); 336 } 337 else { 338 log('Auto Resize disabled'); 339 } 340 } 341 342 function stopMsgsToParent(){ 343 log('Disable outgoing messages'); 344 sendPermit = false; 345 } 346 347 function removeMsgListener(){ 348 log('Remove event listener: Message'); 349 removeEventListener(window, 'message', receiver); 350 } 351 352 function disconnectMutationObserver(){ 353 if (null !== bodyObserver){ 354 /* istanbul ignore next */ // Not testable in PhantonJS 355 bodyObserver.disconnect(); 356 } 357 } 358 359 function stopEventListeners(){ 360 manageEventListeners('remove'); 361 disconnectMutationObserver(); 362 clearInterval(intervalTimer); 363 } 364 365 function teardown(){ 366 stopMsgsToParent(); 367 removeMsgListener(); 368 if (true === autoResize) stopEventListeners(); 369 } 370 371 function injectClearFixIntoBodyElement(){ 372 var clearFix = document.createElement('div'); 373 clearFix.style.clear = 'both'; 374 clearFix.style.display = 'block'; //Guard against this having been globally redefined in CSS. 375 document.body.appendChild(clearFix); 376 } 377 378 function setupInPageLinks(){ 379 380 function getPagePosition (){ 381 return { 382 x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft, 383 y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop 384 }; 385 } 386 387 function getElementPosition(el){ 388 var 389 elPosition = el.getBoundingClientRect(), 390 pagePosition = getPagePosition(); 391 392 return { 393 x: parseInt(elPosition.left,10) + parseInt(pagePosition.x,10), 394 y: parseInt(elPosition.top,10) + parseInt(pagePosition.y,10) 395 }; 396 } 397 398 function findTarget(location){ 399 function jumpToTarget(target){ 400 var jumpPosition = getElementPosition(target); 401 402 log('Moving to in page link (#'+hash+') at x: '+jumpPosition.x+' y: '+jumpPosition.y); 403 sendMsg(jumpPosition.y, jumpPosition.x, 'scrollToOffset'); // X&Y reversed at sendMsg uses height/width 404 } 405 406 var 407 hash = location.split('#')[1] || location, //Remove # if present 408 hashData = decodeURIComponent(hash), 409 target = document.getElementById(hashData) || document.getElementsByName(hashData)[0]; 410 411 if (undefined !== target){ 412 jumpToTarget(target); 413 } else { 414 log('In page link (#' + hash + ') not found in iFrame, so sending to parent'); 415 sendMsg(0,0,'inPageLink','#'+hash); 416 } 417 } 418 419 function checkLocationHash(){ 420 if ('' !== location.hash && '#' !== location.hash){ 421 findTarget(location.href); 422 } 423 } 424 425 function bindAnchors(){ 426 function setupLink(el){ 427 function linkClicked(e){ 428 e.preventDefault(); 429 430 /*jshint validthis:true */ 431 findTarget(this.getAttribute('href')); 432 } 433 434 if ('#' !== el.getAttribute('href')){ 435 addEventListener(el,'click',linkClicked); 436 } 437 } 438 439 Array.prototype.forEach.call( document.querySelectorAll( 'a[href^="#"]' ), setupLink ); 440 } 441 442 function bindLocationHash(){ 443 addEventListener(window,'hashchange',checkLocationHash); 444 } 445 446 function initCheck(){ //check if page loaded with location hash after init resize 447 setTimeout(checkLocationHash,eventCancelTimer); 448 } 449 450 function enableInPageLinks(){ 451 /* istanbul ignore else */ // Not testable in phantonJS 452 if(Array.prototype.forEach && document.querySelectorAll){ 453 log('Setting up location.hash handlers'); 454 bindAnchors(); 455 bindLocationHash(); 456 initCheck(); 457 } else { 458 warn('In page linking not fully supported in this browser! (See README.md for IE8 workaround)'); 459 } 460 } 461 462 if(inPageLinks.enable){ 463 enableInPageLinks(); 464 } else { 465 log('In page linking not enabled'); 466 } 467 468 return { 469 findTarget:findTarget 470 }; 471 } 472 473 function setupPublicMethods(){ 474 log('Enable public methods'); 475 476 win.parentIFrame = { 477 478 autoResize: function autoResizeF(resize){ 479 if (true === resize && false === autoResize) { 480 autoResize=true; 481 startEventListeners(); 482 //sendSize('autoResize','Auto Resize enabled'); 483 } else if (false === resize && true === autoResize) { 484 autoResize=false; 485 stopEventListeners(); 486 } 487 488 return autoResize; 489 }, 490 491 close: function closeF(){ 492 sendMsg(0,0,'close'); 493 teardown(); 494 }, 495 496 getId: function getIdF(){ 497 return myID; 498 }, 499 500 getPageInfo: function getPageInfoF(callback){ 501 if ('function' === typeof callback){ 502 pageInfoCallback = callback; 503 sendMsg(0,0,'pageInfo'); 504 } else { 505 pageInfoCallback = function(){}; 506 sendMsg(0,0,'pageInfoStop'); 507 } 508 }, 509 510 moveToAnchor: function moveToAnchorF(hash){ 511 inPageLinks.findTarget(hash); 512 }, 513 514 reset: function resetF(){ 515 resetIFrame('parentIFrame.reset'); 516 }, 517 518 scrollTo: function scrollToF(x,y){ 519 sendMsg(y,x,'scrollTo'); // X&Y reversed at sendMsg uses height/width 520 }, 521 522 scrollToOffset: function scrollToF(x,y){ 523 sendMsg(y,x,'scrollToOffset'); // X&Y reversed at sendMsg uses height/width 524 }, 525 526 sendMessage: function sendMessageF(msg,targetOrigin){ 527 sendMsg(0,0,'message',JSON.stringify(msg),targetOrigin); 528 }, 529 530 setHeightCalculationMethod: function setHeightCalculationMethodF(heightCalculationMethod){ 531 heightCalcMode = heightCalculationMethod; 532 checkHeightMode(); 533 }, 534 535 setWidthCalculationMethod: function setWidthCalculationMethodF(widthCalculationMethod){ 536 widthCalcMode = widthCalculationMethod; 537 checkWidthMode(); 538 }, 539 540 setTargetOrigin: function setTargetOriginF(targetOrigin){ 541 log('Set targetOrigin: '+targetOrigin); 542 targetOriginDefault = targetOrigin; 543 }, 544 545 size: function sizeF(customHeight, customWidth){ 546 var valString = ''+(customHeight?customHeight:'')+(customWidth?','+customWidth:''); 547 //lockTrigger(); 548 sendSize('size','parentIFrame.size('+valString+')', customHeight, customWidth); 549 } 550 }; 551 } 552 553 function initInterval(){ 554 if ( 0 !== interval ){ 555 log('setInterval: '+interval+'ms'); 556 intervalTimer = setInterval(function(){ 557 sendSize('interval','setInterval: '+interval); 558 },Math.abs(interval)); 559 } 560 } 561 562 /* istanbul ignore next */ //Not testable in PhantomJS 563 function setupBodyMutationObserver(){ 564 function addImageLoadListners(mutation) { 565 function addImageLoadListener(element){ 566 if (false === element.complete) { 567 log('Attach listeners to ' + element.src); 568 element.addEventListener('load', imageLoaded, false); 569 element.addEventListener('error', imageError, false); 570 elements.push(element); 571 } 572 } 573 574 if (mutation.type === 'attributes' && mutation.attributeName === 'src'){ 575 addImageLoadListener(mutation.target); 576 } else if (mutation.type === 'childList'){ 577 Array.prototype.forEach.call( 578 mutation.target.querySelectorAll('img'), 579 addImageLoadListener 580 ); 581 } 582 } 583 584 function removeFromArray(element){ 585 elements.splice(elements.indexOf(element),1); 586 } 587 588 function removeImageLoadListener(element){ 589 log('Remove listeners from ' + element.src); 590 element.removeEventListener('load', imageLoaded, false); 591 element.removeEventListener('error', imageError, false); 592 removeFromArray(element); 593 } 594 595 function imageEventTriggered(event,type,typeDesc){ 596 removeImageLoadListener(event.target); 597 sendSize(type, typeDesc + ': ' + event.target.src, undefined, undefined); 598 } 599 600 function imageLoaded(event) { 601 imageEventTriggered(event,'imageLoad','Image loaded'); 602 } 603 604 function imageError(event) { 605 imageEventTriggered(event,'imageLoadFailed','Image load failed'); 606 } 607 608 function mutationObserved(mutations) { 609 sendSize('mutationObserver','mutationObserver: ' + mutations[0].target + ' ' + mutations[0].type); 610 611 //Deal with WebKit asyncing image loading when tags are injected into the page 612 mutations.forEach(addImageLoadListners); 613 } 614 615 function createMutationObserver(){ 616 var 617 target = document.querySelector('body'), 618 619 config = { 620 attributes : true, 621 attributeOldValue : false, 622 characterData : true, 623 characterDataOldValue : false, 624 childList : true, 625 subtree : true 626 }; 627 628 observer = new MutationObserver(mutationObserved); 629 630 log('Create body MutationObserver'); 631 observer.observe(target, config); 632 633 return observer; 634 } 635 636 var 637 elements = [], 638 MutationObserver = window.MutationObserver || window.WebKitMutationObserver, 639 observer = createMutationObserver(); 640 641 return { 642 disconnect: function (){ 643 if ('disconnect' in observer){ 644 log('Disconnect body MutationObserver'); 645 observer.disconnect(); 646 elements.forEach(removeImageLoadListener); 647 } 648 } 649 }; 650 } 651 652 function setupMutationObserver(){ 653 var forceIntervalTimer = 0 > interval; 654 655 /* istanbul ignore if */ // Not testable in PhantomJS 656 if (window.MutationObserver || window.WebKitMutationObserver){ 657 if (forceIntervalTimer) { 658 initInterval(); 659 } else { 660 bodyObserver = setupBodyMutationObserver(); 661 } 662 } else { 663 log('MutationObserver not supported in this browser!'); 664 initInterval(); 665 } 666 } 667 668 669 // document.documentElement.offsetHeight is not reliable, so 670 // we have to jump through hoops to get a better value. 671 function getComputedStyle(prop,el) { 672 /* istanbul ignore next */ //Not testable in PhantomJS 673 function convertUnitsToPxForIE8(value) { 674 var PIXEL = /^\d+(px)?$/i; 675 676 if (PIXEL.test(value)) { 677 return parseInt(value,base); 678 } 679 680 var 681 style = el.style.left, 682 runtimeStyle = el.runtimeStyle.left; 683 684 el.runtimeStyle.left = el.currentStyle.left; 685 el.style.left = value || 0; 686 value = el.style.pixelLeft; 687 el.style.left = style; 688 el.runtimeStyle.left = runtimeStyle; 689 690 return value; 691 } 692 693 var retVal = 0; 694 el = el || document.body; 695 696 /* istanbul ignore else */ // Not testable in phantonJS 697 if (('defaultView' in document) && ('getComputedStyle' in document.defaultView)) { 698 retVal = document.defaultView.getComputedStyle(el, null); 699 retVal = (null !== retVal) ? retVal[prop] : 0; 700 } else {//IE8 701 retVal = convertUnitsToPxForIE8(el.currentStyle[prop]); 702 } 703 704 return parseInt(retVal,base); 705 } 706 707 function chkEventThottle(timer){ 708 if(timer > throttledTimer/2){ 709 throttledTimer = 2*timer; 710 log('Event throttle increased to ' + throttledTimer + 'ms'); 711 } 712 } 713 714 //Idea from https://github.com/guardian/iframe-messenger 715 function getMaxElement(side,elements) { 716 var 717 elementsLength = elements.length, 718 elVal = 0, 719 maxVal = 0, 720 Side = capitalizeFirstLetter(side), 721 timer = getNow(); 722 723 for (var i = 0; i < elementsLength; i++) { 724 elVal = elements[i].getBoundingClientRect()[side] + getComputedStyle('margin'+Side,elements[i]); 725 if (elVal > maxVal) { 726 maxVal = elVal; 727 } 728 } 729 730 timer = getNow() - timer; 731 732 log('Parsed '+elementsLength+' HTML elements'); 733 log('Element position calculated in ' + timer + 'ms'); 734 735 chkEventThottle(timer); 736 737 return maxVal; 738 } 739 740 function getAllMeasurements(dimention){ 741 return [ 742 dimention.bodyOffset(), 743 dimention.bodyScroll(), 744 dimention.documentElementOffset(), 745 dimention.documentElementScroll() 746 ]; 747 } 748 749 function getTaggedElements(side,tag){ 750 function noTaggedElementsFound(){ 751 warn('No tagged elements ('+tag+') found on page'); 752 return height; //current height 753 } 754 755 var elements = document.querySelectorAll('['+tag+']'); 756 757 return 0 === elements.length ? noTaggedElementsFound() : getMaxElement(side,elements); 758 } 759 760 function getAllElements(){ 761 return document.querySelectorAll('body *'); 762 } 763 764 var 765 getHeight = { 766 bodyOffset: function getBodyOffsetHeight(){ 767 return document.body.offsetHeight + getComputedStyle('marginTop') + getComputedStyle('marginBottom'); 768 }, 769 770 offset: function(){ 771 return getHeight.bodyOffset(); //Backwards compatability 772 }, 773 774 bodyScroll: function getBodyScrollHeight(){ 775 return document.body.scrollHeight; 776 }, 777 778 custom: function getCustomWidth(){ 779 return customCalcMethods.height(); 780 }, 781 782 documentElementOffset: function getDEOffsetHeight(){ 783 return document.documentElement.offsetHeight; 784 }, 785 786 documentElementScroll: function getDEScrollHeight(){ 787 return document.documentElement.scrollHeight; 788 }, 789 790 max: function getMaxHeight(){ 791 return Math.max.apply(null,getAllMeasurements(getHeight)); 792 }, 793 794 min: function getMinHeight(){ 795 return Math.min.apply(null,getAllMeasurements(getHeight)); 796 }, 797 798 grow: function growHeight(){ 799 return getHeight.max(); //Run max without the forced downsizing 800 }, 801 802 lowestElement: function getBestHeight(){ 803 return Math.max(getHeight.bodyOffset(), getMaxElement('bottom',getAllElements())); 804 }, 805 806 taggedElement: function getTaggedElementsHeight(){ 807 return getTaggedElements('bottom','data-iframe-height'); 808 } 809 }, 810 811 getWidth = { 812 bodyScroll: function getBodyScrollWidth(){ 813 return document.body.scrollWidth; 814 }, 815 816 bodyOffset: function getBodyOffsetWidth(){ 817 return document.body.offsetWidth; 818 }, 819 820 custom: function getCustomWidth(){ 821 return customCalcMethods.width(); 822 }, 823 824 documentElementScroll: function getDEScrollWidth(){ 825 return document.documentElement.scrollWidth; 826 }, 827 828 documentElementOffset: function getDEOffsetWidth(){ 829 return document.documentElement.offsetWidth; 830 }, 831 832 scroll: function getMaxWidth(){ 833 return Math.max(getWidth.bodyScroll(), getWidth.documentElementScroll()); 834 }, 835 836 max: function getMaxWidth(){ 837 return Math.max.apply(null,getAllMeasurements(getWidth)); 838 }, 839 840 min: function getMinWidth(){ 841 return Math.min.apply(null,getAllMeasurements(getWidth)); 842 }, 843 844 rightMostElement: function rightMostElement(){ 845 return getMaxElement('right', getAllElements()); 846 }, 847 848 taggedElement: function getTaggedElementsWidth(){ 849 return getTaggedElements('right', 'data-iframe-width'); 850 } 851 }; 852 853 854 function sizeIFrame(triggerEvent, triggerEventDesc, customHeight, customWidth){ 855 856 function resizeIFrame(){ 857 height = currentHeight; 858 width = currentWidth; 859 860 sendMsg(height,width,triggerEvent); 861 } 862 863 function isSizeChangeDetected(){ 864 function checkTolarance(a,b){ 865 var retVal = Math.abs(a-b) <= tolerance; 866 return !retVal; 867 } 868 869 currentHeight = (undefined !== customHeight) ? customHeight : getHeight[heightCalcMode](); 870 currentWidth = (undefined !== customWidth ) ? customWidth : getWidth[widthCalcMode](); 871 872 return checkTolarance(height,currentHeight) || (calculateWidth && checkTolarance(width,currentWidth)); 873 } 874 875 function isForceResizableEvent(){ 876 return !(triggerEvent in {'init':1,'interval':1,'size':1}); 877 } 878 879 function isForceResizableCalcMode(){ 880 return (heightCalcMode in resetRequiredMethods) || (calculateWidth && widthCalcMode in resetRequiredMethods); 881 } 882 883 function logIgnored(){ 884 log('No change in size detected'); 885 } 886 887 function checkDownSizing(){ 888 if (isForceResizableEvent() && isForceResizableCalcMode()){ 889 resetIFrame(triggerEventDesc); 890 } else if (!(triggerEvent in {'interval':1})){ 891 logIgnored(); 892 } 893 } 894 895 var currentHeight,currentWidth; 896 897 if (isSizeChangeDetected() || 'init' === triggerEvent){ 898 lockTrigger(); 899 resizeIFrame(); 900 } else { 901 checkDownSizing(); 902 } 903 } 904 905 var sizeIFrameThrottled = throttle(sizeIFrame); 906 907 function sendSize(triggerEvent, triggerEventDesc, customHeight, customWidth){ 908 function recordTrigger(){ 909 if (!(triggerEvent in {'reset':1,'resetPage':1,'init':1})){ 910 log( 'Trigger event: ' + triggerEventDesc ); 911 } 912 } 913 914 function isDoubleFiredEvent(){ 915 return triggerLocked && (triggerEvent in doubleEventList); 916 } 917 918 if (!isDoubleFiredEvent()){ 919 recordTrigger(); 920 sizeIFrameThrottled(triggerEvent, triggerEventDesc, customHeight, customWidth); 921 } else { 922 log('Trigger event cancelled: '+triggerEvent); 923 } 924 } 925 926 function lockTrigger(){ 927 if (!triggerLocked){ 928 triggerLocked = true; 929 log('Trigger event lock on'); 930 } 931 clearTimeout(triggerLockedTimer); 932 triggerLockedTimer = setTimeout(function(){ 933 triggerLocked = false; 934 log('Trigger event lock off'); 935 log('--'); 936 },eventCancelTimer); 937 } 938 939 function triggerReset(triggerEvent){ 940 height = getHeight[heightCalcMode](); 941 width = getWidth[widthCalcMode](); 942 943 sendMsg(height,width,triggerEvent); 944 } 945 946 function resetIFrame(triggerEventDesc){ 947 var hcm = heightCalcMode; 948 heightCalcMode = heightCalcModeDefault; 949 950 log('Reset trigger event: ' + triggerEventDesc); 951 lockTrigger(); 952 triggerReset('reset'); 953 954 heightCalcMode = hcm; 955 } 956 957 function sendMsg(height,width,triggerEvent,msg,targetOrigin){ 958 function setTargetOrigin(){ 959 if (undefined === targetOrigin){ 960 targetOrigin = targetOriginDefault; 961 } else { 962 log('Message targetOrigin: '+targetOrigin); 963 } 964 } 965 966 function sendToParent(){ 967 var 968 size = height + ':' + width, 969 message = myID + ':' + size + ':' + triggerEvent + (undefined !== msg ? ':' + msg : ''); 970 971 log('Sending message to host page (' + message + ')'); 972 target.postMessage( msgID + message, targetOrigin); 973 } 974 975 if(true === sendPermit){ 976 setTargetOrigin(); 977 sendToParent(); 978 } 979 } 980 981 function receiver(event) { 982 function isMessageForUs(){ 983 return msgID === (''+event.data).substr(0,msgIdLen); //''+ Protects against non-string messages 984 } 985 986 function initFromParent(){ 987 function fireInit(){ 988 initMsg = event.data; 989 target = event.source; 990 991 init(); 992 firstRun = false; 993 setTimeout(function(){ initLock = false;},eventCancelTimer); 994 } 995 996 if (document.body){ 997 fireInit(); 998 } else { 999 log('Waiting for page ready'); 1000 addEventListener(window,'readystatechange',initFromParent); 1001 } 1002 } 1003 1004 function resetFromParent(){ 1005 if (!initLock){ 1006 log('Page size reset by host page'); 1007 triggerReset('resetPage'); 1008 } else { 1009 log('Page reset ignored by init'); 1010 } 1011 } 1012 1013 function resizeFromParent(){ 1014 sendSize('resizeParent','Parent window requested size check'); 1015 } 1016 1017 function moveToAnchor(){ 1018 var anchor = getData(); 1019 inPageLinks.findTarget(anchor); 1020 } 1021 1022 function getMessageType(){ 1023 return event.data.split(']')[1].split(':')[0]; 1024 } 1025 1026 function getData(){ 1027 return event.data.substr(event.data.indexOf(':')+1); 1028 } 1029 1030 function isMiddleTier(){ 1031 return ('iFrameResize' in window); 1032 } 1033 1034 function messageFromParent(){ 1035 var msgBody = getData(); 1036 1037 log('MessageCallback called from parent: ' + msgBody ); 1038 messageCallback(JSON.parse(msgBody)); 1039 log(' --'); 1040 } 1041 1042 function pageInfoFromParent(){ 1043 var msgBody = getData(); 1044 log('PageInfoFromParent called from parent: ' + msgBody ); 1045 pageInfoCallback(JSON.parse(msgBody)); 1046 log(' --'); 1047 } 1048 1049 function isInitMsg(){ 1050 //Test if this message is from a child below us. This is an ugly test, however, updating 1051 //the message format would break backwards compatibity. 1052 return event.data.split(':')[2] in {'true':1,'false':1}; 1053 } 1054 1055 function callFromParent(){ 1056 switch (getMessageType()){ 1057 case 'reset': 1058 resetFromParent(); 1059 break; 1060 case 'resize': 1061 resizeFromParent(); 1062 break; 1063 case 'inPageLink': 1064 case 'moveToAnchor': 1065 moveToAnchor(); 1066 break; 1067 case 'message': 1068 messageFromParent(); 1069 break; 1070 case 'pageInfo': 1071 pageInfoFromParent(); 1072 break; 1073 default: 1074 if (!isMiddleTier() && !isInitMsg()){ 1075 warn('Unexpected message ('+event.data+')'); 1076 } 1077 } 1078 } 1079 1080 function processMessage(){ 1081 if (false === firstRun) { 1082 callFromParent(); 1083 } else if (isInitMsg()) { 1084 initFromParent(); 1085 } else { 1086 log('Ignored message of type "' + getMessageType() + '". Received before initialization.'); 1087 } 1088 } 1089 1090 if (isMessageForUs()){ 1091 processMessage(); 1092 } 1093 } 1094 1095 //Normally the parent kicks things off when it detects the iFrame has loaded. 1096 //If this script is async-loaded, then tell parent page to retry init. 1097 function chkLateLoaded(){ 1098 if('loading' !== document.readyState){ 1099 window.parent.postMessage('[iFrameResizerChild]Ready','*'); 1100 } 1101 } 1102 1103 addEventListener(window, 'message', receiver); 1104 chkLateLoaded(); 1105 1106 1107 1108 })(window || {});