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