diff --git a/app/AppInspector/about.html b/app/AppInspector/about.html index 91b2d13c..8f4349ba 100644 --- a/app/AppInspector/about.html +++ b/app/AppInspector/about.html @@ -1,6 +1,7 @@ -Sencha App Inspector +

+ Original project:
View the project on Sencha Labs - it was originally developed by Sencha Professional Services and is 100% open source! @@ -16,4 +17,5 @@

  • Bruno Tavares
  • KAWANO Shinobu
  • Mitchell Simoens
  • +
  • Jorge Blom-Dahl
  • diff --git a/app/AppInspector/app/controller/Components.js b/app/AppInspector/app/controller/Components.js index 8aca8d8e..c78c14b5 100644 --- a/app/AppInspector/app/controller/Components.js +++ b/app/AppInspector/app/controller/Components.js @@ -34,7 +34,7 @@ Ext.define('AI.controller.Components', { 'ComponentsTreeGrid' ], - init: function(application) { + init: function (application) { var me = this; me.control({ @@ -48,7 +48,8 @@ Ext.define('AI.controller.Components', { 'applyfilter': me.onFilterComponentTree }, 'componentstreegrid#ComponentTree': { - 'itemclick': me.onSelectComponent + 'itemclick': me.onSelectComponent, + 'itemdblclick': me.onDblClickComponent, }, // properties 'gridpanel#ComponentProps': { @@ -68,7 +69,22 @@ Ext.define('AI.controller.Components', { }); }, - onActivate: function(panel) { + onDblClickComponent: function (tree, record, item, index, e, eOpts) { + // Objetivo llevar este componente a la consola + AI.util.InspectedWindow.eval( + AI.util.InspectedWindow.highlight, + record.get('cmpId'), + Ext.emptyFn + ); + + AI.util.InspectedWindow.eval( + AI.util.InspectedWindow.setComponentAsGlobalVar, + record.get('cmpId'), + Ext.emptyFn + ); + }, + + onActivate: function (panel) { // load the "Components" upfront ... var initialLoad = panel.initialLoad, tree = panel.down('#ComponentTree'); @@ -81,7 +97,7 @@ Ext.define('AI.controller.Components', { } }, - onComponentTreeActivate: function(tree) { + onComponentTreeActivate: function (tree) { var nodes = [], root = tree.getRootNode(); @@ -107,7 +123,7 @@ Ext.define('AI.controller.Components', { ); }, - onRefreshComponentsClick: function(btn) { + onRefreshComponentsClick: function (btn) { var tree = btn.up('#ComponentTree'), filter = tree.down('#FilterComponentsTree'); @@ -115,7 +131,7 @@ Ext.define('AI.controller.Components', { this.onComponentTreeActivate(tree); }, - onFilterComponentTree: function(field, value) { + onFilterComponentTree: function (field, value) { var tree = field.up('#ComponentTree'); if (value === '') { @@ -125,7 +141,7 @@ Ext.define('AI.controller.Components', { } }, - onSelectComponent: function(tree, record, item, index, e, eOpts) { + onSelectComponent: function (tree, record, item, index, e, eOpts) { var parent = tree.up('components'), propsGrid = parent.down('#ComponentProps'), propsGridStore = propsGrid.getStore(), @@ -141,7 +157,7 @@ Ext.define('AI.controller.Components', { AI.util.InspectedWindow.eval( AI.util.Component.getInspectedComponent, record.get('cmpId'), - function(result, isException) { + function (result, isException) { if (result) { propsGridStore.loadData(result.properties); methodGridStore.loadData(result.methods); @@ -153,23 +169,23 @@ Ext.define('AI.controller.Components', { ); }, - toggleComponentsDetailsTips: function(grid) { + toggleComponentsDetailsTips: function (grid) { var tips = grid.up('#ComponentInspector').down('toolbar[dock=bottom]'), isProps = grid.itemId === 'ComponentProps', props = tips.query('[tipGroup=props]'), methods = tips.query('[tipGroup=methods]'), i; - for(i = 0; i < props.length; i++) { + for (i = 0; i < props.length; i++) { props[i].setVisible(isProps); } - for(i = 0; i < methods.length; i++) { + for (i = 0; i < methods.length; i++) { methods[i].setVisible(!isProps); } }, - onFilterComponentDetails: function(field, value) { + onFilterComponentDetails: function (field, value) { var grid = field.up('gridpanel'), store = grid.getStore(); @@ -185,7 +201,7 @@ Ext.define('AI.controller.Components', { } }, - onDetailValueEdit: function() { + onDetailValueEdit: function () { // cancel edit to reset original value return false; } diff --git a/app/AppInspector/app/controller/MVC.js b/app/AppInspector/app/controller/MVC.js index 2b245eaa..db91bbfd 100644 --- a/app/AppInspector/app/controller/MVC.js +++ b/app/AppInspector/app/controller/MVC.js @@ -42,7 +42,9 @@ Ext.define('AI.controller.MVC', { activate: this.onActivate }, 'mvc treepanel': { - select: this.onMVCSelect + select: this.onMVCSelect, + 'itemdblclick' : this.onDblClickComponent + }, 'mvc_records gridpanel': { itemclick: this.onRecordGridSelection @@ -73,6 +75,16 @@ Ext.define('AI.controller.MVC', { ); }, + + onDblClickComponent : function( tree, record, item, index, e, eOpts ) { + debugger; + AI.util.InspectedWindow.eval( + AI.util.InspectedWindow.setComponentAsGlobalVar, + record.get('id'), + Ext.emptyFn + ); + }, + onMVCSelect: function(rowmodel, record) { var type = record.get('type'); diff --git a/app/AppInspector/app/controller/Main.js b/app/AppInspector/app/controller/Main.js index b5329fcc..22827df2 100644 --- a/app/AppInspector/app/controller/Main.js +++ b/app/AppInspector/app/controller/Main.js @@ -73,7 +73,8 @@ Ext.define('AI.controller.Main', { tabpanel.down('#AppDetails').setSource(source); tabpanel.child('mvc').setDisabled(!data.isMVC); - tabpanel.down('#LayoutRuns').setDisabled(app.info.framework === 'touch'); + //tabpanel.down('#LayoutRuns').setDisabled(app.info.framework === 'touch'); + //tabpanel.down('#LayoutRuns').setDisabled(true); main.setLoading(false); } diff --git a/app/AppInspector/app/util/InspectedWindow.js b/app/AppInspector/app/util/InspectedWindow.js index dcd1acba..4e7c916c 100644 --- a/app/AppInspector/app/util/InspectedWindow.js +++ b/app/AppInspector/app/util/InspectedWindow.js @@ -62,6 +62,20 @@ Ext.define('AI.util.InspectedWindow', { } }, + setComponentAsGlobalVar : function(cmpId) { + var cmp = Ext.getCmp(cmpId); + var globalVarName = 'x0'; + window[globalVarName + ''] = cmp; + window.console.log('Component stored in window.' + globalVarName); + }, + + + setVariableAsGlobalVar : function(value) { + var globalVarName = 'x0'; + window[globalVarName + ''] = value; + window.console.log('Variable ' + value + 'stored in window.' + globalVarName); + }, + /** * @param {Function} closure * @param {String/Array} argString @@ -211,4 +225,6 @@ Ext.define('AI.util.InspectedWindow', { return data; } + + }); diff --git a/app/AppInspector/app/util/extjs/MVC.js b/app/AppInspector/app/util/extjs/MVC.js index b61b80bd..13067140 100644 --- a/app/AppInspector/app/util/extjs/MVC.js +++ b/app/AppInspector/app/util/extjs/MVC.js @@ -127,7 +127,12 @@ Ext.define('AI.util.extjs.MVC', { if (Ext.isString(store)) { getter = Ext.app.Controller.getGetterName(store, 'Store'); - store = instance[getter](); + //FIXME This fails in ExtJs 6 + try { + store = instance[getter](); + } catch(e) { + store = instance.getStore(store); + } } stores.push({ diff --git a/app/AppInspector/app/view/ComponentsTreeGrid.js b/app/AppInspector/app/view/ComponentsTreeGrid.js index e6df3d7c..6d3802dd 100644 --- a/app/AppInspector/app/view/ComponentsTreeGrid.js +++ b/app/AppInspector/app/view/ComponentsTreeGrid.js @@ -50,6 +50,10 @@ Ext.define('AI.view.ComponentsTreeGrid', { iconCls: 'icn-refresh', text: 'Refresh' }, + { + xtype: 'label', + text: '(Dbl Click to eval)' + }, { xtype: 'tbfill' }, diff --git a/app/AppInspector/app/view/MainView.js b/app/AppInspector/app/view/MainView.js index 683debc0..c5ebe5eb 100644 --- a/app/AppInspector/app/view/MainView.js +++ b/app/AppInspector/app/view/MainView.js @@ -44,15 +44,18 @@ Ext.define('AI.view.MainView', { { xtype: 'about' }, + // { + // xtype: 'query' + // }, { xtype: 'components' }, { xtype: 'stores' }, - { - xtype: 'layouts' - }, + // { + // xtype: 'layouts' + // }, { xtype: 'eventgrid' }, diff --git a/app/AppInspector/index.html b/app/AppInspector/index.html index f9d6c5a5..b954741c 100644 --- a/app/AppInspector/index.html +++ b/app/AppInspector/index.html @@ -7,8 +7,9 @@ - - + + + @@ -16,5 +17,7 @@ +

    Loading...

    +

    Please wait

    \ No newline at end of file diff --git a/app/AppInspector/override.css b/app/AppInspector/override.css new file mode 100644 index 00000000..7112a519 --- /dev/null +++ b/app/AppInspector/override.css @@ -0,0 +1,3 @@ +html, body { + font-size: 14px; +} \ No newline at end of file diff --git a/app/devtools-page.html b/app/devtools-page.html index a4929f44..289e4c8e 100644 --- a/app/devtools-page.html +++ b/app/devtools-page.html @@ -5,6 +5,7 @@ App Inspector for Sencha + diff --git a/app/devtools/elements.js b/app/devtools/elements.js index 69558d3d..e7805fbc 100644 --- a/app/devtools/elements.js +++ b/app/devtools/elements.js @@ -3,6 +3,7 @@ /** * Elements Side Panel */ + var elementsPanel = chrome.devtools.panels.elements, pageDetectSenchaComponent = function pageDetectSenchaComponent() { @@ -10,12 +11,23 @@ var elementsPanel = chrome.devtools.panels.elements, selectedEl = $0, //https://developers.google.com/chrome-developer-tools/docs/commandline-api#0_-_4 ref = ''; - if (window.Ext) { - cmp = Ext.getCmp(selectedEl.id); + var getComponentOrParent = function(domEl, classPrefix) { + var cmp = cmp = Ext.getCmp(domEl.id); + /// + if (cmp && (!classPrefix || Ext.getClassName(cmp).indexOf(classPrefix) > -1 )) { + return cmp; + } else if (domEl && domEl.parentNode && domEl.parentNode != document.body){ + return getComponentOrParent(domEl.parentNode, classPrefix); + } + // + }; + + var getData = function(cmp) { + var data; if (cmp) { data = Object.create(null); //which sets __proto__ to undefined - + // class name if (Ext.getClassName) { ref = Ext.getClassName(cmp); @@ -34,13 +46,51 @@ var elementsPanel = chrome.devtools.panels.elements, data[ref] = cmp; } + return data; + } + + data = Object.create(null); //which sets __proto__ to undefined + + + + if (window.Ext) { + cmp = getComponentOrParent(selectedEl); + + + + var names = []; + try { + names = Ext.ClassManager.names.reduce((a,b) => (a.indexOf(b)==-1) ? a + ","+ b : a).split(',') ; + } catch(e) { + names = Object.keys(Ext.ClassManager.classes) + .filter((k) => k.indexOf("Ext") == -1) + .map((k) => k.substr(0, k.indexOf("."))) + .reduce((a,b) => (a.indexOf(b) == -1) ? (a + "," + b) : a) + .split(",").filter(v => v.length > 0); + } + + var addedPrefixes = 0; + names.forEach(function(name) { + var prefixedCmp = getComponentOrParent(selectedEl, name); + if (prefixedCmp && prefixedCmp != cmp) { + data[name] = getData(prefixedCmp); + addedPrefixes++; + } + }); + + if (addedPrefixes === 0) { + data = getData(cmp); + } else { + data['$0'] = getData(cmp); + } + } return data; }; -elementsPanel.createSidebarPane('Sencha Component', function (sidebar) { +elementsPanel.createSidebarPane('Sencha/ExtJS', function (sidebar) { var onSelectionChanged = function () { sidebar.setExpression('(' + pageDetectSenchaComponent.toString() + ')()'); }; diff --git a/app/devtools/ext-utils.user.js b/app/devtools/ext-utils.user.js new file mode 100644 index 00000000..a3134c27 --- /dev/null +++ b/app/devtools/ext-utils.user.js @@ -0,0 +1,290 @@ +// ==UserScript== +// @name ExtJS Dev Tools Utils +// @namespace http://tampermonkey.net/ +// @version 0.10 +// @description Use the x() function to search for one or more extJs components. Try x("all") +// @homepageURL https://github.com/bommox/tampermonkey-scripts +// @updateUrl https://raw.githubusercontent.com/bommox/tampermonkey-scripts/master/ext-js-utils.user.js +// @author Jorge Blom (@bommox) +// @include http* +// @run-at document-end +// @grant none +// ==/UserScript== + +function registerExtUtils() { + + if (window.Ext === undefined) { + console.log("Tampermonkey ExtJS DEV TOOLS UTILS not used in this page. (no Ext found)"); + return; + } + + var CHECK = "_x_util_created"; + + if (window[CHECK]) { + return; + } else { + window[CHECK] = true; + } + + var CMP_QUERY = "x"; + + var WELCOME_MESSAGE = [ + '', + ' ==============================================================', + ' ExtJS DEV TOOLS UTILS', + ' @Author: Jorge Blom-Dahl (@bommox)', + ' --------------------------------------------------------------', + ' To see available options type', + ' > x("help")' + ].join("\n"); + + var HELP_MESSAGE = [ + ' ', + ' Lookup one component (stored in ' + CMP_QUERY + '0 var):', + ' > By ID: Use ' + CMP_QUERY + '("ext-field-5"). ', + ' > Containing DOM : Use ' + CMP_QUERY + '($0). $0 is the DOM element selected in dev tools.', + ' > Containing DOM, filter className: Use ' + CMP_QUERY + '($0, "App").', + ' Get a list of components:', + ' > All components: ' + CMP_QUERY + '("all"). ', + ' > All components, filter className: ' + CMP_QUERY + '("all", "App"). ', + ' > By alias: ' + CMP_QUERY + '("widget.text"). ', + ' > By className: ' + CMP_QUERY + '("Ext.field.Text"). ', + ' Get Application objects ', + ' > Get application instance: ' + CMP_QUERY + '("application"). ', + ' > Get controllers: ' + CMP_QUERY + '("controllers"). ', + ' > Get stores: ' + CMP_QUERY + '("stores"). ', + '' + + ].join("\n"); + + function help() { + console.log(WELCOME_MESSAGE); + console.log(HELP_MESSAGE); + } + + console.log(WELCOME_MESSAGE); + + window[CMP_QUERY] = function(id, prefix) { + if (id == "all" || id == "ALL") { + id = undefined; + } + return cmp(id, prefix); + }; + + window[CMP_QUERY].getAllCmp = function(prefix) { + return cmp(undefined, prefix); + }; + + window[CMP_QUERY].getStores = function() { + return cmp('stores'); + }; + + window[CMP_QUERY].getControllers = function() { + return cmp('controllers'); + }; + + window[CMP_QUERY].getApp = function() { + return cmp('application'); + }; + + window[CMP_QUERY].help = function() { + return cmp('help'); + }; + + + window[CMP_QUERY + "test"] = function() { + test(); + } + + function getAllComponents() { + return (Ext.ComponentManager.getAll) + ? Ext.ComponentManager.getAll() + : Ext.ComponentManager.all.getArray(); + } + + + function cmp(id, classPrefix) { + + if (id === undefined) { + // Lista todos los componentes + var allComponents = getAllComponents(); + if (classPrefix) { + // Filtra por clase + allComponents = allComponents.filter((c) => c.$className.indexOf(classPrefix) === 0); + } + return getComponentDataArray(allComponents); + } + + + if (typeof(id) == "string" ) { + if (id == "help") { + help(); + return; + } + + id = id.replace("#",""); + var isAlias = Ext.ClassManager.getByAlias(id) != undefined; + if (isAlias) { + // Se resuelve la clase + id = Ext.getClassName(Ext.ClassManager.getByAlias(id)); + } + var isClass = Ext.ClassManager.get(id) != undefined; + var resultCmp = Ext.getCmp(id); + if (resultCmp) { + return selectCmp(resultCmp); + } else if (id == "stores") { + return storeVar(getStores()); + } else if (id == "controllers") { + return storeVar(getControllers()); + } else if (id == "application") { + return storeVar(getApplication()); + } else if (isClass) { + // Es una clase. + var classComponents = getAllComponents().filter((c) => c.$className == id); + return getComponentDataArray(classComponents); + } + } + + if (Ext.getClassName(Ext.get(id)) == "Ext.dom.Element") { + // Es un elemento DOM + // Se busca su padre + var resultCmp = getParentComponent(id, classPrefix); + if (resultCmp) { + return selectCmp(resultCmp); + } + } + console.warn("No component found... here is the documentation:"); + help(); + + return undefined; + } + + function storeVar(value) { + var storedName = CMP_QUERY + '0'; + window[storedName] = value; + console.debug("Object " + Ext.getClassName(value) + " stored in " + storedName); + return value; + } + + function selectCmp(cmp) { + var data = getComponentData(cmp); + console.log("#" + data.id + " " + data.alias + " [" + data.$class + "]"); + console.log(data); + storeVar(data); + return cmp; + } + + function getParentComponent(domEl, classPrefix) { + var cid; + try { + cid = domEl.getAttribute("data-componentid"); + } catch(e) { + return undefined; + } + var cmp; + if (cid) { + cmp = Ext.getCmp(cid) + } + if (cmp && (!classPrefix || Ext.getClassName(cmp).indexOf(classPrefix) > -1 )) { + return cmp; + } else { + return getParentComponent(domEl.parentNode, classPrefix); + } + } + + function newObject() { + return Object.create(null); + } + + + function getComponentData(cmp) { + var result = newObject(); + result.alias = cmp.alias && cmp.alias[0]; + result.id = cmp.id, + result.$class = cmp.$className, + result.dom = cmp.el && cmp.el.dom, + result.cmp = cmp + return result; + } + + function getComponentDataArray(cmpArray) { + var result = newObject(); + console.log(cmpArray.length + " components"); + cmpArray.forEach(function(cmp) { + var data = getComponentData(cmp); + result["[" + data.alias + "] " + "#" + data.id] = data; + }); + return result; + } + + function getApplication() { + var app; + Ext.ClassManager.names && Ext.ClassManager.names.forEach(function(n) { + if (window[n] && window[n].app && window[n].app.controllers) { + app = window[n].app; + } + }); + if (!app) { + Object.keys(Ext.ClassManager.classes) + .filter((k) => k.indexOf("Ext") == -1) + .map((k) => k.substr(0, k.indexOf("."))) + .reduce((a,b) => (a.indexOf(b) == -1) ? (a + "," + b) : a) + .split(",").filter(v => v.length > 0) + .forEach(prefix => { + if (window[prefix] && window[prefix].app && window[prefix].app.$className == "Ext.app.Application") { + app = window[prefix].app; + } + }); + } + + return app; + } + + function getControllers() { + var app = getApplication(); + var result = newObject(); + var controllers = (app.getControllers) + ? app.getControllers() + : app.controllers.items.map(c => Ext.getClassName(c)); + controllers.forEach(c => { + try { + result[c] = app.getController(c); + } catch (e) { } + }); + return result; + } + + function getStores() { + var app = getApplication(); + var result = newObject(); + if (app.getStores) { + app.getStores().forEach(s => { + result[Ext.getClassName(s)] = s; + }); + } else if (app.getStore && app.stores) { + app.stores.forEach(s => { + result[s] = app.getStore(s); + }); + } + return result; + } + + function test() { + console.log("ExtJS Util TEST"); + console.log("> Application"); + console.log(getApplication()); + console.log("> Controllers"); + console.log(getControllers()); + console.log("> Stores"); + console.log(getStores()); + console.log("> All components"); + console.log(cmp()); + } + +}; + +if (chrome && chrome.devtools && chrome.devtools.inspectedWindow ) { + chrome.devtools.inspectedWindow.eval('(' + registerExtUtils + ')()'); +} else { + registerExtUtils(); +} \ No newline at end of file diff --git a/app/devtools/panel.js b/app/devtools/panel.js index 4745a1cc..45d9488a 100644 --- a/app/devtools/panel.js +++ b/app/devtools/panel.js @@ -10,7 +10,7 @@ var AI = { * http://developer.chrome.com/extensions/devtools.html */ chrome.devtools.panels.create( - 'Sencha', + 'Sencha/ExtJS', 'resources/images/panel_icon.png', 'AppInspector/index.html', diff --git a/app/manifest.json b/app/manifest.json index 0f45d067..e4f8c5a7 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 2, "minimum_chrome_version": "31.0", - "name": "App Inspector for Sencha™", + "name": "Sencha™ and ExtJS™ Debugger", "description": "__MSG_appDescription__", - "version": "2.0.4", + "version": "3.0.1", "icons": { "16": "AppInspector/resources/images/icon-16x16.png", "48": "AppInspector/resources/images/icon-48x48.png", diff --git a/package.json b/package.json index 54c47503..1bcf264d 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name" : "appinspector", "description" : "Google Chrome™ Dev Tools extension for debugging Sencha™ applications.", - "version" : "2.0.0", + "version" : "2.1.0", "private" : true, "contributors" : [ { @@ -23,6 +23,10 @@ { "name" : "Mitchell Simoens", "url" : "https://github.com/mitchellsimoens" + }, + { + "name" : "Jorge Blom-Dahl", + "url" : "https://github.com/bommox" } ], "repository" : {