iframeResizer.js (32229B)
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.js 7 * Desc: Force iframes to size to content. 8 * Requires: iframeResizer.contentWindow.js to be loaded into the target frame. 9 * Doc: https://github.com/davidjbradshaw/iframe-resizer 10 * Author: David J. Bradshaw - dave@bradshaw.net 11 * Contributor: Jure Mav - jure.mav@gmail.com 12 * Contributor: Reed Dadoune - reed@dadoune.com 13 */ 14 (function (undefined) { 15 'use strict'; 16 17 if (typeof window === 'undefined') return; // don't run for server side render 18 19 var count = 0, 20 logEnabled = false, 21 hiddenCheckEnabled = false, 22 msgHeader = 'message', 23 msgHeaderLen = msgHeader.length, 24 msgId = '[iFrameSizer]', 25 //Must match iframe msg ID 26 msgIdLen = msgId.length, 27 pagePosition = null, 28 requestAnimationFrame = window.requestAnimationFrame, 29 resetRequiredMethods = { 30 max: 1, 31 scroll: 1, 32 bodyScroll: 1, 33 documentElementScroll: 1 34 }, 35 settings = {}, 36 timer = null, 37 logId = 'Host Page', 38 defaults = { 39 autoResize: true, 40 bodyBackground: null, 41 bodyMargin: null, 42 bodyMarginV1: 8, 43 bodyPadding: null, 44 checkOrigin: true, 45 inPageLinks: false, 46 enablePublicMethods: true, 47 heightCalculationMethod: 'bodyOffset', 48 id: 'iFrameResizer', 49 interval: 32, 50 log: false, 51 maxHeight: Infinity, 52 maxWidth: Infinity, 53 minHeight: 0, 54 minWidth: 0, 55 resizeFrom: 'parent', 56 scrolling: false, 57 sizeHeight: true, 58 sizeWidth: false, 59 warningTimeout: 5000, 60 tolerance: 0, 61 widthCalculationMethod: 'scroll', 62 closedCallback: function closedCallback() {}, 63 initCallback: function initCallback() {}, 64 messageCallback: function messageCallback() { 65 warn('MessageCallback function not defined'); 66 }, 67 resizedCallback: function resizedCallback() {}, 68 scrollCallback: function scrollCallback() { 69 return true; 70 } 71 }; 72 73 function addEventListener(obj, evt, func) { 74 /* istanbul ignore else */ 75 // Not testable in PhantonJS 76 if ('addEventListener' in window) { 77 obj.addEventListener(evt, func, false); 78 } else if ('attachEvent' in window) { 79 //IE 80 obj.attachEvent('on' + evt, func); 81 } 82 } 83 84 function removeEventListener(el, evt, func) { 85 /* istanbul ignore else */ 86 // Not testable in phantonJS 87 if ('removeEventListener' in window) { 88 el.removeEventListener(evt, func, false); 89 } else if ('detachEvent' in window) { 90 //IE 91 el.detachEvent('on' + evt, func); 92 } 93 } 94 95 function setupRequestAnimationFrame() { 96 var vendors = ['moz', 'webkit', 'o', 'ms'], 97 x; // Remove vendor prefixing if prefixed and break early if not 98 99 for (x = 0; x < vendors.length && !requestAnimationFrame; x += 1) { 100 requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; 101 } 102 103 if (!requestAnimationFrame) { 104 log('setup', 'RequestAnimationFrame not supported'); 105 } 106 } 107 108 function getMyID(iframeId) { 109 var retStr = 'Host page: ' + iframeId; 110 111 if (window.top !== window.self) { 112 if (window.parentIFrame && window.parentIFrame.getId) { 113 retStr = window.parentIFrame.getId() + ': ' + iframeId; 114 } else { 115 retStr = 'Nested host page: ' + iframeId; 116 } 117 } 118 119 return retStr; 120 } 121 122 function formatLogHeader(iframeId) { 123 return msgId + '[' + getMyID(iframeId) + ']'; 124 } 125 126 function isLogEnabled(iframeId) { 127 return settings[iframeId] ? settings[iframeId].log : logEnabled; 128 } 129 130 function log(iframeId, msg) { 131 output('log', iframeId, msg, isLogEnabled(iframeId)); 132 } 133 134 function info(iframeId, msg) { 135 output('info', iframeId, msg, isLogEnabled(iframeId)); 136 } 137 138 function warn(iframeId, msg) { 139 output('warn', iframeId, msg, true); 140 } 141 142 function output(type, iframeId, msg, enabled) { 143 if (true === enabled && 'object' === _typeof(window.console)) { 144 console[type](formatLogHeader(iframeId), msg); 145 } 146 } 147 148 function iFrameListener(event) { 149 function resizeIFrame() { 150 function resize() { 151 setSize(messageData); 152 setPagePosition(iframeId); 153 callback('resizedCallback', messageData); 154 } 155 156 ensureInRange('Height'); 157 ensureInRange('Width'); 158 syncResize(resize, messageData, 'init'); 159 } 160 161 function processMsg() { 162 var data = msg.substr(msgIdLen).split(':'); 163 return { 164 iframe: settings[data[0]] && settings[data[0]].iframe, 165 id: data[0], 166 height: data[1], 167 width: data[2], 168 type: data[3] 169 }; 170 } 171 172 function ensureInRange(Dimension) { 173 var max = Number(settings[iframeId]['max' + Dimension]), 174 min = Number(settings[iframeId]['min' + Dimension]), 175 dimension = Dimension.toLowerCase(), 176 size = Number(messageData[dimension]); 177 log(iframeId, 'Checking ' + dimension + ' is in range ' + min + '-' + max); 178 179 if (size < min) { 180 size = min; 181 log(iframeId, 'Set ' + dimension + ' to min value'); 182 } 183 184 if (size > max) { 185 size = max; 186 log(iframeId, 'Set ' + dimension + ' to max value'); 187 } 188 189 messageData[dimension] = '' + size; 190 } 191 192 function isMessageFromIFrame() { 193 function checkAllowedOrigin() { 194 function checkList() { 195 var i = 0, 196 retCode = false; 197 log(iframeId, 'Checking connection is from allowed list of origins: ' + checkOrigin); 198 199 for (; i < checkOrigin.length; i++) { 200 if (checkOrigin[i] === origin) { 201 retCode = true; 202 break; 203 } 204 } 205 206 return retCode; 207 } 208 209 function checkSingle() { 210 var remoteHost = settings[iframeId] && settings[iframeId].remoteHost; 211 log(iframeId, 'Checking connection is from: ' + remoteHost); 212 return origin === remoteHost; 213 } 214 215 return checkOrigin.constructor === Array ? checkList() : checkSingle(); 216 } 217 218 var origin = event.origin, 219 checkOrigin = settings[iframeId] && settings[iframeId].checkOrigin; 220 221 if (checkOrigin && '' + origin !== 'null' && !checkAllowedOrigin()) { 222 throw new Error('Unexpected message received from: ' + origin + ' for ' + messageData.iframe.id + '. Message was: ' + event.data + '. This error can be disabled by setting the checkOrigin: false option or by providing of array of trusted domains.'); 223 } 224 225 return true; 226 } 227 228 function isMessageForUs() { 229 return msgId === ('' + msg).substr(0, msgIdLen) && msg.substr(msgIdLen).split(':')[0] in settings; //''+Protects against non-string msg 230 } 231 232 function isMessageFromMetaParent() { 233 //Test if this message is from a parent above us. This is an ugly test, however, updating 234 //the message format would break backwards compatibity. 235 var retCode = messageData.type in { 236 true: 1, 237 false: 1, 238 undefined: 1 239 }; 240 241 if (retCode) { 242 log(iframeId, 'Ignoring init message from meta parent page'); 243 } 244 245 return retCode; 246 } 247 248 function getMsgBody(offset) { 249 return msg.substr(msg.indexOf(':') + msgHeaderLen + offset); 250 } 251 252 function forwardMsgFromIFrame(msgBody) { 253 log(iframeId, 'MessageCallback passed: {iframe: ' + messageData.iframe.id + ', message: ' + msgBody + '}'); 254 callback('messageCallback', { 255 iframe: messageData.iframe, 256 message: JSON.parse(msgBody) 257 }); 258 log(iframeId, '--'); 259 } 260 261 function getPageInfo() { 262 var bodyPosition = document.body.getBoundingClientRect(), 263 iFramePosition = messageData.iframe.getBoundingClientRect(); 264 return JSON.stringify({ 265 iframeHeight: iFramePosition.height, 266 iframeWidth: iFramePosition.width, 267 clientHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0), 268 clientWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), 269 offsetTop: parseInt(iFramePosition.top - bodyPosition.top, 10), 270 offsetLeft: parseInt(iFramePosition.left - bodyPosition.left, 10), 271 scrollTop: window.pageYOffset, 272 scrollLeft: window.pageXOffset 273 }); 274 } 275 276 function sendPageInfoToIframe(iframe, iframeId) { 277 function debouncedTrigger() { 278 trigger('Send Page Info', 'pageInfo:' + getPageInfo(), iframe, iframeId); 279 } 280 281 debounceFrameEvents(debouncedTrigger, 32, iframeId); 282 } 283 284 function startPageInfoMonitor() { 285 function setListener(type, func) { 286 function sendPageInfo() { 287 if (settings[id]) { 288 sendPageInfoToIframe(settings[id].iframe, id); 289 } else { 290 stop(); 291 } 292 } 293 294 ['scroll', 'resize'].forEach(function (evt) { 295 log(id, type + evt + ' listener for sendPageInfo'); 296 func(window, evt, sendPageInfo); 297 }); 298 } 299 300 function stop() { 301 setListener('Remove ', removeEventListener); 302 } 303 304 function start() { 305 setListener('Add ', addEventListener); 306 } 307 308 var id = iframeId; //Create locally scoped copy of iFrame ID 309 310 start(); 311 312 if (settings[id]) { 313 settings[id].stopPageInfo = stop; 314 } 315 } 316 317 function stopPageInfoMonitor() { 318 if (settings[iframeId] && settings[iframeId].stopPageInfo) { 319 settings[iframeId].stopPageInfo(); 320 delete settings[iframeId].stopPageInfo; 321 } 322 } 323 324 function checkIFrameExists() { 325 var retBool = true; 326 327 if (null === messageData.iframe) { 328 warn(iframeId, 'IFrame (' + messageData.id + ') not found'); 329 retBool = false; 330 } 331 332 return retBool; 333 } 334 335 function getElementPosition(target) { 336 var iFramePosition = target.getBoundingClientRect(); 337 getPagePosition(iframeId); 338 return { 339 x: Math.floor(Number(iFramePosition.left) + Number(pagePosition.x)), 340 y: Math.floor(Number(iFramePosition.top) + Number(pagePosition.y)) 341 }; 342 } 343 344 function scrollRequestFromChild(addOffset) { 345 /* istanbul ignore next */ 346 //Not testable in Karma 347 function reposition() { 348 pagePosition = newPosition; 349 scrollTo(); 350 log(iframeId, '--'); 351 } 352 353 function calcOffset() { 354 return { 355 x: Number(messageData.width) + offset.x, 356 y: Number(messageData.height) + offset.y 357 }; 358 } 359 360 function scrollParent() { 361 if (window.parentIFrame) { 362 window.parentIFrame['scrollTo' + (addOffset ? 'Offset' : '')](newPosition.x, newPosition.y); 363 } else { 364 warn(iframeId, 'Unable to scroll to requested position, window.parentIFrame not found'); 365 } 366 } 367 368 var offset = addOffset ? getElementPosition(messageData.iframe) : { 369 x: 0, 370 y: 0 371 }, 372 newPosition = calcOffset(); 373 log(iframeId, 'Reposition requested from iFrame (offset x:' + offset.x + ' y:' + offset.y + ')'); 374 375 if (window.top !== window.self) { 376 scrollParent(); 377 } else { 378 reposition(); 379 } 380 } 381 382 function scrollTo() { 383 if (false !== callback('scrollCallback', pagePosition)) { 384 setPagePosition(iframeId); 385 } else { 386 unsetPagePosition(); 387 } 388 } 389 390 function findTarget(location) { 391 function jumpToTarget() { 392 var jumpPosition = getElementPosition(target); 393 log(iframeId, 'Moving to in page link (#' + hash + ') at x: ' + jumpPosition.x + ' y: ' + jumpPosition.y); 394 pagePosition = { 395 x: jumpPosition.x, 396 y: jumpPosition.y 397 }; 398 scrollTo(); 399 log(iframeId, '--'); 400 } 401 402 function jumpToParent() { 403 if (window.parentIFrame) { 404 window.parentIFrame.moveToAnchor(hash); 405 } else { 406 log(iframeId, 'In page link #' + hash + ' not found and window.parentIFrame not found'); 407 } 408 } 409 410 var hash = location.split('#')[1] || '', 411 hashData = decodeURIComponent(hash), 412 target = document.getElementById(hashData) || document.getElementsByName(hashData)[0]; 413 414 if (target) { 415 jumpToTarget(); 416 } else if (window.top !== window.self) { 417 jumpToParent(); 418 } else { 419 log(iframeId, 'In page link #' + hash + ' not found'); 420 } 421 } 422 423 function callback(funcName, val) { 424 return chkCallback(iframeId, funcName, val); 425 } 426 427 function actionMsg() { 428 if (settings[iframeId] && settings[iframeId].firstRun) firstRun(); 429 430 switch (messageData.type) { 431 case 'close': 432 if (settings[iframeId].closeRequestCallback) chkCallback(iframeId, 'closeRequestCallback', settings[iframeId].iframe);else closeIFrame(messageData.iframe); 433 break; 434 435 case 'message': 436 forwardMsgFromIFrame(getMsgBody(6)); 437 break; 438 439 case 'scrollTo': 440 scrollRequestFromChild(false); 441 break; 442 443 case 'scrollToOffset': 444 scrollRequestFromChild(true); 445 break; 446 447 case 'pageInfo': 448 sendPageInfoToIframe(settings[iframeId] && settings[iframeId].iframe, iframeId); 449 startPageInfoMonitor(); 450 break; 451 452 case 'pageInfoStop': 453 stopPageInfoMonitor(); 454 break; 455 456 case 'inPageLink': 457 findTarget(getMsgBody(9)); 458 break; 459 460 case 'reset': 461 resetIFrame(messageData); 462 break; 463 464 case 'init': 465 resizeIFrame(); 466 callback('initCallback', messageData.iframe); 467 break; 468 469 default: 470 resizeIFrame(); 471 } 472 } 473 474 function hasSettings(iframeId) { 475 var retBool = true; 476 477 if (!settings[iframeId]) { 478 retBool = false; 479 warn(messageData.type + ' No settings for ' + iframeId + '. Message was: ' + msg); 480 } 481 482 return retBool; 483 } 484 485 function iFrameReadyMsgReceived() { 486 for (var iframeId in settings) { 487 trigger('iFrame requested init', createOutgoingMsg(iframeId), document.getElementById(iframeId), iframeId); 488 } 489 } 490 491 function firstRun() { 492 if (settings[iframeId]) { 493 settings[iframeId].firstRun = false; 494 } 495 } 496 497 function clearWarningTimeout() { 498 if (settings[iframeId]) { 499 clearTimeout(settings[iframeId].msgTimeout); 500 settings[iframeId].warningTimeout = 0; 501 } 502 } 503 504 var msg = event.data, 505 messageData = {}, 506 iframeId = null; 507 508 if ('[iFrameResizerChild]Ready' === msg) { 509 iFrameReadyMsgReceived(); 510 } else if (isMessageForUs()) { 511 messageData = processMsg(); 512 iframeId = logId = messageData.id; 513 514 if (settings[iframeId]) { 515 settings[iframeId].loaded = true; 516 } 517 518 if (!isMessageFromMetaParent() && hasSettings(iframeId)) { 519 log(iframeId, 'Received: ' + msg); 520 521 if (checkIFrameExists() && isMessageFromIFrame()) { 522 actionMsg(); 523 } 524 } 525 } else { 526 info(iframeId, 'Ignored: ' + msg); 527 } 528 } 529 530 function chkCallback(iframeId, funcName, val) { 531 var func = null, 532 retVal = null; 533 534 if (settings[iframeId]) { 535 func = settings[iframeId][funcName]; 536 537 if ('function' === typeof func) { 538 retVal = func(val); 539 } else { 540 throw new TypeError(funcName + ' on iFrame[' + iframeId + '] is not a function'); 541 } 542 } 543 544 return retVal; 545 } 546 547 function removeIframeListeners(iframe) { 548 var iframeId = iframe.id; 549 delete settings[iframeId]; 550 } 551 552 function closeIFrame(iframe) { 553 var iframeId = iframe.id; 554 log(iframeId, 'Removing iFrame: ' + iframeId); 555 556 try { 557 // Catch race condition error with React 558 if (iframe.parentNode) { 559 iframe.parentNode.removeChild(iframe); 560 } 561 } catch (e) {} 562 563 chkCallback(iframeId, 'closedCallback', iframeId); 564 log(iframeId, '--'); 565 removeIframeListeners(iframe); 566 } 567 568 function getPagePosition(iframeId) { 569 if (null === pagePosition) { 570 pagePosition = { 571 x: window.pageXOffset !== undefined ? window.pageXOffset : document.documentElement.scrollLeft, 572 y: window.pageYOffset !== undefined ? window.pageYOffset : document.documentElement.scrollTop 573 }; 574 log(iframeId, 'Get page position: ' + pagePosition.x + ',' + pagePosition.y); 575 } 576 } 577 578 function setPagePosition(iframeId) { 579 if (null !== pagePosition) { 580 window.scrollTo(pagePosition.x, pagePosition.y); 581 log(iframeId, 'Set page position: ' + pagePosition.x + ',' + pagePosition.y); 582 unsetPagePosition(); 583 } 584 } 585 586 function unsetPagePosition() { 587 pagePosition = null; 588 } 589 590 function resetIFrame(messageData) { 591 function reset() { 592 setSize(messageData); 593 trigger('reset', 'reset', messageData.iframe, messageData.id); 594 } 595 596 log(messageData.id, 'Size reset requested by ' + ('init' === messageData.type ? 'host page' : 'iFrame')); 597 getPagePosition(messageData.id); 598 syncResize(reset, messageData, 'reset'); 599 } 600 601 function setSize(messageData) { 602 function setDimension(dimension) { 603 if (!messageData.id) { 604 log('undefined', 'messageData id not set'); 605 return; 606 } 607 608 messageData.iframe.style[dimension] = messageData[dimension] + 'px'; 609 log(messageData.id, 'IFrame (' + iframeId + ') ' + dimension + ' set to ' + messageData[dimension] + 'px'); 610 } 611 612 function chkZero(dimension) { 613 //FireFox sets dimension of hidden iFrames to zero. 614 //So if we detect that set up an event to check for 615 //when iFrame becomes visible. 616 617 /* istanbul ignore next */ 618 //Not testable in PhantomJS 619 if (!hiddenCheckEnabled && '0' === messageData[dimension]) { 620 hiddenCheckEnabled = true; 621 log(iframeId, 'Hidden iFrame detected, creating visibility listener'); 622 fixHiddenIFrames(); 623 } 624 } 625 626 function processDimension(dimension) { 627 setDimension(dimension); 628 chkZero(dimension); 629 } 630 631 var iframeId = messageData.iframe.id; 632 633 if (settings[iframeId]) { 634 if (settings[iframeId].sizeHeight) { 635 processDimension('height'); 636 } 637 638 if (settings[iframeId].sizeWidth) { 639 processDimension('width'); 640 } 641 } 642 } 643 644 function syncResize(func, messageData, doNotSync) { 645 /* istanbul ignore if */ 646 //Not testable in PhantomJS 647 if (doNotSync !== messageData.type && requestAnimationFrame) { 648 log(messageData.id, 'Requesting animation frame'); 649 requestAnimationFrame(func); 650 } else { 651 func(); 652 } 653 } 654 655 function trigger(calleeMsg, msg, iframe, id, noResponseWarning) { 656 function postMessageToIFrame() { 657 var target = settings[id] && settings[id].targetOrigin; 658 log(id, '[' + calleeMsg + '] Sending msg to iframe[' + id + '] (' + msg + ') targetOrigin: ' + target); 659 iframe.contentWindow.postMessage(msgId + msg, target); 660 } 661 662 function iFrameNotFound() { 663 warn(id, '[' + calleeMsg + '] IFrame(' + id + ') not found'); 664 } 665 666 function chkAndSend() { 667 if (iframe && 'contentWindow' in iframe && null !== iframe.contentWindow) { 668 //Null test for PhantomJS 669 postMessageToIFrame(); 670 } else { 671 iFrameNotFound(); 672 } 673 } 674 675 function warnOnNoResponse() { 676 function warning() { 677 if (settings[id] && !settings[id].loaded && !errorShown) { 678 errorShown = true; 679 warn(id, 'IFrame has not responded within ' + settings[id].warningTimeout / 1000 + ' seconds. Check iFrameResizer.contentWindow.js has been loaded in iFrame. This message can be ignored if everything is working, or you can set the warningTimeout option to a higher value or zero to suppress this warning.'); 680 } 681 } 682 683 if (!!noResponseWarning && settings[id] && !!settings[id].warningTimeout) { 684 settings[id].msgTimeout = setTimeout(warning, settings[id].warningTimeout); 685 } 686 } 687 688 var errorShown = false; 689 id = id || iframe.id; 690 691 if (settings[id]) { 692 chkAndSend(); 693 warnOnNoResponse(); 694 } 695 } 696 697 function createOutgoingMsg(iframeId) { 698 return iframeId + ':' + settings[iframeId].bodyMarginV1 + ':' + settings[iframeId].sizeWidth + ':' + settings[iframeId].log + ':' + settings[iframeId].interval + ':' + settings[iframeId].enablePublicMethods + ':' + settings[iframeId].autoResize + ':' + settings[iframeId].bodyMargin + ':' + settings[iframeId].heightCalculationMethod + ':' + settings[iframeId].bodyBackground + ':' + settings[iframeId].bodyPadding + ':' + settings[iframeId].tolerance + ':' + settings[iframeId].inPageLinks + ':' + settings[iframeId].resizeFrom + ':' + settings[iframeId].widthCalculationMethod; 699 } 700 701 function setupIFrame(iframe, options) { 702 function setLimits() { 703 function addStyle(style) { 704 if (Infinity !== settings[iframeId][style] && 0 !== settings[iframeId][style]) { 705 iframe.style[style] = settings[iframeId][style] + 'px'; 706 log(iframeId, 'Set ' + style + ' = ' + settings[iframeId][style] + 'px'); 707 } 708 } 709 710 function chkMinMax(dimension) { 711 if (settings[iframeId]['min' + dimension] > settings[iframeId]['max' + dimension]) { 712 throw new Error('Value for min' + dimension + ' can not be greater than max' + dimension); 713 } 714 } 715 716 chkMinMax('Height'); 717 chkMinMax('Width'); 718 addStyle('maxHeight'); 719 addStyle('minHeight'); 720 addStyle('maxWidth'); 721 addStyle('minWidth'); 722 } 723 724 function newId() { 725 var id = options && options.id || defaults.id + count++; 726 727 if (null !== document.getElementById(id)) { 728 id = id + count++; 729 } 730 731 return id; 732 } 733 734 function ensureHasId(iframeId) { 735 logId = iframeId; 736 737 if ('' === iframeId) { 738 iframe.id = iframeId = newId(); 739 logEnabled = (options || {}).log; 740 logId = iframeId; 741 log(iframeId, 'Added missing iframe ID: ' + iframeId + ' (' + iframe.src + ')'); 742 } 743 744 return iframeId; 745 } 746 747 function setScrolling() { 748 log(iframeId, 'IFrame scrolling ' + (settings[iframeId] && settings[iframeId].scrolling ? 'enabled' : 'disabled') + ' for ' + iframeId); 749 iframe.style.overflow = false === (settings[iframeId] && settings[iframeId].scrolling) ? 'hidden' : 'auto'; 750 751 switch (settings[iframeId] && settings[iframeId].scrolling) { 752 case 'omit': 753 break; 754 755 case true: 756 iframe.scrolling = 'yes'; 757 break; 758 759 case false: 760 iframe.scrolling = 'no'; 761 break; 762 763 default: 764 iframe.scrolling = settings[iframeId] ? settings[iframeId].scrolling : 'no'; 765 } 766 } //The V1 iFrame script expects an int, where as in V2 expects a CSS 767 //string value such as '1px 3em', so if we have an int for V2, set V1=V2 768 //and then convert V2 to a string PX value. 769 770 771 function setupBodyMarginValues() { 772 if ('number' === typeof (settings[iframeId] && settings[iframeId].bodyMargin) || '0' === (settings[iframeId] && settings[iframeId].bodyMargin)) { 773 settings[iframeId].bodyMarginV1 = settings[iframeId].bodyMargin; 774 settings[iframeId].bodyMargin = '' + settings[iframeId].bodyMargin + 'px'; 775 } 776 } 777 778 function checkReset() { 779 // Reduce scope of firstRun to function, because IE8's JS execution 780 // context stack is borked and this value gets externally 781 // changed midway through running this function!!! 782 var firstRun = settings[iframeId] && settings[iframeId].firstRun, 783 resetRequertMethod = settings[iframeId] && settings[iframeId].heightCalculationMethod in resetRequiredMethods; 784 785 if (!firstRun && resetRequertMethod) { 786 resetIFrame({ 787 iframe: iframe, 788 height: 0, 789 width: 0, 790 type: 'init' 791 }); 792 } 793 } 794 795 function setupIFrameObject() { 796 if (Function.prototype.bind && settings[iframeId]) { 797 //Ignore unpolyfilled IE8. 798 settings[iframeId].iframe.iFrameResizer = { 799 close: closeIFrame.bind(null, settings[iframeId].iframe), 800 removeListeners: removeIframeListeners.bind(null, settings[iframeId].iframe), 801 resize: trigger.bind(null, 'Window resize', 'resize', settings[iframeId].iframe), 802 moveToAnchor: function moveToAnchor(anchor) { 803 trigger('Move to anchor', 'moveToAnchor:' + anchor, settings[iframeId].iframe, iframeId); 804 }, 805 sendMessage: function sendMessage(message) { 806 message = JSON.stringify(message); 807 trigger('Send Message', 'message:' + message, settings[iframeId].iframe, iframeId); 808 } 809 }; 810 } 811 } //We have to call trigger twice, as we can not be sure if all 812 //iframes have completed loading when this code runs. The 813 //event listener also catches the page changing in the iFrame. 814 815 816 function init(msg) { 817 function iFrameLoaded() { 818 trigger('iFrame.onload', msg, iframe, undefined, true); 819 checkReset(); 820 } 821 822 addEventListener(iframe, 'load', iFrameLoaded); 823 trigger('init', msg, iframe, undefined, true); 824 } 825 826 function checkOptions(options) { 827 if ('object' !== _typeof(options)) { 828 throw new TypeError('Options is not an object'); 829 } 830 } 831 832 function copyOptions(options) { 833 for (var option in defaults) { 834 if (defaults.hasOwnProperty(option)) { 835 settings[iframeId][option] = options.hasOwnProperty(option) ? options[option] : defaults[option]; 836 } 837 } 838 } 839 840 function getTargetOrigin(remoteHost) { 841 return '' === remoteHost || 'file://' === remoteHost ? '*' : remoteHost; 842 } 843 844 function processOptions(options) { 845 options = options || {}; 846 settings[iframeId] = { 847 firstRun: true, 848 iframe: iframe, 849 remoteHost: iframe.src.split('/').slice(0, 3).join('/') 850 }; 851 checkOptions(options); 852 copyOptions(options); 853 854 if (settings[iframeId]) { 855 settings[iframeId].targetOrigin = true === settings[iframeId].checkOrigin ? getTargetOrigin(settings[iframeId].remoteHost) : '*'; 856 } 857 } 858 859 function beenHere() { 860 return iframeId in settings && 'iFrameResizer' in iframe; 861 } 862 863 var iframeId = ensureHasId(iframe.id); 864 865 if (!beenHere()) { 866 processOptions(options); 867 setScrolling(); 868 setLimits(); 869 setupBodyMarginValues(); 870 init(createOutgoingMsg(iframeId)); 871 setupIFrameObject(); 872 } else { 873 warn(iframeId, 'Ignored iFrame, already setup.'); 874 } 875 } 876 877 function debouce(fn, time) { 878 if (null === timer) { 879 timer = setTimeout(function () { 880 timer = null; 881 fn(); 882 }, time); 883 } 884 } 885 886 var frameTimer = {}; 887 888 function debounceFrameEvents(fn, time, frameId) { 889 if (!frameTimer[frameId]) { 890 frameTimer[frameId] = setTimeout(function () { 891 frameTimer[frameId] = null; 892 fn(); 893 }, time); 894 } 895 } //Not testable in PhantomJS 896 897 /* istanbul ignore next */ 898 899 900 function fixHiddenIFrames() { 901 function checkIFrames() { 902 function checkIFrame(settingId) { 903 function chkDimension(dimension) { 904 return '0px' === (settings[settingId] && settings[settingId].iframe.style[dimension]); 905 } 906 907 function isVisible(el) { 908 return null !== el.offsetParent; 909 } 910 911 if (settings[settingId] && isVisible(settings[settingId].iframe) && (chkDimension('height') || chkDimension('width'))) { 912 trigger('Visibility change', 'resize', settings[settingId].iframe, settingId); 913 } 914 } 915 916 for (var settingId in settings) { 917 checkIFrame(settingId); 918 } 919 } 920 921 function mutationObserved(mutations) { 922 log('window', 'Mutation observed: ' + mutations[0].target + ' ' + mutations[0].type); 923 debouce(checkIFrames, 16); 924 } 925 926 function createMutationObserver() { 927 var target = document.querySelector('body'), 928 config = { 929 attributes: true, 930 attributeOldValue: false, 931 characterData: true, 932 characterDataOldValue: false, 933 childList: true, 934 subtree: true 935 }, 936 observer = new MutationObserver(mutationObserved); 937 observer.observe(target, config); 938 } 939 940 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; 941 if (MutationObserver) createMutationObserver(); 942 } 943 944 function resizeIFrames(event) { 945 function resize() { 946 sendTriggerMsg('Window ' + event, 'resize'); 947 } 948 949 log('window', 'Trigger event: ' + event); 950 debouce(resize, 16); 951 } //Not testable in PhantomJS 952 953 /* istanbul ignore next */ 954 955 956 function tabVisible() { 957 function resize() { 958 sendTriggerMsg('Tab Visable', 'resize'); 959 } 960 961 if ('hidden' !== document.visibilityState) { 962 log('document', 'Trigger event: Visiblity change'); 963 debouce(resize, 16); 964 } 965 } 966 967 function sendTriggerMsg(eventName, event) { 968 function isIFrameResizeEnabled(iframeId) { 969 return settings[iframeId] && 'parent' === settings[iframeId].resizeFrom && settings[iframeId].autoResize && !settings[iframeId].firstRun; 970 } 971 972 for (var iframeId in settings) { 973 if (isIFrameResizeEnabled(iframeId)) { 974 trigger(eventName, event, document.getElementById(iframeId), iframeId); 975 } 976 } 977 } 978 979 function setupEventListeners() { 980 addEventListener(window, 'message', iFrameListener); 981 addEventListener(window, 'resize', function () { 982 resizeIFrames('resize'); 983 }); 984 addEventListener(document, 'visibilitychange', tabVisible); 985 addEventListener(document, '-webkit-visibilitychange', tabVisible); //Andriod 4.4 986 987 addEventListener(window, 'focusin', function () { 988 resizeIFrames('focus'); 989 }); //IE8-9 990 991 addEventListener(window, 'focus', function () { 992 resizeIFrames('focus'); 993 }); 994 } 995 996 function factory() { 997 function init(options, element) { 998 function chkType() { 999 if (!element.tagName) { 1000 throw new TypeError('Object is not a valid DOM element'); 1001 } else if ('IFRAME' !== element.tagName.toUpperCase()) { 1002 throw new TypeError('Expected <IFRAME> tag, found <' + element.tagName + '>'); 1003 } 1004 } 1005 1006 if (element) { 1007 chkType(); 1008 setupIFrame(element, options); 1009 iFrames.push(element); 1010 } 1011 } 1012 1013 function warnDeprecatedOptions(options) { 1014 if (options && options.enablePublicMethods) { 1015 warn('enablePublicMethods option has been removed, public methods are now always available in the iFrame'); 1016 } 1017 } 1018 1019 var iFrames; 1020 setupRequestAnimationFrame(); 1021 setupEventListeners(); 1022 return function iFrameResizeF(options, target) { 1023 iFrames = []; //Only return iFrames past in on this call 1024 1025 warnDeprecatedOptions(options); 1026 1027 switch (_typeof(target)) { 1028 case 'undefined': 1029 case 'string': 1030 Array.prototype.forEach.call(document.querySelectorAll(target || 'iframe'), init.bind(undefined, options)); 1031 break; 1032 1033 case 'object': 1034 init(options, target); 1035 break; 1036 1037 default: 1038 throw new TypeError('Unexpected data type (' + _typeof(target) + ')'); 1039 } 1040 1041 return iFrames; 1042 }; 1043 } 1044 1045 function createJQueryPublicMethod($) { 1046 if (!$.fn) { 1047 info('', 'Unable to bind to jQuery, it is not fully loaded.'); 1048 } else if (!$.fn.iFrameResize) { 1049 $.fn.iFrameResize = function $iFrameResizeF(options) { 1050 function init(index, element) { 1051 setupIFrame(element, options); 1052 } 1053 1054 return this.filter('iframe').each(init).end(); 1055 }; 1056 } 1057 } 1058 1059 if (window.jQuery) { 1060 createJQueryPublicMethod(window.jQuery); 1061 } 1062 1063 if (typeof define === 'function' && define.amd) { 1064 define([], factory); 1065 } else if ((typeof module === "undefined" ? "undefined" : _typeof(module)) === 'object' && _typeof(module.exports) === 'object') { 1066 //Node for browserfy 1067 module.exports = factory(); 1068 } else { 1069 window.iFrameResize = window.iFrameResize || factory(); 1070 } 1071 })();