Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

major overhaul

  • Loading branch information...
commit 4682d4baf262fd7e1260e383dcfd401e4065b36b 1 parent 3800d92
@btford btford authored
Showing with 15,564 additions and 121 deletions.
  1. +6 −0 css/panel.css
  2. +1 −1  js/controllers/DepsCtrl.js
  3. +36 −8 js/controllers/ModelCtrl.js
  4. +1 −1  js/controllers/OptionsCtrl.js
  5. +1 −1  js/controllers/PerfCtrl.js
  6. +1 −1  js/directives/d3.js
  7. +31 −7 js/directives/jsonTree.js
  8. +0 −43 js/directives/modelTree.js
  9. +46 −0 js/directives/scopeTree.js
  10. +1 −1  js/directives/slider.js
  11. +1 −1  js/directives/tabs.js
  12. +1 −1  js/directives/watcherTree.js
  13. +1 −1  js/filters/first.js
  14. +1 −1  js/filters/precision.js
  15. +1 −1  js/filters/sortByTime.js
  16. +346 −14 js/inject/debug.js
  17. +6 −1 js/panelApp.js
  18. +2 −2 js/services/appContext.js
  19. +1 −1  js/services/appCss.js
  20. +1 −1  js/services/appDeps.js
  21. +1 −1  js/services/appHighlight.js
  22. +1 −1  js/services/appInfo.js
  23. +1 −1  js/services/appInspect.js
  24. +26 −4 js/services/appModel.js
  25. +1 −1  js/services/appPerf.js
  26. +1 −1  js/services/appWatch.js
  27. +1 −1  js/services/chromeExtension.js
  28. +1 −1  js/services/filesystem.js
  29. +0 −11 js/services/poll.js
  30. +1 −2  panel.html
  31. +26 −10 panes/model.html
  32. +24 −0 testacular.e2e.conf
  33. +14,886 −0 tests/angular-expose-isolate-scopes.js
  34. +20 −0 tests/apply.html
  35. +34 −0 tests/grid.html
  36. +25 −0 tests/isolate.html
  37. +30 −0 tests/transclusion.html
View
6 css/panel.css
@@ -37,6 +37,12 @@ body {
margin: 0;
}
+bat-scope-tree .selected {
+ font-weight: bold;
+ text-decoration: underline;
+ color: #333;
+}
+
/*
* Slider widget style based on jquery-ui-bootstrap
* http://addyosmani.github.com/jquery-ui-bootstrap
View
2  js/controllers/DepsCtrl.js
@@ -1,4 +1,4 @@
-panelApp.controller('DepsCtrl', function DepsCtrl($scope, appDeps, poll) {
+angular.module('panelApp').controller('DepsCtrl', function DepsCtrl($scope, appDeps) {
$scope.$on('poll', function () {
appDeps.get(function (deps) {
$scope.$apply(function () {
View
44 js/controllers/ModelCtrl.js
@@ -1,15 +1,18 @@
-
-panelApp.controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel, poll) {
+angular.module('panelApp').controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel) {
$scope.inspect = function () {
appContext.inspect(this.val.id);
};
+ $scope.select = function () {
+ $scope.selectedScope = this.val.id;
+ };
// TODO: fix this
$scope.edit = function () {
appContext.executeOnScope(this.val.id, function (scope, elt, args) {
- scope[args.name] = args.value;
- scope.$apply();
+ scope.$apply(function () {
+ scope[args.name] = args.value;
+ });
}, {
name: this.key,
value: JSON.parse(this.item)
@@ -17,9 +20,17 @@ panelApp.controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel
};
$scope.roots = [];
+ $scope.model = null;
+
$scope.selectedRoot = null;
+ $scope.selectedScope = null;
+
+ $scope.enableInspector = appModel.enableInspector;
+
$scope.$on('poll', function () {
+
+ // get the list of root scopes
appModel.getRootScopes(function (rootScopes) {
$scope.$apply(function () {
$scope.roots = rootScopes;
@@ -28,13 +39,30 @@ panelApp.controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel
} else if (!$scope.selectedRoot) {
$scope.selectedRoot = $scope.roots[0];
}
+ if ($scope.selectedRoot && !$scope.selectedScope) {
+ $scope.selectedScope = $scope.selectedRoot;
+ }
});
});
- appModel.getModelTree($scope.selectedRoot, function (tree) {
- $scope.$apply(function () {
- $scope.tree = tree;
+
+ // get scope tree
+ if ($scope.selectedRoot) {
+ appModel.getScopeTree($scope.selectedRoot, function (tree) {
+ $scope.$apply(function () {
+ $scope.tree = tree;
+ });
});
- });
+ }
+
+ // get models on the selected scope
+ if ($scope.selectedScope) {
+ appModel.getModel($scope.selectedScope, function (model) {
+ $scope.$apply(function () {
+ $scope.model = model;
+ });
+ });
+ }
});
+
});
View
2  js/controllers/OptionsCtrl.js
@@ -1,4 +1,4 @@
-panelApp.controller('OptionsCtrl', function OptionsCtrl($scope, appInfo, appHighlight) {
+angular.module('panelApp').controller('OptionsCtrl', function OptionsCtrl($scope, appInfo, appHighlight) {
$scope.debugger = {
scopes: false,
View
2  js/controllers/PerfCtrl.js
@@ -1,4 +1,4 @@
-panelApp.controller('PerfCtrl', function PerfCtrl($scope, appContext, appPerf, appModel, appWatch, filesystem, poll) {
+angular.module('panelApp').controller('PerfCtrl', function PerfCtrl($scope, appContext, appPerf, appModel, appWatch, filesystem) {
$scope.histogram = [];
View
2  js/directives/d3.js
@@ -1,6 +1,6 @@
// D3 visualization
// TODO: D3 as a service
-panelApp.directive('batD3', function () {
+angular.module('panelApp').directive('batD3', function () {
return {
restrict: 'E',
terminal: true,
View
38 js/directives/jsonTree.js
@@ -1,4 +1,4 @@
-panelApp.directive('batJsonTree', function($compile) {
+angular.module('panelApp').directive('batJsonTree', function($compile) {
return {
restrict: 'E',
terminal: true,
@@ -12,29 +12,53 @@ panelApp.directive('batJsonTree', function($compile) {
var buildDom = function (object) {
var html = '';
- if (object == undefined) {
- html += 'null';
+ var prop;
+ if (object === undefined) {
+ html += '<i>undefined</i>';
+ } else if (object === null) {
+ html += '<i>null</i>';
} else if (object instanceof Array) {
var i;
- html += '<div class="scope-branch">[';
- for (i = 0; i < object.length; i++) {
- html += buildDom(object[i]) + ', ';
+ html += '<div class="scope-branch">[ ';
+ if (object.length > 0) {
+ html += buildDom(object[i]);
+ for (i = 1; i < object.length; i++) {
+ html += ',' + buildDom(object[i]);
+ }
}
html += ']</div>';
} else if (object instanceof Object) {
+ html += ' { ';
for (prop in object) {
if (object.hasOwnProperty(prop)) {
html += '<div class="scope-branch">' + prop + ': ' + buildDom(object[prop]) + '</div>';
}
}
+ html += ' } ';
} else {
html += '<span>' + object.toString() + '</span>';
}
return html;
};
+ var isEmpty = function (object) {
+ var prop;
+ for (prop in object) {
+ if (object.hasOwnProperty(prop)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
scope.$watch('val', function (newVal, oldVal) {
- element.html(buildDom(newVal));
+ if (newVal === null) {
+ element.html('<div class="alert alert-info">Select a scope to view its models.</div>');
+ } else if (isEmpty(newVal)) {
+ element.html('<pre>{ This scope has no models }</pre>');
+ } else {
+ element.html('<pre>' + buildDom(newVal) + '</pre>');
+ }
});
}
};
View
43 js/directives/modelTree.js
@@ -1,43 +0,0 @@
-panelApp.directive('batModelTree', function ($compile) {
-
- // make toggle settings persist across $compile
- var modelState = {};
- var scopeState = {};
-
- return {
- restrict: 'E',
- terminal: true,
- scope: {
- val: '=',
- edit: '=',
- inspect: '='
- },
- link: function (scope, element, attrs) {
- // this is more complicated then it should be
- // see: https://github.com/angular/angular.js/issues/898
- element.append(
- '<div class="scope-branch">' +
- '<a href ng-click="inspect()">Scope ({{val.id}})</a>' +
- '<span ng-show="val.children.length"> | <a href ng-click="scopeState[val.id] = !scopeState[val.id]">scopes</a></span>' +
- '<span ng-show="val.locals"> | <a href ng-click="modelState[val.id] = !modelState[val.id]">models</a></span>' +
-
- '<div ng-show="modelState[val.id]">' +
- '<bat-json-tree val="val.locals" ></bat-json-tree>' +
- '</div>' +
-
- '<div ng-hide="scopeState[val.id]">' +
- '<div ng-repeat="child in val.children">' +
- '<bat-model-tree val="child" inspect="inspect" edit="edit"></bat-model-tree>' +
- '</div>' +
- '</div>' +
-
- '</div>');
-
- var childScope = scope.$new();
- childScope.modelState = modelState;
- childScope.scopeState = scopeState;
-
- $compile(element.contents())(childScope);
- }
- };
-});
View
46 js/directives/scopeTree.js
@@ -0,0 +1,46 @@
+angular.module('panelApp').directive('batScopeTree', function ($compile) {
+
+ // make toggle settings persist across $compile
+ var modelState = {};
+ var scopeState = {};
+
+ var selected = null;
+
+ var template =
+ '<div class="scope-branch">' +
+ '<a href ng-click="inspect()">&lt;</a> ' +
+ '<a href ng-click="select()" ng-class="{selected: selectedScope == val.id}">Scope ({{val.id}})</a>' +
+ '<div ng-repeat="child in val.children">' +
+ '<bat-scope-tree ' +
+ 'val="child" ' +
+ 'inspect="inspect" ' +
+ 'select="select" ' +
+ 'selected-scope="selectedScope">' +
+ '</bat-scope-tree>' +
+ '</div>' +
+ '</div>';
+
+ return {
+ restrict: 'E',
+ terminal: true,
+ scope: {
+ val: '=',
+ select: '=',
+ selectedScope: '=',
+ inspect: '='
+ },
+ link: function (scope, element, attrs) {
+ // this is more complicated then it should be
+ // see: https://github.com/angular/angular.js/issues/898
+ element.append(template);
+
+ var childScope = scope.$new();
+
+ childScope.select = scope.select;
+ //childScope.selectedScope = scope.selectedScope;
+ childScope.inspect = scope.inspect;
+
+ $compile(element.contents())(childScope);
+ }
+ };
+});
View
2  js/directives/slider.js
@@ -1,5 +1,5 @@
// range slider
-panelApp.directive('batSlider', function($compile) {
+angular.module('panelApp').directive('batSlider', function($compile) {
return {
restrict: 'E',
terminal: true,
View
2  js/directives/tabs.js
@@ -1,4 +1,4 @@
-panelApp.directive('batTabs', function ($compile, $templateCache, $http) {
+angular.module('panelApp').directive('batTabs', function ($compile, $templateCache, $http) {
return {
restrict: 'E',
transclude: true,
View
2  js/directives/watcherTree.js
@@ -1,5 +1,5 @@
// watchers tree
-panelApp.directive('batWatcherTree', function($compile) {
+angular.module('panelApp').directive('batWatcherTree', function($compile) {
// make toggle settings persist across $compile
var scopeState = {};
View
2  js/filters/first.js
@@ -1,5 +1,5 @@
// returns the first line of a multi-line string
-panelApp.filter('first', function () {
+angular.module('panelApp').filter('first', function () {
return function (input, output) {
return input.split("\n")[0];
};
View
2  js/filters/precision.js
@@ -1,5 +1,5 @@
// returns the number's first 4 decimals
-panelApp.filter('precision', function () {
+angular.module('panelApp').filter('precision', function () {
return function (input, output) {
return input.toPrecision(4);
};
View
2  js/filters/sortByTime.js
@@ -1,6 +1,6 @@
// Sort watchers by time
// Used by the performance tab
-panelApp.filter('sortByTime', function () {
+angular.module('panelApp').filter('sortByTime', function () {
return function (input, min, max) {
var copy = input.slice(0);
View
360 js/inject/debug.js
@@ -76,6 +76,9 @@ var inject = function () {
nu; // The new object or array
switch (typeof value) {
case 'object':
+ if (value instanceof HTMLElement) {
+ return value.innerHTML;
+ }
if (!value) {
return null;
}
@@ -181,6 +184,73 @@ var inject = function () {
}());
}
+ // throttle based on _.throttle from Lo-Dash
+ // https://github.com/bestiejs/lodash/blob/master/lodash.js#L4625
+ var throttle = function (func, wait) {
+ var args,
+ result,
+ thisArg,
+ timeoutId,
+ lastCalled = 0;
+
+ function trailingCall() {
+ lastCalled = new Date();
+ timeoutId = null;
+ result = func.apply(thisArg, args);
+ }
+ return function() {
+ var now = new Date(),
+ remaining = wait - (now - lastCalled);
+
+ args = arguments;
+ thisArg = this;
+
+ if (remaining <= 0) {
+ clearTimeout(timeoutId);
+ timeoutId = null;
+ lastCalled = now;
+ result = func.apply(thisArg, args);
+ }
+ else if (!timeoutId) {
+ timeoutId = setTimeout(trailingCall, remaining);
+ }
+ return result;
+ };
+ };
+
+ var debounce = function (func, wait, immediate) {
+ var args,
+ result,
+ thisArg,
+ timeoutId;
+
+ function delayed() {
+ timeoutId = null;
+ if (!immediate) {
+ result = func.apply(thisArg, args);
+ }
+ }
+ return function() {
+ var isImmediate = immediate && !timeoutId;
+ args = arguments;
+ thisArg = this;
+
+ clearTimeout(timeoutId);
+ timeoutId = setTimeout(delayed, wait);
+
+ if (isImmediate) {
+ result = func.apply(thisArg, args);
+ }
+ return result;
+ };
+ };
+
+ var updateScopeModelCache = function (scope) {
+ debug.models[scope.$id] = getScopeLocals(scope);
+ debug.scopeDirty[scope.$id] = false;
+ };
+
+ var popover = null;
// Public API
// ==========
@@ -199,14 +269,21 @@ var inject = function () {
return ids;
},
+ // returns null or cached scope
+ getModel: function (id) {
+ if (debug.scopeDirty[id]) {
+ updateScopeModelCache(debug.scopes[id]);
+ return debug.models[id];
+ }
+ },
+
getScopeTree: function (id) {
- if (debug.rootScopeDirty[id] === false) {
+ if (debug.scopeTreeDirty[id] === false) {
return;
}
var traverse = function (sc) {
var tree = {
id: sc.$id,
- locals: debug.scopes[sc.$id],
children: []
};
@@ -224,9 +301,10 @@ var inject = function () {
var tree = traverse(root);
if (tree) {
- debug.rootScopeDirty[id] = false;
+ debug.scopeTreeDirty[id] = false;
}
+
return tree;
},
@@ -266,6 +344,241 @@ var inject = function () {
var tree = traverse(root);
return tree;
+ },
+
+ enable: function () {
+ if (popover) {
+ return;
+ }
+ var angular = window.angular;
+ popover = angular.element(
+ '<div style="position: fixed; left: 50px; top: 50px; z-index: 9999; background-color: #f1f1f1; box-shadow: 0 15px 10px -10px rgba(0, 0, 0, 0.5), 0 1px 4px rgba(0, 0, 0, 0.3), 0 0 40px rgba(0, 0, 0, 0.1) inset;">' +
+ '<div style="position: relative" style="min-width: 300px; min-height: 100px;">' +
+ '<div style="min-width: 100px; min-height: 50px; padding: 5px;"><pre>{ Please select a scope }</pre></div>' +
+ '<button style="position: absolute; top: -15px; left: -15px; cursor: move;">⇱</button>' +
+ '<button style="position: absolute; top: -15px; left: 10px;">+</button>' +
+ '<button style="position: absolute; top: -15px; right: -15px;">x</button>' +
+ '<style>' +
+ '.ng-scope.batarang-selected { border: 1px solid red; } ' +
+ '.bat-indent { margin-left: 20px; }' +
+ '</style>' +
+ '</div>' +
+ '</div>');
+ angular.element(window.document.body).append(popover);
+ var popoverContent = angular.element(angular.element(popover.children('div')[0]).children()[0]);
+ var dragElt = angular.element(angular.element(popover.children('div')[0]).children()[1]);
+ var selectElt = angular.element(angular.element(popover.children('div')[0]).children()[2]);
+ var closeElt = angular.element(angular.element(popover.children('div')[0]).children()[3]);
+
+ console.log(closeElt);
+
+ var currentScope = null,
+ currentElt = null;
+
+ function onMove (ev) {
+ var x = ev.clientX,
+ y = ev.clientY;
+
+ if (x > window.outerWidth - 100) {
+ x = window.outerWidth - 100;
+ } else if (x < 0) {
+ x = 0;
+ }
+ if (y > window.outerHeight - 100) {
+ y = window.outerHeight - 100;
+ } else if (y < 0) {
+ y = 0;
+ }
+
+ x += 5;
+ y += 5;
+
+ popover.css('left', x + 'px');
+ popover.css('top', y + 'px');
+ }
+
+ closeElt.bind('click', function () {
+ popover.remove();
+ popover = null;
+ });
+
+ selectElt.bind('click', bindSelectScope);
+
+ var selecting = false;
+ function bindSelectScope () {
+ if (selecting) {
+ return;
+ }
+ setTimeout(function () {
+ selecting = true;
+ selectElt.attr('disabled', true);
+ angular.element(document.body).css('cursor', 'crosshair');
+ angular.element(document.getElementsByClassName('ng-scope'))
+ .bind('click', onSelectScope)
+ .bind('mouseover', onHoverScope);
+ }, 30);
+ }
+
+ var hoverScopeElt = null;
+
+ function markHoverElt () {
+ if (hoverScopeElt) {
+ hoverScopeElt.addClass('batarang-selected');
+ }
+ }
+ function unmarkHoverElt () {
+ if (hoverScopeElt) {
+ hoverScopeElt.removeClass('batarang-selected');
+ }
+ }
+
+ function onSelectScope (ev) {
+ render(this);
+ angular.element(document.getElementsByClassName('ng-scope'))
+ .unbind('click', onSelectScope)
+ .unbind('mouseover', onHoverScope);
+ unmarkHoverElt();
+ selecting = false;
+ selectElt.attr('disabled', false);
+ angular.element(document.body).css('cursor', '');
+ hovering = false;
+ }
+
+ var hovering = false;
+ function onHoverScope (ev) {
+ if (hovering) {
+ return;
+ }
+ hovering = true;
+ var that = this;
+ setTimeout(function () {
+ unmarkHoverElt();
+ hoverScopeElt = angular.element(that);
+ markHoverElt();
+ hovering = false;
+ render(that);
+ }, 100);
+ }
+
+ function onUnhoverScope (ev) {
+ angular.element(this).css('border', '');
+ }
+
+ dragElt.bind('mousedown', function (ev) {
+ ev.preventDefault();
+ rendering = true;
+ angular.element(document).bind('mousemove', onMove);
+ });
+ angular.element(document).bind('mouseup', function () {
+ angular.element(document).unbind('mousemove', onMove);
+ setTimeout(function () {
+ rendering = false;
+ }, 120);
+ });
+
+ function renderTree (data) {
+ var tree = angular.element('<div class="bat-indent"></div>');
+ angular.forEach(data, function (val, key) {
+ var toAppend;
+ if (val === undefined) {
+ toAppend = '<i>undefined</i>';
+ } else if (val === null) {
+ toAppend = '<i>null</i>';
+ } else if (val instanceof Array) {
+ toAppend = '[ ... ]';
+ } else if (val instanceof Object) {
+ toAppend = '{ ... }';
+ } else {
+ toAppend = val.toString();
+ }
+ if (data instanceof Array) {
+ toAppend = '<div>' +
+ toAppend +
+ ((key===data.length-1)?'':',') +
+ '</div>';
+ } else {
+ toAppend = '<div>' +
+ key +
+ ': ' +
+ toAppend +
+ (key!==0?'':',') +
+ '</div>';
+ }
+ toAppend = angular.element(toAppend);
+ if (val instanceof Array || val instanceof Object) {
+ function recur () {
+ toAppend.unbind('click', recur);
+ toAppend.html('');
+ toAppend
+ .append(angular.element('<span>' +
+ key + ': ' +
+ ((val instanceof Array)?'[':'{') +
+ '<span>').bind('click', collapse))
+ .append(renderTree(val))
+ .append('<span>' + ((val instanceof Array)?']':'}') + '<span>');
+ }
+ function collapse () {
+ toAppend.html('');
+ toAppend.append(angular.element('<div>' +
+ key +
+ ': ' +
+ ((val instanceof Array)?'[ ... ]':'{ ... }') +
+ '</div>').bind('click', recur));
+ }
+ toAppend.bind('click', recur);
+ }
+ tree.append(toAppend);
+ });
+
+ return tree;
+ }
+
+ var isEmpty = function (object) {
+ var prop;
+ for (prop in object) {
+ if (object.hasOwnProperty(prop)) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ var objLength = function (object) {
+ var prop, len = 0;
+ for (prop in object) {
+ if (object.hasOwnProperty(prop)) {
+ len += 1;
+ }
+ }
+ return len;
+ };
+
+ var rendering = false;
+ var render = function (elt) {
+ if (rendering) {
+ return;
+ }
+ rendering = true;
+ setTimeout(function () {
+ var scope = angular.element(elt).scope();
+ rendering = false;
+ if (scope === currentScope) {
+ return;
+ }
+ currentScope = scope;
+ currentElt = elt;
+
+ var models = getScopeLocals(scope);
+ popoverContent.children().remove();
+ if (isEmpty(models)) {
+ popoverContent.append(angular.element('<i>This scope has no models</i>'));
+ } else {
+ popoverContent.append(renderTree(models));
+ }
+
+ }, 100);
+ };
+
}
};
@@ -282,14 +595,16 @@ var inject = function () {
watchPerf: {},
applyPerf: {},
- // map of scope.$ids --> model objects
+ // map of scope.$ids --> $scope objects
scopes: {},
+ // map of scope.$ids --> model objects
+ models: {},
+ // map of $ids --> bools
+ scopeDirty: {},
- // map of $ids --> refs to root scopes
+ // map of $ids --> refs to $rootScope objects
rootScopes: {},
-
- // map of $ids --> bools
- rootScopeDirty: {},
+ scopeTreeDirty: {},
deps: []
};
@@ -363,7 +678,7 @@ var inject = function () {
});
$provide.decorator('$rootScope', function ($delegate) {
-
+
var watchFnToHumanReadableString = function (fn) {
if (fn.exp) {
return fn.exp.trim();
@@ -398,7 +713,7 @@ var inject = function () {
$delegate.__proto__.$watch = function (watchExpression, applyFunction) {
var thatScope = this;
var watchStr = watchFnToHumanReadableString(watchExpression);
-
+
if (!debug.watchPerf[watchStr]) {
debug.watchPerf[watchStr] = {
time: 0,
@@ -442,8 +757,8 @@ var inject = function () {
var start = performance.now();
var ret = unpatchedApplyFunction.apply(this, arguments);
var end = performance.now();
+ debug.scopeDirty[this.$id] = true;
- debug.scopes[thatScope.$id] = getScopeLocals(thatScope);
//TODO: move these checks out of here and into registering the watcher
if (!debug.applyPerf[applyStr]) {
debug.applyPerf[applyStr] = {
@@ -453,7 +768,6 @@ var inject = function () {
}
debug.applyPerf[applyStr].time += (end - start);
debug.applyPerf[applyStr].calls += 1;
- debug.rootScopeDirty[thatScope.$root.$id] = true;
return ret;
};
}
@@ -469,12 +783,16 @@ var inject = function () {
if (debug.watchers[this.$id]) {
delete debug.watchers[this.$id];
}
+ if (debug.models[this.$id]) {
+ delete debug.models[this.$id];
+ }
if (debug.scopes[this.$id]) {
delete debug.scopes[this.$id];
}
return _destroy.apply(this, arguments);
};
+
// patch $new
// ----------
var _new = $delegate.__proto__.$new;
@@ -483,6 +801,7 @@ var inject = function () {
var ret = _new.apply(this, arguments);
if (ret.$root) {
debug.rootScopes[ret.$root.$id] = ret.$root;
+ debug.scopeTreeDirty[ret.$root.$id] = true;
}
// create empty watchers array for this scope
@@ -490,11 +809,23 @@ var inject = function () {
debug.watchers[ret.$id] = [];
}
- debug.rootScopeDirty[ret.$root.$id] = true;
+ debug.scopes[ret.$id] = ret;
+ debug.scopeDirty[ret.$id] = true;
return ret;
};
+
+ // patch $digest
+ // -------------
+ var _digest = $delegate.__proto__.$digest;
+ $delegate.__proto__.$digest = function (fn) {
+ var ret = _digest.apply(this, arguments);
+ debug.scopeDirty[this.$id] = true;
+ return ret;
+ };
+
+
// patch $apply
// ------------
var _apply = $delegate.__proto__.$apply;
@@ -502,6 +833,7 @@ var inject = function () {
var start = performance.now();
var ret = _apply.apply(this, arguments);
var end = performance.now();
+ debug.scopeDirty[this.$id] = true;
// If the debugging option is enabled, log to console
// --------------------------------------------------
@@ -521,7 +853,7 @@ var inject = function () {
// Return a script element with the above code embedded in it
var script = window.document.createElement('script');
script.innerHTML = '(' + fn.toString() + '(window))';
-
+
return script;
}()));
};
View
7 js/panelApp.js
@@ -1 +1,6 @@
-var panelApp = angular.module('panelApp', []);
+// Broadcast poll events
+angular.module('panelApp', []).run(function ($rootScope) {
+ setInterval(function () {
+ $rootScope.$broadcast('poll');
+ }, 500);
+});
View
4 js/services/appContext.js
@@ -1,5 +1,5 @@
// Service for running code in the context of the application being debugged
-panelApp.factory('appContext', function (chromeExtension) {
+angular.module('panelApp').factory('appContext', function (chromeExtension) {
// Public API
// ==========
@@ -79,7 +79,7 @@ panelApp.factory('appContext', function (chromeExtension) {
// TODO: depreciate this; only poll from now on?
// There are some cases where you need to gather data on a once-per-bootstrap basis, for
// instance getting the version of AngularJS
-
+
// TODO: move to chromeExtension?
watchRefresh: function (cb) {
var port = chrome.extension.connect();
View
2  js/services/appCss.js
@@ -1,5 +1,5 @@
// Service for injecting CSS into the application
-panelApp.factory('appCss', function (chromeExtension) {
+angular.module('panelApp').factory('appCss', function (chromeExtension) {
return {
addCssRule: function (args) {
chromeExtension.eval(function (window, args) {
View
2  js/services/appDeps.js
@@ -1,5 +1,5 @@
// Service for retrieving and caching application dependencies
-panelApp.factory('appDeps', function (chromeExtension, appContext) {
+angular.module('panelApp').factory('appDeps', function (chromeExtension, appContext) {
var _depsCache = [];
View
2  js/services/appHighlight.js
@@ -1,5 +1,5 @@
// Service for highlighting parts of the application
-panelApp.factory('appHighlight', function (appCss) {
+angular.module('panelApp').factory('appHighlight', function (appCss) {
//TODO: improve look of highlighting; for instance, if an element is bound and a scope,
// you will only see the most recently applied outline
View
2  js/services/appInfo.js
@@ -1,5 +1,5 @@
// Service for running code in the context of the application being debugged
-panelApp.factory('appInfo', function (chromeExtension, appContext) {
+angular.module('panelApp').factory('appInfo', function (chromeExtension, appContext) {
var _versionCache = null,
_srcCache = null;
View
2  js/services/appInspect.js
@@ -1,5 +1,5 @@
// Service for highlighting parts of the application
-panelApp.factory('appInspect', function (chromeExtension) {
+angular.module('panelApp').factory('appInspect', function (chromeExtension) {
return {
enable: function () {
chromeExtension.eval(function (window) {
View
30 js/services/appModel.js
@@ -1,7 +1,8 @@
// Service for running code in the context of the application being debugged
-panelApp.factory('appModel', function (chromeExtension, appContext) {
+angular.module('panelApp').factory('appModel', function (chromeExtension, appContext) {
- var _scopeCache = {},
+ var _scopeTreeCache = {},
+ _scopeCache = {},
_rootScopeCache = [];
@@ -27,18 +28,39 @@ panelApp.factory('appModel', function (chromeExtension, appContext) {
});
},
- getModelTree: function (id, callback) {
+ // only runs callback if model has changed since last call
+ getModel: function (id, callback) {
if (!id) {
return;
}
chromeExtension.eval(function (window, args) {
- return window.__ngDebug.getScopeTree(args.id);
+ return window.__ngDebug.getModel(args.id);
}, {id: id}, function (tree) {
if (tree) {
_scopeCache[id] = tree;
}
callback(_scopeCache[id]);
});
+ },
+
+ getScopeTree: function (id, callback) {
+ if (!id) {
+ return;
+ }
+ chromeExtension.eval(function (window, args) {
+ return window.__ngDebug.getScopeTree(args.id);
+ }, {id: id}, function (tree) {
+ if (tree) {
+ _scopeTreeCache[id] = tree;
+ }
+ callback(_scopeTreeCache[id]);
+ });
+ },
+
+ enableInspector: function (argument) {
+ chromeExtension.eval(function (window, args) {
+ return window.__ngDebug.enable();
+ });
}
};
});
View
2  js/services/appPerf.js
@@ -1,5 +1,5 @@
// Service for retrieving and caching performance data
-panelApp.factory('appPerf', function (chromeExtension, appContext) {
+angular.module('panelApp').factory('appPerf', function (chromeExtension, appContext) {
var _histogramCache = [],
_watchNameToPerf = {},
View
2  js/services/appWatch.js
@@ -1,5 +1,5 @@
// Service for running code in the context of the application being debugged
-panelApp.factory('appWatch', function (chromeExtension) {
+angular.module('panelApp').factory('appWatch', function (chromeExtension) {
var _watchCache = {};
View
2  js/services/chromeExtension.js
@@ -1,5 +1,5 @@
// abstraction layer for Chrome Extension APIs
-panelApp.value('chromeExtension', {
+angular.module('panelApp').value('chromeExtension', {
sendRequest: function (requestName, cb) {
chrome.extension.sendRequest({
script: requestName,
View
2  js/services/filesystem.js
@@ -1,5 +1,5 @@
// Service for exporting as JSON
-panelApp.factory('filesystem', function(chromeExtension) {
+angular.module('panelApp').factory('filesystem', function(chromeExtension) {
// taken from:
// http://html5-demos.appspot.com/static/html5storage/index.html#slide59
View
11 js/services/poll.js
@@ -1,11 +0,0 @@
-// Service for broadcasting poll events
-panelApp.factory('poll', function ($rootScope) {
-
- setInterval(function () {
- $rootScope.$broadcast('poll');
- }, 500);
-
- return {
- setInterval: function (int) {}
- };
-});
View
3  panel.html
@@ -17,7 +17,7 @@
<script src="js/directives/d3.js"></script>
<script src="js/directives/jsonTree.js"></script>
- <script src="js/directives/modelTree.js"></script>
+ <script src="js/directives/scopeTree.js"></script>
<script src="js/directives/slider.js"></script>
<script src="js/directives/tabs.js"></script>
<script src="js/directives/watcherTree.js"></script>
@@ -36,7 +36,6 @@
<script src="js/services/appWatch.js"></script>
<script src="js/services/chromeExtension.js"></script>
<script src="js/services/filesystem.js"></script>
- <script src="js/services/poll.js"></script>
<script src="js/controllers/DepsCtrl.js"></script>
<script src="js/controllers/ModelCtrl.js"></script>
View
36 panes/model.html
@@ -1,13 +1,29 @@
<div ng-controller="ModelCtrl">
- <h2>Models</h2>
- <div ng-show="roots.length > 1">
- <label for="select-root">Root
- <select
- ng-options="root.toString() for root in roots"
- ng-model="selectedRoot"></select>
- </label>
+
+ <div class="span6">
+ <h2>Scopes</h2>
+ <div ng-show="roots.length > 1">
+ <label for="select-root">Root
+ <select
+ ng-options="root.toString() for root in roots"
+ ng-model="selectedRoot"></select>
+ </label>
+ </div>
+ <pre>
+ <bat-scope-tree
+ val="tree"
+ inspect="inspect"
+ select="select"
+ selected-scope="selectedScope"
+ edit="edit"></bat-scope-tree>
+ </pre>
+ </div>
+
+ <div class="span6">
+ <h2>Models<span ng-show="selectedScope"> for ({{selectedScope}})</span></h2>
+ <bat-json-tree val="model"></bat-json-tree>
+ </div>
+ <div class="span6">
+ <button class="btn" ng-click="enableInspector()">Enable Inspector</button>
</div>
- <pre>
- <bat-model-tree val="tree" inspect="inspect" edit="edit"></bat-model-tree>
- </pre>
</div>
View
24 testacular.e2e.conf
@@ -0,0 +1,24 @@
+
+files = [
+ JASMINE,
+ JASMINE_ADAPTER,
+ 'js/lib/angular.js',
+ 'js/lib/angular-mocks.js',
+
+ 'js/panelApp.js',
+ 'js/controllers/*.js',
+ 'js/directives/*.js',
+ 'js/filters/*.js',
+ 'js/services/*.js',
+
+ 'test/mock/*.js',
+ 'test/*.js'
+];
+
+exclude = [];
+
+autoWatch = true;
+autoWatchInterval = 1;
+logLevel = LOG_INFO;
+logColors = true;
+
View
14,886 tests/angular-expose-isolate-scopes.js
14,886 additions, 0 deletions not shown
View
20 tests/apply.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html ng-app="testApp">
+<head>
+ <script src="angular-expose-isolate-scopes.js"></script>
+ <script>
+ angular.module('testApp', [])
+ .controller('TestCtrl', function ($scope) {
+ $scope.someArray = [];
+ setTimeout(function () {
+ $scope.$digest(function() {
+ $scope.someArray.push(1);
+ });
+ }, 0)
+ });
+ </script>
+</head>
+<body ng-controller="TestCtrl">
+ <my-trans-dir this="person"></my-trans-dir>
+</body>
+</html>
View
34 tests/grid.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html ng-app="myApp">
+ <head lang="en">
+ <meta charset="utf-8">
+ <title>Custom Plunker</title>
+ <link rel="stylesheet" type="text/css" href="http://angular-ui.github.com/ng-grid/css/ng-grid.css" />
+ <style>
+ /*style.css*/
+ .gridStyle {
+ border: 1px solid rgb(212,212,212);
+ width: 400px;
+ height: 300px
+ }
+ </style>
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
+ <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.2/angular.min.js"></script>
+ <script src="http://angular-ui.github.com/ng-grid/lib/ng-grid.debug.js"></script>
+ <script>
+ // main.js
+ var app = angular.module('myApp', ['ngGrid']);
+ app.controller('MyCtrl', function($scope) {
+ $scope.myData = [{name: "Moroni", age: 50},
+ {name: "Tiancum", age: 43},
+ {name: "Jacob", age: 27},
+ {name: "Nephi", age: 29},
+ {name: "Enos", age: 34}];
+ $scope.gridOptions = { data: 'myData' };
+ });
+ </script>
+ </head>
+ <body ng-controller="MyCtrl">
+ <div class="gridStyle" ng-grid="gridOptions"></div>
+ </body>
+</html>
View
25 tests/isolate.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html ng-app="testApp">
+<head>
+ <script src="angular-expose-isolate-scopes.js"></script>
+ <script>
+ angular.module('testApp', [])
+ .controller('TestCtrl', function ($scope) {
+ $scope.person = 'bob';
+ $scope.other = 'john';
+ })
+ .directive('myTransDir', function () {
+ return {
+ restrict: 'E',
+ template: '{{that}}',
+ scope: {
+ that: '=this'
+ }
+ };
+ });
+ </script>
+</head>
+<body ng-controller="TestCtrl">
+ <my-trans-dir this="person"></my-trans-dir>
+</body>
+</html>
View
30 tests/transclusion.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html ng-app="testApp">
+<head>
+ <script src="angular-expose-isolate-scopes.js"></script>
+ <script>
+ angular.module('testApp', [])
+ .controller('TestCtrl', function ($scope) {
+ $scope.person = 'bob';
+ $scope.other = 'bob';
+ })
+ .directive('myTransDir', function () {
+ return {
+ restrict: 'E',
+ template: '{{that}} pre-trans' +
+ '<div ng-transclude></div>' +
+ 'post-trans {{that}}',
+ transclude: true,
+ scope: {
+ that: '=this'
+ }
+ };
+ });
+ </script>
+</head>
+<body ng-controller="TestCtrl">
+ <my-trans-dir this="person">
+ this is inside
+ </my-trans-dir>
+</body>
+</html>
Please sign in to comment.
Something went wrong with that request. Please try again.