Last active
August 29, 2015 14:14
-
-
Save trevorparscal/c0bf02b02c96d98cfc56 to your computer and use it in GitHub Desktop.
Transplant the CSS styles from as parent document to a frame's document
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Transplant the CSS styles from as parent document to a frame's document. | |
* | |
* This loops over the style sheets in the parent document, and copies their nodes to the | |
* frame's document. It then polls the document to see when all styles have loaded, and once they | |
* have, resolves the promise. | |
* | |
* If the styles still haven't loaded after a long time (5 seconds by default), we give up waiting | |
* and resolve the promise anyway. This protects against cases like a display: none; iframe in | |
* Firefox, where the styles won't load until the iframe becomes visible. | |
* | |
* For details of how we arrived at the strategy used in this function, see #load. | |
* | |
* @license MIT | |
* | |
* @static | |
* @inheritable | |
* @param {HTMLDocument} parentDoc Document to transplant styles from | |
* @param {HTMLDocument} frameDoc Document to transplant styles to | |
* @param {number} [timeout=5000] How long to wait before giving up (in ms). If 0, never give up. | |
* @return {jQuery.Promise} Promise resolved when styles have loaded | |
*/ | |
function transplantStyles( parentDoc, frameDoc, timeout ) { | |
var i, numSheets, styleNode, styleText, newNode, timeoutID, pollNodeId, $pendingPollNodes, | |
$pollNodes = $( [] ), | |
// Fake font-family value | |
fontFamily = 'oo-ui-frame-transplantStyles-loaded', | |
nextIndex = parentDoc.oouiFrameTransplantStylesNextIndex || 0, | |
deferred = $.Deferred(); | |
for ( i = 0, numSheets = parentDoc.styleSheets.length; i < numSheets; i++ ) { | |
styleNode = parentDoc.styleSheets[ i ].ownerNode; | |
if ( styleNode.disabled ) { | |
continue; | |
} | |
if ( styleNode.nodeName.toLowerCase() === 'link' ) { | |
// External stylesheet; use @import | |
styleText = '@import url(' + styleNode.href + ');'; | |
} else { | |
// Internal stylesheet; just copy the text | |
// For IE10 we need to fall back to .cssText, BUT that's undefined in | |
// other browsers, so fall back to '' rather than 'undefined' | |
styleText = styleNode.textContent || parentDoc.styleSheets[ i ].cssText || ''; | |
} | |
// Create a node with a unique ID that we're going to monitor to see when the CSS | |
// has loaded | |
if ( styleNode.oouiFrameTransplantStylesId ) { | |
// If we're nesting transplantStyles operations and this node already has | |
// a CSS rule to wait for loading, reuse it | |
pollNodeId = styleNode.oouiFrameTransplantStylesId; | |
} else { | |
// Otherwise, create a new ID | |
pollNodeId = 'oo-ui-frame-transplantStyles-loaded-' + nextIndex; | |
nextIndex++; | |
// Add #pollNodeId { font-family: ... } to the end of the stylesheet / after the @import | |
// The font-family rule will only take effect once the @import finishes | |
styleText += '\n' + '#' + pollNodeId + ' { font-family: ' + fontFamily + '; }'; | |
} | |
// Create a node with id=pollNodeId | |
$pollNodes = $pollNodes.add( $( '<div>', frameDoc ) | |
.attr( 'id', pollNodeId ) | |
.appendTo( frameDoc.body ) | |
); | |
// Add our modified CSS as a <style> tag | |
newNode = frameDoc.createElement( 'style' ); | |
newNode.textContent = styleText; | |
newNode.oouiFrameTransplantStylesId = pollNodeId; | |
frameDoc.head.appendChild( newNode ); | |
} | |
frameDoc.oouiFrameTransplantStylesNextIndex = nextIndex; | |
// Poll every 100ms until all external stylesheets have loaded | |
$pendingPollNodes = $pollNodes; | |
timeoutID = setTimeout( function pollExternalStylesheets() { | |
while ( | |
$pendingPollNodes.length > 0 && | |
$pendingPollNodes.eq( 0 ).css( 'font-family' ) === fontFamily | |
) { | |
$pendingPollNodes = $pendingPollNodes.slice( 1 ); | |
} | |
if ( $pendingPollNodes.length === 0 ) { | |
// We're done! | |
if ( timeoutID !== null ) { | |
timeoutID = null; | |
$pollNodes.remove(); | |
deferred.resolve(); | |
} | |
} else { | |
timeoutID = setTimeout( pollExternalStylesheets, 100 ); | |
} | |
}, 100 ); | |
// ...but give up after a while | |
if ( timeout !== 0 ) { | |
setTimeout( function () { | |
if ( timeoutID ) { | |
clearTimeout( timeoutID ); | |
timeoutID = null; | |
$pollNodes.remove(); | |
deferred.reject(); | |
} | |
}, timeout || 5000 ); | |
} | |
return deferred.promise(); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment