From 7ac32b9cb1c5e6a209a3a79f22e676bb04991b76 Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Fri, 13 Jan 2017 15:47:06 +0530 Subject: [PATCH 1/7] Added Reverse Inspect in Live Preview using WebSockets, and now forward inspect brings the html element in focus to the viewport in Live Preview --- src/LiveDevelopment/Agents/RemoteFunctions.js | 36 +++++++ src/LiveDevelopment/LiveDevelopment.js | 3 +- .../transports/WebSocketTransport.js | 53 +++++++++ .../node/WebSocketTransportDomain.js | 102 ++++++++++++++++++ .../transports/node/package.json | 6 ++ src/language/HTMLInstrumentation.js | 17 +++ 6 files changed, 216 insertions(+), 1 deletion(-) create mode 100644 src/LiveDevelopment/transports/WebSocketTransport.js create mode 100644 src/LiveDevelopment/transports/node/WebSocketTransportDomain.js create mode 100644 src/LiveDevelopment/transports/node/package.json diff --git a/src/LiveDevelopment/Agents/RemoteFunctions.js b/src/LiveDevelopment/Agents/RemoteFunctions.js index 5f302609f30..20131dad7bb 100644 --- a/src/LiveDevelopment/Agents/RemoteFunctions.js +++ b/src/LiveDevelopment/Agents/RemoteFunctions.js @@ -319,6 +319,10 @@ function RemoteFunctions(experimental) { if (this.trigger) { _trigger(element, "highlight", 1); } + + if (!window.event) { + element.scrollIntoViewIfNeeded(); + } this.elements.push(element); this._makeHighlightDiv(element, doAnimation); @@ -824,7 +828,39 @@ function RemoteFunctions(experimental) { if (experimental) { window.document.addEventListener("keydown", onKeyDown); } + + var _ws = null; + + function onDocumentClick(event) { + var element = event.target, + currentDataId, + newDataId; + if (element && element.hasAttribute('data-brackets-id')) { + _ws.send(JSON.stringify({ + type: "message", + message: element.getAttribute('data-brackets-id') + })); + } + } + window.document.addEventListener("click", onDocumentClick); + + + function createWebSocket() { + _ws = new WebSocket("ws://localhost:8125"); + _ws.onopen = function () { + }; + + _ws.onmessage = function (evt) { + var received_msg = evt.data; + }; + + _ws.onclose = function () { + // websocket is closed. + }; + } + + createWebSocket(); return { "DOMEditHandler" : DOMEditHandler, "keepAlive" : keepAlive, diff --git a/src/LiveDevelopment/LiveDevelopment.js b/src/LiveDevelopment/LiveDevelopment.js index cd54b987b85..de0509fd348 100644 --- a/src/LiveDevelopment/LiveDevelopment.js +++ b/src/LiveDevelopment/LiveDevelopment.js @@ -94,7 +94,8 @@ define(function LiveDevelopment(require, exports, module) { ProjectManager = require("project/ProjectManager"), Strings = require("strings"), StringUtils = require("utils/StringUtils"), - UserServer = require("LiveDevelopment/Servers/UserServer").UserServer; + UserServer = require("LiveDevelopment/Servers/UserServer").UserServer, + WebSocketTransport = require("LiveDevelopment/transports/WebSocketTransport"); // Inspector var Inspector = require("LiveDevelopment/Inspector/Inspector"); diff --git a/src/LiveDevelopment/transports/WebSocketTransport.js b/src/LiveDevelopment/transports/WebSocketTransport.js new file mode 100644 index 00000000000..af3c9e998ba --- /dev/null +++ b/src/LiveDevelopment/transports/WebSocketTransport.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2014 - present Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +// This transport provides a WebSocket connection between Brackets and a live browser preview. +// This is just a thin wrapper around the Node extension (WebSocketTransportDomain) that actually +// provides the WebSocket server and handles the communication. We also rely on an injected script in +// the browser for the other end of the transport. + +define(function (require, exports, module) { + "use strict"; + + var FileUtils = require("file/FileUtils"), + NodeDomain = require("utils/NodeDomain"), + EditorManager = require("editor/EditorManager"), + HTMLInstrumentation = require("language/HTMLInstrumentation"); + + // The node extension that actually provides the WebSocket server. + + var domainPath = FileUtils.getNativeBracketsDirectoryPath() + "/" + FileUtils.getNativeModuleDirectoryPath(module) + "/node/WebSocketTransportDomain"; + + var WebSocketTransportDomain = new NodeDomain("webSocketTransport", domainPath); + + // Events + + WebSocketTransportDomain.on("message", function (obj, message) { + console.log("WebSocketTransport - event - message" + " - " + message); + var editor = EditorManager.getActiveEditor(), position; + position = HTMLInstrumentation.getPositionFromTagid(editor, parseInt(message, 10)); + if (position) { + editor.setCursorPos(position.line, position.ch, true); + } + }); +}); diff --git a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js new file mode 100644 index 00000000000..4b75b1690bb --- /dev/null +++ b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2014 - present Adobe Systems Incorporated. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + */ + +/*eslint-env node */ +/*jslint node: true */ +"use strict"; + +var WebSocketServer = require("ws").Server; + +/** + * @private + * The WebSocket server we listen for incoming connections on. + * @type {?WebSocketServer} + */ +var _wsServer; + +/** + * @private + * The Brackets domain manager for registering node extensions. + * @type {?DomainManager} + */ +var _domainManager; + +// This must match the port declared in RemoteFunctions.js:createWebSocket +// TODO: randomize this? +var SOCKET_PORT = 8125; + +/** + * @private + * Creates the WebSocketServer and handles incoming connections. + */ +function _createServer() { + if (!_wsServer) { + // TODO: make port configurable, or use random port + _wsServer = new WebSocketServer({port: SOCKET_PORT}); + _wsServer.on("connection", function (ws) { + ws.on("message", function (msg) { + console.log("WebSocketServer - received - " + msg); + var msgObj; + try { + msgObj = JSON.parse(msg); + } catch (e) { + console.error("webSocketTransport: Error parsing message: " + msg); + return; + } + + if (msgObj.type === "message") { + _domainManager.emitEvent("webSocketTransport", "message", msgObj.message); + } else { + console.error("webSocketTransport: Got bad socket message type: " + msg); + } + }).on("error", function (e) { + console.error("webSocketTransport: Error on socket : " + e); + }).on("close", function () { + console.log("webSocketTransport closed"); + }); + }).on("error", function (e) { + console.error("webSocketTransport: Error on live preview server creation: " + e); + }); + } +} + +/** + * Initializes the domain and registers commands. + * @param {DomainManager} domainManager The DomainManager for the server + */ +function init(domainManager) { + _domainManager = domainManager; + if (!domainManager.hasDomain("webSocketTransport")) { + domainManager.registerDomain("webSocketTransport", {major: 0, minor: 1}); + } + domainManager.registerEvent( + "webSocketTransport", + "message", + [ + {name: "msg", type: "string", description: "JSON message from client page"} + ] + ); + _createServer(); +} + +exports.init = init; diff --git a/src/LiveDevelopment/transports/node/package.json b/src/LiveDevelopment/transports/node/package.json new file mode 100644 index 00000000000..632f777c135 --- /dev/null +++ b/src/LiveDevelopment/transports/node/package.json @@ -0,0 +1,6 @@ +{ + "name": "brackets-livedev-server", + "dependencies": { + "ws": "~0.4.31" + } +} diff --git a/src/language/HTMLInstrumentation.js b/src/language/HTMLInstrumentation.js index f763ec83e63..00a10881f14 100644 --- a/src/language/HTMLInstrumentation.js +++ b/src/language/HTMLInstrumentation.js @@ -87,6 +87,22 @@ define(function (require, exports, module) { return pos1 && pos2 && pos1.line === pos2.line && pos1.ch === pos2.ch; } + function getPositionFromTagid(editor, tagId) { + var marks = editor._codeMirror.getAllMarks(), + i, + markFound, + position; + + for (i = 0; i < marks.length; i++) { + if (marks[i].tagID === tagId) { + markFound = marks[i]; + } + } + if (markFound) { + return markFound.find().from; + } + } + /** * @private * Filters the given marks to find the ones that correspond to instrumented tags, @@ -805,6 +821,7 @@ define(function (require, exports, module) { exports._markText = _markText; exports._getMarkerAtDocumentPos = _getMarkerAtDocumentPos; exports._getTagIDAtDocumentPos = _getTagIDAtDocumentPos; + exports.getPositionFromTagid = getPositionFromTagid; exports._markTextFromDOM = _markTextFromDOM; exports._updateDOM = _updateDOM; exports._allowIncremental = allowIncremental; From 8c141ce2d6522881b31c199c41a41fe55ba1c30e Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Tue, 17 Jan 2017 14:03:07 +0530 Subject: [PATCH 2/7] Added commands to start and close the WebSocketServer used in Live Preview and also now port is read from preferences file --- src/LiveDevelopment/Agents/RemoteAgent.js | 11 +++-- src/LiveDevelopment/Agents/RemoteFunctions.js | 4 +- src/LiveDevelopment/LiveDevelopment.js | 9 +++- .../transports/WebSocketTransport.js | 13 +++++- .../node/WebSocketTransportDomain.js | 46 ++++++++++++++++--- src/language/HTMLInstrumentation.js | 4 +- src/nls/root/strings.js | 3 +- 7 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/LiveDevelopment/Agents/RemoteAgent.js b/src/LiveDevelopment/Agents/RemoteAgent.js index 855b19a1873..6bb71e7c839 100644 --- a/src/LiveDevelopment/Agents/RemoteAgent.js +++ b/src/LiveDevelopment/Agents/RemoteAgent.js @@ -33,10 +33,11 @@ define(function RemoteAgent(require, exports, module) { "use strict"; - var LiveDevelopment = require("LiveDevelopment/LiveDevelopment"), - EventDispatcher = require("utils/EventDispatcher"), - Inspector = require("LiveDevelopment/Inspector/Inspector"), - RemoteFunctions = require("text!LiveDevelopment/Agents/RemoteFunctions.js"); + var LiveDevelopment = require("LiveDevelopment/LiveDevelopment"), + EventDispatcher = require("utils/EventDispatcher"), + Inspector = require("LiveDevelopment/Inspector/Inspector"), + RemoteFunctions = require("text!LiveDevelopment/Agents/RemoteFunctions.js"), + PreferencesManager = require("preferences/PreferencesManager"); var _load; // deferred load var _objectId; // the object id of the remote object @@ -130,7 +131,7 @@ define(function RemoteAgent(require, exports, module) { _stopKeepAliveInterval(); // inject RemoteFunctions - var command = "window._LD=" + RemoteFunctions + "(" + LiveDevelopment.config.experimental + ");"; + var command = "window._LD=" + RemoteFunctions + "(" + LiveDevelopment.config.experimental + "," + PreferencesManager.get("livedev.wsPort") + ");"; Inspector.Runtime.evaluate(command, function onEvaluate(response) { if (response.error || response.wasThrown) { diff --git a/src/LiveDevelopment/Agents/RemoteFunctions.js b/src/LiveDevelopment/Agents/RemoteFunctions.js index 20131dad7bb..2e1181aeb2e 100644 --- a/src/LiveDevelopment/Agents/RemoteFunctions.js +++ b/src/LiveDevelopment/Agents/RemoteFunctions.js @@ -30,7 +30,7 @@ * modules should define a single function that returns an object of all * exported functions. */ -function RemoteFunctions(experimental) { +function RemoteFunctions(experimental, remoteWSPort) { "use strict"; var lastKeepAliveTime = Date.now(); @@ -847,7 +847,7 @@ function RemoteFunctions(experimental) { function createWebSocket() { - _ws = new WebSocket("ws://localhost:8125"); + _ws = new WebSocket("ws://localhost:" + remoteWSPort); _ws.onopen = function () { }; diff --git a/src/LiveDevelopment/LiveDevelopment.js b/src/LiveDevelopment/LiveDevelopment.js index de0509fd348..57d4438d118 100644 --- a/src/LiveDevelopment/LiveDevelopment.js +++ b/src/LiveDevelopment/LiveDevelopment.js @@ -95,7 +95,8 @@ define(function LiveDevelopment(require, exports, module) { Strings = require("strings"), StringUtils = require("utils/StringUtils"), UserServer = require("LiveDevelopment/Servers/UserServer").UserServer, - WebSocketTransport = require("LiveDevelopment/transports/WebSocketTransport"); + WebSocketTransport = require("LiveDevelopment/transports/WebSocketTransport"), + PreferencesManager = require("preferences/PreferencesManager"); // Inspector var Inspector = require("LiveDevelopment/Inspector/Inspector"); @@ -196,6 +197,10 @@ define(function LiveDevelopment(require, exports, module) { * Handles of registered servers */ var _regServers = []; + + PreferencesManager.definePreference("livedev.wsPort", "number", 8125, { + description: Strings.DESCRIPTION_LIVEDEV_WEBSOCKET_PORT + }); function _isPromisePending(promise) { return promise && promise.state() === "pending"; @@ -850,6 +855,7 @@ define(function LiveDevelopment(require, exports, module) { * @return {jQuery.Promise} Always return a resolved promise once the connection is closed */ function _close(doCloseWindow, reason) { + WebSocketTransport.closeWebSocketServer(); if (_closeDeferred) { return _closeDeferred; } else { @@ -1363,6 +1369,7 @@ define(function LiveDevelopment(require, exports, module) { // wait for server (StaticServer, Base URL or file:) prepareServerPromise .done(function () { + WebSocketTransport.createWebSocketServer(PreferencesManager.get("livedev.wsPort")); _doLaunchAfterServerReady(doc); }) .fail(function () { diff --git a/src/LiveDevelopment/transports/WebSocketTransport.js b/src/LiveDevelopment/transports/WebSocketTransport.js index af3c9e998ba..209f50f8d05 100644 --- a/src/LiveDevelopment/transports/WebSocketTransport.js +++ b/src/LiveDevelopment/transports/WebSocketTransport.js @@ -45,9 +45,20 @@ define(function (require, exports, module) { WebSocketTransportDomain.on("message", function (obj, message) { console.log("WebSocketTransport - event - message" + " - " + message); var editor = EditorManager.getActiveEditor(), position; - position = HTMLInstrumentation.getPositionFromTagid(editor, parseInt(message, 10)); + position = HTMLInstrumentation.getPositionFromTagId(editor, parseInt(message, 10)); if (position) { editor.setCursorPos(position.line, position.ch, true); } }); + + function createWebSocketServer(port) { + WebSocketTransportDomain.exec("start", parseInt(port, 10)); + } + + function closeWebSocketServer() { + WebSocketTransportDomain.exec("close"); + } + + exports.createWebSocketServer = createWebSocketServer; + exports.closeWebSocketServer = closeWebSocketServer; }); diff --git a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js index 4b75b1690bb..316d7d80754 100644 --- a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js +++ b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js @@ -41,18 +41,14 @@ var _wsServer; */ var _domainManager; -// This must match the port declared in RemoteFunctions.js:createWebSocket -// TODO: randomize this? -var SOCKET_PORT = 8125; - /** * @private * Creates the WebSocketServer and handles incoming connections. */ -function _createServer() { +function _createServer(socketPort) { if (!_wsServer) { // TODO: make port configurable, or use random port - _wsServer = new WebSocketServer({port: SOCKET_PORT}); + _wsServer = new WebSocketServer({port: socketPort}); _wsServer.on("connection", function (ws) { ws.on("message", function (msg) { console.log("WebSocketServer - received - " + msg); @@ -80,6 +76,24 @@ function _createServer() { } } +/** + * Initializes the socket server. + * @param {number} port + */ +function _cmdStart(port) { + _createServer(port); +} + +/** + * Kill the WebSocketServer + */ +function _cmdClose() { + if (_wsServer) { + _wsServer.close(); + _wsServer = null; + } +} + /** * Initializes the domain and registers commands. * @param {DomainManager} domainManager The DomainManager for the server @@ -96,7 +110,25 @@ function init(domainManager) { {name: "msg", type: "string", description: "JSON message from client page"} ] ); - _createServer(); + domainManager.registerCommand( + "webSocketTransport", // domain name + "start", // command name + _cmdStart, // command handler function + false, // this command is synchronous in Node + "Creates the WS server", + [ + {name: "port", type: "number", description: "Port on which server needs to listen"} + ], + [] + ); + domainManager.registerCommand( + "webSocketTransport", // domain name + "close", // command name + _cmdClose, // command handler function + false, // this command is synchronous in Node + "Kills the websocket server", + [] + ); } exports.init = init; diff --git a/src/language/HTMLInstrumentation.js b/src/language/HTMLInstrumentation.js index 00a10881f14..85f7f222204 100644 --- a/src/language/HTMLInstrumentation.js +++ b/src/language/HTMLInstrumentation.js @@ -87,7 +87,7 @@ define(function (require, exports, module) { return pos1 && pos2 && pos1.line === pos2.line && pos1.ch === pos2.ch; } - function getPositionFromTagid(editor, tagId) { + function getPositionFromTagId(editor, tagId) { var marks = editor._codeMirror.getAllMarks(), i, markFound, @@ -821,7 +821,7 @@ define(function (require, exports, module) { exports._markText = _markText; exports._getMarkerAtDocumentPos = _getMarkerAtDocumentPos; exports._getTagIDAtDocumentPos = _getTagIDAtDocumentPos; - exports.getPositionFromTagid = getPositionFromTagid; + exports.getPositionFromTagId = getPositionFromTagId; exports._markTextFromDOM = _markTextFromDOM; exports._updateDOM = _updateDOM; exports._allowIncremental = allowIncremental; diff --git a/src/nls/root/strings.js b/src/nls/root/strings.js index 193cba4193e..3541b304ac1 100644 --- a/src/nls/root/strings.js +++ b/src/nls/root/strings.js @@ -789,5 +789,6 @@ define({ "DEFAULT_PREFERENCES_JSON_DEFAULT" : "Default", "DESCRIPTION_PURE_CODING_SURFACE" : "true to enable code only mode and hide all other UI elements in {APP_NAME}", "DESCRIPTION_INDENT_LINE_COMMENT" : "true to enable indenting of line comments", - "DESCRIPTION_RECENT_FILES_NAV" : "Enable/disable navigation in recent files" + "DESCRIPTION_RECENT_FILES_NAV" : "Enable/disable navigation in recent files", + "DESCRIPTION_LIVEDEV_WEBSOCKET_PORT" : "Port on which WebSocket Server runs for Live Preview" }); From 06b5418aec9211b1aa2bc14a5ba4e86c99de6569 Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Wed, 18 Jan 2017 10:45:52 +0530 Subject: [PATCH 3/7] Addressed review comments- used window.scrollTo instead of scrollIntoViewIfNeeded, added a check for port number in RemoteFunctions and some minor changes --- src/LiveDevelopment/Agents/RemoteFunctions.js | 36 +++++++++++++++---- .../transports/WebSocketTransport.js | 16 +++++---- .../node/WebSocketTransportDomain.js | 17 +++++++-- src/language/HTMLInstrumentation.js | 5 +-- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/LiveDevelopment/Agents/RemoteFunctions.js b/src/LiveDevelopment/Agents/RemoteFunctions.js index 2e1181aeb2e..c79361fc6a9 100644 --- a/src/LiveDevelopment/Agents/RemoteFunctions.js +++ b/src/LiveDevelopment/Agents/RemoteFunctions.js @@ -22,7 +22,7 @@ */ /*jslint forin: true */ -/*global Node */ +/*global Node, document */ /*theseus instrument: false */ /** @@ -98,6 +98,23 @@ function RemoteFunctions(experimental, remoteWSPort) { element.removeAttribute(key); } } + + // Checks if the element is in Viewport in the client browser + function isInViewport(element) { + var rect = element.getBoundingClientRect(); + var html = document.documentElement; + return ( + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= (window.innerHeight || html.clientHeight) && + rect.right <= (window.innerWidth || html.clientWidth) + ); + } + + // returns the distance from the top of the closest relatively positioned parent element + function getDocumentOffsetTop(element) { + return element.offsetTop + (element.offsetParent ? getDocumentOffsetTop(element.offsetParent) : 0); + } // construct the info menu function Menu(element) { @@ -320,8 +337,12 @@ function RemoteFunctions(experimental, remoteWSPort) { _trigger(element, "highlight", 1); } - if (!window.event) { - element.scrollIntoViewIfNeeded(); + if (!window.event && !isInViewport(element)) { + var top = getDocumentOffsetTop(element); + if (top) { + top -= (window.innerHeight / 2); + window.scrollTo(0, top); + } } this.elements.push(element); @@ -835,7 +856,8 @@ function RemoteFunctions(experimental, remoteWSPort) { var element = event.target, currentDataId, newDataId; - if (element && element.hasAttribute('data-brackets-id')) { + + if (_ws && element && element.hasAttribute('data-brackets-id')) { _ws.send(JSON.stringify({ type: "message", message: element.getAttribute('data-brackets-id') @@ -852,7 +874,6 @@ function RemoteFunctions(experimental, remoteWSPort) { }; _ws.onmessage = function (evt) { - var received_msg = evt.data; }; _ws.onclose = function () { @@ -860,7 +881,10 @@ function RemoteFunctions(experimental, remoteWSPort) { }; } - createWebSocket(); + if (remoteWSPort) { + createWebSocket(); + } + return { "DOMEditHandler" : DOMEditHandler, "keepAlive" : keepAlive, diff --git a/src/LiveDevelopment/transports/WebSocketTransport.js b/src/LiveDevelopment/transports/WebSocketTransport.js index 209f50f8d05..9972bc85118 100644 --- a/src/LiveDevelopment/transports/WebSocketTransport.js +++ b/src/LiveDevelopment/transports/WebSocketTransport.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 - present Adobe Systems Incorporated. All rights reserved. + * Copyright (c) 2017 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -21,10 +21,12 @@ * */ -// This transport provides a WebSocket connection between Brackets and a live browser preview. -// This is just a thin wrapper around the Node extension (WebSocketTransportDomain) that actually -// provides the WebSocket server and handles the communication. We also rely on an injected script in -// the browser for the other end of the transport. +/** + * This transport provides a WebSocket connection between Brackets and a live browser preview. + * This is just a thin wrapper around the Node extension (WebSocketTransportDomain) that actually + * provides the WebSocket server and handles the communication. We also rely on an injected script in + * the browser for the other end of the transport. + */ define(function (require, exports, module) { "use strict"; @@ -44,8 +46,8 @@ define(function (require, exports, module) { WebSocketTransportDomain.on("message", function (obj, message) { console.log("WebSocketTransport - event - message" + " - " + message); - var editor = EditorManager.getActiveEditor(), position; - position = HTMLInstrumentation.getPositionFromTagId(editor, parseInt(message, 10)); + var editor = EditorManager.getActiveEditor(), + position = HTMLInstrumentation.getPositionFromTagId(editor, parseInt(message, 10)); if (position) { editor.setCursorPos(position.line, position.ch, true); } diff --git a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js index 316d7d80754..47c95510936 100644 --- a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js +++ b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 - present Adobe Systems Incorporated. All rights reserved. + * Copyright (c) 2017 - present Adobe Systems Incorporated. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -103,13 +103,19 @@ function init(domainManager) { if (!domainManager.hasDomain("webSocketTransport")) { domainManager.registerDomain("webSocketTransport", {major: 0, minor: 1}); } + domainManager.registerEvent( "webSocketTransport", "message", [ - {name: "msg", type: "string", description: "JSON message from client page"} + { + name: "msg", + type: "string", + description: "JSON message from client page" + } ] ); + domainManager.registerCommand( "webSocketTransport", // domain name "start", // command name @@ -117,10 +123,15 @@ function init(domainManager) { false, // this command is synchronous in Node "Creates the WS server", [ - {name: "port", type: "number", description: "Port on which server needs to listen"} + { + name: "port", + type: "number", + description: "Port on which server needs to listen" + } ], [] ); + domainManager.registerCommand( "webSocketTransport", // domain name "close", // command name diff --git a/src/language/HTMLInstrumentation.js b/src/language/HTMLInstrumentation.js index 85f7f222204..1e30be263c3 100644 --- a/src/language/HTMLInstrumentation.js +++ b/src/language/HTMLInstrumentation.js @@ -90,8 +90,7 @@ define(function (require, exports, module) { function getPositionFromTagId(editor, tagId) { var marks = editor._codeMirror.getAllMarks(), i, - markFound, - position; + markFound; for (i = 0; i < marks.length; i++) { if (marks[i].tagID === tagId) { @@ -100,6 +99,8 @@ define(function (require, exports, module) { } if (markFound) { return markFound.find().from; + } else { + return null; } } From 71c4148e8763a18706d71596582849c55b0e737e Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Wed, 18 Jan 2017 17:14:36 +0530 Subject: [PATCH 4/7] Added dependency of ws in the root directory --- npm-shrinkwrap.json | 25 +++++++++++++++++++ package.json | 3 ++- .../transports/node/package.json | 6 ----- src/config.json | 7 +++--- 4 files changed, 31 insertions(+), 10 deletions(-) delete mode 100644 src/LiveDevelopment/transports/node/package.json diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 468bde46e56..ac6be64c3b3 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -62,6 +62,11 @@ "from": "chokidar@1.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.6.0.tgz" }, + "commander": { + "version": "2.1.0", + "from": "commander@>=2.1.0 <2.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz" + }, "concat-map": { "version": "0.0.1", "from": "concat-map@0.0.1", @@ -207,6 +212,11 @@ "from": "minimatch@>=3.0.2 <4.0.0", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz" }, + "nan": { + "version": "1.0.0", + "from": "nan@>=1.0.0 <1.1.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-1.0.0.tgz" + }, "normalize-path": { "version": "2.0.1", "from": "normalize-path@>=2.0.1 <3.0.0", @@ -217,6 +227,11 @@ "from": "object.omit@>=2.0.0 <3.0.0", "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz" }, + "options": { + "version": "0.0.6", + "from": "options@>=0.0.5", + "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz" + }, "parse-glob": { "version": "3.0.4", "from": "parse-glob@>=3.0.4 <4.0.0", @@ -277,10 +292,20 @@ "from": "string_decoder@>=0.10.0 <0.11.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, + "tinycolor": { + "version": "0.0.1", + "from": "tinycolor@>=0.0.0 <1.0.0", + "resolved": "https://registry.npmjs.org/tinycolor/-/tinycolor-0.0.1.tgz" + }, "util-deprecate": { "version": "1.0.2", "from": "util-deprecate@>=1.0.1 <1.1.0", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + }, + "ws": { + "version": "0.4.32", + "from": "ws@>=0.4.31 <0.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz" } } } diff --git a/package.json b/package.json index 6f05d07cd2e..39825cd1b5a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "dependencies": { "anymatch": "1.3.0", "chokidar": "1.6.0", - "lodash": "4.15.0" + "lodash": "4.15.0", + "ws": "~0.4.31" }, "devDependencies": { "glob": "7.0.6", diff --git a/src/LiveDevelopment/transports/node/package.json b/src/LiveDevelopment/transports/node/package.json deleted file mode 100644 index 632f777c135..00000000000 --- a/src/LiveDevelopment/transports/node/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "brackets-livedev-server", - "dependencies": { - "ws": "~0.4.31" - } -} diff --git a/src/config.json b/src/config.json index 78c75f94a01..cf2d454df5d 100644 --- a/src/config.json +++ b/src/config.json @@ -20,7 +20,7 @@ "extension_url": "https://s3.amazonaws.com/extend.brackets/{0}/{0}-{1}.zip", "linting.enabled_by_default": true, "build_timestamp": "", - "healthDataServerURL": "https://healthdev.brackets.io/healthDataLog" + "healthDataServerURL": "https://health.brackets.io/healthDataLog" }, "name": "Brackets", "version": "1.9.0-0", @@ -38,7 +38,8 @@ "dependencies": { "anymatch": "1.3.0", "chokidar": "1.6.0", - "lodash": "4.15.0" + "lodash": "4.15.0", + "ws": "~0.4.31" }, "devDependencies": { "glob": "7.0.6", @@ -78,4 +79,4 @@ "url": "https://github.com/adobe/brackets/blob/master/LICENSE" } ] -} +} \ No newline at end of file From dbe1329438272918ed5ee06a9ffcacd447b07330 Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Wed, 18 Jan 2017 18:49:09 +0530 Subject: [PATCH 5/7] Used window.document instead of defining global document in RemoteFunctions.js --- src/LiveDevelopment/Agents/RemoteFunctions.js | 10 +++++----- src/config.json | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/LiveDevelopment/Agents/RemoteFunctions.js b/src/LiveDevelopment/Agents/RemoteFunctions.js index c79361fc6a9..7060ce5a3fd 100644 --- a/src/LiveDevelopment/Agents/RemoteFunctions.js +++ b/src/LiveDevelopment/Agents/RemoteFunctions.js @@ -22,7 +22,7 @@ */ /*jslint forin: true */ -/*global Node, document */ +/*global Node */ /*theseus instrument: false */ /** @@ -102,7 +102,7 @@ function RemoteFunctions(experimental, remoteWSPort) { // Checks if the element is in Viewport in the client browser function isInViewport(element) { var rect = element.getBoundingClientRect(); - var html = document.documentElement; + var html = window.document.documentElement; return ( rect.top >= 0 && rect.left >= 0 && @@ -864,20 +864,20 @@ function RemoteFunctions(experimental, remoteWSPort) { })); } } - - window.document.addEventListener("click", onDocumentClick); function createWebSocket() { _ws = new WebSocket("ws://localhost:" + remoteWSPort); _ws.onopen = function () { + window.document.addEventListener("click", onDocumentClick); }; _ws.onmessage = function (evt) { }; _ws.onclose = function () { - // websocket is closed. + // websocket is closed + window.document.removeEventListener("click", onDocumentClick); }; } diff --git a/src/config.json b/src/config.json index cf2d454df5d..619a19b0a3b 100644 --- a/src/config.json +++ b/src/config.json @@ -20,7 +20,7 @@ "extension_url": "https://s3.amazonaws.com/extend.brackets/{0}/{0}-{1}.zip", "linting.enabled_by_default": true, "build_timestamp": "", - "healthDataServerURL": "https://health.brackets.io/healthDataLog" + "healthDataServerURL": "https://healthdev.brackets.io/healthDataLog" }, "name": "Brackets", "version": "1.9.0-0", @@ -79,4 +79,4 @@ "url": "https://github.com/adobe/brackets/blob/master/LICENSE" } ] -} \ No newline at end of file +} From aae3943eff424d1cfb95c900bbfa10c0ad310097 Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Sat, 21 Jan 2017 23:23:54 +0530 Subject: [PATCH 6/7] Added JSDoc description in WebSocketTransportDomain.js and used lodash instead of for loop in HTMLInstrumentation.js --- .../transports/node/WebSocketTransportDomain.js | 7 +++++++ src/language/HTMLInstrumentation.js | 11 +++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js index 47c95510936..2c6218c6c9e 100644 --- a/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js +++ b/src/LiveDevelopment/transports/node/WebSocketTransportDomain.js @@ -21,6 +21,13 @@ * */ +/** + * WebSocketTransportDomain creates a websocket server for Live Preview + * It receives the message containing tagID from the Remote Client(onClick) + * and emits an event which is listened by WebSocketTransport which + * brings the cursor to the tag corresponding to that particular tagID + */ + /*eslint-env node */ /*jslint node: true */ "use strict"; diff --git a/src/language/HTMLInstrumentation.js b/src/language/HTMLInstrumentation.js index 1e30be263c3..75efc3fc660 100644 --- a/src/language/HTMLInstrumentation.js +++ b/src/language/HTMLInstrumentation.js @@ -53,7 +53,8 @@ define(function (require, exports, module) { var DocumentManager = require("document/DocumentManager"), HTMLSimpleDOM = require("./HTMLSimpleDOM"), - HTMLDOMDiff = require("./HTMLDOMDiff"); + HTMLDOMDiff = require("./HTMLDOMDiff"), + _ = require("thirdparty/lodash"); var allowIncremental = true; @@ -92,11 +93,9 @@ define(function (require, exports, module) { i, markFound; - for (i = 0; i < marks.length; i++) { - if (marks[i].tagID === tagId) { - markFound = marks[i]; - } - } + markFound = _.find(marks, function (mark) { + return (mark.tagID === tagId); + }); if (markFound) { return markFound.find().from; } else { From 819d1221924170a7a0af8e12ea4eeb36c8ac9749 Mon Sep 17 00:00:00 2001 From: Saurabh Kathpalia Date: Wed, 25 Jan 2017 11:35:17 +0530 Subject: [PATCH 7/7] Added copy task of copying LiveDevelopment/transports to dist --- Gruntfile.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Gruntfile.js b/Gruntfile.js index 25c14983dc0..9f56ba54111 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -69,6 +69,7 @@ module.exports = function (grunt) { 'dependencies.js', 'thirdparty/requirejs/require.js', 'LiveDevelopment/launch.html', + 'LiveDevelopment/transports/**', 'LiveDevelopment/MultiBrowserImpl/transports/**', 'LiveDevelopment/MultiBrowserImpl/launchers/**' ]