UNPKG

monaca-lib

Version:

Monaca cloud API bindings for JavaScript

606 lines (541 loc) 20.5 kB
/* * Copyright (C) 2011 Google Inc. All rights reserved. * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. * Copyright (C) 2008 Matt Lilek <webkit@mattlilek.com> * Copyright (C) 2009 Joseph Pecoraro * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ WebInspector.DOMPresentationUtils = {} WebInspector.DOMPresentationUtils.decorateNodeLabel = function(node, parentElement) { var title = node.nodeNameInCorrectCase(); var nameElement = createElement("span"); nameElement.textContent = title; parentElement.appendChild(nameElement); var idAttribute = node.getAttribute("id"); if (idAttribute) { var idElement = createElement("span"); parentElement.appendChild(idElement); var part = "#" + idAttribute; title += part; idElement.createTextChild(part); // Mark the name as extra, since the ID is more important. nameElement.className = "extra"; } var classAttribute = node.getAttribute("class"); if (classAttribute) { var classes = classAttribute.split(/\s+/); var foundClasses = {}; if (classes.length) { var classesElement = createElement("span"); classesElement.className = "extra"; parentElement.appendChild(classesElement); for (var i = 0; i < classes.length; ++i) { var className = classes[i]; if (className && !(className in foundClasses)) { var part = "." + className; title += part; classesElement.createTextChild(part); foundClasses[className] = true; } } } } parentElement.title = title; } /** * @param {!Element} container * @param {string} nodeTitle */ WebInspector.DOMPresentationUtils.createSpansForNodeTitle = function(container, nodeTitle) { var match = nodeTitle.match(/([^#.]+)(#[^.]+)?(\..*)?/); container.createChild("span", "webkit-html-tag-name").textContent = match[1]; if (match[2]) container.createChild("span", "webkit-html-attribute-value").textContent = match[2]; if (match[3]) container.createChild("span", "webkit-html-attribute-name").textContent = match[3]; } /** * @param {?WebInspector.DOMNode} node * @return {!Node} */ WebInspector.DOMPresentationUtils.linkifyNodeReference = function(node) { if (!node) return createTextNode(WebInspector.UIString("<node>")); var root = createElement("div"); var shadowRoot = root.createShadowRoot(); shadowRoot.appendChild(WebInspector.View.createStyleElement("components/nodeLink.css")); var link = shadowRoot.createChild("div", "node-link"); WebInspector.DOMPresentationUtils.decorateNodeLabel(node, link); link.addEventListener("click", WebInspector.Revealer.reveal.bind(WebInspector.Revealer, node, undefined), false); link.addEventListener("mouseover", node.highlight.bind(node, undefined, undefined), false); link.addEventListener("mouseleave", node.domModel().hideDOMNodeHighlight.bind(node.domModel()), false); return root; } /** * @param {!WebInspector.DeferredDOMNode} deferredNode * @return {!Node} */ WebInspector.DOMPresentationUtils.linkifyDeferredNodeReference = function(deferredNode) { var root = createElement("div"); var shadowRoot = root.createShadowRoot(); shadowRoot.appendChild(WebInspector.View.createStyleElement("components/nodeLink.css")); var link = shadowRoot.createChild("div", "node-link"); link.addEventListener("click", deferredNode.resolve.bind(deferredNode, onDeferredNodeResolved), false); /** * @param {?WebInspector.DOMNode} node */ function onDeferredNodeResolved(node) { WebInspector.Revealer.reveal(node); } return root; } /** * @param {!WebInspector.Target} target * @param {string} originalImageURL * @param {boolean} showDimensions * @param {function(!Element=)} userCallback * @param {!Object=} precomputedFeatures */ WebInspector.DOMPresentationUtils.buildImagePreviewContents = function(target, originalImageURL, showDimensions, userCallback, precomputedFeatures) { var resource = target.resourceTreeModel.resourceForURL(originalImageURL); var imageURL = originalImageURL; if (!isImageResource(resource) && precomputedFeatures && precomputedFeatures.currentSrc) { imageURL = precomputedFeatures.currentSrc; resource = target.resourceTreeModel.resourceForURL(imageURL); } if (!isImageResource(resource)) { userCallback(); return; } var imageElement = createElement("img"); imageElement.addEventListener("load", buildContent, false); imageElement.addEventListener("error", errorCallback, false); resource.populateImageSource(imageElement); function errorCallback() { // Drop the event parameter when invoking userCallback. userCallback(); } /** * @param {?WebInspector.Resource} resource * @return {boolean} */ function isImageResource(resource) { return !!resource && resource.resourceType() === WebInspector.resourceTypes.Image; } function buildContent() { var container = createElement("table"); container.className = "image-preview-container"; var naturalWidth = precomputedFeatures ? precomputedFeatures.naturalWidth : imageElement.naturalWidth; var naturalHeight = precomputedFeatures ? precomputedFeatures.naturalHeight : imageElement.naturalHeight; var offsetWidth = precomputedFeatures ? precomputedFeatures.offsetWidth : naturalWidth; var offsetHeight = precomputedFeatures ? precomputedFeatures.offsetHeight : naturalHeight; var description; if (showDimensions) { if (offsetHeight === naturalHeight && offsetWidth === naturalWidth) description = WebInspector.UIString("%d \xd7 %d pixels", offsetWidth, offsetHeight); else description = WebInspector.UIString("%d \xd7 %d pixels (Natural: %d \xd7 %d pixels)", offsetWidth, offsetHeight, naturalWidth, naturalHeight); } container.createChild("tr").createChild("td", "image-container").appendChild(imageElement); if (description) container.createChild("tr").createChild("td").createChild("span", "description").textContent = description; if (imageURL !== originalImageURL) container.createChild("tr").createChild("td").createChild("span", "description").textContent = String.sprintf("currentSrc: %s", imageURL.trimMiddle(100)); userCallback(container); } } /** * @param {!WebInspector.Target} target * @param {!WebInspector.Linkifier} linkifier * @param {!Array.<!ConsoleAgent.CallFrame>=} stackTrace * @param {!ConsoleAgent.AsyncStackTrace=} asyncStackTrace * @return {!Element} */ WebInspector.DOMPresentationUtils.buildStackTracePreviewContents = function(target, linkifier, stackTrace, asyncStackTrace) { var element = createElementWithClass("table", "stack-preview-container"); /** * @param {!Array.<!ConsoleAgent.CallFrame>} stackTrace */ function appendStackTrace(stackTrace) { for (var stackFrame of stackTrace) { var row = createElement("tr"); row.createChild("td").textContent = WebInspector.beautifyFunctionName(stackFrame.functionName); row.createChild("td").textContent = " @ "; row.createChild("td").appendChild(linkifier.linkifyConsoleCallFrame(target, stackFrame)); element.appendChild(row); } } if (stackTrace) appendStackTrace(stackTrace); while (asyncStackTrace) { var callFrames = asyncStackTrace.callFrames; if (!callFrames || !callFrames.length) break; var row = element.createChild("tr"); row.createChild("td", "stack-preview-async-description").textContent = WebInspector.asyncStackTraceLabel(asyncStackTrace.description); row.createChild("td"); row.createChild("td"); appendStackTrace(callFrames); asyncStackTrace = asyncStackTrace.asyncStackTrace; } return element; } /** * @param {!WebInspector.DOMNode} node * @param {boolean=} justSelector * @return {string} */ WebInspector.DOMPresentationUtils.fullQualifiedSelector = function(node, justSelector) { if (node.nodeType() !== Node.ELEMENT_NODE) return node.localName() || node.nodeName().toLowerCase(); return WebInspector.DOMPresentationUtils.cssPath(node, justSelector); } /** * @param {!WebInspector.DOMNode} node * @return {string} */ WebInspector.DOMPresentationUtils.simpleSelector = function(node) { var lowerCaseName = node.localName() || node.nodeName().toLowerCase(); if (node.nodeType() !== Node.ELEMENT_NODE) return lowerCaseName; if (lowerCaseName === "input" && node.getAttribute("type") && !node.getAttribute("id") && !node.getAttribute("class")) return lowerCaseName + "[type=\"" + node.getAttribute("type") + "\"]"; if (node.getAttribute("id")) return lowerCaseName + "#" + node.getAttribute("id"); if (node.getAttribute("class")) return (lowerCaseName === "div" ? "" : lowerCaseName) + "." + node.getAttribute("class").trim().replace(/\s+/g, "."); return lowerCaseName; } /** * @param {!WebInspector.DOMNode} node * @param {boolean=} optimized * @return {string} */ WebInspector.DOMPresentationUtils.cssPath = function(node, optimized) { if (node.nodeType() !== Node.ELEMENT_NODE) return ""; var steps = []; var contextNode = node; while (contextNode) { var step = WebInspector.DOMPresentationUtils._cssPathStep(contextNode, !!optimized, contextNode === node); if (!step) break; // Error - bail out early. steps.push(step); if (step.optimized) break; contextNode = contextNode.parentNode; } steps.reverse(); return steps.join(" > "); } /** * @param {!WebInspector.DOMNode} node * @param {boolean} optimized * @param {boolean} isTargetNode * @return {?WebInspector.DOMNodePathStep} */ WebInspector.DOMPresentationUtils._cssPathStep = function(node, optimized, isTargetNode) { if (node.nodeType() !== Node.ELEMENT_NODE) return null; var id = node.getAttribute("id"); if (optimized) { if (id) return new WebInspector.DOMNodePathStep(idSelector(id), true); var nodeNameLower = node.nodeName().toLowerCase(); if (nodeNameLower === "body" || nodeNameLower === "head" || nodeNameLower === "html") return new WebInspector.DOMNodePathStep(node.nodeNameInCorrectCase(), true); } var nodeName = node.nodeNameInCorrectCase(); if (id) return new WebInspector.DOMNodePathStep(nodeName + idSelector(id), true); var parent = node.parentNode; if (!parent || parent.nodeType() === Node.DOCUMENT_NODE) return new WebInspector.DOMNodePathStep(nodeName, true); /** * @param {!WebInspector.DOMNode} node * @return {!Array.<string>} */ function prefixedElementClassNames(node) { var classAttribute = node.getAttribute("class"); if (!classAttribute) return []; return classAttribute.split(/\s+/g).filter(Boolean).map(function(name) { // The prefix is required to store "__proto__" in a object-based map. return "$" + name; }); } /** * @param {string} id * @return {string} */ function idSelector(id) { return "#" + escapeIdentifierIfNeeded(id); } /** * @param {string} ident * @return {string} */ function escapeIdentifierIfNeeded(ident) { if (isCSSIdentifier(ident)) return ident; var shouldEscapeFirst = /^(?:[0-9]|-[0-9-]?)/.test(ident); var lastIndex = ident.length - 1; return ident.replace(/./g, function(c, i) { return ((shouldEscapeFirst && i === 0) || !isCSSIdentChar(c)) ? escapeAsciiChar(c, i === lastIndex) : c; }); } /** * @param {string} c * @param {boolean} isLast * @return {string} */ function escapeAsciiChar(c, isLast) { return "\\" + toHexByte(c) + (isLast ? "" : " "); } /** * @param {string} c */ function toHexByte(c) { var hexByte = c.charCodeAt(0).toString(16); if (hexByte.length === 1) hexByte = "0" + hexByte; return hexByte; } /** * @param {string} c * @return {boolean} */ function isCSSIdentChar(c) { if (/[a-zA-Z0-9_-]/.test(c)) return true; return c.charCodeAt(0) >= 0xA0; } /** * @param {string} value * @return {boolean} */ function isCSSIdentifier(value) { return /^-?[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value); } var prefixedOwnClassNamesArray = prefixedElementClassNames(node); var needsClassNames = false; var needsNthChild = false; var ownIndex = -1; var elementIndex = -1; var siblings = parent.children(); for (var i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.length; ++i) { var sibling = siblings[i]; if (sibling.nodeType() !== Node.ELEMENT_NODE) continue; elementIndex += 1; if (sibling === node) { ownIndex = elementIndex; continue; } if (needsNthChild) continue; if (sibling.nodeNameInCorrectCase() !== nodeName) continue; needsClassNames = true; var ownClassNames = prefixedOwnClassNamesArray.keySet(); var ownClassNameCount = 0; for (var name in ownClassNames) ++ownClassNameCount; if (ownClassNameCount === 0) { needsNthChild = true; continue; } var siblingClassNamesArray = prefixedElementClassNames(sibling); for (var j = 0; j < siblingClassNamesArray.length; ++j) { var siblingClass = siblingClassNamesArray[j]; if (!ownClassNames.hasOwnProperty(siblingClass)) continue; delete ownClassNames[siblingClass]; if (!--ownClassNameCount) { needsNthChild = true; break; } } } var result = nodeName; if (isTargetNode && nodeName.toLowerCase() === "input" && node.getAttribute("type") && !node.getAttribute("id") && !node.getAttribute("class")) result += "[type=\"" + node.getAttribute("type") + "\"]"; if (needsNthChild) { result += ":nth-child(" + (ownIndex + 1) + ")"; } else if (needsClassNames) { for (var prefixedName in prefixedOwnClassNamesArray.keySet()) result += "." + escapeIdentifierIfNeeded(prefixedName.substr(1)); } return new WebInspector.DOMNodePathStep(result, false); } /** * @param {!WebInspector.DOMNode} node * @param {boolean=} optimized * @return {string} */ WebInspector.DOMPresentationUtils.xPath = function(node, optimized) { if (node.nodeType() === Node.DOCUMENT_NODE) return "/"; var steps = []; var contextNode = node; while (contextNode) { var step = WebInspector.DOMPresentationUtils._xPathValue(contextNode, optimized); if (!step) break; // Error - bail out early. steps.push(step); if (step.optimized) break; contextNode = contextNode.parentNode; } steps.reverse(); return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/"); } /** * @param {!WebInspector.DOMNode} node * @param {boolean=} optimized * @return {?WebInspector.DOMNodePathStep} */ WebInspector.DOMPresentationUtils._xPathValue = function(node, optimized) { var ownValue; var ownIndex = WebInspector.DOMPresentationUtils._xPathIndex(node); if (ownIndex === -1) return null; // Error. switch (node.nodeType()) { case Node.ELEMENT_NODE: if (optimized && node.getAttribute("id")) return new WebInspector.DOMNodePathStep("//*[@id=\"" + node.getAttribute("id") + "\"]", true); ownValue = node.localName(); break; case Node.ATTRIBUTE_NODE: ownValue = "@" + node.nodeName(); break; case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: ownValue = "text()"; break; case Node.PROCESSING_INSTRUCTION_NODE: ownValue = "processing-instruction()"; break; case Node.COMMENT_NODE: ownValue = "comment()"; break; case Node.DOCUMENT_NODE: ownValue = ""; break; default: ownValue = ""; break; } if (ownIndex > 0) ownValue += "[" + ownIndex + "]"; return new WebInspector.DOMNodePathStep(ownValue, node.nodeType() === Node.DOCUMENT_NODE); } /** * @param {!WebInspector.DOMNode} node * @return {number} */ WebInspector.DOMPresentationUtils._xPathIndex = function(node) { // Returns -1 in case of error, 0 if no siblings matching the same expression, <XPath index among the same expression-matching sibling nodes> otherwise. function areNodesSimilar(left, right) { if (left === right) return true; if (left.nodeType() === Node.ELEMENT_NODE && right.nodeType() === Node.ELEMENT_NODE) return left.localName() === right.localName(); if (left.nodeType() === right.nodeType()) return true; // XPath treats CDATA as text nodes. var leftType = left.nodeType() === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left.nodeType(); var rightType = right.nodeType() === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right.nodeType(); return leftType === rightType; } var siblings = node.parentNode ? node.parentNode.children() : null; if (!siblings) return 0; // Root node - no siblings. var hasSameNamedElements; for (var i = 0; i < siblings.length; ++i) { if (areNodesSimilar(node, siblings[i]) && siblings[i] !== node) { hasSameNamedElements = true; break; } } if (!hasSameNamedElements) return 0; var ownIndex = 1; // XPath indices start with 1. for (var i = 0; i < siblings.length; ++i) { if (areNodesSimilar(node, siblings[i])) { if (siblings[i] === node) return ownIndex; ++ownIndex; } } return -1; // An error occurred: |node| not found in parent's children. } /** * @constructor * @param {string} value * @param {boolean} optimized */ WebInspector.DOMNodePathStep = function(value, optimized) { this.value = value; this.optimized = optimized || false; } WebInspector.DOMNodePathStep.prototype = { /** * @override * @return {string} */ toString: function() { return this.value; } }