diff --git a/css/panel.css b/css/panel.css
index 3748ec1..a014d18 100644
--- a/css/panel.css
+++ b/css/panel.css
@@ -344,6 +344,99 @@ li .status:empty {
}
+
+/* */
+
+.source-code {
+ font-family: Menlo, monospace;
+ font-size: 11px;
+ /*white-space: pre-wrap;*/
+}
+
+.source-code li {
+ display: list-item;
+ text-align: -webkit-match-parent;
+}
+
+.outline-disclosure,
+.outline-disclosure ol {
+ list-style-type: none;
+ -webkit-padding-start: 12px;
+ margin: 0;
+}
+
+.outline-disclosure ol.children.expanded {
+ display: block;
+}
+
+.outline-disclosure > ol {
+ position: relative;
+ padding: 2px 6px !important;
+ margin: 0;
+ cursor: default;
+ min-width: 100%;
+}
+
+.outline-disclosure li {
+ padding: 0 0 0 14px;
+ margin-top: 1px;
+ margin-left: -2px;
+ word-wrap: break-word;
+}
+
+.outline-disclosure li.selected .selection {
+ display: block;
+ background-color: rgb(212, 212, 212);
+}
+
+.elements-tree-outline li.parent::before {
+ top: 0 !important;
+}
+.outline-disclosure li.parent::before {
+ -webkit-mask-position: -4px -96px;
+ background-color: rgb(110, 110, 110);
+}
+.outline-disclosure li.parent::before {
+ -webkit-user-select: none;
+ -webkit-mask-image: url(../img/statusbarButtonGlyphs.png);
+ -webkit-mask-size: 320px 120px;
+ content: "a";
+ color: transparent;
+ text-shadow: none;
+ position: relative;
+ top: 2px;
+ margin-right: 1px;
+ height: 12px;
+}
+.outline-disclosure li.parent::before {
+ float: left;
+ width: 8px;
+ padding-right: 2px;
+}
+
+.webkit-html-tag {
+ color: rgb(136, 18, 128);
+}
+
+.webkit-html-attribute-name {
+ color: rgb(153, 69, 0);
+}
+
+.webkit-html-attribute-value {
+ color: rgb(26, 26, 166);
+}
+
+.webkit-html-doctype {
+ color: rgb(192, 192, 192);
+}
+
+.webkit-html-comment {
+ color: rgb(35, 110, 37);
+}
+
+
+
+
/* bat-json-tree */
bat-json-tree {
diff --git a/js/directives/scopeTree.js b/js/directives/scopeTree.js
index e1ced9f..ba0240b 100644
--- a/js/directives/scopeTree.js
+++ b/js/directives/scopeTree.js
@@ -6,19 +6,75 @@ angular.module('panelApp').directive('batScopeTree', function ($compile) {
var selected = null;
+
+ var maybe = function (fn) {
+ return function (val) {
+ if (val === (void 0)) {
+ return;
+ }
+ return fn.apply(this, arguments);
+ };
+ };
+
+ var repeaterPredicate = function (child) {
+ return child.name && child.name['ng-repeat'];
+ };
+
+ var notRepeatedPredicate = function (child) {
+ return !repeaterPredicate(child);
+ };
+
+ var name = function (name) {
+ if (!name) {
+ return '???';
+ }
+ if (name['ng-repeat']) {
+ return name.lhs;
+ }
+ var n = '';
+ [
+ 'ng-app',
+ 'ng-controller'
+ ].
+ forEach(function (prop) {
+ if (name[prop]) {
+ n += prop + '="' + name[prop] + '"';
+ }
+ });
+ if (n.length === 0) {
+ n += name.tag;
+ if (name.classes.length > 0) {
+ n += '.' + name.classes.join('.');
+ }
+ }
+ return n;
+ };
+
var template =
- '
' +
- '
< ' +
- '
Scope ({{val.id}}) ' +
- '
' +
- '
' +
- ' ' +
+ '
' +
+ '' +
+ '{{c}} ' +
+ '{{name(val.name)}} ' +
+ '{{xc}} ' +
+ ' ' +
+ '' +
+ '' +
+ '' +
+ ' ' +
'
' +
- ' ';
+ '
' +
+ ' ' +
+ '';
return {
restrict: 'E',
@@ -26,7 +82,7 @@ angular.module('panelApp').directive('batScopeTree', function ($compile) {
scope: {
val: '=',
select: '=',
- selectedScope: '=',
+ selectedScopeId: '=',
inspect: '='
},
link: function (scope, element, attrs) {
@@ -34,8 +90,32 @@ angular.module('panelApp').directive('batScopeTree', function ($compile) {
// see: https://github.com/angular/angular.js/issues/898
element.append(template);
+ scope.name = name;
+ scope.c = '{{';
+ scope.xc = '}}';
+
var childScope = scope.$new();
+ childScope.ungrouped = [];
+ childScope.grouped = {};
+
+ childScope.$watch('val.children', function (newChildren) {
+ if (!newChildren) {
+ return;
+ }
+ var grouped = childScope.grouped;
+ newChildren.
+ filter(repeaterPredicate).
+ forEach(function (child) {
+ var repOver = child.name['ng-repeat'];
+ grouped[repOver] = grouped[repOver] || [];
+ grouped[repOver].push(child);
+ }, {});
+
+ childScope.ungrouped = newChildren.filter(notRepeatedPredicate);
+ });
+
+
childScope.select = scope.select;
//childScope.selectedScope = scope.selectedScope;
childScope.inspect = scope.inspect;
diff --git a/js/inject/debug.js b/js/inject/debug.js
index 50d2e6b..3b76fed 100644
--- a/js/inject/debug.js
+++ b/js/inject/debug.js
@@ -163,6 +163,7 @@ var instument = function (window) {
// Utils
// =====
+ // this is silly
var getWatchTree = function (id) {
var traverse = function (sc) {
var tree = {
@@ -189,9 +190,14 @@ var instument = function (window) {
var getScopeTree = function (id) {
+
+ var names = api.niceNames();
+
var traverse = function (sc) {
var tree = {
id: sc.$id,
+ name: names[sc.$id],
+ watchers: debug.watchers[sc.$id],
children: []
};
@@ -273,6 +279,50 @@ var instument = function (window) {
}
};
+ var summarizeObject = function (obj) {
+ var summary = {}, keys;
+ if (obj instanceof Array) {
+ keys = obj.map(function (e, i) { return i; });
+ } else if (typeof obj === 'object') {
+ keys = Object.keys(obj);
+ } else {
+ return '=' + obj.toString().substr(0, 10);
+ }
+
+ var id;
+
+ if (keys.some(function (key) {
+ var lowKey = key.toLowerCase();
+ if (lowKey.indexOf('name') !== -1 ||
+ lowKey.indexOf('id') !== -1) {
+ return id = key;
+ }
+ })) {
+ return '.' + id + '="' + obj[id].toString() + '"';
+ }
+
+ if (keys.length > 5) {
+ keys = keys.slice(0, 5);
+ }
+
+ keys.forEach(function (key) {
+ var val = obj[key];
+ if (val instanceof Array) {
+ summary[key] = '[ … ]';
+ } else if (typeof val === 'object') {
+ summary[key] = '{ … }';
+ } else if (typeof val === 'function') {
+ summary[key] = 'fn';
+ } else {
+ summary[key] = obj[key].toString()
+ if (summary[key].length > 10) {
+ summary[key] = summary[key].substr(0, 10) + '…';
+ }
+ }
+ });
+ return '=' + JSON.stringify(summary);
+ }
+
// Public API
// ==========
@@ -289,6 +339,50 @@ var instument = function (window) {
fireCustomEvent: fireCustomEvent,
+ niceNames: function () {
+ var ngScopeElts = document.getElementsByClassName('ng-scope');
+ ngScopeElts = Array.prototype.slice.call(ngScopeElts);
+ return ngScopeElts.
+ reduce(function (acc, elt) {
+ var $elt = angular.element(elt);
+ var scope = $elt.scope();
+
+ var name = {};
+
+ [
+ 'ng-app',
+ 'ng-controller',
+ 'ng-repeat'
+ ].
+ forEach(function (attr) {
+ var val = $elt.attr(attr),
+ className = $elt[0].className;
+ if (val) {
+ name[attr] = val;
+ if (attr === 'ng-repeat') {
+ var lhs = /(.+) in/.exec(val);
+ lhs = lhs[1];
+ name.lhs = lhs + summarizeObject(scope[lhs]);
+ }
+ } else if (className.indexOf(attr) !== -1) {
+ val = (new RegExp(attr + ': ([a-zA-Z0-9]+);')).exec(className);
+ val = val[1];
+ name[attr] = val;
+ }
+ });
+
+ if (Object.keys(name).length === 0) {
+ name.tag = $elt[0].tagName.toLowerCase();
+ name.classes = $elt[0].className.
+ replace(/(\W*ng-scope\W*)/, ' ').
+ split(' ').
+ filter(function (i) { return i; });
+ }
+ acc[scope.$id] = name;
+ return acc;
+ }, {});
+ },
+
getModel: function (id, path) {
// lol chrome
diff --git a/panes/model.html b/panes/model.html
index 78ed3b5..a10cf2a 100644
--- a/panes/model.html
+++ b/panes/model.html
@@ -21,13 +21,128 @@
Enable Inspector
+
+
+
+
+
+
+ <!DOCTYPE html>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <html class ="no-js ng-app: docsApp; ng-scope " lang ="en " ng-controller ="DocsController " >
+
+
+
+
+
+
+ <head > … </head >
+
+
+ <body >
+
+
+
+ <header class ="header " >
+
+
+
+ <div class ="navbar navbar-fixed-top " > … </div >
+
+
+ </header >
+
+
+
+ <div role ="main " class ="container " >
+
+
+
+ <div class ="row clear-navbar " > </div >
+
+
+ <div class ="row " > … </div >
+
+
+
+ <div class ="span12 " > … </div >
+
+
+
+
+
+
+
+
+
+ </div >
+
+
+ </div >
+
+
+ <div class ="row " > … </div >
+
+
+ </div >
+
+
+
+ <div id ="fader " ng-show ="subpage " style ="display: none; " > </div >
+
+
+ <div id ="subpage " ng-show ="subpage " style ="display: none; " > … </div >
+
+
+ <footer class ="footer " > … </footer >
+
+
+ <div id ="__ngDebugElement " style ="display: none; " > … </div >
+
+
+ </body >
+
+
+ </html >
+
+
+
+
+
+
+