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