define(function(require, exports, module) { main.consumes = [ "DebugPanel", "ui", "util", "debugger", "callstack", "panels", "layout" ]; main.provides = ["variables"]; return main; function main(options, imports, register) { var DebugPanel = imports.DebugPanel; var ui = imports.ui; var callstack = imports.callstack; var debug = imports.debugger; var util = imports.util; var layout = imports.layout; var panels = imports.panels; var markup = require("text!./variables.xml"); var Tree = require("ace_tree/tree"); var TreeData = require("./variablesdp"); var TreeEditor = require("ace_tree/edit"); /***** Initialization *****/ var plugin = new DebugPanel("Ajax.org", main.consumes, { caption: "Local Variables", index: 300 }); var emit = plugin.getEmitter(); var activeFrame, dbg, cached = {}; var model, datagrid; // UI Elements var loaded = false; function load() { if (loaded) return false; loaded = true; model = new TreeData(); model.emptyMessage = "No variables to display"; model.columns = [{ caption: "Variable", value: "name", defaultValue: "Scope", width: "40%", icon: "debugger/genericvariable_obj.gif", type: "tree" }, { caption: "Value", value: "value", width: "60%", editor: "textbox" }, { caption: "Type", value: "type", width: "55" }]; model.loadChildren = function(node, callback) { emit("expand", { node: node, expand: callback }); }; // Set and clear the dbg variable debug.on("attach", function(e) { dbg = e.implementation; }); debug.on("detach", function(e) { dbg = null; }); debug.on("stateChange", function(e) { plugin[e.action](); }); callstack.on("scopeUpdate", function(e) { updateScope(e.scope, e.variables); }); callstack.on("framesLoad", function(e) { // Clear the cached states of the variable datagrid clearCache(); }); // When clicking on a frame in the call stack show it // in the variables datagrid callstack.on("frameActivate", function(e) { // @todo reload the clicked frame recursively + keep state loadFrame(e.frame); }, plugin); // Variables plugin.on("expand", function(e) { if (e.node.tagName == "variable") { if (!e.node.children) return e.expand(); dbg.getProperties(e.node, function(err, properties) { if (err) return console.error(err); //updateVariable(e.node, properties); e.expand(); }); } // Local scope // else if (e.scope.type == 1) { // //updateScope(e.scope); // e.expand(); // } // Other scopes else if (e.node.tagName == "scope") { dbg.getScope(model.frame/*debug.activeFrame*/, e.node, function(err, vars) { if (err) return console.error(err); //updateScope(e.node, vars); e.expand(); }); } }, plugin); } var drawn; function draw(options) { if (drawn) return; drawn = true; // Create UI elements ui.insertMarkup(options.aml, markup, plugin); var datagridEl = plugin.getElement("datagrid"); datagrid = new Tree(datagridEl.$ext); datagrid.setTheme({ cssClass: "blackdg" }); datagrid.setOption("maxLines", 200); layout.on("eachTheme", function(e) { var height = parseInt(ui.getStyleRule(".blackdg .row", "height"), 10) || 24; // model.rowHeightInner = height - 1; model.rowHeight = height; if (e.changed) datagrid.resize(true); }); datagrid.setDataProvider(model); datagrid.edit = new TreeEditor(datagrid); panels.on("afterAnimate", function(e) { if (panels.isActive("debugger")) datagrid && datagrid.resize(); }); datagridEl.on("contextmenu", function() { return false; }); datagrid.on("rename", function(e) { var node = e.node; var value = e.value; var parents = []; var variable = activeFrame.findVariable(node, null, parents); var oldValue = variable.value; model.setAttribute(variable, "value", value); function undo() { model.setAttribute(variable, "value", oldValue); } // Set new value dbg.setVariable(variable, parents, value, debug.activeFrame, function(err) { if (err) return undo(); // Reload properties of the variable // dbg.getProperties(variable, function(err, properties) { updateVariable(variable, variable.properties, node); emit("variableEdit", { value: value, oldValue: oldValue, node: node, variable: variable, frame: activeFrame, parents: parents }); // }); }); }); datagrid.on("beforeRename", function(e) { if (!plugin.enabled) return e.preventDefault(); if (!dbg.features.updateScopeVariables) return e.preventDefault(); // Don't allow setting the value of scopes if (e.node.tagName == "scope") return e.preventDefault(); // Don't allow setting "this" if (e.node.name == "this") return e.preventDefault(); }); } /***** Methods *****/ function loadFrame(frame) { if (frame == activeFrame) return; model.frame = frame; if (!frame) { model.setRoot({}); } else { if (cached[frame.id]) model.setRoot(cached[frame.id]); else { model.setRoot([].concat(frame.variables, frame.scopes).filter(Boolean)); cached[frame.id] = model.root; } } activeFrame = frame; } function updateNode(node, variable, oldVar) { var isOpen = node.isOpen; model.close(node, null, false); if (isOpen) model.open(node, null, false); else model._signal("change", node); } function updateScope(scope, variables) { updateNode(scope); } function updateVariable(variable, properties, node) { updateNode(variable); } function clearCache() { cached = {}; } /***** Lifecycle *****/ plugin.on("load", function() { load(); plugin.once("draw", draw); }); plugin.on("enable", function() { drawn && datagrid.enable(); }); plugin.on("disable", function() { drawn && datagrid.disable(); }); plugin.on("unload", function() { loaded = false; drawn = false; }); /***** Register and define API *****/ /** * The local variables and scopes panel for the * {@link debugger Cloud9 debugger}. * * This panel displays the local variables and scopes to the user. A * user can expand variables and scopes to inspect properties and * variables and edit them. * * @singleton * @extends DebugPanel **/ plugin.freezePublicAPI({ /** * @ignore */ get model() { return model; }, /** * Sets the frame that the variables and scopes are displayed for. * @param {debugger.Frame} frame The frame to display the variables and scopes from. */ loadFrame: loadFrame, /** * Clears the variable/scope cache */ clearCache: clearCache }); register(null, { variables: plugin }); } });