_html2canvas.Renderer = function(parseQueue, options){ // http://www.w3.org/TR/CSS21/zindex.html function createRenderQueue(parseQueue) { var queue = [], rootContext; rootContext = (function buildStackingContext(rootNode) { var rootContext = {}; function insert(context, node, specialParent) { var zi = (node.zIndex.zindex === 'auto') ? 0 : Number(node.zIndex.zindex), contextForChildren = context, // the stacking context for children isPositioned = node.zIndex.isPositioned, isFloated = node.zIndex.isFloated, stub = {node: node}, childrenDest = specialParent; // where children without z-index should be pushed into if (node.zIndex.ownStacking) { // '!' comes before numbers in sorted array contextForChildren = stub.context = { '!': [{node:node, children: []}]}; childrenDest = undefined; } else if (isPositioned || isFloated) { childrenDest = stub.children = []; } if (zi === 0 && specialParent) { specialParent.push(stub); } else { if (!context[zi]) { context[zi] = []; } context[zi].push(stub); } node.zIndex.children.forEach(function(childNode) { insert(contextForChildren, childNode, childrenDest); }); } insert(rootContext, rootNode); return rootContext; })(parseQueue); function sortZ(context) { Object.keys(context).sort().forEach(function(zi) { var nonPositioned = [], floated = [], positioned = [], list = []; // positioned after static context[zi].forEach(function(v) { if (v.node.zIndex.isPositioned || v.node.zIndex.opacity < 1) { // http://www.w3.org/TR/css3-color/#transparency // non-positioned element with opactiy < 1 should be stacked as if it were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. positioned.push(v); } else if (v.node.zIndex.isFloated) { floated.push(v); } else { nonPositioned.push(v); } }); (function walk(arr) { arr.forEach(function(v) { list.push(v); if (v.children) { walk(v.children); } }); })(nonPositioned.concat(floated, positioned)); list.forEach(function(v) { if (v.context) { sortZ(v.context); } else { queue.push(v.node); } }); }); } sortZ(rootContext); return queue; } function getRenderer(rendererName) { var renderer; if (typeof options.renderer === "string" && _html2canvas.Renderer[rendererName] !== undefined) { renderer = _html2canvas.Renderer[rendererName](options); } else if (typeof rendererName === "function") { renderer = rendererName(options); } else { throw new Error("Unknown renderer"); } if ( typeof renderer !== "function" ) { throw new Error("Invalid renderer defined"); } return renderer; } return getRenderer(options.renderer)(parseQueue, options, document, createRenderQueue(parseQueue.stack), _html2canvas); };