/*global QUnit:false */ (function( QUnit, window, undefined ) { "use strict"; var trim = function( s ) { if ( !s ) { return ""; } return typeof s.trim === "function" ? s.trim() : s.replace( /^\s+|\s+$/g, "" ); }; var normalizeWhitespace = function( s ) { if ( !s ) { return ""; } return trim( s.replace( /\s+/g, " " ) ); }; var dedupeFlatDict = function( dictToDedupe, parentDict ) { var key, val; if ( parentDict ) { for ( key in dictToDedupe ) { val = dictToDedupe[key]; if ( val && ( val === parentDict[key] ) ) { delete dictToDedupe[key]; } } } return dictToDedupe; }; var objectKeys = Object.keys || (function() { var hasOwn = function( obj, propName ) { return Object.prototype.hasOwnProperty.call( obj, propName ); }; return function( obj ) { var keys = [], key; for ( key in obj ) { if ( hasOwn( obj, key ) ) { keys.push( key ); } } return keys; }; })(); /** * Calculate based on `currentStyle`/`getComputedStyle` styles instead */ var getElementStyles = (function() { // Memoized var camelCase = (function() { var camelCaseFn = (function() { // Matches dashed string for camelizing var rmsPrefix = /^-ms-/, msPrefixFix = "ms-", rdashAlpha = /-([\da-z])/gi, camelCaseReplacerFn = function( all, letter ) { return ( letter + "" ).toUpperCase(); }; return function( s ) { return s.replace(rmsPrefix, msPrefixFix).replace(rdashAlpha, camelCaseReplacerFn); }; })(); var camelCaseMemoizer = {}; return function( s ) { var temp = camelCaseMemoizer[s]; if ( temp ) { return temp; } temp = camelCaseFn( s ); camelCaseMemoizer[s] = temp; return temp; }; })(); var styleKeySortingFn = function( a, b ) { return camelCase( a ) < camelCase( b ); }; return function( elem ) { var styleCount, i, key, styles = {}, styleKeys = [], style = elem.ownerDocument.defaultView ? elem.ownerDocument.defaultView.getComputedStyle( elem, null ) : elem.currentStyle; // `getComputedStyle` if ( style && style.length && style[0] && style[style[0]] ) { styleCount = style.length; while ( styleCount-- ) { styleKeys.push( style[styleCount] ); } styleKeys.sort( styleKeySortingFn ); for ( i = 0, styleCount = styleKeys.length ; i < styleCount ; i++ ) { key = styleKeys[i]; if ( key !== "cssText" && typeof style[key] === "string" && style[key] ) { styles[camelCase( key )] = style[key]; } } } // `currentStyle` support: IE < 9.0, Opera < 10.6 else { for ( key in style ) { styleKeys.push( key ); } styleKeys.sort(); for ( i = 0, styleCount = styleKeys.length ; i < styleCount ; i++ ) { key = styleKeys[i]; if ( key !== "cssText" && typeof style[key] === "string" && style[key] ) { styles[key] = style[key]; } } } return styles; }; })(); var serializeElementNode = function( elementNode, rootNodeStyles ) { var subNodes, i, len, styles, attrName, serializedNode = { NodeType: elementNode.nodeType, NodeName: elementNode.nodeName.toLowerCase(), Attributes: {}, ChildNodes: [] }; subNodes = elementNode.attributes; for ( i = 0, len = subNodes.length ; i < len ; i++ ) { attrName = subNodes[i].name.toLowerCase(); if ( attrName === "class" ) { serializedNode.Attributes[attrName] = normalizeWhitespace( subNodes[i].value ); } else if ( attrName !== "style" ) { serializedNode.Attributes[attrName] = subNodes[i].value; } // Ignore the "style" attribute completely } // Only add the style attribute if there is 1+ pertinent rules styles = dedupeFlatDict( getElementStyles( elementNode ), rootNodeStyles ); if ( styles && objectKeys( styles ).length ) { serializedNode.Attributes["style"] = styles; } subNodes = elementNode.childNodes; for ( i = 0, len = subNodes.length; i < len; i++ ) { serializedNode.ChildNodes.push( serializeNode( subNodes[i], rootNodeStyles ) ); } return serializedNode; }; var serializeNode = function( node, rootNodeStyles ) { var serializedNode; switch (node.nodeType) { case 1: // Node.ELEMENT_NODE serializedNode = serializeElementNode( node, rootNodeStyles ); break; case 3: // Node.TEXT_NODE serializedNode = { NodeType: node.nodeType, NodeName: node.nodeName.toLowerCase(), NodeValue: node.nodeValue }; break; case 4: // Node.CDATA_SECTION_NODE case 7: // Node.PROCESSING_INSTRUCTION_NODE case 8: // Node.COMMENT_NODE serializedNode = { NodeType: node.nodeType, NodeName: node.nodeName.toLowerCase(), NodeValue: trim( node.nodeValue ) }; break; case 5: // Node.ENTITY_REFERENCE_NODE case 6: // Node.ENTITY_NODE case 9: // Node.DOCUMENT_NODE case 10: // Node.DOCUMENT_TYPE_NODE case 11: // Node.DOCUMENT_FRAGMENT_NODE case 12: // Node.NOTATION_NODE serializedNode = { NodeType: node.nodeType, NodeName: node.nodeName }; break; case 2: // Node.ATTRIBUTE_NODE throw new Error( "`node.nodeType` was `Node.ATTRIBUTE_NODE` (2), which is not supported by this method" ); default: throw new Error( "`node.nodeType` was not recognized: " + node.nodeType ); } return serializedNode; }; var serializeHtml = function( html ) { var scratch = getCleanSlate(), rootNode = scratch.container(), rootNodeStyles = getElementStyles( rootNode ), serializedHtml = [], kids, i, len; rootNode.innerHTML = trim( html ); kids = rootNode.childNodes; for ( i = 0, len = kids.length; i < len; i++ ) { serializedHtml.push( serializeNode( kids[i], rootNodeStyles ) ); } scratch.reset(); return serializedHtml; }; var getCleanSlate = (function() { var containerElId = "qunit-html-addon-container", iframeReady = false, iframeLoaded = function() { iframeReady = true; }, iframeReadied = function() { if (iframe.readyState === "complete" || iframe.readyState === 4) { iframeReady = true; } }, iframeApi, iframe, iframeWin, iframeDoc; if ( !iframeApi ) { QUnit.begin(function() { // Initialize the background iframe! if ( !iframe || !iframeWin || !iframeDoc ) { iframe = window.document.createElement( "iframe" ); QUnit.addEvent( iframe, "load", iframeLoaded ); QUnit.addEvent( iframe, "readystatechange", iframeReadied ); iframe.style.position = "absolute"; iframe.style.top = iframe.style.left = "-1000px"; iframe.height = iframe.width = 0; // `getComputedStyle` behaves inconsistently cross-browser when not attached to a live DOM window.document.body.appendChild( iframe ); iframeWin = iframe.contentWindow || iframe.window || iframe.contentDocument && iframe.contentDocument.defaultView || iframe.document && ( iframe.document.defaultView || iframe.document.window ) || window.frames[( iframe.name || iframe.id )]; iframeDoc = iframeWin && iframeWin.document || iframe.contentDocument || iframe.document; var iframeContents = [ "", "", "
", "