You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
878 lines
27 KiB
JavaScript
878 lines
27 KiB
JavaScript
var inject = function () {
|
|
document.head.appendChild((function () {
|
|
|
|
var fn = function bootstrap (window) {
|
|
|
|
var angular = window.angular;
|
|
|
|
// Helper to determine if the root 'ng' module has been loaded
|
|
// window.angular may be available if the app is bootstrapped asynchronously, but 'ng' might
|
|
// finish loading later.
|
|
var ngLoaded = function () {
|
|
if (!window.angular) {
|
|
return false;
|
|
}
|
|
try {
|
|
window.angular.module('ng');
|
|
}
|
|
catch (e) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
if (!ngLoaded()) {
|
|
(function () {
|
|
// TODO: var name
|
|
var areWeThereYet = function (ev) {
|
|
if (ev.srcElement.tagName === 'SCRIPT') {
|
|
var oldOnload = ev.srcElement.onload;
|
|
ev.srcElement.onload = function () {
|
|
if (ngLoaded()) {
|
|
|
|
document.removeEventListener('DOMNodeInserted', areWeThereYet);
|
|
bootstrap(window);
|
|
}
|
|
if (oldOnload) {
|
|
oldOnload.apply(this, arguments);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
document.addEventListener('DOMNodeInserted', areWeThereYet);
|
|
}());
|
|
return;
|
|
}
|
|
|
|
// do not patch twice
|
|
if (window.__ngDebug) {
|
|
return;
|
|
}
|
|
|
|
// Helpers
|
|
// =======
|
|
|
|
// polyfill for performance.now on older webkit
|
|
if (!performance.now) {
|
|
performance.now = performance.webkitNow;
|
|
}
|
|
|
|
// Based on cycle.js
|
|
// https://github.com/douglascrockford/JSON-js/blob/master/cycle.js
|
|
|
|
// Make a deep copy of an object or array, assuring that there is at most
|
|
// one instance of each object or array in the resulting structure. The
|
|
// duplicate references (which might be forming cycles) are replaced with
|
|
// an object of the form
|
|
// {$ref: PATH}
|
|
// where the PATH is a JSONPath string that locates the first occurrence.
|
|
var decycle = function (object) {
|
|
var objects = [], // Keep a reference to each unique object or array
|
|
paths = []; // Keep the path to each unique object or array
|
|
|
|
return (function derez(value, path) {
|
|
var i, // The loop counter
|
|
name, // Property name
|
|
nu; // The new object or array
|
|
switch (typeof value) {
|
|
case 'object':
|
|
if (value instanceof HTMLElement) {
|
|
return value.innerHTML.toString().trim();
|
|
}
|
|
if (!value) {
|
|
return null;
|
|
}
|
|
for (i = 0; i < objects.length; i += 1) {
|
|
if (objects[i] === value) {
|
|
return {$ref: paths[i]};
|
|
}
|
|
}
|
|
objects.push(value);
|
|
paths.push(path);
|
|
if (value instanceof Array) {
|
|
nu = [];
|
|
for (i = 0; i < value.length; i += 1) {
|
|
nu[i] = derez(value[i], path + '[' + i + ']');
|
|
}
|
|
} else {
|
|
nu = {};
|
|
for (name in value) {
|
|
if (name[0] !== '$' && Object.prototype.hasOwnProperty.call(value, name)) {
|
|
nu[name] = derez(value[name],
|
|
path + '[' + JSON.stringify(name) + ']');
|
|
}
|
|
}
|
|
}
|
|
return nu;
|
|
case 'number':
|
|
case 'string':
|
|
case 'boolean':
|
|
return value;
|
|
}
|
|
}(object, '$'));
|
|
};
|
|
// End
|
|
// ===
|
|
|
|
// given a scope object, return an object with deep clones
|
|
// of the models exposed on that scope
|
|
var getScopeLocals = function (scope) {
|
|
var scopeLocals = {}, prop;
|
|
for (prop in scope) {
|
|
if (scope.hasOwnProperty(prop) && prop !== 'this' && prop[0] !== '$') {
|
|
scopeLocals[prop] = decycle(scope[prop]);
|
|
}
|
|
}
|
|
return scopeLocals;
|
|
};
|
|
|
|
// helper to extract dependencies from function arguments
|
|
// not all versions of AngularJS expose annotate
|
|
var annotate = angular.injector().annotate;
|
|
if (!annotate) {
|
|
annotate = (function () {
|
|
|
|
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
|
|
var FN_ARG_SPLIT = /,/;
|
|
var FN_ARG = /^\s*(_?)(.+?)\1\s*$/;
|
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
|
|
|
// TODO: should I keep these assertions?
|
|
function assertArg(arg, name, reason) {
|
|
if (!arg) {
|
|
throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required"));
|
|
}
|
|
return arg;
|
|
}
|
|
function assertArgFn(arg, name, acceptArrayAnnotation) {
|
|
if (acceptArrayAnnotation && angular.isArray(arg)) {
|
|
arg = arg[arg.length - 1];
|
|
}
|
|
|
|
assertArg(angular.isFunction(arg), name, 'not a function, got ' +
|
|
(arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg));
|
|
return arg;
|
|
}
|
|
|
|
return function (fn) {
|
|
var $inject,
|
|
fnText,
|
|
argDecl,
|
|
last;
|
|
|
|
if (typeof fn == 'function') {
|
|
if (!($inject = fn.$inject)) {
|
|
$inject = [];
|
|
fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
|
argDecl = fnText.match(FN_ARGS);
|
|
argDecl[1].split(FN_ARG_SPLIT).forEach(function(arg) {
|
|
arg.replace(FN_ARG, function(all, underscore, name) {
|
|
$inject.push(name);
|
|
});
|
|
});
|
|
fn.$inject = $inject;
|
|
}
|
|
} else if (angular.isArray(fn)) {
|
|
last = fn.length - 1;
|
|
assertArgFn(fn[last], 'fn');
|
|
$inject = fn.slice(0, last);
|
|
} else {
|
|
assertArgFn(fn, 'fn', true);
|
|
}
|
|
return $inject;
|
|
};
|
|
}());
|
|
}
|
|
|
|
// 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
|
|
// ==========
|
|
|
|
var api = window.__ngDebug = {
|
|
|
|
getDeps: function () {
|
|
return debug.deps;
|
|
},
|
|
|
|
getRootScopeIds: function () {
|
|
var ids = [];
|
|
angular.forEach(debug.rootScopes, function (elt, id) {
|
|
ids.push(id);
|
|
});
|
|
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.scopeTreeDirty[id] === false) {
|
|
return;
|
|
}
|
|
var traverse = function (sc) {
|
|
var tree = {
|
|
id: sc.$id,
|
|
children: []
|
|
};
|
|
|
|
var child = sc.$$childHead;
|
|
if (child) {
|
|
do {
|
|
tree.children.push(traverse(child));
|
|
} while (child !== sc.$$childTail && (child = child.$$nextSibling));
|
|
}
|
|
|
|
return tree;
|
|
};
|
|
|
|
var root = debug.rootScopes[id];
|
|
var tree = traverse(root);
|
|
|
|
if (tree) {
|
|
debug.scopeTreeDirty[id] = false;
|
|
}
|
|
|
|
|
|
return tree;
|
|
},
|
|
|
|
getWatchPerf: function () {
|
|
var changes = [];
|
|
angular.forEach(debug.watchPerf, function (info, name) {
|
|
if (info.time > 0) {
|
|
changes.push({
|
|
name: name,
|
|
time: info.time
|
|
});
|
|
info.time = 0;
|
|
}
|
|
});
|
|
return changes;
|
|
},
|
|
|
|
getWatchTree: function (id) {
|
|
var traverse = function (sc) {
|
|
var tree = {
|
|
id: sc.$id,
|
|
watchers: debug.watchers[sc.$id],
|
|
children: []
|
|
};
|
|
|
|
var child = sc.$$childHead;
|
|
if (child) {
|
|
do {
|
|
tree.children.push(traverse(child));
|
|
} while (child !== sc.$$childTail && (child = child.$$nextSibling));
|
|
}
|
|
|
|
return tree;
|
|
};
|
|
|
|
var root = debug.rootScopes[id];
|
|
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.bat-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]);
|
|
|
|
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('bat-selected');
|
|
}
|
|
}
|
|
function unmarkHoverElt () {
|
|
if (hoverScopeElt) {
|
|
hoverScopeElt.removeClass('bat-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);
|
|
};
|
|
|
|
}
|
|
};
|
|
|
|
|
|
// Private state
|
|
// =============
|
|
|
|
//var bootstrap = window.angular.bootstrap;
|
|
var debug = {
|
|
// map of scopes --> watcher function name strings
|
|
watchers: {},
|
|
|
|
// maps of watch/apply exp/fns to perf data
|
|
watchPerf: {},
|
|
applyPerf: {},
|
|
|
|
// map of scope.$ids --> $scope objects
|
|
scopes: {},
|
|
// map of scope.$ids --> model objects
|
|
models: {},
|
|
// map of $ids --> bools
|
|
scopeDirty: {},
|
|
|
|
// map of $ids --> refs to $rootScope objects
|
|
rootScopes: {},
|
|
scopeTreeDirty: {},
|
|
|
|
deps: []
|
|
};
|
|
|
|
|
|
// Instrumentation
|
|
// ===============
|
|
|
|
var ng = angular.module('ng');
|
|
ng.config(function ($provide) {
|
|
// methods to patch
|
|
|
|
// $provide.provider
|
|
var temp = $provide.provider;
|
|
$provide.provider = function (name, definition) {
|
|
if (!definition) {
|
|
angular.forEach(name, function (definition, name) {
|
|
var tempGet = definition.$get;
|
|
definition.$get = function () {
|
|
debug.deps.push({
|
|
name: name,
|
|
imports: annotate(tempGet)
|
|
});
|
|
return tempGet.apply(this, arguments);
|
|
};
|
|
});
|
|
} else if (definition.$get instanceof Array) {
|
|
// it should have a $get
|
|
var tempGet = definition.$get[definition.$get.length - 1];
|
|
|
|
definition.$get[definition.$get.length - 1] = function () {
|
|
debug.deps.push({
|
|
name: name,
|
|
imports: annotate(tempGet)
|
|
});
|
|
return tempGet.apply(this, arguments);
|
|
};
|
|
} else if (typeof definition === 'object') {
|
|
// it should have a $get
|
|
var tempGet = definition.$get;
|
|
|
|
// preserve original annotations
|
|
definition.$get = annotate(definition.$get);
|
|
definition.$get.push(function () {
|
|
debug.deps.push({
|
|
name: name,
|
|
imports: annotate(tempGet)
|
|
});
|
|
return tempGet.apply(this, arguments);
|
|
});
|
|
} else {
|
|
debug.deps.push({
|
|
name: name,
|
|
imports: annotate(definition)
|
|
});
|
|
}
|
|
return temp.apply(this, arguments);
|
|
};
|
|
|
|
// $provide.(factory|service)
|
|
[
|
|
'factory',
|
|
'service'
|
|
].forEach(function (met) {
|
|
var temp = $provide[met];
|
|
$provide[met] = function (name, definition) {
|
|
if (typeof name === 'object') {
|
|
angular.forEach(name, function (value, key) {
|
|
name[key] = function () {
|
|
debug.deps.push({
|
|
name: key,
|
|
imports: annotate(value)
|
|
});
|
|
return value.apply(this, arguments);
|
|
};
|
|
});
|
|
} else {
|
|
debug.deps.push({
|
|
name: name,
|
|
imports: annotate(definition)
|
|
});
|
|
}
|
|
return temp.apply(this, arguments);
|
|
};
|
|
});
|
|
|
|
$provide.decorator('$rootScope', function ($delegate) {
|
|
|
|
var watchFnToHumanReadableString = function (fn) {
|
|
if (fn.exp) {
|
|
return fn.exp.trim();
|
|
} else if (fn.name) {
|
|
return fn.name.trim();
|
|
} else {
|
|
return fn.toString();
|
|
}
|
|
};
|
|
|
|
var applyFnToLogString = function (fn) {
|
|
var str;
|
|
if (fn) {
|
|
if (fn.name) {
|
|
str = fn.name;
|
|
} else if (fn.toString().split('\n').length > 1) {
|
|
str = 'fn () { ' + fn.toString().split('\n')[1].trim() + ' /* ... */ }';
|
|
} else {
|
|
str = fn.toString().trim().substr(0, 30) + '...';
|
|
}
|
|
} else {
|
|
str = '$apply';
|
|
}
|
|
return str;
|
|
};
|
|
|
|
|
|
// patch registering watchers
|
|
// ==========================
|
|
|
|
var _watch = $delegate.__proto__.$watch;
|
|
$delegate.__proto__.$watch = function (watchExpression, applyFunction) {
|
|
var thatScope = this;
|
|
var watchStr = watchFnToHumanReadableString(watchExpression);
|
|
|
|
if (!debug.watchPerf[watchStr]) {
|
|
debug.watchPerf[watchStr] = {
|
|
time: 0,
|
|
calls: 0
|
|
};
|
|
}
|
|
if (!debug.watchers[thatScope.$id]) {
|
|
debug.watchers[thatScope.$id] = [];
|
|
}
|
|
debug.watchers[thatScope.$id].push(watchStr);
|
|
|
|
// patch watchExpression
|
|
// ---------------------
|
|
var w = watchExpression;
|
|
if (typeof w === 'function') {
|
|
watchExpression = function () {
|
|
var start = performance.now();
|
|
var ret = w.apply(this, arguments);
|
|
var end = performance.now();
|
|
debug.watchPerf[watchStr].time += (end - start);
|
|
debug.watchPerf[watchStr].calls += 1;
|
|
return ret;
|
|
};
|
|
} else {
|
|
watchExpression = function () {
|
|
var start = performance.now();
|
|
var ret = thatScope.$eval(w);
|
|
var end = performance.now();
|
|
debug.watchPerf[watchStr].time += (end - start);
|
|
debug.watchPerf[watchStr].calls += 1;
|
|
return ret;
|
|
};
|
|
}
|
|
|
|
// patch applyFunction
|
|
// -------------------
|
|
if (typeof applyFunction === 'function') {
|
|
var applyStr = applyFunction.toString();
|
|
var unpatchedApplyFunction = applyFunction;
|
|
applyFunction = function () {
|
|
var start = performance.now();
|
|
var ret = unpatchedApplyFunction.apply(this, arguments);
|
|
var end = performance.now();
|
|
debug.scopeDirty[this.$id] = true;
|
|
|
|
//TODO: move these checks out of here and into registering the watcher
|
|
if (!debug.applyPerf[applyStr]) {
|
|
debug.applyPerf[applyStr] = {
|
|
time: 0,
|
|
calls: 0
|
|
};
|
|
}
|
|
debug.applyPerf[applyStr].time += (end - start);
|
|
debug.applyPerf[applyStr].calls += 1;
|
|
return ret;
|
|
};
|
|
}
|
|
|
|
return _watch.apply(this, arguments);
|
|
};
|
|
|
|
|
|
// patch $destroy
|
|
// --------------
|
|
var _destroy = $delegate.__proto__.$destroy;
|
|
$delegate.__proto__.$destroy = 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;
|
|
$delegate.__proto__.$new = 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
|
|
if (!debug.watchers[ret.$id]) {
|
|
debug.watchers[ret.$id] = [];
|
|
}
|
|
|
|
debug.scopes[ret.$id] = ret;
|
|
debug.scopes[this.$id] = this;
|
|
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;
|
|
$delegate.__proto__.$apply = function (fn) {
|
|
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
|
|
// --------------------------------------------------
|
|
if (debug.log) {
|
|
console.log(applyFnToLogString(fn) + '\t\t' + (end - start).toPrecision(4) + 'ms');
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
|
|
return $delegate;
|
|
});
|
|
});
|
|
};
|
|
|
|
// Return a script element with the above code embedded in it
|
|
var script = window.document.createElement('script');
|
|
script.innerHTML = '(' + fn.toString() + '(window))';
|
|
|
|
return script;
|
|
}()));
|
|
};
|
|
|
|
// only inject if cookie is set
|
|
if (document.cookie.indexOf('__ngDebug=true') != -1) {
|
|
document.addEventListener('DOMContentLoaded', inject);
|
|
}
|