| |
| /* |
| * weinre is available under *either* the terms of the modified BSD license *or* the |
| * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text. |
| * |
| * Copyright (c) 2010, 2011 IBM Corporation |
| */ |
| |
| requireClass ../common/Weinre |
| requireClass ../common/IDGenerator |
| |
| //----------------------------------------------------------------------------- |
| class NodeStore |
| this.__nodeMap = {} |
| this.__nodeDataMap = {} |
| this.inspectedNodes = [] |
| |
| document.addEventListener("DOMSubtreeModified", handleDOMSubtreeModified, false) |
| document.addEventListener("DOMNodeInserted", handleDOMNodeInserted, false) |
| document.addEventListener("DOMNodeRemoved", handleDOMNodeRemoved, false) |
| document.addEventListener("DOMAttrModified", handleDOMAttrModified, false) |
| document.addEventListener("DOMCharacterDataModified", handleDOMCharacterDataModified, false) |
| |
| //----------------------------------------------------------------------------- |
| method addInspectedNode(nodeId) |
| this.inspectedNodes.unshift(nodeId) |
| if (this.inspectedNodes.length > 5) { |
| this.inspectedNodes = this.inspectedNodes.slice(0,5) |
| } |
| |
| //----------------------------------------------------------------------------- |
| method getInspectedNode(index) |
| return this.inspectedNodes[index] |
| |
| //----------------------------------------------------------------------------- |
| method getNode(nodeId) |
| return this.__nodeMap[nodeId] |
| |
| //----------------------------------------------------------------------------- |
| method checkNodeId(node) |
| return IDGenerator.checkId(node) |
| |
| //----------------------------------------------------------------------------- |
| method getNodeId(node) |
| var id = this.checkNodeId(node) |
| if (id) { |
| return id |
| } |
| |
| return IDGenerator.getId(node, this.__nodeMap) |
| |
| //----------------------------------------------------------------------------- |
| method getNodeData(nodeId, depth) |
| return this.serializeNode(this.getNode(nodeId), depth) |
| |
| //----------------------------------------------------------------------------- |
| method getPreviousSiblingId(node) |
| while (true) { |
| var sib = node.previousSibling |
| if (!sib) return 0 |
| |
| var id = this.checkNodeId(sib) |
| if (id) return id |
| |
| node = sib |
| } |
| |
| //----------------------------------------------------------------------------- |
| method nextNodeId() |
| return "" + IDGenerator.next() |
| |
| //----------------------------------------------------------------------------- |
| method serializeNode(node, depth) |
| var nodeName = "" |
| var nodeValue = null |
| var localName = null |
| var id = this.getNodeId(node) |
| |
| switch(node.nodeType) { |
| case Node.TEXT_NODE: |
| case Node.COMMENT_NODE: |
| case Node.CDATA_SECTION_NODE: |
| nodeValue = node.nodeValue |
| break |
| case Node.ATTRIBUTE_NODE: |
| localName = node.localName |
| break |
| case Node.DOCUMENT_FRAGMENT_NODE: |
| break |
| case Node.DOCUMENT_NODE: |
| case Node.ELEMENT_NODE: |
| default: |
| nodeName = node.nodeName |
| localName = node.localName |
| break |
| } |
| |
| var nodeData = { |
| id: id, |
| nodeType: node.nodeType, |
| nodeName: nodeName, |
| localName: localName, |
| nodeValue: nodeValue |
| } |
| |
| if (node.nodeType == Node.ELEMENT_NODE || node.nodeType == Node.DOCUMENT_NODE || node.nodeType == Node.DOCUMENT_FRAGMENT_NODE) { |
| nodeData.childNodeCount = this.childNodeCount(node) |
| var children = this.serializeNodeChildren(node, depth) |
| if (children.length) { |
| nodeData.children = children |
| } |
| |
| if (node.nodeType == Node.ELEMENT_NODE) { |
| nodeData.attributes = [] |
| for (var i=0; i<node.attributes.length; i++) { |
| nodeData.attributes.push(node.attributes[i].nodeName) |
| nodeData.attributes.push(node.attributes[i].nodeValue) |
| } |
| } |
| |
| else if (node.nodeType == Node.DOCUMENT_NODE) { |
| nodeData.documentURL = window.location.href |
| } |
| } |
| |
| else if (node.nodeType == Node.DOCUMENT_TYPE_NODE) { |
| nodeData.publicId = node.publicId |
| nodeData.systemId = node.systemId |
| nodeData.internalSubset = node.internalSubset |
| } |
| |
| else if (node.nodeType == Node.ATTRIBUTE_NODE) { |
| nodeData.name = node.nodeName |
| nodeData.value = node.nodeValue |
| } |
| |
| return nodeData |
| |
| //----------------------------------------------------------------------------- |
| method serializeNodeChildren(node, depth) |
| var result = [] |
| var childIds = this.childNodeIds(node) |
| |
| if (depth == 0) { |
| if (childIds.length == 1) { |
| var childNode = this.getNode(childIds[0]) |
| if (childNode.nodeType == Node.TEXT_NODE) { |
| result.push(this.serializeNode(childNode)) |
| } |
| } |
| return result |
| } |
| |
| depth--; |
| for (var i=0; i<childIds.length; i++) { |
| result.push(this.serializeNode(this.getNode(childIds[i]), depth)) |
| } |
| |
| return result |
| |
| //----------------------------------------------------------------------------- |
| method childNodeCount(node) |
| return this.childNodeIds(node).length |
| |
| //----------------------------------------------------------------------------- |
| method childNodeIds(node) |
| var ids = [] |
| |
| for (var i=0; i<node.childNodes.length; i++) { |
| if (this.isToBeSkipped(node.childNodes[i])) continue |
| ids.push(this.getNodeId(node.childNodes[i])) |
| } |
| |
| return ids |
| |
| //----------------------------------------------------------------------------- |
| method isToBeSkipped(node) |
| if (!node) return true |
| if (node.__weinreHighlighter) return true |
| if (node.nodeType != Node.TEXT_NODE) return false |
| return !!node.nodeValue.match(/^\s*$/) |
| |
| //----------------------------------------------------------------------------- |
| function handleDOMSubtreeModified(event) |
| if (!event.attrChange) return |
| |
| NodeStore.handleDOMAttrModified(event) |
| |
| //----------------------------------------------------------------------------- |
| function handleDOMNodeInserted(event) |
| var targetId = Weinre.nodeStore.checkNodeId(event.target) |
| var parentId = Weinre.nodeStore.checkNodeId(event.relatedNode) |
| |
| if (!parentId) return |
| |
| var child = Weinre.nodeStore.serializeNode(event.target, 0) |
| var previous = Weinre.nodeStore.getPreviousSiblingId(event.target) |
| Weinre.wi.DOMNotify.childNodeInserted(parentId, previous, child) |
| |
| //----------------------------------------------------------------------------- |
| function handleDOMNodeRemoved(event) |
| var targetId = Weinre.nodeStore.checkNodeId(event.target) |
| var parentId = Weinre.nodeStore.checkNodeId(event.relatedNode) |
| |
| if (!parentId) return |
| |
| if (targetId) { |
| Weinre.wi.DOMNotify.childNodeRemoved(parentId, targetId) |
| } |
| else { |
| var childCount = Weinre.nodeStore.childNodeCount(event.relatedNode) |
| Weinre.wi.DOMNotify.childNodeCountUpdated(parentId, childCount) |
| } |
| |
| //----------------------------------------------------------------------------- |
| // This event is not actually fired in WebKit, but DOMSubtreeModified may |
| // be fired for attribute changes. Doesn't seem to be at the moment. |
| function handleDOMAttrModified(event) |
| var targetId = Weinre.nodeStore.checkNodeId(event.target) |
| if (!targetId) return |
| |
| attrs = [] |
| for (var i=0; i<event.target.attributes.length; i++) { |
| attrs.push(event.target.attributes[i].name) |
| attrs.push(event.target.attributes[i].value) |
| } |
| |
| Weinre.wi.DOMNotify.attributesUpdated(targetId, attrs) |
| |
| //----------------------------------------------------------------------------- |
| function handleDOMCharacterDataModified(event) |
| var targetId = Weinre.nodeStore.checkNodeId(event.target) |
| if (!targetId) return |
| |
| Weinre.wi.DOMNotify.characterDataModified(targetId, event.newValue) |