balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

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 })();