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