From 2512c63e972660b02dd62fd03b8786f649a8badd Mon Sep 17 00:00:00 2001 From: Brian Ford Date: Thu, 25 Jul 2013 14:09:00 -0700 Subject: [PATCH] wip --- css/panel.css | 93 +++++++++++++++++++++++++++++ js/directives/scopeTree.js | 104 ++++++++++++++++++++++++++++---- js/inject/debug.js | 94 +++++++++++++++++++++++++++++ panes/model.html | 119 ++++++++++++++++++++++++++++++++++++- 4 files changed, 396 insertions(+), 14 deletions(-) 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}}' + + '' + + '
    ' + + '<!-- {{repeat}} -->' + + '' + + '' + '
    ' + - '
'; + '' + + '' + + ''; 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 @@
+ + +
+
    + +
  1. + <!DOCTYPE html> +
  2. +
  3. + <!--[if lt IE 7]> <html class="no-js lt-ie9 lt-ie8 lt-ie7 ng-app: docsApp;" lang="en" ng-controller="DocsController"> <![endif]--> +
  4. +
  5. + <!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8 ng-app: docsApp;" lang="en" ng-controller="DocsController"> <![endif]--> +
  6. +
  7. + <!--[if IE 8]> <html class="no-js lt-ie9 ng-app: docsApp;" lang="en" ng-controller="DocsController"> <![endif]--> +
  8. +
  9. + <!--[if gt IE 8]><!--> +
  10. +
  11. + <html class=​"no-js ng-app:​ docsApp;​ ng-scope" lang=​"en" ng-controller=​"DocsController"> +
  12. +
      +
    1. + <!--<![endif]--> +
    2. +
    3. + <head></head> +
    4. +
    5. + <body> +
    6. +
        +
      1. + <header class=​"header"> +
      2. +
          +
        1. + <div class=​"navbar navbar-fixed-top"></div> +
        2. +
        3. + </header> +
        4. +
        +
      3. + <div role=​"main" class=​"container"> +
      4. +
          +
        1. + <div class=​"row clear-navbar"></div> +
        2. +
        3. + <div class=​"row"></div> +
        4. +
            +
          1. + <div class=​"span12"></div> +
          2. +
              +
            1. +<!--[if lt IE 7]> +<p class="alert alert-error">Your browser is <em>ancient!</em> +<a href="http://browsehappy.com/">Upgrade to a different browser</a> or +<a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to +experience this site. +</p> +<![endif]--> +
            2. +
            3. + <!--[if lt IE 9]> +<div class="alert"> +You are using an old version of Internet Explorer. +For better and safer browsing experience please <a href="http://www.microsoft.com/IE9">upgrade IE</a> +or install <a href="http://google.com/chrome">Google Chrome browser</a>. +</div> +<![endif]--> +
            4. +
            5. + </div> +
            6. +
            +
          3. </div>
          4. +
          +
        5. + <div class=​"row"></div> +
        6. +
        7. + </div> +
        8. +
        +
      5. + <div id=​"fader" ng-show=​"subpage" style=​"display:​ none;​"></div> +
      6. +
      7. + <div id=​"subpage" ng-show=​"subpage" style=​"display:​ none;​"></div> +
      8. +
      9. + <footer class=​"footer"></footer> +
      10. +
      11. + <div id=​"__ngDebugElement" style=​"display:​ none;​"></div> +
      12. +
      13. + </body> +
      14. +
      +
    7. </html>
    8. +
    +
+ + + + +