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 - [email protected] 7 * Contributor: Jure Mav - [email protected] 8 * Contributor: Reed Dadoune - [email protected] 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 clientHeight: Math.max(document.documentElement.clientHeight, window.innerHeight || 0), 260 clientWidth: Math.max(document.documentElement.clientWidth, window.innerWidth || 0), 261 offsetTop: parseInt(iFramePosition.top - bodyPosition.top, 10), 262 offsetLeft: parseInt(iFramePosition.left - bodyPosition.left, 10), 263 scrollTop: window.pageYOffset, 264 scrollLeft: window.pageXOffset 265 }); 266 } 267 268 function sendPageInfoToIframe(iframe,iframeId){ 269 function debouncedTrigger(){ 270 trigger( 271 'Send Page Info', 272 'pageInfo:' + getPageInfo(), 273 iframe, 274 iframeId 275 ); 276 } 277 278 debouce(debouncedTrigger,32); 279 } 280 281 282 function startPageInfoMonitor(){ 283 function setListener(type,func){ 284 function sendPageInfo(){ 285 if (settings[id]){ 286 sendPageInfoToIframe(settings[id].iframe,id); 287 } else { 288 stop(); 289 } 290 } 291 292 ['scroll','resize'].forEach(function(evt){ 293 log(id, type + evt + ' listener for sendPageInfo'); 294 func(window,evt,sendPageInfo); 295 }); 296 } 297 298 function stop(){ 299 setListener('Remove ', removeEventListener); 300 } 301 302 function start(){ 303 setListener('Add ', addEventListener); 304 } 305 306 var id = iframeId; //Create locally scoped copy of iFrame ID 307 308 start(); 309 310 settings[id].stopPageInfo = stop; 311 } 312 313 function stopPageInfoMonitor(){ 314 if (settings[iframeId] && settings[iframeId].stopPageInfo){ 315 settings[iframeId].stopPageInfo(); 316 delete settings[iframeId].stopPageInfo; 317 } 318 } 319 320 function checkIFrameExists(){ 321 var retBool = true; 322 323 if (null === messageData.iframe) { 324 warn(iframeId,'IFrame ('+messageData.id+') not found'); 325 retBool = false; 326 } 327 return retBool; 328 } 329 330 function getElementPosition(target){ 331 var iFramePosition = target.getBoundingClientRect(); 332 333 getPagePosition(iframeId); 334 335 return { 336 x: Math.floor( Number(iFramePosition.left) + Number(pagePosition.x) ), 337 y: Math.floor( Number(iFramePosition.top) + Number(pagePosition.y) ) 338 }; 339 } 340 341 function scrollRequestFromChild(addOffset){ 342 /* istanbul ignore next */ //Not testable in Karma 343 function reposition(){ 344 pagePosition = newPosition; 345 scrollTo();
346 log(iframeId,'--'); 347 } 348 349 function calcOffset(){ 350 return { 351 x: Number(messageData.width) + offset.x, 352 y: Number(messageData.height) + offset.y 353 }; 354 } 355 356 function scrollParent(){ 357 if (window.parentIFrame){ 358 window.parentIFrame['scrollTo'+(addOffset?'Offset':'')](newPosition.x,newPosition.y); 359 } else { 360 warn(iframeId,'Unable to scroll to requested position, window.parentIFrame not found'); 361 } 362 } 363 364 var 365 offset = addOffset ? getElementPosition(messageData.iframe) : {x:0,y:0}, 366 newPosition = calcOffset(); 367 368 log(iframeId,'Reposition requested from iFrame (offset x:'+offset.x+' y:'+offset.y+')'); 369 370 if(window.top!==window.self){ 371 scrollParent(); 372 } else { 373 reposition(); 374 } 375 } 376 377 function scrollTo(){ 378 if (false !== callback('scrollCallback',pagePosition)){ 379 setPagePosition(iframeId); 380 } else { 381 unsetPagePosition(); 382 } 383 } 384 385 function findTarget(location){ 386 function jumpToTarget(){ 387 var jumpPosition = getElementPosition(target); 388 389 log(iframeId,'Moving to in page link (#'+hash+') at x: '+jumpPosition.x+' y: '+jumpPosition.y); 390 pagePosition = { 391 x: jumpPosition.x, 392 y: jumpPosition.y 393 }; 394 395 scrollTo(); 396 log(iframeId,'--'); 397 } 398 399 function jumpToParent(){ 400 if (window.parentIFrame){ 401 window.parentIFrame.moveToAnchor(hash); 402 } else { 403 log(iframeId,'In page link #'+hash+' not found and window.parentIFrame not found'); 404 } 405 } 406 407 var 408 hash = location.split('#')[1] || '', 409 hashData = decodeURIComponent(hash), 410 target = document.getElementById(hashData) || document.getElementsByName(hashData)[0]; 411 412 if (target){ 413 jumpToTarget(); 414 } else if(window.top!==window.self){ 415 jumpToParent(); 416 } else { 417 log(iframeId,'In page link #'+hash+' not found'); 418 } 419 } 420 421 function callback(funcName,val){ 422 return chkCallback(iframeId,funcName,val); 423 } 424 425 function actionMsg(){ 426 427 if(settings[iframeId].firstRun) firstRun(); 428 429 switch(messageData.type){ 430 case 'close': 431 closeIFrame(messageData.iframe); 432 break; 433 case 'message': 434 forwardMsgFromIFrame(getMsgBody(6)); 435 break; 436 case 'scrollTo': 437 scrollRequestFromChild(false); 438 break; 439 case 'scrollToOffset': 440 scrollRequestFromChild(true); 441 break; 442 case 'pageInfo': 443 sendPageInfoToIframe(settings[iframeId].iframe,iframeId); 444 startPageInfoMonitor(); 445 break; 446 case 'pageInfoStop': 447 stopPageInfoMonitor(); 448 break; 449 case 'inPageLink': 450 findTarget(getMsgBody(9)); 451 break; 452 case 'reset': 453 resetIFrame(messageData); 454 break; 455 case 'init': 456 resizeIFrame(); 457 callback('initCallback',messageData.iframe); 458 callback('resizedCallback',messageData); 459 break; 460 default: 461 resizeIFrame(); 462 callback('resizedCallback',messageData); 463 } 464 } 465 466 function hasSettings(iframeId){ 467 var retBool = true; 468 469 if (!settings[iframeId]){ 470 retBool = false; 471 warn(messageData.type + ' No settings for ' + iframeId + '. Message was: ' + msg); 472 } 473 474 return retBool; 475 } 476 477 function iFrameReadyMsgReceived(){ 478 for (var iframeId in settings){ 479 trigger('iFrame requested init',createOutgoingMsg(iframeId),document.getElementById(iframeId),iframeId); 480 } 481 } 482 483 function firstRun() { 484 settings[iframeId].firstRun = false; 485 } 486 487 var 488 msg = event.data, 489 messageData = {}, 490 iframeId = null; 491 492 if('[iFrameResizerChild]Ready' === msg){ 493 iFrameReadyMsgReceived(); 494 } else if (isMessageForUs()){ 495 messageData = processMsg(); 496 iframeId = logId = messageData.id; 497 498 if (!isMessageFromMetaParent() && hasSettings(iframeId)){ 499 log(iframeId,'Received: '+msg); 500 501 if ( checkIFrameExists() && isMessageFromIFrame() ){ 502 actionMsg(); 503 } 504 } 505 } else { 506 info(iframeId,'Ignored: '+msg); 507 } 508 509 } 510 511 512 function chkCallback(iframeId,funcName,val){ 513 var 514 func = null, 515 retVal = null; 516 517 if(settings[iframeId]){ 518 func = settings[iframeId][funcName]; 519 520 if( 'function' === typeof func){ 521 retVal = func(val); 522 } else { 523 throw new TypeError(funcName+' on iFrame['+iframeId+'] is not a function'); 524 } 525 } 526 527 return retVal; 528 } 529 530 function closeIFrame(iframe){ 531 var iframeId = iframe.id; 532 533 log(iframeId,'Removing iFrame: '+iframeId); 534 iframe.parentNode.removeChild(iframe); 535 chkCallback(iframeId,'closedCallback',iframeId);
536 log(iframeId,'--'); 537 delete settings[iframeId]; 538 } 539 540 function getPagePosition(iframeId){ 541 if(null === pagePosition){ 542 pagePosition = { 543 x: (window.pageXOffset !== undefined) ? window.pageXOffset : document.documentElement.scrollLeft, 544 y: (window.pageYOffset !== undefined) ? window.pageYOffset : document.documentElement.scrollTop 545 }; 546 log(iframeId,'Get page position: '+pagePosition.x+','+pagePosition.y); 547 } 548 } 549 550 function setPagePosition(iframeId){ 551 if(null !== pagePosition){ 552 window.scrollTo(pagePosition.x,pagePosition.y); 553 log(iframeId,'Set page position: '+pagePosition.x+','+pagePosition.y); 554 unsetPagePosition(); 555 } 556 } 557 558 function unsetPagePosition(){ 559 pagePosition = null; 560 } 561 562 function resetIFrame(messageData){ 563 function reset(){ 564 setSize(messageData); 565 trigger('reset','reset',messageData.iframe,messageData.id); 566 } 567 568 log(messageData.id,'Size reset requested by '+('init'===messageData.type?'host page':'iFrame')); 569 getPagePosition(messageData.id); 570 syncResize(reset,messageData,'reset'); 571 } 572 573 function setSize(messageData){ 574 function setDimension(dimension){ 575 messageData.iframe.style[dimension] = messageData[dimension] + 'px'; 576 log( 577 messageData.id, 578 'IFrame (' + iframeId + 579 ') ' + dimension + 580 ' set to ' + messageData[dimension] + 'px' 581 ); 582 } 583 584 function chkZero(dimension){ 585 //FireFox sets dimension of hidden iFrames to zero. 586 //So if we detect that set up an event to check for 587 //when iFrame becomes visible. 588 589 /* istanbul ignore next */ //Not testable in PhantomJS 590 if (!hiddenCheckEnabled && '0' === messageData[dimension]){ 591 hiddenCheckEnabled = true; 592 log(iframeId,'Hidden iFrame detected, creating visibility listener'); 593 fixHiddenIFrames(); 594 } 595 } 596 597 function processDimension(dimension){ 598 setDimension(dimension); 599 chkZero(dimension); 600 } 601 602 var iframeId = messageData.iframe.id; 603 604 if(settings[iframeId]){ 605 if( settings[iframeId].sizeHeight) { processDimension('height'); } 606 if( settings[iframeId].sizeWidth ) { processDimension('width'); } 607 } 608 } 609 610 function syncResize(func,messageData,doNotSync){ 611 /* istanbul ignore if */ //Not testable in PhantomJS 612 if(doNotSync!==messageData.type && requestAnimationFrame){ 613 log(messageData.id,'Requesting animation frame'); 614 requestAnimationFrame(func); 615 } else { 616 func(); 617 } 618 } 619 620 function trigger(calleeMsg,msg,iframe,id){ 621 function postMessageToIFrame(){ 622 var target = settings[id].targetOrigin; 623 log(id,'[' + calleeMsg + '] Sending msg to iframe['+id+'] ('+msg+') targetOrigin: '+target); 624 iframe.contentWindow.postMessage( msgId + msg, target ); 625 } 626 627 function iFrameNotFound(){ 628 info(id,'[' + calleeMsg + '] IFrame('+id+') not found'); 629 if(settings[id]) { 630 delete settings[id]; 631 } 632 } 633 634 function chkAndSend(){ 635 if(iframe && 'contentWindow' in iframe && (null !== iframe.contentWindow)){ //Null test for PhantomJS 636 postMessageToIFrame(); 637 } else { 638 iFrameNotFound(); 639 } 640 } 641 642 id = id || iframe.id; 643 644 if(settings[id]) { 645 chkAndSend(); 646 } 647 648 } 649 650 function createOutgoingMsg(iframeId){ 651 return iframeId + 652 ':' + settings[iframeId].bodyMarginV1 + 653 ':' + settings[iframeId].sizeWidth + 654 ':' + settings[iframeId].log + 655 ':' + settings[iframeId].interval + 656 ':' + settings[iframeId].enablePublicMethods + 657 ':' + settings[iframeId].autoResize + 658 ':' + settings[iframeId].bodyMargin + 659 ':' + settings[iframeId].heightCalculationMethod + 660 ':' + settings[iframeId].bodyBackground + 661 ':' + settings[iframeId].bodyPadding + 662 ':' + settings[iframeId].tolerance + 663 ':' + settings[iframeId].inPageLinks + 664 ':' + settings[iframeId].resizeFrom + 665 ':' + settings[iframeId].widthCalculationMethod; 666 } 667 668 function setupIFrame(iframe,options){ 669 function setLimits(){ 670 function addStyle(style){ 671 if ((Infinity !== settings[iframeId][style]) && (0 !== settings[iframeId][style])){ 672 iframe.style[style] = settings[iframeId][style] + 'px'; 673 log(iframeId,'Set '+style+' = '+settings[iframeId][style]+'px'); 674 } 675 } 676 677 function chkMinMax(dimension){ 678 if (settings[iframeId]['min'+dimension]>settings[iframeId]['max'+dimension]){ 679 throw new Error('Value for min'+dimension+' can not be greater than max'+dimension); 680 } 681 } 682 683 chkMinMax('Height'); 684 chkMinMax('Width'); 685 686 addStyle('maxHeight'); 687 addStyle('minHeight'); 688 addStyle('maxWidth'); 689 addStyle('minWidth'); 690 } 691 692 function newId(){ 693 var id = ((options && options.id) || defaults.id + count++); 694 if (null!==document.getElementById(id)){ 695 id = id + count++; 696 } 697 return id; 698 } 699 700 function ensureHasId(iframeId){ 701 logId=iframeId; 702 if (''===iframeId){ 703 iframe.id = iframeId = newId(); 704 logEnabled = (options || {}).log; 705 logId=iframeId; 706 log(iframeId,'Added missing iframe ID: '+ iframeId +' (' + iframe.
706src + ')'); 707 } 708 709 710 return iframeId; 711 } 712 713 function setScrolling(){ 714 log(iframeId,'IFrame scrolling ' + (settings[iframeId].scrolling ? 'enabled' : 'disabled') + ' for ' + iframeId); 715 iframe.style.overflow = false === settings[iframeId].scrolling ? 'hidden' : 'auto'; 716 iframe.scrolling = false === settings[iframeId].scrolling ? 'no' : 'yes'; 717 } 718 719 //The V1 iFrame script expects an int, where as in V2 expects a CSS 720 //string value such as '1px 3em', so if we have an int for V2, set V1=V2 721 //and then convert V2 to a string PX value. 722 function setupBodyMarginValues(){ 723 if (('number'===typeof(settings[iframeId].bodyMargin)) || ('0'===settings[iframeId].bodyMargin)){ 724 settings[iframeId].bodyMarginV1 = settings[iframeId].bodyMargin; 725 settings[iframeId].bodyMargin = '' + settings[iframeId].bodyMargin + 'px'; 726 } 727 } 728 729 function checkReset(){ 730 // Reduce scope of firstRun to function, because IE8's JS execution 731 // context stack is borked and this value gets externally 732 // changed midway through running this function!!! 733 var 734 firstRun = settings[iframeId].firstRun, 735 resetRequertMethod = settings[iframeId].heightCalculationMethod in resetRequiredMethods; 736 737 if (!firstRun && resetRequertMethod){ 738 resetIFrame({iframe:iframe, height:0, width:0, type:'init'}); 739 } 740 } 741 742 function setupIFrameObject(){ 743 if(Function.prototype.bind){ //Ignore unpolyfilled IE8. 744 settings[iframeId].iframe.iFrameResizer = { 745 746 close : closeIFrame.bind(null,settings[iframeId].iframe), 747 748 resize : trigger.bind(null,'Window resize', 'resize', settings[iframeId].iframe), 749 750 moveToAnchor : function(anchor){ 751 trigger('Move to anchor','inPageLink:'+anchor, settings[iframeId].iframe,iframeId); 752 }, 753 754 sendMessage : function(message){ 755 message = JSON.stringify(message); 756 trigger('Send Message','message:'+message, settings[iframeId].iframe,iframeId); 757 } 758 }; 759 } 760 } 761 762 //We have to call trigger twice, as we can not be sure if all 763 //iframes have completed loading when this code runs. The 764 //event listener also catches the page changing in the iFrame. 765 function init(msg){ 766 function iFrameLoaded(){ 767 trigger('iFrame.onload',msg,iframe); 768 checkReset(); 769 } 770 771 addEventListener(iframe,'load',iFrameLoaded); 772 trigger('init',msg,iframe); 773 } 774 775 function checkOptions(options){ 776 if ('object' !== typeof options){ 777 throw new TypeError('Options is not an object'); 778 } 779 } 780 781 function copyOptions(options){ 782 for (var option in defaults) { 783 if (defaults.hasOwnProperty(option)){ 784 settings[iframeId][option] = options.hasOwnProperty(option) ? options[option] : defaults[option]; 785 } 786 } 787 } 788 789 function getTargetOrigin (remoteHost){ 790 return ('' === remoteHost || 'file://' === remoteHost) ? '*' : remoteHost; 791 } 792 793 function processOptions(options){ 794 options = options || {}; 795 settings[iframeId] = { 796 firstRun : true, 797 iframe : iframe, 798 remoteHost : iframe.src.split('/').slice(0,3).join('/') 799 }; 800 801 checkOptions(options); 802 copyOptions(options); 803 804 settings[iframeId].targetOrigin = true === settings[iframeId].checkOrigin ? getTargetOrigin(settings[iframeId].remoteHost) : '*'; 805 } 806 807 function beenHere(){ 808 return (iframeId in settings && 'iFrameResizer' in iframe); 809 } 810 811 var iframeId = ensureHasId(iframe.id); 812 813 if (!beenHere()){ 814 processOptions(options); 815 setScrolling(); 816 setLimits(); 817 setupBodyMarginValues(); 818 init(createOutgoingMsg(iframeId)); 819 setupIFrameObject(); 820 } else { 821 warn(iframeId,'Ignored iFrame, already setup.'); 822 } 823 } 824 825 function debouce(fn,time){ 826 if (null === timer){ 827 timer = setTimeout(function(){ 828 timer = null; 829 fn(); 830 }, time); 831 } 832 } 833 834 /* istanbul ignore next */ //Not testable in PhantomJS 835 function fixHiddenIFrames(){ 836 function checkIFrames(){ 837 function checkIFrame(settingId){ 838 function chkDimension(dimension){ 839 return '0px' === settings[settingId].iframe.style[dimension]; 840 } 841 842 function isVisible(el) { 843 return (null !== el.offsetParent); 844 } 845
846 if (isVisible(settings[settingId].iframe) && (chkDimension('height') || chkDimension('width'))){ 847 trigger('Visibility change', 'resize', settings[settingId].iframe,settingId); 848 } 849 } 850 851 for (var settingId in settings){ 852 checkIFrame(settingId); 853 } 854 } 855 856 function mutationObserved(mutations){ 857 log('window','Mutation observed: ' + mutations[0].target + ' ' + mutations[0].type); 858 debouce(checkIFrames,16); 859 } 860 861 function createMutationObserver(){ 862 var 863 target = document.querySelector('body'), 864 865 config = { 866 attributes : true, 867 attributeOldValue : false, 868 characterData : true, 869 characterDataOldValue : false, 870 childList : true, 871 subtree : true 872 }, 873 874 observer = new MutationObserver(mutationObserved); 875 876 observer.observe(target, config); 877 } 878 879 var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; 880 881 if (MutationObserver) createMutationObserver(); 882 } 883 884 885 function resizeIFrames(event){ 886 function resize(){ 887 sendTriggerMsg('Window '+event,'resize'); 888 } 889 890 log('window','Trigger event: '+event); 891 debouce(resize,16); 892 } 893 894 /* istanbul ignore next */ //Not testable in PhantomJS 895 function tabVisible() { 896 function resize(){ 897 sendTriggerMsg('Tab Visable','resize'); 898 } 899 900 if('hidden' !== document.visibilityState) { 901 log('document','Trigger event: Visiblity change'); 902 debouce(resize,16); 903 } 904 } 905 906 function sendTriggerMsg(eventName,event){ 907 function isIFrameResizeEnabled(iframeId) { 908 return 'parent' === settings[iframeId].resizeFrom && 909 settings[iframeId].autoResize && 910 !settings[iframeId].firstRun; 911 } 912 913 for (var iframeId in settings){ 914 if(isIFrameResizeEnabled(iframeId)){ 915 trigger(eventName,event,document.getElementById(iframeId),iframeId); 916 } 917 } 918 } 919 920 function setupEventListeners(){ 921 addEventListener(window,'message',iFrameListener); 922 923 addEventListener(window,'resize', function(){resizeIFrames('resize');}); 924 925 addEventListener(document,'visibilitychange',tabVisible); 926 addEventListener(document,'-webkit-visibilitychange',tabVisible); //Andriod 4.4 927 addEventListener(window,'focusin',function(){resizeIFrames('focus');}); //IE8-9 928 addEventListener(window,'focus',function(){resizeIFrames('focus');}); 929 } 930 931 932 function factory(){ 933 function init(options,element){ 934 function chkType(){ 935 if(!element.tagName) { 936 throw new TypeError('Object is not a valid DOM element'); 937 } else if ('IFRAME' !== element.tagName.toUpperCase()) { 938 throw new TypeError('Expected <IFRAME> tag, found <'+element.tagName+'>'); 939 } 940 } 941 942 if(element) { 943 chkType(); 944 setupIFrame(element, options); 945 iFrames.push(element); 946 } 947 } 948 949 var iFrames; 950 951 setupRequestAnimationFrame(); 952 setupEventListeners(); 953 954 return function iFrameResizeF(options,target){ 955 iFrames = []; //Only return iFrames past in on this call 956 957 switch (typeof(target)){ 958 case 'undefined': 959 case 'string': 960 Array.prototype.forEach.call( 961 document.querySelectorAll( target || 'iframe' ), 962 init.bind(undefined, options) 963 ); 964 break; 965 case 'object': 966 init(options,target); 967 break; 968 default: 969 throw new TypeError('Unexpected data type ('+typeof(target)+')'); 970 } 971 972 return iFrames; 973 }; 974 } 975 976 function createJQueryPublicMethod($){ 977 $.fn.iFrameResize = function $iFrameResizeF(options) { 978 return this.filter('iframe').each(function (index, element) { 979 setupIFrame(element, options); 980 }).end(); 981 }; 982 } 983 984 if (window.jQuery) { createJQueryPublicMethod(jQuery); } 985 986 if (typeof define === 'function' && define.amd) { 987 define([],factory); 988 } else if (typeof module === 'object' && typeof module.exports === 'object') { //Node for browserfy 989 module.exports = factory(); 990 } else { 991 window.iFrameResize = window.iFrameResize || factory(); 992 } 993 994})(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.