refactor(*): basically change everything
🍕
This commit is contained in:
parent
706a63cc51
commit
b40377531e
17
app.css
17
app.css
@ -1,17 +0,0 @@
|
|||||||
.offsetTab {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
.table-hover tr:hover td,
|
|
||||||
.table-hover tr:hover th {
|
|
||||||
background-color: #F3F3F3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.suppressedMessage {
|
|
||||||
margin-left: 15px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.condenseAlert {
|
|
||||||
padding: 4px 8px;
|
|
||||||
margin: 4px 0px;
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<script src="js/devtoolsBackground.js"></script>
|
<script src="devtoolsBackground.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -3,6 +3,6 @@ var panels = chrome.devtools.panels;
|
|||||||
var angularPanel = panels.create(
|
var angularPanel = panels.create(
|
||||||
"AngularJS",
|
"AngularJS",
|
||||||
"img/angular.png",
|
"img/angular.png",
|
||||||
"hint.html"
|
"panel/app.html"
|
||||||
);
|
);
|
||||||
|
|
87
hint.html
87
hint.html
@ -1,87 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html ng-csp ng-app="ngHintUI">
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="bower_components/angular/angular-csp.css">
|
|
||||||
<link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css">
|
|
||||||
<link rel="stylesheet" href="app.css">
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div ng-controller="HintController">
|
|
||||||
<div class="row" style="padding:10px">
|
|
||||||
<div class="col-md-12">
|
|
||||||
<ul class="nav nav-tabs">
|
|
||||||
<li ng-class="{active: module === modName}" ng-repeat="(modName, value) in messageData" ng-click="setModule(modName)">
|
|
||||||
<a data-toggle="tab">
|
|
||||||
{{modName}}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="row" style="padding: 5px 0px">
|
|
||||||
<div class="col-md-2">
|
|
||||||
<!--
|
|
||||||
<ul class="nav nav-pills nav-stacked">
|
|
||||||
<li ng-class="{active: type === label}" ng-repeat="label in labels" ng-click="setType(label)">
|
|
||||||
<a class="offsetTab">{{label}}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
-->
|
|
||||||
<!--
|
|
||||||
<div ng-show="suppressedMessagesLength">
|
|
||||||
<hr>
|
|
||||||
<h5 class="offsetTab">Suppressed Errors:</h5>
|
|
||||||
<ul class="nav nav-pills nav-stacked">
|
|
||||||
<li ng-repeat="(key, message) in suppressedMessages">
|
|
||||||
<div class="suppressedMessage">
|
|
||||||
<div class='alert alert-warning condenseAlert'>
|
|
||||||
{{message}}
|
|
||||||
|
|
||||||
<button type="button" class="close" style="margin-top:-5px" ng-click="unsuppressMessage(key)">
|
|
||||||
<span aria-hidden="true">×</span>
|
|
||||||
<span class="sr-only">Close</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
<div class="col-md-10">
|
|
||||||
<div class="input-group">
|
|
||||||
<div class="input-group-addon"><span class="glyphicon glyphicon-search"></span></div>
|
|
||||||
<input class="form-control" type="text" placeholder="Search Messages" ng-model="search">
|
|
||||||
</div>
|
|
||||||
<table class="table table-striped table-hover">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>No.</th>
|
|
||||||
<th>Module</th>
|
|
||||||
<th>Message</th>
|
|
||||||
<th>Severity</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr ng-repeat="hint in hints">
|
|
||||||
<td>{{$index + 1}}</td>
|
|
||||||
<td>{{hint.module}}</td>
|
|
||||||
<td>{{hint.message}}</td>
|
|
||||||
<td>{{hint.severity}}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- scripts -->
|
|
||||||
<script src="bower_components/angular/angular.js"></script>
|
|
||||||
<script src="bower_components/angular-bootstrap/ui-bootstrap.js"></script>
|
|
||||||
<script src="bower_components/jquery/dist/jquery.js"></script>
|
|
||||||
|
|
||||||
<script src="hintApp.js"></script>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
30
hint.js
30
hint.js
@ -1,11 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* This gets loaded into the context of the app you are inspecting
|
||||||
|
*/
|
||||||
require('./loader.js');
|
require('./loader.js');
|
||||||
require('angular-hint');
|
require('angular-hint');
|
||||||
var eventProxyElement = document.getElementById('__ngDebugElement');
|
|
||||||
|
|
||||||
var customEvent = document.createEvent('Event');
|
angular.hint.onMessage = function (moduleName, message, messageType, category) {
|
||||||
customEvent.initEvent('myCustomEvent', true, true);
|
|
||||||
|
|
||||||
angular.hint.onMessage = function (moduleName, message, messageType) {
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
message = moduleName;
|
message = moduleName;
|
||||||
moduleName = 'Unknown'
|
moduleName = 'Unknown'
|
||||||
@ -13,10 +12,25 @@ angular.hint.onMessage = function (moduleName, message, messageType) {
|
|||||||
if (typeof messageType === 'undefined') {
|
if (typeof messageType === 'undefined') {
|
||||||
messageType = 1;
|
messageType = 1;
|
||||||
}
|
}
|
||||||
eventProxyElement.innerText = JSON.stringify({
|
sendMessage({
|
||||||
module: moduleName,
|
module: moduleName,
|
||||||
message: message,
|
message: message,
|
||||||
severity: messageType
|
severity: messageType,
|
||||||
|
category: category
|
||||||
});
|
});
|
||||||
eventProxyElement.dispatchEvent(customEvent);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
angular.hint.emit = function (ev, data) {
|
||||||
|
data.event = ev;
|
||||||
|
sendMessage(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
var eventProxyElement = document.getElementById('__ngBatarangElement');
|
||||||
|
|
||||||
|
var customEvent = document.createEvent('Event');
|
||||||
|
customEvent.initEvent('batarangDataEvent', true, true);
|
||||||
|
|
||||||
|
function sendMessage (obj) {
|
||||||
|
eventProxyElement.innerText = JSON.stringify(obj);
|
||||||
|
eventProxyElement.dispatchEvent(customEvent);
|
||||||
|
}
|
||||||
|
49
hintApp.js
49
hintApp.js
@ -1,49 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
angular.module('ngHintUI', []).
|
|
||||||
controller('HintController', ['$scope', 'hintService', HintController]).
|
|
||||||
service('hintService', ['$rootScope', hintService]);
|
|
||||||
|
|
||||||
function HintController($scope, hintService) {
|
|
||||||
resetMessageData();
|
|
||||||
|
|
||||||
hintService.onRefresh(resetMessageData);
|
|
||||||
|
|
||||||
function resetMessageData() {
|
|
||||||
$scope.hints = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
hintService.onHint(function(hint) {
|
|
||||||
$scope.hints.push(hint);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function hintService($rootScope) {
|
|
||||||
var onHintCallback,
|
|
||||||
onRefreshCallback;
|
|
||||||
|
|
||||||
this.onHint = function(cb) {
|
|
||||||
onHintCallback = cb;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.onRefresh = function(cb) {
|
|
||||||
onRefreshCallback = cb;
|
|
||||||
};
|
|
||||||
|
|
||||||
var port = chrome.extension.connect();
|
|
||||||
port.postMessage(chrome.devtools.inspectedWindow.tabId);
|
|
||||||
port.onMessage.addListener(function(msg) {
|
|
||||||
$rootScope.$apply(function () {
|
|
||||||
if (msg === 'refresh') {
|
|
||||||
onRefreshCallback();
|
|
||||||
} else {
|
|
||||||
var hint = JSON.parse(msg);
|
|
||||||
onHintCallback(hint);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
port.onDisconnect.addListener(function (a) {
|
|
||||||
console.log(a);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
describe('hintService', function() {
|
|
||||||
var hintService;
|
|
||||||
|
|
||||||
beforeEach(module('ngHintUI'));
|
|
||||||
beforeEach(inject(function(_hintService_) {
|
|
||||||
hintService = _hintService_;
|
|
||||||
}));
|
|
||||||
|
|
||||||
var messageFunction = {
|
|
||||||
addListener: jasmine.createSpy('messageFunction')
|
|
||||||
}
|
|
||||||
var postMessageFunction = jasmine.createSpy('postMessageFunction');
|
|
||||||
var onDisconnectFunction = {
|
|
||||||
addListener: jasmine.createSpy('onDisconnect')
|
|
||||||
}
|
|
||||||
chrome = {
|
|
||||||
extension: {
|
|
||||||
connect: function() {
|
|
||||||
return {
|
|
||||||
onMessage: messageFunction,
|
|
||||||
postMessage: postMessageFunction,
|
|
||||||
onDisconnect: onDisconnectFunction
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
devtools: {
|
|
||||||
inspectedWindow: {
|
|
||||||
tabId: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should set the function to be executed for each hint', function() {
|
|
||||||
var onHintFunction = function() {
|
|
||||||
console.log('Do this when passed a hint.');
|
|
||||||
};
|
|
||||||
hintService.setHintFunction(onHintFunction);
|
|
||||||
expect(hintService.getHintFunction()).toEqual(onHintFunction);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should set the function to be executed on a refresh', function() {
|
|
||||||
var onRefreshFunction = function() {
|
|
||||||
console.log('Do this when the page is refreshed.');
|
|
||||||
};
|
|
||||||
hintService.setRefreshFunction(onRefreshFunction);
|
|
||||||
expect(hintService.getRefreshFunction()).toEqual(onRefreshFunction);
|
|
||||||
});
|
|
||||||
});
|
|
220
hint_test.js
220
hint_test.js
@ -1,220 +0,0 @@
|
|||||||
describe('angularHint', function() {
|
|
||||||
var $controller, $rootScope, hintService;
|
|
||||||
|
|
||||||
beforeEach(module('ngHintUI'));
|
|
||||||
beforeEach(inject(function(_$controller_, _$rootScope_) {
|
|
||||||
$controller = _$controller_;
|
|
||||||
$rootScope = _$rootScope_;
|
|
||||||
}));
|
|
||||||
beforeEach(function() {
|
|
||||||
hintService = {
|
|
||||||
setHintFunction: function(funct) {
|
|
||||||
this.onHint = funct;
|
|
||||||
},
|
|
||||||
setRefreshFunction: function(funct) {
|
|
||||||
this.onRefresh = funct;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('on receiving a hint', function() {
|
|
||||||
it('should give the hintService onHint a helpful function to format messages', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
expect(hintService.onHint.toString().indexOf("var result = msg.split('##')")).not.toEqual(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should create message data arrays for each module', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
var aFakeMessage = 'Directives##You spelled ng-repeat wrong!##Error Messages';
|
|
||||||
var aFakeMessage2 = 'Modules##You did not load a module!##Error Messages';
|
|
||||||
hintService.onHint(aFakeMessage);
|
|
||||||
hintService.onHint(aFakeMessage2);
|
|
||||||
var expectedMessageData = {
|
|
||||||
'Error Messages': ['You spelled ng-repeat wrong!'],
|
|
||||||
'Warning Messages': [],
|
|
||||||
'Suggestion Messages': [],
|
|
||||||
'All Messages': [{
|
|
||||||
'message': 'You spelled ng-repeat wrong!',
|
|
||||||
'type': 'Error Messages'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
var expectedMessageData2 = {
|
|
||||||
'Error Messages': ['You did not load a module!'],
|
|
||||||
'Warning Messages': [],
|
|
||||||
'Suggestion Messages': [],
|
|
||||||
'All Messages': [{
|
|
||||||
'message': 'You did not load a module!',
|
|
||||||
'type': 'Error Messages'
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
expect(scope.messageData['Directives']).toEqual(expectedMessageData);
|
|
||||||
expect(scope.messageData['Modules']).toEqual(expectedMessageData2);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should create message data arrays for each type of message', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
var aFakeErrorMessage = 'Modules##You did not load a module!##Error Messages';
|
|
||||||
var aFakeWarningMessage = 'Modules##You used a bad module name!##Warning Messages';
|
|
||||||
var aFakeSuggestionMessage = 'Modules##Maybe you should not make modules.##Suggestion Messages';
|
|
||||||
hintService.onHint(aFakeErrorMessage);
|
|
||||||
hintService.onHint(aFakeWarningMessage);
|
|
||||||
hintService.onHint(aFakeSuggestionMessage);
|
|
||||||
|
|
||||||
var expectedMessageData = {
|
|
||||||
'Error Messages': ['You did not load a module!'],
|
|
||||||
'Warning Messages': ['You used a bad module name!'],
|
|
||||||
'Suggestion Messages': ['Maybe you should not make modules.'],
|
|
||||||
'All Messages': [
|
|
||||||
{'message': 'You did not load a module!', 'type': 'Error Messages'},
|
|
||||||
{'message': 'You used a bad module name!', 'type': 'Warning Messages'},
|
|
||||||
{'message': 'Maybe you should not make modules.', 'type': 'Suggestion Messages'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
expect(scope.messageData['Modules']).toEqual(expectedMessageData);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should create an object to hold all recorded messages', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
var aFakeErrorMessage = 'Modules##You did not load a module!##Error Messages';
|
|
||||||
var aFakeWarningMessage = 'Directives##Did you know you wrote ng-reepeet?##Warning Messages';
|
|
||||||
var aFakeSuggestionMessage = 'Controllers##Maybe you should use a better name.##Suggestion Messages';
|
|
||||||
hintService.onHint(aFakeErrorMessage);
|
|
||||||
hintService.onHint(aFakeWarningMessage);
|
|
||||||
hintService.onHint(aFakeSuggestionMessage);
|
|
||||||
|
|
||||||
var expectedAllMessagesObject = {
|
|
||||||
'Error Messages': ['You did not load a module!'],
|
|
||||||
'Warning Messages': ['Did you know you wrote ng-reepeet?'],
|
|
||||||
'Suggestion Messages': ['Maybe you should use a better name.'],
|
|
||||||
'All Messages': [
|
|
||||||
{'message': 'You did not load a module!', 'type': 'Error Messages', 'module': 'Modules'},
|
|
||||||
{'message': 'Did you know you wrote ng-reepeet?', 'type': 'Warning Messages', 'module': 'Directives'},
|
|
||||||
{'message': 'Maybe you should use a better name.', 'type': 'Suggestion Messages', 'module': 'Controllers'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
expect(scope.messageData['All']).toEqual(expectedAllMessagesObject);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('onRefresh', function() {
|
|
||||||
it('should use the hintService to clear the old messages on refresh', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
var aFakeErrorMessage = 'Modules##You did not load a module!##Error Messages';
|
|
||||||
var aFakeWarningMessage = 'Directives##Did you know you wrote ng-reepeet?##Warning Messages';
|
|
||||||
var aFakeSuggestionMessage = 'Controllers##Maybe you should use a better name.##Suggestion Messages';
|
|
||||||
hintService.onHint(aFakeErrorMessage);
|
|
||||||
hintService.onHint(aFakeWarningMessage);
|
|
||||||
hintService.onHint(aFakeSuggestionMessage);
|
|
||||||
|
|
||||||
var expectedAllMessagesObject = {
|
|
||||||
'Error Messages': ['You did not load a module!'],
|
|
||||||
'Warning Messages': ['Did you know you wrote ng-reepeet?'],
|
|
||||||
'Suggestion Messages': ['Maybe you should use a better name.'],
|
|
||||||
'All Messages': [
|
|
||||||
{'message': 'You did not load a module!', 'type': 'Error Messages', 'module': 'Modules'},
|
|
||||||
{'message': 'Did you know you wrote ng-reepeet?', 'type': 'Warning Messages', 'module': 'Directives'},
|
|
||||||
{'message': 'Maybe you should use a better name.', 'type': 'Suggestion Messages', 'module': 'Controllers'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(scope.messageData['All']).toEqual(expectedAllMessagesObject);
|
|
||||||
|
|
||||||
hintService.onRefresh();
|
|
||||||
var expectedRefreshData = {
|
|
||||||
'Error Messages': [],
|
|
||||||
'Warning Messages': [],
|
|
||||||
'Suggestion Messages': [],
|
|
||||||
'All Messages': []
|
|
||||||
}
|
|
||||||
expect(scope.messageData['All']).toEqual(expectedRefreshData);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setModule', function() {
|
|
||||||
it('should to set the currently viewed module in the UI', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
scope.setModule('Directives');
|
|
||||||
expect(scope.module).toEqual('Directives');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should be set to All by default', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
expect(scope.module).toEqual('All');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('setType', function() {
|
|
||||||
it('should set the type of message being viewed in the UI', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
scope.setType('Error Messages');
|
|
||||||
expect(scope.type).toEqual('Error Messages');
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should be set to All Messages by default', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope, hintService: hintService});
|
|
||||||
expect(scope.type).toEqual('All Messages');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//TO DO CARLOS WHO WROTE THESE METHODS
|
|
||||||
describe('message suppression', function() {
|
|
||||||
describe('suppressMessage', function() {
|
|
||||||
it('should put a message into the list of suppressedMessages', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope});
|
|
||||||
scope.suppressMessage('an error message that will be suppressed and hid from display and yay');
|
|
||||||
expect(scope.suppressedMessages['suppressedandhid']).toEqual('...error message that will be suppressed and hid from display...');
|
|
||||||
});
|
|
||||||
it('should increment suppressedMessagesLength', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope});
|
|
||||||
scope.suppressMessage('an error message that should increment the counter of messages and yay');
|
|
||||||
expect(scope.suppressedMessagesLength).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('unsuppressMessage', function() {
|
|
||||||
it('should remove a message into the list of suppressedMessages', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope});
|
|
||||||
scope.suppressMessage('an error message that will be suppressed and hid from display and yay');
|
|
||||||
scope.unsuppressMessage('suppressedandhid');
|
|
||||||
expect(scope.suppressedMessages['suppressedandhid']).toBeUndefined();
|
|
||||||
});
|
|
||||||
it('should decrement suppressedMessagesLength', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope});
|
|
||||||
scope.suppressMessage('an error message that should increment the counter of messages');
|
|
||||||
scope.unsuppressMessage('suppressedandhid');
|
|
||||||
expect(scope.suppressedMessagesLength).toBe(0);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('isSuppressed', function() {
|
|
||||||
it('should detect if a message is currently suppressed', function() {
|
|
||||||
var scope = $rootScope.$new();
|
|
||||||
var ctrl = $controller('HintController', {$scope: scope});
|
|
||||||
scope.suppressMessage('an error message that will be suppressed and hid from display');
|
|
||||||
var res = scope.isSuppressed('an error message that will be suppressed and hid from display');
|
|
||||||
expect(res).toBe(true);
|
|
||||||
|
|
||||||
res = scope.isSuppressed('this is an error message that has not ever been suppressed');
|
|
||||||
expect(res).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
BIN
img/angular.png
Normal file
BIN
img/angular.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
img/statusbarButtonGlyphs.png
Normal file
BIN
img/statusbarButtonGlyphs.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
BIN
img/statusbarButtonGlyphs_2x.png
Normal file
BIN
img/statusbarButtonGlyphs_2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
BIN
img/webstore-icon.png
Normal file
BIN
img/webstore-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
@ -1,7 +1,7 @@
|
|||||||
var html = document.getElementsByTagName('html')[0];
|
var html = document.getElementsByTagName('html')[0];
|
||||||
|
|
||||||
var eventProxyElement = document.createElement('div');
|
var eventProxyElement = document.createElement('div');
|
||||||
eventProxyElement.id = '__ngDebugElement';
|
eventProxyElement.id = '__ngBatarangElement';
|
||||||
eventProxyElement.style.display = 'none';
|
eventProxyElement.style.display = 'none';
|
||||||
html.appendChild(eventProxyElement);
|
html.appendChild(eventProxyElement);
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ html.appendChild(eventProxyElement);
|
|||||||
var script = window.document.createElement('script');
|
var script = window.document.createElement('script');
|
||||||
script.src = chrome.extension.getURL('dist/hint.js');
|
script.src = chrome.extension.getURL('dist/hint.js');
|
||||||
|
|
||||||
eventProxyElement.addEventListener('myCustomEvent', function () {
|
eventProxyElement.addEventListener('batarangDataEvent', function () {
|
||||||
var eventData = eventProxyElement.innerText;
|
var eventData = eventProxyElement.innerText;
|
||||||
chrome.extension.sendMessage(eventData);
|
chrome.extension.sendMessage(eventData);
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
|
/*
|
||||||
|
* This karma conf tests just the panel app
|
||||||
|
*/
|
||||||
|
|
||||||
module.exports = function(config) {
|
module.exports = function(config) {
|
||||||
config.set({
|
config.set({
|
||||||
frameworks: ['browserify', 'jasmine'],
|
frameworks: ['browserify', 'jasmine'],
|
||||||
files: [
|
files: [
|
||||||
'bower_components/angular/angular.js',
|
'bower_components/angular/angular.js',
|
||||||
'bower_components/angular-mocks/angular-mocks.js',
|
'bower_components/angular-mocks/angular-mocks.js',
|
||||||
'hint.js',
|
'panel/app.js',
|
||||||
'hintApp.js',
|
'panel/**/*.js',
|
||||||
'hintCtrl.js',
|
'panel/**/*.spec.js'
|
||||||
'hintService.js',
|
|
||||||
'*_test.js'
|
|
||||||
],
|
],
|
||||||
exclude: [],
|
exclude: [],
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
|
@ -10,6 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"browserify": "^5.9.1",
|
"browserify": "^5.9.1",
|
||||||
"gulp": "^3.8.7",
|
"gulp": "^3.8.7",
|
||||||
|
"karma-bro": "^0.6.0",
|
||||||
|
"karma-sauce-launcher": "^0.2.9",
|
||||||
"vinyl-source-stream": "^0.1.1"
|
"vinyl-source-stream": "^0.1.1"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
305
panel/app.css
Normal file
305
panel/app.css
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
.col {
|
||||||
|
float: left;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.col-2 {
|
||||||
|
float: left;
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
.scope-branch {
|
||||||
|
margin-left: 30px;
|
||||||
|
background-color: rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.well-top {
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.well-bottom {
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
border-top: none;
|
||||||
|
background-color: #E0E0E0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bat-nav-check {
|
||||||
|
background-color: #fff;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-bottom-color: transparent;
|
||||||
|
border-radius: 4px 4px 0 0;
|
||||||
|
|
||||||
|
padding: 8px 12px 8px 12px;
|
||||||
|
margin-right: 2px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
.bat-nav-check input[type="checkbox"] {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mimic Chrome's Devtools */
|
||||||
|
/* split */
|
||||||
|
|
||||||
|
|
||||||
|
.split-view {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-disclosure,
|
||||||
|
.outline-disclosure ol {
|
||||||
|
list-style-type: none;
|
||||||
|
-webkit-padding-start: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-vertical > .split-view-contents-first {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-vertical > .split-view-contents {
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-contents {
|
||||||
|
position: absolute;
|
||||||
|
overflow: auto;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-vertical > .split-view-sidebar.split-view-contents-second:not(.maximized) {
|
||||||
|
border-left: 1px solid rgb(64%, 64%, 64%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-stack > .sidebar-pane.visible:nth-last-of-type(1) {
|
||||||
|
border-bottom: 1px solid rgb(189, 189, 189);
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-vertical > .split-view-contents-second {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-vertical > .split-view-resizer {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 5px;
|
||||||
|
z-index: 1500;
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fill {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-title {
|
||||||
|
position: relative;
|
||||||
|
background: rgb(230, 230, 230);
|
||||||
|
height: 20px;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-top: 1px solid rgb(189, 189, 189);
|
||||||
|
border-bottom: 1px solid rgb(189, 189, 189);
|
||||||
|
line-height: 18px;
|
||||||
|
background-origin: padding;
|
||||||
|
background-clip: padding;
|
||||||
|
margin-top: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-title::before {
|
||||||
|
background-image: url(../img/statusbarButtonGlyphs.png);
|
||||||
|
background-size: 320px 144px;
|
||||||
|
background-position: -4px -96px;
|
||||||
|
opacity: 0.5;
|
||||||
|
float: left;
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
margin-right: 2px;
|
||||||
|
content: "a";
|
||||||
|
color: transparent;
|
||||||
|
position: relative;
|
||||||
|
top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-title.expanded::before {
|
||||||
|
background-position: -20px -96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-toolbar {
|
||||||
|
line-height: 18px;
|
||||||
|
left: 0;
|
||||||
|
right: 4px;
|
||||||
|
top: 0;
|
||||||
|
height: 20px;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-toolbar > * {
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-subtitle {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-pane-subtitle input, .section > .header input[type=checkbox] {
|
||||||
|
font-size: inherit;
|
||||||
|
height: 1em;
|
||||||
|
width: 1em;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0.25em;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* sidebar tree */
|
||||||
|
|
||||||
|
.sidebar-tree,
|
||||||
|
.sidebar-tree .children {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tree-item {
|
||||||
|
position: relative;
|
||||||
|
height: 36px;
|
||||||
|
padding: 0 5px 0 5px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: hidden;
|
||||||
|
margin-top: 1px;
|
||||||
|
line-height: 34px;
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tree-item.selected {
|
||||||
|
color: white;
|
||||||
|
border-top: 1px solid rgb(151, 151, 151);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(180, 180, 180)), to(rgb(138, 138, 138)));
|
||||||
|
text-shadow: rgba(0, 0, 0, 0.33) 1px 1px 0;
|
||||||
|
background-origin: padding-box;
|
||||||
|
background-clip: padding-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body:focus .sidebar-tree-item.selected {
|
||||||
|
border-top: 1px solid rgb(68, 128, 200);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(92, 147, 213)), to(rgb(21, 83, 170)));
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tree-item .icon {
|
||||||
|
float: left;
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tree-item .titles {
|
||||||
|
position: relative;
|
||||||
|
top: 5px;
|
||||||
|
line-height: 12px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-tree-item .titles.no-subtitle {
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
background-color: rgb(232, 232, 232);
|
||||||
|
}
|
||||||
|
|
||||||
|
.split-view-vertical > .split-view-sidebar.split-view-contents-first:not(.maximized) {
|
||||||
|
border-right: 1px solid rgb(64%, 64%, 64%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-launcher-view-tree-item > .icon {
|
||||||
|
padding: 15px;
|
||||||
|
background-image: url(../img/toolbarIcons.png);
|
||||||
|
background-position-x: -160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .status {
|
||||||
|
float: right;
|
||||||
|
height: 16px;
|
||||||
|
margin-top: 9px;
|
||||||
|
margin-left: 4px;
|
||||||
|
line-height: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li .status:empty {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* audit stuff */
|
||||||
|
|
||||||
|
.audit-result-view .severity-warning {
|
||||||
|
background-position: -246px -96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@media (-webkit-min-device-pixel-ratio: 1.5)
|
||||||
|
.audit-result-view .severity-severe,
|
||||||
|
.audit-result-view .severity-warning,
|
||||||
|
.audit-result-view .severity-info {
|
||||||
|
background-image: url(../img/statusbarButtonGlyphs_2x.png);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
.audit-result-tree,
|
||||||
|
.audit-result-tree ol {
|
||||||
|
list-style-type: none;
|
||||||
|
-webkit-padding-start: 12px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.audit-result-tree {
|
||||||
|
line-height: 16px;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-result-tree li.parent {
|
||||||
|
margin-left: -12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-result-tree li {
|
||||||
|
padding: 0 0 0 14px;
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-bottom: 1px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
margin-left: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-result {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-result-tree li.parent::before {
|
||||||
|
background-position: -4px -96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.audit-result-view .severity-severe,
|
||||||
|
.audit-result-view .severity-warning,
|
||||||
|
.audit-result-view .severity-info {
|
||||||
|
background-image: url(../img/statusbarButtonGlyphs.png);
|
||||||
|
background-size: 320px 144px;
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
margin-right: -10px;
|
||||||
|
height: 10px;
|
||||||
|
position: relative;
|
||||||
|
left: -28px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
32
panel/app.html
Normal file
32
panel/app.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html ng-csp ng-app="batarang.app">
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="reset.css">
|
||||||
|
<link rel="stylesheet" href="app.css">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="components/json-tree/json-tree.css">
|
||||||
|
<link rel="stylesheet" href="components/scope-tree/scope-tree.css">
|
||||||
|
|
||||||
|
<script src="../bower_components/angular/angular.js"></script>
|
||||||
|
|
||||||
|
<!-- components -->
|
||||||
|
<script src="components/inspected-app/inspected-app.js"></script>
|
||||||
|
<script src="components/code/code.js"></script>
|
||||||
|
<script src="components/json-tree/json-tree.js"></script>
|
||||||
|
<script src="components/scope-tree/scope-tree.js"></script>
|
||||||
|
<script src="components/tabs/tabs.js"></script>
|
||||||
|
<script src="components/vertical-split/vertical-split.js"></script>
|
||||||
|
|
||||||
|
<!-- panes -->
|
||||||
|
<script src="hints/hints.js"></script>
|
||||||
|
<script src="scopes/scopes.js"></script>
|
||||||
|
|
||||||
|
<script src="app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<bat-tabs>
|
||||||
|
<bat-pane title="Scopes" src="scopes/scopes.html"></bat-pane>
|
||||||
|
<bat-pane title="Hints" src="hints/hints.html"></bat-pane>
|
||||||
|
</bat-tabs>
|
||||||
|
</body>
|
||||||
|
</html>
|
16
panel/app.js
Normal file
16
panel/app.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('batarang.app', [
|
||||||
|
'batarang.app.hint',
|
||||||
|
'batarang.app.scopes',
|
||||||
|
|
||||||
|
'batarang.scope-tree',
|
||||||
|
'batarang.code',
|
||||||
|
'batarang.inspected-app',
|
||||||
|
'batarang.json-tree',
|
||||||
|
'batarang.scope-tree',
|
||||||
|
'batarang.tabs',
|
||||||
|
'batarang.vertical-split'
|
||||||
|
]).
|
||||||
|
// immediately instantiate this service
|
||||||
|
run(['inspectedApp', angular.noop]);
|
28
panel/components/code/code.js
Normal file
28
panel/components/code/code.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('batarang.code', []).
|
||||||
|
|
||||||
|
directive('batCode', function() {
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
terminal: true,
|
||||||
|
scope: {
|
||||||
|
batCode: '='
|
||||||
|
},
|
||||||
|
link: function (scope, element, attrs) {
|
||||||
|
scope.$watch('batCode', function (newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
element.html(replaceCodeInString(newVal));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// super lite version of markdown
|
||||||
|
var CODE_RE = /\`(.+?)\`/g;
|
||||||
|
function replaceCodeInString(str) {
|
||||||
|
return str.replace(CODE_RE, function (match, contents) {
|
||||||
|
return ['<code>', contents, '</code>'].join('');
|
||||||
|
});
|
||||||
|
}
|
75
panel/components/inspected-app/inspected-app.js
Normal file
75
panel/components/inspected-app/inspected-app.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('batarang.inspected-app', []).
|
||||||
|
service('inspectedApp', ['$rootScope', inspectedAppService]);
|
||||||
|
|
||||||
|
function inspectedAppService($rootScope) {
|
||||||
|
|
||||||
|
// TODO: maybe state should live elsewhere
|
||||||
|
var scopes = this.scopes = {},
|
||||||
|
hints = this.hints = [];
|
||||||
|
|
||||||
|
this.watch = function (scopeId, path) {
|
||||||
|
return invokeAngularHintMethod('watch', scopeId, path);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.unwatch = function (scopeId, path) {
|
||||||
|
return invokeAngularHintMethod('unwatch', scopeId, path);
|
||||||
|
};
|
||||||
|
|
||||||
|
function invokeAngularHintMethod(method, scopeId, path) {
|
||||||
|
var args = [parseInt(scopeId, 10), path || ''].map(JSON.stringify).join(',');
|
||||||
|
chrome.devtools.inspectedWindow.eval('angular.hint.' + method + '(' + args + ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
var port = chrome.extension.connect();
|
||||||
|
port.postMessage(chrome.devtools.inspectedWindow.tabId);
|
||||||
|
port.onMessage.addListener(function(msg) {
|
||||||
|
$rootScope.$applyAsync(function () {
|
||||||
|
if (msg === 'refresh') {
|
||||||
|
onRefreshMessage();
|
||||||
|
} else {
|
||||||
|
var hint = JSON.parse(msg);
|
||||||
|
onHintMessage(hint);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
port.onDisconnect.addListener(function (a) {
|
||||||
|
console.log(a);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onHintMessage(hint) {
|
||||||
|
if (hint.message) {
|
||||||
|
hints.push(hint);
|
||||||
|
} else if (hint.event === 'model:change') {
|
||||||
|
scopes[hint.id].models[hint.path] = (typeof hint.value === 'undefined') ?
|
||||||
|
undefined : JSON.parse(hint.value);
|
||||||
|
} else if (hint.event === 'scope:new') {
|
||||||
|
addNewScope(hint);
|
||||||
|
} else if (hint.event === 'scope:link') {
|
||||||
|
scopes[hint.id].descriptor = hint.descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hint.event) {
|
||||||
|
$rootScope.$broadcast(hint.event, hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(hint);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRefreshMessage() {
|
||||||
|
hints.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addNewScope (hint) {
|
||||||
|
scopes[hint.child] = {
|
||||||
|
parent: hint.parent,
|
||||||
|
children: [],
|
||||||
|
models: {}
|
||||||
|
};
|
||||||
|
if (scopes[hint.parent]) {
|
||||||
|
scopes[hint.parent].children.push(hint.child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
54
panel/components/inspected-app/inspected-app.spec.js
Normal file
54
panel/components/inspected-app/inspected-app.spec.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
describe('inspectedApp', function() {
|
||||||
|
var inspectedApp;
|
||||||
|
|
||||||
|
beforeEach(module('batarang.inspected-app'));
|
||||||
|
beforeEach(function() {
|
||||||
|
window.chrome = createMockChrome();
|
||||||
|
});
|
||||||
|
beforeEach(inject(function(_inspectedApp_) {
|
||||||
|
inspectedApp = _inspectedApp_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('watch', function () {
|
||||||
|
it('should call chrome devtools APIs', function() {
|
||||||
|
inspectedApp.watch(1, '');
|
||||||
|
expect(chrome.devtools.inspectedWindow.eval).toHaveBeenCalledWith('angular.hint.watch(1,"")');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('unwatch', function () {
|
||||||
|
it('should call chrome devtools APIs', function() {
|
||||||
|
inspectedApp.unwatch(1, '');
|
||||||
|
expect(chrome.devtools.inspectedWindow.eval).toHaveBeenCalledWith('angular.hint.unwatch(1,"")');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function createMockChrome() {
|
||||||
|
return {
|
||||||
|
extension: {
|
||||||
|
connect: createMockSocket
|
||||||
|
},
|
||||||
|
devtools: {
|
||||||
|
inspectedWindow: {
|
||||||
|
tabId: 1,
|
||||||
|
eval: jasmine.createSpy('inspectedWindowEval')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createListenerSpy(name) {
|
||||||
|
return {
|
||||||
|
addListener: jasmine.createSpy(name)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMockSocket() {
|
||||||
|
return {
|
||||||
|
onMessage: createListenerSpy('messageFunction'),
|
||||||
|
postMessage: jasmine.createSpy('postMessageFunction'),
|
||||||
|
onDisconnect: createListenerSpy('onDisconnect')
|
||||||
|
};
|
||||||
|
}
|
75
panel/components/json-tree/json-tree.css
Normal file
75
panel/components/json-tree/json-tree.css
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/* bat-json-tree */
|
||||||
|
|
||||||
|
bat-json-tree {
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-family: Menlo, monospace;
|
||||||
|
display: block;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .name {
|
||||||
|
color: rgb(136, 19, 145);
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .console-formatted-string {
|
||||||
|
color: rgb(196, 26, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree
|
||||||
|
.console-formatted-null,
|
||||||
|
.console-formatted-undefined {
|
||||||
|
color: rgb(128, 128, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .console-formatted-number {
|
||||||
|
color: rgb(28, 0, 207);
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree
|
||||||
|
.console-formatted-object,
|
||||||
|
.console-formatted-node,
|
||||||
|
.console-formatted-array {
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .properties-tree li {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
-webkit-user-select: text;
|
||||||
|
cursor: default;
|
||||||
|
padding-top: 2px;
|
||||||
|
line-height: 12px;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .properties-tree li.parent {
|
||||||
|
margin-left: -15px;
|
||||||
|
}
|
||||||
|
bat-json-tree .properties-tree li.parent li {
|
||||||
|
padding-left: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .properties-tree li.parent::before {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
background-image: url(../../img/statusbarButtonGlyphs.png);
|
||||||
|
background-size: 320px 120px;
|
||||||
|
opacity: 0.5;
|
||||||
|
content: "a";
|
||||||
|
width: 8px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 2px;
|
||||||
|
color: transparent;
|
||||||
|
text-shadow: none;
|
||||||
|
margin-top: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .properties-tree li.parent::before {
|
||||||
|
background-position: -4px -96px;
|
||||||
|
}
|
||||||
|
|
||||||
|
bat-json-tree .properties-tree li.parent.expanded::before {
|
||||||
|
background-position: -20px -96px;
|
||||||
|
}
|
129
panel/components/json-tree/json-tree.js
Normal file
129
panel/components/json-tree/json-tree.js
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
angular.module('batarang.json-tree', []).
|
||||||
|
directive('batJsonTree', ['$compile', 'inspectedApp', batJsonTreeDirective]);
|
||||||
|
|
||||||
|
var BAT_JSON_TREE_TEMPLATE = '<div class="properties-tree"></div>';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: remove dependency on inspectedApp service
|
||||||
|
*/
|
||||||
|
function batJsonTreeDirective($compile, inspectedApp) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
terminal: true,
|
||||||
|
scope: {
|
||||||
|
scopeId: '=',
|
||||||
|
val: '='
|
||||||
|
},
|
||||||
|
link: jsonTreeLinkFn
|
||||||
|
};
|
||||||
|
|
||||||
|
function jsonTreeLinkFn(scope, element, attrs) {
|
||||||
|
var root = angular.element(BAT_JSON_TREE_TEMPLATE)
|
||||||
|
element.append(root);
|
||||||
|
|
||||||
|
var branches = {
|
||||||
|
'': root
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.$watch('val', function (val) {
|
||||||
|
Object.
|
||||||
|
keys(val).
|
||||||
|
filter(function (key) {
|
||||||
|
return key.substr(0, 2) !== '$$';
|
||||||
|
}).
|
||||||
|
sort(byPathDepth).
|
||||||
|
forEach(function (key) {
|
||||||
|
buildDom(val[key], key);
|
||||||
|
});
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
|
||||||
|
function buildDom(object, depth) {
|
||||||
|
branches[depth].html('');
|
||||||
|
|
||||||
|
if (!typeof object === 'undefined') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buildBranch = function (key) {
|
||||||
|
var val = object[key];
|
||||||
|
var fullPath = depth;
|
||||||
|
if (depth) {
|
||||||
|
fullPath += '.';
|
||||||
|
}
|
||||||
|
fullPath += key;
|
||||||
|
|
||||||
|
var parentElt = angular.element('<li title>' +
|
||||||
|
'<span class="name">' + key + '</span>' +
|
||||||
|
'<span class="separator">: </span>' +
|
||||||
|
'</li>'),
|
||||||
|
childElt;
|
||||||
|
|
||||||
|
if (val === null) {
|
||||||
|
childElt = angular.element('<span class="value console-formatted-null">null</span>');
|
||||||
|
} else if (val['~object'] || val['~array-length'] !== undefined) {
|
||||||
|
parentElt.addClass('parent');
|
||||||
|
|
||||||
|
// you can't expand an empty array
|
||||||
|
if (val['~array-length'] !== 0) {
|
||||||
|
parentElt.on('click', function () {
|
||||||
|
inspectedApp.watch(scope.scopeId, fullPath);
|
||||||
|
parentElt.addClass('expanded');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val['~object']) {
|
||||||
|
childElt = angular.element('<span class="console-formatted-object">Object</span>');
|
||||||
|
} else {
|
||||||
|
childElt = angular.element(
|
||||||
|
'<span class="console-formatted-object">Array[' +
|
||||||
|
val['~array-length'] +
|
||||||
|
']</span>');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: what doe sregex look like?
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
val = '"' + val + '"';
|
||||||
|
}
|
||||||
|
childElt = angular.element(
|
||||||
|
'<span class="console-formatted-' + (typeof val) + '">' +
|
||||||
|
val +
|
||||||
|
'</span>');
|
||||||
|
}
|
||||||
|
|
||||||
|
parentElt.append(childElt);
|
||||||
|
branches[fullPath] = childElt;
|
||||||
|
|
||||||
|
return parentElt;
|
||||||
|
};
|
||||||
|
|
||||||
|
var properties;
|
||||||
|
if (object instanceof Array) {
|
||||||
|
properties = object.map(function (item, i) {
|
||||||
|
return i;
|
||||||
|
});
|
||||||
|
} else if (object != null) {
|
||||||
|
properties = Object.keys(object);
|
||||||
|
} else {
|
||||||
|
properties = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.
|
||||||
|
map(buildBranch).
|
||||||
|
forEach(function (elt) {
|
||||||
|
branches[depth].append(elt);
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function byPathDepth(a, b) { // sort '' first
|
||||||
|
if (a === '') {
|
||||||
|
return -1;
|
||||||
|
} else if (b === '') {
|
||||||
|
return 1;
|
||||||
|
} else { // sort by tree depth
|
||||||
|
return a.split('.').length - b.split('.').length;
|
||||||
|
}
|
||||||
|
}
|
127
panel/components/scope-tree/scope-tree.css
Normal file
127
panel/components/scope-tree/scope-tree.css
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
|
||||||
|
/* Stolen from WebKit Inspector CSS */
|
||||||
|
|
||||||
|
.source-code {
|
||||||
|
font-size: 11px !important;
|
||||||
|
font-family: Menlo, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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-user-select: none;
|
||||||
|
background-image: url(../../img/statusbarButtonGlyphs.png);
|
||||||
|
background-size: 320px 144px;
|
||||||
|
opacity: 0.5;
|
||||||
|
float: left;
|
||||||
|
width: 8px;
|
||||||
|
height: 10px;
|
||||||
|
content: "a";
|
||||||
|
color: transparent;
|
||||||
|
margin-left: 3px;
|
||||||
|
margin-right: 4px;
|
||||||
|
position: relative;
|
||||||
|
top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.outline-disclosure .selection {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 13px;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-disclosure .selection:not(.selected):hover {
|
||||||
|
display: block;
|
||||||
|
left: 3px;
|
||||||
|
right: 3px;
|
||||||
|
background-color: rgba(56, 121, 217, 0.1);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.outline-disclosure .selection.selected {
|
||||||
|
display: block;
|
||||||
|
background-color: rgb(212, 212, 212);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
:focus .sidebar-tree-item.selected {
|
||||||
|
border-top: 1px solid rgb(68, 128, 200);
|
||||||
|
background-image: -webkit-gradient(linear, left top, left bottom, from(rgb(92, 147, 213)), to(rgb(21, 83, 170)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* */
|
||||||
|
|
||||||
|
/*
|
||||||
|
bat-scope-tree .selected {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
*/
|
102
panel/components/scope-tree/scope-tree.js
Normal file
102
panel/components/scope-tree/scope-tree.js
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
angular.module('batarang.scope-tree', []).
|
||||||
|
|
||||||
|
directive('batScopeTree', batScopeTreeDirective);
|
||||||
|
|
||||||
|
// TODO: tabindex
|
||||||
|
function newBranchElement(descriptor) {
|
||||||
|
return angular.element([
|
||||||
|
'<ol class="children expanded">',
|
||||||
|
'<div class="selection"></div>',
|
||||||
|
'<span>',
|
||||||
|
'<span class="webkit-html-tag"><</span>',
|
||||||
|
'<span class="webkit-html-attribute">Scope #', descriptor, '</span>',
|
||||||
|
'<span class="webkit-html-tag">></span>',
|
||||||
|
'</span>',
|
||||||
|
'</ol>'].join(''));
|
||||||
|
}
|
||||||
|
|
||||||
|
function batScopeTreeDirective($compile) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
terminal: true,
|
||||||
|
scope: {
|
||||||
|
batModel: '='
|
||||||
|
},
|
||||||
|
link: function (scope, element, attrs) {
|
||||||
|
|
||||||
|
// scope.$id –> DOM node
|
||||||
|
var map = {};
|
||||||
|
var selectedElt = angular.element();
|
||||||
|
|
||||||
|
// init
|
||||||
|
var scopes = scope.batModel;
|
||||||
|
if (scopes) {
|
||||||
|
Object.keys(scopes).forEach(function (scopeId) {
|
||||||
|
var parentId = scopes[scopeId].parent;
|
||||||
|
renderScopeElement(scopeId, parentId);
|
||||||
|
renderScopeDescriptorElement(scopeId, scopes[scopeId].descriptor);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.$on('scope:new', function (ev, data) {
|
||||||
|
renderScopeElement(data.child, data.parent);
|
||||||
|
});
|
||||||
|
|
||||||
|
scope.$on('scope:link', function (ev, data) {
|
||||||
|
renderScopeDescriptorElement(data.id, data.descriptor);
|
||||||
|
});
|
||||||
|
|
||||||
|
function renderScopeElement (id, parentId) {
|
||||||
|
if (map[id]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var elt = map[id] = newBranchElement(id);
|
||||||
|
var parentElt = map[parentId] || element;
|
||||||
|
|
||||||
|
elt.children().eq(1).on('click', function () {
|
||||||
|
scope.$apply(function () {
|
||||||
|
scope.$emit('inspected-scope:change', {
|
||||||
|
id: id
|
||||||
|
});
|
||||||
|
selectedElt.children().eq(0).removeClass('selected');
|
||||||
|
selectedElt.children().eq(1).removeClass('selected');
|
||||||
|
|
||||||
|
selectedElt = elt;
|
||||||
|
|
||||||
|
selectedElt.children().eq(0).addClass('selected');
|
||||||
|
selectedElt.children().eq(1).addClass('selected');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
parentElt.append(elt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderScopeDescriptorElement (id, descriptor) {
|
||||||
|
var elt = map[id];
|
||||||
|
if (!elt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elt.children().eq(1).children().eq(1).html(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.$on('scope:destroy', function (ev, data) {
|
||||||
|
var id = data.id;
|
||||||
|
var elt = map[id];
|
||||||
|
if (elt) {
|
||||||
|
elt.remove();
|
||||||
|
}
|
||||||
|
delete map[id];
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function repeaterPredicate (child) {
|
||||||
|
return child.name && child.name['ng-repeat'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function notRepeatedPredicate (child) {
|
||||||
|
return !repeaterPredicate(child);
|
||||||
|
}
|
||||||
|
|
39
panel/components/tabs/tabs.html
Normal file
39
panel/components/tabs/tabs.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<div class="split-view split-view-vertical visible">
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="split-view-contents
|
||||||
|
scroll-target
|
||||||
|
split-view-contents-first
|
||||||
|
split-view-sidebar
|
||||||
|
sidebar"
|
||||||
|
style="width: 200px;">
|
||||||
|
<ol class="sidebar-tree" tabindex="0">
|
||||||
|
<li ng-repeat="pane in panes"
|
||||||
|
ng-click="select(pane)"
|
||||||
|
class="sidebar-tree-item
|
||||||
|
profile-launcher-view-tree-item"
|
||||||
|
ng-class="{selected:pane.selected}">
|
||||||
|
|
||||||
|
<img class="icon">
|
||||||
|
<div class="status"></div>
|
||||||
|
<div class="titles no-subtitle">
|
||||||
|
<span class="title">{{pane.title}}</span>
|
||||||
|
<span class="subtitle"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="split-view-contents
|
||||||
|
scroll-target
|
||||||
|
split-view-contents-second
|
||||||
|
outline-disclosure
|
||||||
|
bat-tabs-inside"
|
||||||
|
style="left: 200px;">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div ng-transclude></div>
|
||||||
|
|
||||||
|
</div>
|
70
panel/components/tabs/tabs.js
Normal file
70
panel/components/tabs/tabs.js
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
angular.module('batarang.tabs', []).
|
||||||
|
|
||||||
|
directive('batTabs', function ($compile, $templateCache, $http) {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
transclude: true,
|
||||||
|
scope: {},
|
||||||
|
templateUrl: 'components/tabs/tabs.html',
|
||||||
|
|
||||||
|
replace: true,
|
||||||
|
controller: function ($scope) {
|
||||||
|
var panes = $scope.panes = [];
|
||||||
|
|
||||||
|
this.addPane = function(pane) {
|
||||||
|
panes.push(pane);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
link: function (scope, element, attr) {
|
||||||
|
|
||||||
|
var lastScope;
|
||||||
|
var insideElt = angular.element(element[0].getElementsByClassName('bat-tabs-inside')[0]);
|
||||||
|
|
||||||
|
function destroyLastScope() {
|
||||||
|
if (lastScope) {
|
||||||
|
lastScope.$destroy();
|
||||||
|
lastScope = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.select = function (pane) {
|
||||||
|
$http.get(pane.src, { cache: $templateCache }).
|
||||||
|
then(function (response) {
|
||||||
|
var template = response.data;
|
||||||
|
insideElt.html(template);
|
||||||
|
destroyLastScope();
|
||||||
|
|
||||||
|
var link = $compile(insideElt.contents());
|
||||||
|
lastScope = scope.$new();
|
||||||
|
link(lastScope);
|
||||||
|
});
|
||||||
|
|
||||||
|
angular.forEach(scope.panes, function(pane) {
|
||||||
|
pane.selected = false;
|
||||||
|
});
|
||||||
|
pane.selected = true;
|
||||||
|
scope.currentPane = pane;
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.lastPane = scope.panes[0];
|
||||||
|
scope.select(scope.panes[scope.panes.length - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}).
|
||||||
|
directive('batPane', function() {
|
||||||
|
return {
|
||||||
|
require: '^batTabs',
|
||||||
|
restrict: 'E',
|
||||||
|
scope: {
|
||||||
|
title: '@',
|
||||||
|
src: '@'
|
||||||
|
},
|
||||||
|
link: function (scope, element, attrs, tabsCtrl) {
|
||||||
|
tabsCtrl.addPane({
|
||||||
|
title: attrs.title,
|
||||||
|
src: attrs.src
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
87
panel/components/vertical-split/vertical-split.js
Normal file
87
panel/components/vertical-split/vertical-split.js
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
angular.module('batarang.vertical-split', []).
|
||||||
|
constant('defaultSplit', 360).
|
||||||
|
directive('batVerticalSplit', function ($document, defaultSplit) {
|
||||||
|
|
||||||
|
var classes = [
|
||||||
|
'split-view',
|
||||||
|
'split-view-vertical',
|
||||||
|
'visible'
|
||||||
|
];
|
||||||
|
|
||||||
|
var body = angular.element($document[0].body);
|
||||||
|
|
||||||
|
return {
|
||||||
|
restrict: 'A',
|
||||||
|
compile: function (element) {
|
||||||
|
classes.forEach(element.addClass.bind(element));
|
||||||
|
|
||||||
|
var children = element.children();
|
||||||
|
var left = angular.element(children[0]);
|
||||||
|
var right = angular.element(children[1]);
|
||||||
|
|
||||||
|
|
||||||
|
return function (scope, element, attr) {
|
||||||
|
var slider = angular.element('<div class="split-view-resizer" style="right: ' + defaultSplit + 'px; margin-right: -2.5px;"></div>');
|
||||||
|
|
||||||
|
var drag = function (ev) {
|
||||||
|
var x = $document[0].body.clientWidth - ev.x;
|
||||||
|
left.css('right', x + 'px');
|
||||||
|
right.css('width', x + 'px');
|
||||||
|
slider.css('right', x + 'px');
|
||||||
|
};
|
||||||
|
|
||||||
|
var oldCursor;
|
||||||
|
|
||||||
|
slider.bind('mousedown', function (ev) {
|
||||||
|
drag(ev);
|
||||||
|
oldCursor = body.css('cursor');
|
||||||
|
body.css('cursor', 'ew-resize');
|
||||||
|
$document.bind('mousemove', drag);
|
||||||
|
$document.bind('mouseup', stopDrag);
|
||||||
|
});
|
||||||
|
|
||||||
|
var stopDrag = function () {
|
||||||
|
body.css('cursor', oldCursor);
|
||||||
|
$document.unbind('mousemove', drag);
|
||||||
|
$document.unbind('mouseup', stopDrag);
|
||||||
|
};
|
||||||
|
|
||||||
|
element.append(slider);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}).
|
||||||
|
directive('batVerticalLeft', function (defaultSplit) {
|
||||||
|
var classes = [
|
||||||
|
'split-view-contents',
|
||||||
|
'scroll-target',
|
||||||
|
'split-view-contents-first',
|
||||||
|
'outline-disclosure'
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
require: '^batVerticalSplit',
|
||||||
|
restrict: 'A',
|
||||||
|
compile: function (element) {
|
||||||
|
classes.forEach(element.addClass.bind(element));
|
||||||
|
element.css('right', defaultSplit + 'px');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}).
|
||||||
|
directive('batVerticalRight', function (defaultSplit) {
|
||||||
|
var classes = [
|
||||||
|
'split-view-contents',
|
||||||
|
'scroll-target',
|
||||||
|
'split-view-contents-second',
|
||||||
|
'split-view-sidebar'
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
require: '^batVerticalSplit',
|
||||||
|
restrict: 'A',
|
||||||
|
compile: function (element) {
|
||||||
|
classes.forEach(element.addClass.bind(element));
|
||||||
|
element.css('width', defaultSplit + 'px');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
24
panel/hints/hints.html
Normal file
24
panel/hints/hints.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<div class="sidebar-pane-stack audit-result-view fill visible" ng-controller="HintController">
|
||||||
|
|
||||||
|
<div class="sidebar-pane-title expanded"
|
||||||
|
ng-repeat="(groupName, group) in groupedHints">{{groupName}}
|
||||||
|
<div class="sidebar-pane-toolbar"></div>
|
||||||
|
|
||||||
|
<div class="sidebar-pane visible" ng-repeat="(summary, hints) in group">
|
||||||
|
<div class="body audit-result-tree">
|
||||||
|
<ol>
|
||||||
|
<li class="parent audit-result">
|
||||||
|
<div class="severity-warning"></div>{{summary}} ({{hints.length}})
|
||||||
|
</li>
|
||||||
|
<li class="parent-expanded expanded selected">
|
||||||
|
Controller names should start with an uppercase character and end with the suffix <code>Controller</code>. For example: <code>UserController</code>.
|
||||||
|
</li>
|
||||||
|
<ol class="children expanded">
|
||||||
|
<li class="selected" ng-repeat="hint in hints" bat-code="hint.message"></li>
|
||||||
|
</ol>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
24
panel/hints/hints.js
Normal file
24
panel/hints/hints.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('batarang.app.hint', []).
|
||||||
|
controller('HintController', ['$scope', 'inspectedApp', HintController]);
|
||||||
|
|
||||||
|
function HintController($scope, inspectedApp) {
|
||||||
|
$scope.$watch(function () {
|
||||||
|
return inspectedApp.hints.length;
|
||||||
|
}, function () {
|
||||||
|
var newHints = inspectedApp.hints;
|
||||||
|
$scope.groupedHints = {};
|
||||||
|
newHints.forEach(function (hint) {
|
||||||
|
var moduleName = hint.module || 'Hints';
|
||||||
|
var category = hint.category || (moduleName + ' Stuff');
|
||||||
|
if (!$scope.groupedHints[moduleName]) {
|
||||||
|
$scope.groupedHints[moduleName] = {};
|
||||||
|
}
|
||||||
|
if (!$scope.groupedHints[moduleName][category]) {
|
||||||
|
$scope.groupedHints[moduleName][category] = [];
|
||||||
|
}
|
||||||
|
$scope.groupedHints[moduleName][category].push(hint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
26
panel/reset.css
Normal file
26
panel/reset.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/* reset */
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: defaults for other platforms?? */
|
||||||
|
body {
|
||||||
|
cursor: default;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 12px;
|
||||||
|
margin: 0;
|
||||||
|
tab-size: 4;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
color: rgb(48, 57, 66);
|
||||||
|
font-family: 'Lucida Grande', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
}
|
39
panel/scopes/scopes.html
Normal file
39
panel/scopes/scopes.html
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<div bat-vertical-split ng-controller="ScopesController">
|
||||||
|
|
||||||
|
<div bat-vertical-left class="source-code">
|
||||||
|
<bat-scope-tree bat-model="scopes"></bat-scope-tree>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div bat-vertical-right>
|
||||||
|
<div class="sidebar-pane-stack fill visible">
|
||||||
|
|
||||||
|
<div class="sidebar-pane-title"
|
||||||
|
ng-class="{expanded: modelsExpanded}"
|
||||||
|
ng-click="modelsExpanded = !modelsExpanded">Models</div>
|
||||||
|
|
||||||
|
<div class="sidebar-pane visible" ng-if="modelsExpanded">
|
||||||
|
<div class="body">
|
||||||
|
<div class="section expanded">
|
||||||
|
<bat-json-tree val="scopes[inspectedScope].models" scope-id="inspectedScope"></bat-json-tree>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-pane-title"
|
||||||
|
ng-class="{expanded: watchExpanded}"
|
||||||
|
ng-click="watchExpanded = !watchExpanded">Watchers</div>
|
||||||
|
|
||||||
|
<div class="sidebar-pane visible" ng-if="watchExpanded">
|
||||||
|
<div class="body">
|
||||||
|
<div class="section expanded">
|
||||||
|
<div class="watcher-list">
|
||||||
|
<li ng-repeat="watcher in watchers">{{watcher}}</li>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
22
panel/scopes/scopes.js
Normal file
22
panel/scopes/scopes.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('batarang.app.scopes', []).
|
||||||
|
controller('ScopesController', ['$scope', 'inspectedApp', ScopesController]);
|
||||||
|
|
||||||
|
function ScopesController($scope, inspectedApp) {
|
||||||
|
$scope.scopes = inspectedApp.scopes;
|
||||||
|
|
||||||
|
$scope.watch = inspectedApp.watch;
|
||||||
|
|
||||||
|
$scope.inspectedScope = null;
|
||||||
|
|
||||||
|
$scope.$on('inspected-scope:change', function (ev, data) {
|
||||||
|
inspectScope(data.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
function inspectScope(scopeId) {
|
||||||
|
$scope.watch(scopeId);
|
||||||
|
$scope.inspectedScope = scopeId;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user