Skip to content
This repository was archived by the owner on Sep 6, 2021. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 95 additions & 21 deletions src/LiveDevelopment/Agents/RemoteAgent.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,14 @@ define(function RemoteAgent(require, exports, module) {

var LiveDevelopment = require("LiveDevelopment/LiveDevelopment"),
Inspector = require("LiveDevelopment/Inspector/Inspector"),
RemoteFunctions = require("text!LiveDevelopment/Agents/RemoteFunctions.js");
RemoteFunctions = require("text!LiveDevelopment/Agents/RemoteFunctions.js"),
jQueryRemoteSrc = require("text!thirdparty/jQuery-2.0.1.min.js");

var $REMOTE = "window._LDjQuery";

var _load; // deferred load
var _objectId; // the object id of the remote object
var _jQueryRemoteObjectId;
var _intervalId; // interval used to send keepAlive events

// WebInspector Event: DOM.attributeModified
Expand All @@ -54,40 +58,61 @@ define(function RemoteAgent(require, exports, module) {
}
}

/** Call a remote function
* The parameters are passed on to the remote functions. Nodes are resolved
* and sent as objectIds.
* @param {string} function name
*/
function call(method, varargs) {
console.assert(_objectId, "Attempted to call remote method without objectId set.");
var args = Array.prototype.slice.call(arguments, 1);
function _call(objectId, method, varargs) {
console.assert(objectId, "Attempted to call remote method without objectId set.");
var args = Array.prototype.slice.call(arguments, 2),
callback,
deferred = new $.Deferred();

// if the last argument is a function it is the callback function
var callback;
if (typeof args[args.length - 1] === "function") {
callback = args.pop();
}

// Resolve node parameters
var i;
for (i in args) {
if (args[i].nodeId) {
args[i] = args[i].resolve();
args = args.map(function (arg) {
if (arg && arg.nodeId) {
return arg.resolve();
}
}

return arg;
});

$.when.apply(undefined, args).done(function onResolvedAllNodes() {
var i, arg, params = [];
for (i in arguments) {
arg = args[i];
var params = [];

args.forEach(function (arg) {
if (arg.objectId) {
params.push({objectId: arg.objectId});
} else {
params.push({value: arg});
}
}
Inspector.Runtime.callFunctionOn(_objectId, "_LD." + method, params, undefined, callback);
});

Inspector.Runtime.callFunctionOn(objectId, method, params, undefined, callback)
.then(deferred.resolve, deferred.reject);
});

return deferred.promise();
}

/** Call a remote function
* The parameters are passed on to the remote functions. Nodes are resolved
* and sent as objectIds.
* @param {string} function name
*/
function call(method, varargs) {
var argsArray = [_objectId, "_LD." + method];

if (arguments.length > 1) {
argsArray = argsArray.concat(Array.prototype.slice.call(arguments, 1));
}

return _call.apply(null, argsArray);
}

function jQueryRemote(method, varargs) {
return _call(_jQueryRemoteObjectId, "_LDjQuery." + method, varargs);
}

function _stopKeepAliveInterval() {
Expand Down Expand Up @@ -116,7 +141,9 @@ define(function RemoteAgent(require, exports, module) {
// WebInspector Event: Page.loadEventFired
function _onLoadEventFired(event, res) {
// res = {timestamp}
var command = "window._LD=" + RemoteFunctions + "(" + LiveDevelopment.config.experimental + ")";

// inject RemoteFunctions
var command = "window._LD=" + RemoteFunctions + "(" + LiveDevelopment.config.experimental + ");";

Inspector.Runtime.evaluate(command, function onEvaluate(response) {
if (response.error || response.wasThrown) {
Expand All @@ -128,6 +155,17 @@ define(function RemoteAgent(require, exports, module) {
_startKeepAliveInterval();
}
});

// inject jQuery
command = jQueryRemoteSrc + "window._LDjQuery=jQuery.noConflict(true);";

Inspector.Runtime.evaluate(command, function onEvaluate(response) {
if (response.error || response.wasThrown) {
_load.reject(null, response.error);
} else {
_jQueryRemoteObjectId = response.result.objectId;
}
});
}

/** Initialize the agent */
Expand All @@ -147,8 +185,44 @@ define(function RemoteAgent(require, exports, module) {
_stopKeepAliveInterval();
}

// Prototype DOM manipulation
var REMOTE_ELEMENT_METHODS = [
"attr", "removeAttr",
"before", "after", "append", "prepend",
"text",
"detach", "remove",
"html",
"replaceWith"
];

function RemoteElement(dataBracketsId) {
var self = this;

this._queryBracketsId = "var $result = " + $REMOTE + '("[data-brackets-id=\\"' + dataBracketsId + '\\"]");';
this._dataBracketsId = dataBracketsId;

REMOTE_ELEMENT_METHODS.forEach(function (methodName) {
self[methodName] = self._eval.bind(self, methodName);
});
}

RemoteElement.prototype._eval = function (method, varargs) {
// Convert method arguments to JSON string to escape string args
var argsArray = JSON.stringify(Array.prototype.slice.call(arguments, 1)).replace(/\\/g, "\\\\").replace(/\"/g, "\\\""),
argsAssign = "var args = JSON.parse(\"" + argsArray + "\");",
fnApply = "$result." + method + ".apply($result, args)";

return Inspector.Runtime.evaluate(argsAssign + this._queryBracketsId + fnApply);
};

function remoteElement(dataBracketsId) {
return new RemoteElement(dataBracketsId);
}

// Export public functions
exports.call = call;
exports.jQuery = jQueryRemote;
exports.load = load;
exports.unload = unload;
exports.remoteElement = remoteElement;
});
46 changes: 32 additions & 14 deletions src/LiveDevelopment/Documents/HTMLDocument.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ define(function HTMLDocumentModule(require, exports, module) {
HighlightAgent = require("LiveDevelopment/Agents/HighlightAgent"),
HTMLInstrumentation = require("language/HTMLInstrumentation"),
Inspector = require("LiveDevelopment/Inspector/Inspector"),
LiveDevelopment = require("LiveDevelopment/LiveDevelopment");
LiveDevelopment = require("LiveDevelopment/LiveDevelopment"),
RemoteAgent = require("LiveDevelopment/Agents/RemoteAgent");

/** Constructor
*
Expand All @@ -74,10 +75,10 @@ define(function HTMLDocumentModule(require, exports, module) {
// Used by highlight agent to highlight editor text as selected in browser
this.onHighlight = this.onHighlight.bind(this);
$(HighlightAgent).on("highlight", this.onHighlight);

this.onChange = this.onChange.bind(this);
$(this.editor).on("change", this.onChange);
}

this.onChange = this.onChange.bind(this);
$(this.editor).on("change", this.onChange);
};

/**
Expand Down Expand Up @@ -149,17 +150,34 @@ define(function HTMLDocumentModule(require, exports, module) {

/** Triggered on change by the editor */
HTMLDocument.prototype.onChange = function onChange(event, editor, change) {
if (!this.editor) {
return;
}
var codeMirror = this.editor._codeMirror;
while (change) {
var from = codeMirror.indexFromPos(change.from);
var to = codeMirror.indexFromPos(change.to);
var text = change.text.join("\n");
DOMAgent.applyChange(from, to, text);
change = change.next;
var marker = HTMLInstrumentation._getMarkerAtDocumentPos(
this.editor,
editor.getCursorPos()
);

// HACK, replace the whole tag
if (marker && marker.tagID) {
var range = marker.find(),
text = marker.doc.getRange(range.from, range.to);

// HACK maintain ID
text = text.replace(">", " data-brackets-id='" + marker.tagID + "'>");

// FIXME incorrectly replaces body elements with content only, missing body element
RemoteAgent.remoteElement(marker.tagID).replaceWith(text);
}

// if (!this.editor) {
// return;
// }
// var codeMirror = this.editor._codeMirror;
// while (change) {
// var from = codeMirror.indexFromPos(change.from);
// var to = codeMirror.indexFromPos(change.to);
// var text = change.text.join("\n");
// DOMAgent.applyChange(from, to, text);
// change = change.next;
// }
};

/** Triggered by the HighlightAgent to highlight a node in the editor */
Expand Down
39 changes: 21 additions & 18 deletions src/language/HTMLInstrumentation.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,19 +269,8 @@ define(function (require, exports, module) {
mark.tagID = tag.tagID;
});
}

/**
* Get the instrumented tagID at the specified position. Returns -1 if
* there are no instrumented tags at the location.
* The _markText() function must be called before calling this function.
*
* NOTE: This function is "private" for now (has a leading underscore), since
* the API is likely to change in the future.
*
* @param {Editor} editor The editor to scan.
* @return {number} tagID at the specified position, or -1 if there is no tag
*/
function _getTagIDAtDocumentPos(editor, pos) {

function _getMarkerAtDocumentPos(editor, pos) {
var i,
cm = editor._codeMirror,
marks = cm.findMarksAt(pos),
Expand All @@ -307,17 +296,31 @@ define(function (require, exports, module) {
}
}

if (match) {
return match.tagID;
}

return -1;
return match;
}

/**
* Get the instrumented tagID at the specified position. Returns -1 if
* there are no instrumented tags at the location.
* The _markText() function must be called before calling this function.
*
* NOTE: This function is "private" for now (has a leading underscore), since
* the API is likely to change in the future.
*
* @param {Editor} editor The editor to scan.
* @return {number} tagID at the specified position, or -1 if there is no tag
*/
function _getTagIDAtDocumentPos(editor, pos) {
var match = _getMarkerAtDocumentPos(editor, pos);

return (match) ? match.tagID : -1;
}

$(DocumentManager).on("beforeDocumentDelete", _removeDocFromCache);

exports.scanDocument = scanDocument;
exports.generateInstrumentedHTML = generateInstrumentedHTML;
exports._markText = _markText;
exports._getMarkerAtDocumentPos = _getMarkerAtDocumentPos;
exports._getTagIDAtDocumentPos = _getTagIDAtDocumentPos;
});