GitHub PullRequestReviewCommentEvent at 2014-05-12 16:55:57
Repository: brackets-livedev2
Actor: dangoor
Payload: {"comment":{"url":"https://api.github.com/repos/njx/brackets-livedev2/pulls/comments/12540380","id":12540380,"diff_hunk":"@@ -0,0 +1,857 @@\n+/*\n+ * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.\n+ *  \n+ * Permission is hereby granted, free of charge, to any person obtaining a\n+ * copy of this software and associated documentation files (the \"Software\"), \n+ * to deal in the Software without restriction, including without limitation \n+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, \n+ * and/or sell copies of the Software, and to permit persons to whom the \n+ * Software is furnished to do so, subject to the following conditions:\n+ *  \n+ * The above copyright notice and this permission notice shall be included in\n+ * all copies or substantial portions of the Software.\n+ *  \n+ * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, \n+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER \n+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING \n+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \n+ * DEALINGS IN THE SOFTWARE.\n+ * \n+ */\n+\n+\n+/*jslint vars: true, plusplus: true, browser: true, nomen: true, indent: 4, forin: true, maxerr: 50, regexp: true */\n+/*global define, $, window, navigator, Node, console */\n+/*theseus instrument: false */\n+\n+/**\n+ * RemoteFunctions define the functions to be executed in the browser. This\n+ * modules should define a single function that returns an object of all\n+ * exported functions.\n+ */\n+function RemoteFunctions(experimental) {\n+    \"use strict\";\n+\n+    var lastKeepAliveTime = Date.now();\n+    \n+    /**\n+     * @type {DOMEditHandler}\n+     */\n+    var _editHandler;\n+    \n+    var HIGHLIGHT_CLASSNAME = \"__brackets-ld-highlight\",\n+        KEEP_ALIVE_TIMEOUT  = 3000;   // Keep alive timeout value, in milliseconds\n+    \n+    // determine whether an event should be processed for Live Development\n+    function _validEvent(event) {\n+        if (navigator.platform.substr(0, 3) === \"Mac\") {\n+            // Mac\n+            return event.metaKey;\n+        } else {\n+            // Windows\n+            return event.ctrlKey;\n+        }\n+    }\n+\n+    // determine the color for a type\n+    function _typeColor(type, highlight) {\n+        switch (type) {\n+        case \"html\":\n+            return highlight ? \"#eec\" : \"#ffe\";\n+        case \"css\":\n+            return highlight ? \"#cee\" : \"#eff\";\n+        case \"js\":\n+            return highlight ? \"#ccf\" : \"#eef\";\n+        default:\n+            return highlight ? \"#ddd\" : \"#eee\";\n+        }\n+    }\n+\n+    // compute the screen offset of an element\n+    function _screenOffset(element, key) {\n+        var bounds = element.getBoundingClientRect();\n+        if (key === \"offsetLeft\") {\n+            return bounds.left + window.pageXOffset;\n+        } else {\n+            return bounds.top + window.pageYOffset;\n+        }\n+    }\n+\n+    // set an event on a element\n+    function _trigger(element, name, value, autoRemove) {\n+        var key = \"data-ld-\" + name;\n+        if (value !== undefined && value !== null) {\n+            element.setAttribute(key, value);\n+            if (autoRemove) {\n+                window.setTimeout(element.removeAttribute.bind(element, key));\n+            }\n+        } else {\n+            element.removeAttribute(key);\n+        }\n+    }\n+\n+    // construct the info menu\n+    function Menu(element) {\n+        this.element = element;\n+        _trigger(this.element, \"showgoto\", 1, true);\n+        window.setTimeout(window.remoteShowGoto);\n+        this.remove = this.remove.bind(this);\n+    }\n+\n+    Menu.prototype = {\n+        onClick: function (url, event) {\n+            event.preventDefault();\n+            _trigger(this.element, \"goto\", url, true);\n+            this.remove();\n+        },\n+\n+        createBody: function () {\n+            if (this.body) {\n+                return;\n+            }\n+\n+            // compute the position on screen\n+            var x = _screenOffset(this.element, \"offsetLeft\");\n+            var y = _screenOffset(this.element, \"offsetTop\") + this.element.offsetHeight;\n+\n+            // create the container\n+            this.body = document.createElement(\"div\");\n+            this.body.style.setProperty(\"z-index\", 2147483647);\n+            this.body.style.setProperty(\"position\", \"absolute\");\n+            this.body.style.setProperty(\"left\", x + \"px\");\n+            this.body.style.setProperty(\"top\", y + \"px\");\n+            this.body.style.setProperty(\"font-size\", \"11pt\");\n+\n+            // draw the background\n+            this.body.style.setProperty(\"background\", \"#fff\");\n+            this.body.style.setProperty(\"border\", \"1px solid #888\");\n+            this.body.style.setProperty(\"-webkit-box-shadow\", \"2px 2px 6px 0px #ccc\");\n+            this.body.style.setProperty(\"border-radius\", \"6px\");\n+            this.body.style.setProperty(\"padding\", \"6px\");\n+        },\n+\n+        addItem: function (target) {\n+            var item = document.createElement(\"div\");\n+            item.style.setProperty(\"padding\", \"2px 6px\");\n+            if (this.body.childNodes.length > 0) {\n+                item.style.setProperty(\"border-top\", \"1px solid #ccc\");\n+            }\n+            item.style.setProperty(\"cursor\", \"pointer\");\n+            item.style.setProperty(\"background\", _typeColor(target.type));\n+            item.innerHTML = target.name;\n+            item.addEventListener(\"click\", this.onClick.bind(this, target.url));\n+\n+            if (target.file) {\n+                var file = document.createElement(\"i\");\n+                file.style.setProperty(\"float\", \"right\");\n+                file.style.setProperty(\"margin-left\", \"12px\");\n+                file.innerHTML = \" \" + target.file;\n+                item.appendChild(file);\n+            }\n+            this.body.appendChild(item);\n+        },\n+\n+        show: function () {\n+            if (!this.body) {\n+                this.body = this.createBody();\n+            }\n+            if (!this.body.parentNode) {\n+                document.body.appendChild(this.body);\n+            }\n+            document.addEventListener(\"click\", this.remove);\n+        },\n+\n+        remove: function () {\n+            if (this.body && this.body.parentNode) {\n+                document.body.removeChild(this.body);\n+            }\n+            document.removeEventListener(\"click\", this.remove);\n+        }\n+\n+    };\n+\n+    function Editor(element) {\n+        this.onBlur = this.onBlur.bind(this);\n+        this.onKeyPress = this.onKeyPress.bind(this);\n+\n+        this.element = element;\n+        this.element.setAttribute(\"contenteditable\", \"true\");\n+        this.element.focus();\n+        this.element.addEventListener(\"blur\", this.onBlur);\n+        this.element.addEventListener(\"keypress\", this.onKeyPress);\n+\n+        this.revertText = this.element.innerHTML;\n+\n+        _trigger(this.element, \"edit\", 1);\n+    }\n+\n+    Editor.prototype = {\n+        onBlur: function (event) {\n+            this.element.removeAttribute(\"contenteditable\");\n+            this.element.removeEventListener(\"blur\", this.onBlur);\n+            this.element.removeEventListener(\"keypress\", this.onKeyPress);\n+            _trigger(this.element, \"edit\", 0, true);\n+        },\n+\n+        onKeyPress: function (event) {\n+            switch (event.which) {\n+            case 13: // return\n+                this.element.blur();\n+                break;\n+            case 27: // esc\n+                this.element.innerHTML = this.revertText;\n+                this.element.blur();\n+                break;\n+            }\n+        }\n+    };\n+\n+    function Highlight(color, trigger) {\n+        this.color = color;\n+        this.trigger = !!trigger;\n+        this.elements = [];\n+        this.selector = \"\";\n+    }\n+\n+    Highlight.prototype = {\n+        _elementExists: function (element) {\n+            var i;\n+            for (i in this.elements) {\n+                if (this.elements[i] === element) {\n+                    return true;\n+                }\n+            }\n+            return false;\n+        },\n+\n+        _makeHighlightDiv: function (element, doAnimation) {\n+            var elementBounds = element.getBoundingClientRect(),\n+                highlight = window.document.createElement(\"div\"),\n+                styles = window.getComputedStyle(element);\n+            \n+            // Don't highlight elements with 0 width & height\n+            if (elementBounds.width === 0 && elementBounds.height === 0) {\n+                return;\n+            }\n+            \n+            highlight.className = HIGHLIGHT_CLASSNAME;\n+            \n+            var stylesToSet = {\n+                \"left\": _screenOffset(element, \"offsetLeft\") + \"px\",\n+                \"top\": _screenOffset(element, \"offsetTop\") + \"px\",\n+                \"width\": elementBounds.width + \"px\",\n+                \"height\": elementBounds.height + \"px\",\n+                \"z-index\": 2000000,\n+                \"margin\": 0,\n+                \"padding\": 0,\n+                \"position\": \"absolute\",\n+                \"pointer-events\": \"none\",\n+                \"border-top-left-radius\": styles.borderTopLeftRadius,\n+                \"border-top-right-radius\": styles.borderTopRightRadius,\n+                \"border-bottom-left-radius\": styles.borderBottomLeftRadius,\n+                \"border-bottom-right-radius\": styles.borderBottomRightRadius,\n+                \"border-style\": \"solid\",\n+                \"border-width\": \"1px\",\n+                \"border-color\": \"#00a2ff\",\n+                \"box-shadow\": \"0 0 1px #fff\",\n+                \"box-sizing\": \"border-box\"\n+            };\n+            \n+            var animateStartValues = {\n+                \"background-color\": \"rgba(0, 162, 255, 0.5)\",\n+                \"opacity\": 0\n+            };\n+            \n+            var animateEndValues = {\n+                \"background-color\": \"rgba(0, 162, 255, 0)\",\n+                \"opacity\": 1\n+            };\n+            \n+            var transitionValues = {\n+                \"-webkit-transition-property\": \"opacity, background-color\",\n+                \"-webkit-transition-duration\": \"300ms, 2.3s\",\n+                \"transition-property\": \"opacity, background-color\",\n+                \"transition-duration\": \"300ms, 2.3s\"\n+            };\n+            \n+            function _setStyleValues(styleValues, obj) {\n+                var prop;\n+                \n+                for (prop in styleValues) {\n+                    obj.setProperty(prop, styleValues[prop]);\n+                }\n+            }\n+\n+            _setStyleValues(stylesToSet, highlight.style);\n+            _setStyleValues(\n+                doAnimation ? animateStartValues : animateEndValues,\n+                highlight.style\n+            );\n+            \n+            \n+            if (doAnimation) {\n+                _setStyleValues(transitionValues, highlight.style);\n+                \n+                window.setTimeout(function () {\n+                    _setStyleValues(animateEndValues, highlight.style);\n+                }, 0);\n+            }\n+        \n+            window.document.body.appendChild(highlight);\n+        },\n+        \n+        add: function (element, doAnimation) {\n+            if (this._elementExists(element) || element === document) {\n+                return;\n+            }\n+            if (this.trigger) {\n+                _trigger(element, \"highlight\", 1);\n+            }\n+            this.elements.push(element);\n+            \n+            this._makeHighlightDiv(element, doAnimation);\n+        },\n+\n+        clear: function () {\n+            var i, highlights = window.document.querySelectorAll(\".\" + HIGHLIGHT_CLASSNAME),\n+                body = window.document.body;\n+        \n+            for (i = 0; i < highlights.length; i++) {\n+                body.removeChild(highlights[i]);\n+            }\n+\n+            if (this.trigger) {\n+                for (i = 0; i < this.elements.length; i++) {\n+                    _trigger(this.elements[i], \"highlight\", 0);\n+                }\n+            }\n+            \n+            this.elements = [];\n+        },\n+        \n+        redraw: function () {\n+            var i, highlighted;\n+            \n+            // When redrawing a selector-based highlight, run a new selector\n+            // query to ensure we have the latest set of elements to highlight.\n+            if (this.selector) {\n+                highlighted = window.document.querySelectorAll(this.selector);\n+            } else {\n+                highlighted = this.elements.slice(0);\n+            }\n+            \n+            this.clear();\n+            for (i = 0; i < highlighted.length; i++) {\n+                this.add(highlighted[i], false);\n+            }\n+        }\n+    };\n+\n+    var _currentEditor;\n+    function _toggleEditor(element) {\n+        _currentEditor = new Editor(element);\n+    }\n+\n+    var _currentMenu;\n+    function _toggleMenu(element) {\n+        if (_currentMenu) {\n+            _currentMenu.remove();\n+        }\n+        _currentMenu = new Menu(element);\n+    }\n+\n+    var _localHighlight;\n+    var _remoteHighlight;\n+    var _setup = false;\n+\n+\n+    /** Event Handlers ***********************************************************/\n+\n+    function onMouseOver(event) {\n+        if (_validEvent(event)) {\n+            _localHighlight.add(event.target, true);\n+        }\n+    }\n+\n+    function onMouseOut(event) {\n+        if (_validEvent(event)) {\n+            _localHighlight.clear();\n+        }\n+    }\n+\n+    function onMouseMove(event) {\n+        onMouseOver(event);\n+        document.removeEventListener(\"mousemove\", onMouseMove);\n+    }\n+\n+    function onClick(event) {\n+        if (_validEvent(event)) {\n+            event.preventDefault();\n+            event.stopPropagation();\n+            if (event.altKey) {\n+                _toggleEditor(event.target);\n+            } else {\n+                _toggleMenu(event.target);\n+            }\n+        }\n+    }\n+\n+    function onKeyUp(event) {\n+        if (_setup && !_validEvent(event)) {\n+            document.removeEventListener(\"keyup\", onKeyUp);\n+            document.removeEventListener(\"mouseover\", onMouseOver);\n+            document.removeEventListener(\"mouseout\", onMouseOut);\n+            document.removeEventListener(\"mousemove\", onMouseMove);\n+            document.removeEventListener(\"click\", onClick);\n+            _localHighlight.clear();\n+            _localHighlight = undefined;\n+            _setup = false;\n+        }\n+    }\n+\n+    function onKeyDown(event) {\n+        if (!_setup && _validEvent(event)) {\n+            document.addEventListener(\"keyup\", onKeyUp);\n+            document.addEventListener(\"mouseover\", onMouseOver);\n+            document.addEventListener(\"mouseout\", onMouseOut);\n+            document.addEventListener(\"mousemove\", onMouseMove);\n+            document.addEventListener(\"click\", onClick);\n+            _localHighlight = new Highlight(\"#ecc\", true);\n+            _setup = true;\n+        }\n+    }\n+\n+    /** Public Commands **********************************************************/\n+\n+    // keep alive. Called once a second when a Live Development connection is active.\n+    // If several seconds have passed without this method being called, we can assume\n+    // that the connection has been severed and we should remove all our code/hooks.\n+    function keepAlive() {\n+        lastKeepAliveTime = Date.now();\n+    }\n+    \n+    // show goto\n+    function showGoto(targets) {\n+        if (!_currentMenu) {\n+            return;\n+        }\n+        _currentMenu.createBody();\n+        var i;\n+        for (i in targets) {\n+            _currentMenu.addItem(targets[i]);\n+        }\n+        _currentMenu.show();\n+    }\n+\n+    // remove active highlights\n+    function hideHighlight() {\n+        if (_remoteHighlight) {\n+            _remoteHighlight.clear();\n+            _remoteHighlight = null;\n+        }\n+    }\n+\n+    // highlight a node\n+    function highlight(node, clear) {\n+        if (!_remoteHighlight) {\n+            _remoteHighlight = new Highlight(\"#cfc\");\n+        }\n+        if (clear) {\n+            _remoteHighlight.clear();\n+        }\n+        _remoteHighlight.add(node, true);\n+    }\n+\n+    // highlight a rule\n+    function highlightRule(rule) {\n+        hideHighlight();\n+        var i, nodes = document.querySelectorAll(rule);\n+        for (i = 0; i < nodes.length; i++) {\n+            highlight(nodes[i]);\n+        }\n+        _remoteHighlight.selector = rule;\n+    }\n+    \n+    // redraw active highlights\n+    function redrawHighlights() {\n+        if (_remoteHighlight) {\n+            _remoteHighlight.redraw();\n+        }\n+    }\n+    \n+    window.addEventListener(\"resize\", redrawHighlights);\n+    // Add a capture-phase scroll listener to update highlights when\n+    // any element scrolls.\n+    \n+    function _scrollHandler(e) {\n+        // Document scrolls can be updated immediately. Any other scrolls\n+        // need to be updated on a timer to ensure the layout is correct.\n+        if (e.target === document) {\n+            redrawHighlights();\n+        } else {\n+            if (_remoteHighlight || _localHighlight) {\n+                window.setTimeout(redrawHighlights, 0);\n+            }\n+        }\n+    }\n+    \n+    window.addEventListener(\"scroll\", _scrollHandler, true);\n+    \n+    var aliveTest = window.setInterval(function () {\n+        if (Date.now() > lastKeepAliveTime + KEEP_ALIVE_TIMEOUT) {\n+            // Remove highlights\n+            hideHighlight();\n+            \n+            // Remove listeners\n+            window.removeEventListener(\"resize\", redrawHighlights);\n+            window.removeEventListener(\"scroll\", _scrollHandler, true);\n+            \n+            // Clear this interval\n+            window.clearInterval(aliveTest);\n+        }\n+    }, 1000);\n+    \n+    /**\n+     * Constructor\n+     * @param {Document} htmlDocument\n+     */\n+    function DOMEditHandler(htmlDocument) {\n+        this.htmlDocument = htmlDocument;\n+        this.rememberedNodes = null;\n+        this.entityParseParent = htmlDocument.createElement(\"div\");\n+    }\n+\n+    /**\n+     * @private\n+     * Find the first matching element with the specified data-brackets-id\n+     * @param {string} id\n+     * @return {Element}\n+     */\n+    DOMEditHandler.prototype._queryBracketsID = function (id) {\n+        if (!id) {\n+            return null;\n+        }\n+        \n+        if (this.rememberedNodes && this.rememberedNodes[id]) {\n+            return this.rememberedNodes[id];\n+        }\n+        \n+        var results = this.htmlDocument.querySelectorAll(\"[data-brackets-id='\" + id + \"']\");\n+        return results && results[0];\n+    };\n+    \n+    /**\n+     * @private\n+     * Insert a new child element\n+     * @param {Element} targetElement Parent element already in the document\n+     * @param {Element} childElement New child element\n+     * @param {Object} edit\n+     */\n+    DOMEditHandler.prototype._insertChildNode = function (targetElement, childElement, edit) {\n+        var before = this._queryBracketsID(edit.beforeID),\n+            after  = this._queryBracketsID(edit.afterID);\n+        \n+        if (edit.firstChild) {\n+            before = targetElement.firstChild;\n+        } else if (edit.lastChild) {\n+            after = targetElement.lastChild;\n+        }\n+        \n+        if (before) {\n+            targetElement.insertBefore(childElement, before);\n+        } else if (after && (after !== targetElement.lastChild)) {\n+            targetElement.insertBefore(childElement, after.nextSibling);\n+        } else {\n+            targetElement.appendChild(childElement);\n+        }\n+    };\n+    \n+    /**\n+     * @private\n+     * Given a string containing encoded entity references, returns the string with the entities decoded.\n+     * @param {string} text The text to parse.\n+     * @return {string} The decoded text.\n+     */\n+    DOMEditHandler.prototype._parseEntities = function (text) {\n+        // Kind of a hack: just set the innerHTML of a div to the text, which will parse the entities, then\n+        // read the content out.\n+        var result;\n+        this.entityParseParent.innerHTML = text;\n+        result = this.entityParseParent.textContent;\n+        this.entityParseParent.textContent = \"\";\n+        return result;\n+    };\n+    \n+    /**\n+     * @private\n+     * @param {Node} node\n+     * @return {boolean} true if node expects its content to be raw text (not parsed for entities) according to the HTML5 spec.\n+     */\n+    function _isRawTextNode(node) {\n+        return (node.nodeType === Node.ELEMENT_NODE && /script|style|noscript|noframes|noembed|iframe|xmp/i.test(node.tagName));\n+    }\n+    \n+    /**\n+     * @private\n+     * Replace a range of text and comment nodes with an optional new text node\n+     * @param {Element} targetElement\n+     * @param {Object} edit\n+     */\n+    DOMEditHandler.prototype._textReplace = function (targetElement, edit) {\n+        function prevIgnoringHighlights(node) {\n+            do {\n+                node = node.previousSibling;\n+            } while (node && node.className === HIGHLIGHT_CLASSNAME);\n+            return node;\n+        }\n+        function nextIgnoringHighlights(node) {\n+            do {\n+                node = node.nextSibling;\n+            } while (node && node.className === HIGHLIGHT_CLASSNAME);\n+            return node;\n+        }\n+        function lastChildIgnoringHighlights(node) {\n+            node = (node.childNodes.length ? node.childNodes.item(node.childNodes.length - 1) : null);\n+            if (node && node.className === HIGHLIGHT_CLASSNAME) {\n+                node = prevIgnoringHighlights(node);\n+            }\n+            return node;\n+        }\n+        \n+        var start           = (edit.afterID)  ? this._queryBracketsID(edit.afterID)  : null,\n+            startMissing    = edit.afterID && !start,\n+            end             = (edit.beforeID) ? this._queryBracketsID(edit.beforeID) : null,\n+            endMissing      = edit.beforeID && !end,\n+            moveNext        = start && nextIgnoringHighlights(start),\n+            current         = moveNext || (end && prevIgnoringHighlights(end)) || lastChildIgnoringHighlights(targetElement),\n+            next,\n+            textNode        = (edit.content !== undefined) ? this.htmlDocument.createTextNode(_isRawTextNode(targetElement) ? edit.content : this._parseEntities(edit.content)) : null,\n+            lastRemovedWasText,\n+            isText;\n+        \n+        // remove all nodes inside the range\n+        while (current && (current !== end)) {\n+            isText = current.nodeType === Node.TEXT_NODE;\n+\n+            // if start is defined, delete following text nodes\n+            // if start is not defined, delete preceding text nodes\n+            next = (moveNext) ? nextIgnoringHighlights(current) : prevIgnoringHighlights(current);\n+\n+            // only delete up to the nearest element.\n+            // if the start/end tag was deleted in a prior edit, stop removing\n+            // nodes when we hit adjacent text nodes\n+            if ((current.nodeType === Node.ELEMENT_NODE) ||\n+                    ((startMissing || endMissing) && (isText && lastRemovedWasText))) {\n+                break;\n+            } else {\n+                lastRemovedWasText = isText;\n+\n+                current.remove();\n+                current = next;\n+            }\n+        }\n+        \n+        if (textNode) {\n+            // OK to use nextSibling here (not nextIgnoringHighlights) because we do literally\n+            // want to insert immediately after the start tag.\n+            if (start && start.nextSibling) {\n+                targetElement.insertBefore(textNode, start.nextSibling);\n+            } else if (end) {\n+                targetElement.insertBefore(textNode, end);\n+            } else {\n+                targetElement.appendChild(textNode);\n+            }\n+        }\n+    };\n+    \n+    /**\n+     * @private\n+     * Apply an array of DOM edits to the document\n+     * @param {Array.<Object>} edits\n+     */\n+    DOMEditHandler.prototype.apply = function (edits) {\n+        var targetID,\n+            targetElement,\n+            childElement,\n+            self = this;\n+        \n+        this.rememberedNodes = {};\n+        \n+        edits.forEach(function (edit) {\n+            var editIsSpecialTag = edit.type === \"elementInsert\" && (edit.tag === \"html\" || edit.tag === \"head\" || edit.tag === \"body\");\n+            \n+            if (edit.type === \"rememberNodes\") {\n+                edit.tagIDs.forEach(function (tagID) {\n+                    var node = self._queryBracketsID(tagID);\n+                    self.rememberedNodes[tagID] = node;\n+                    node.remove();\n+                });\n+                return;\n+            }\n+            \n+            targetID = edit.type.match(/textReplace|textDelete|textInsert|elementInsert|elementMove/) ? edit.parentID : edit.tagID;\n+            targetElement = self._queryBracketsID(targetID);\n+            \n+            if (!targetElement && !editIsSpecialTag) {\n+                console.error(\"data-brackets-id=\" + targetID + \" not found\");\n+                return;\n+            }\n+            \n+            switch (edit.type) {\n+            case \"attrChange\":\n+            case \"attrAdd\":\n+                targetElement.setAttribute(edit.attribute, self._parseEntities(edit.value));\n+                break;\n+            case \"attrDelete\":\n+                targetElement.removeAttribute(edit.attribute);\n+                break;\n+            case \"elementDelete\":\n+                targetElement.remove();\n+                break;\n+            case \"elementInsert\":\n+                childElement = null;\n+                if (editIsSpecialTag) {\n+                    // If we already have one of these elements (which we should), then\n+                    // just copy the attributes and set the ID.\n+                    childElement = self.htmlDocument[edit.tag === \"html\" ? \"documentElement\" : edit.tag];\n+                    if (!childElement) {\n+                        // Treat this as a normal insertion.\n+                        editIsSpecialTag = false;\n+                    }\n+                }\n+                if (!editIsSpecialTag) {\n+                    childElement = self.htmlDocument.createElement(edit.tag);\n+                }\n+                \n+                Object.keys(edit.attributes).forEach(function (attr) {\n+                    childElement.setAttribute(attr, self._parseEntities(edit.attributes[attr]));\n+                });\n+                childElement.setAttribute(\"data-brackets-id\", edit.tagID);\n+                \n+                if (!editIsSpecialTag) {\n+                    self._insertChildNode(targetElement, childElement, edit);\n+                }\n+                break;\n+            case \"elementMove\":\n+                childElement = self._queryBracketsID(edit.tagID);\n+                self._insertChildNode(targetElement, childElement, edit);\n+                break;\n+            case \"textInsert\":\n+                var textElement = self.htmlDocument.createTextNode(_isRawTextNode(targetElement) ? edit.content : self._parseEntities(edit.content));\n+                self._insertChildNode(targetElement, textElement, edit);\n+                break;\n+            case \"textReplace\":\n+            case \"textDelete\":\n+                self._textReplace(targetElement, edit);\n+                break;\n+            }\n+        });\n+        \n+        this.rememberedNodes = {};\n+        \n+        // update highlight after applying diffs\n+        redrawHighlights();\n+    };\n+    \n+    function applyDOMEdits(edits) {\n+        _editHandler.apply(edits);\n+    }\n+    \n+    /**\n+     *\n+     * @param {Element} elem\n+     */\n+    function _domElementToJSON(elem) {\n+        var json = { tag: elem.tagName.toLowerCase(), attributes: {}, children: [] },\n+            i,\n+            len,\n+            node,\n+            value;\n+        \n+        len = elem.attributes.length;\n+        for (i = 0; i < len; i++) {\n+            node = elem.attributes.item(i);\n+            value = (node.name === \"data-brackets-id\") ? parseInt(node.value, 10) : node.value;\n+            json.attributes[node.name] = value;\n+        }\n+        \n+        len = elem.childNodes.length;\n+        for (i = 0; i < len; i++) {\n+            node = elem.childNodes.item(i);\n+            \n+            // ignores comment nodes and visuals generated by live preview\n+            if (node.nodeType === Node.ELEMENT_NODE && node.className !== HIGHLIGHT_CLASSNAME) {\n+                json.children.push(_domElementToJSON(node));\n+            } else if (node.nodeType === Node.TEXT_NODE) {\n+                json.children.push({ content: node.nodeValue });\n+            }\n+        }\n+        \n+        return json;\n+    }\n+    \n+    function getSimpleDOM() {\n+        return JSON.stringify(_domElementToJSON(document.documentElement));\n+    }\n+    \n+    function reloadCSS(url) {","path":"protocol/remote/RemoteFunctions.js","position":799,"original_position":799,"commit_id":"7117ce63a452c68df61f1bc849d9a8d7cdf43e1a","original_commit_id":"7117ce63a452c68df61f1bc849d9a8d7cdf43e1a","user":{"login":"dangoor","id":55970,"avatar_url":"https://avatars.githubusercontent.com/u/55970?","gravatar_id":"f4749cce627b584fb9e59966a5d2c924","url":"https://api.github.com/users/dangoor","html_url":"https://github.com/dangoor","followers_url":"https://api.github.com/users/dangoor/followers","following_url":"https://api.github.com/users/dangoor/following{/other_user}","gists_url":"https://api.github.com/users/dangoor/gists{/gist_id}","starred_url":"https://api.github.com/users/dangoor/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dangoor/subscriptions","organizations_url":"https://api.github.com/users/dangoor/orgs","repos_url":"https://api.github.com/users/dangoor/repos","events_url":"https://api.github.com/users/dangoor/events{/privacy}","received_events_url":"https://api.github.com/users/dangoor/received_events","type":"User","site_admin":false},"body":"This is another case where if we had a persistent project model in Brackets, we could have a representation of the relationships between the files.","created_at":"2014-05-12T16:55:57Z","updated_at":"2014-05-12T16:55:57Z","html_url":"https://github.com/njx/brackets-livedev2/pull/2#discussion_r12540380","pull_request_url":"https://api.github.com/repos/njx/brackets-livedev2/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/njx/brackets-livedev2/pulls/comments/12540380"},"html":{"href":"https://github.com/njx/brackets-livedev2/pull/2#discussion_r12540380"},"pull_request":{"href":"https://api.github.com/repos/njx/brackets-livedev2/pulls/2"}}}}