Compare commits
141 Commits
Author | SHA1 | Date |
---|---|---|
Chakib Benziane | a2f6528268 | 10 years ago |
Richard Littauer | db89db9431 | 10 years ago |
Brian Ford | 154772b7f4 | 10 years ago |
Brian Ford | f83c7f5ca6 | 10 years ago |
Brian Ford | a219e1cdd3 | 10 years ago |
Brian Ford | 68b72bfa6f | 10 years ago |
Brian Ford | 0cf05a7588 | 10 years ago |
Brian Ford | f2a9044e3b | 10 years ago |
Brian Ford | ca0dc695e5 | 10 years ago |
Brian Ford | fce0052322 | 10 years ago |
Brian Ford | 287074b73e | 10 years ago |
Brian Ford | bd32b00172 | 10 years ago |
Brian Ford | e902ecae96 | 10 years ago |
Brian Ford | 8c67a3e77e | 10 years ago |
Brian Ford | d7008a1368 | 10 years ago |
Brian Ford | d3e7bd6ec4 | 10 years ago |
Brian Ford | 2d0e8abff0 | 10 years ago |
Brian Ford | 60bdec9949 | 10 years ago |
Brian Ford | bd2ebbfd39 | 10 years ago |
Brian Ford | bec405c8c7 | 10 years ago |
Brian Ford | 010d8b573b | 10 years ago |
Brian Ford | 738ba58e77 | 10 years ago |
Brian Ford | f745b92053 | 10 years ago |
Brian Ford | 5cee2720b7 | 10 years ago |
Brian Ford | 5fd469035a | 10 years ago |
Brian Ford | a521bb9ea3 | 10 years ago |
Brian Ford | aa1c792d1a | 10 years ago |
Brian Ford | 1e5783f848 | 10 years ago |
Brian Ford | f8e205beb6 | 10 years ago |
Brian Ford | 1cfef9bd07 | 10 years ago |
Brian Ford | f1c54f3f24 | 10 years ago |
Brian Ford | 1e3aa47522 | 10 years ago |
Brian Ford | 5dc1e14aeb | 10 years ago |
Brian Ford | 48a4c65ddc | 10 years ago |
Brian Ford | e0ac7065df | 10 years ago |
Brian Ford | 2caeb0d288 | 10 years ago |
Brian Ford | d28787930e | 10 years ago |
Brian Ford | 2d92189c48 | 10 years ago |
Brian Ford | aee24175f4 | 10 years ago |
Brian Ford | 6a193f0d14 | 10 years ago |
Brian Ford | 2768929c9c | 10 years ago |
Brian Ford | 727ab2da9c | 10 years ago |
Brian Ford | a192204441 | 10 years ago |
Brian Ford | 3a999ad92a | 10 years ago |
Brian Ford | 239c560c74 | 10 years ago |
Brian Ford | eb2e7555d4 | 10 years ago |
Brian Ford | 147cd0b796 | 10 years ago |
Brian Ford | f9c028bc26 | 10 years ago |
Michał Gołębiowski | 86d7c40af5 | 10 years ago |
Brian Ford | 626c2b23f6 | 10 years ago |
Brian Ford | 7363f3debb | 10 years ago |
Brian Ford | 89b577c88d | 10 years ago |
Brian Ford | c35956c61d | 10 years ago |
Brian Ford | d1f0888929 | 10 years ago |
Brian Ford | 918ecd5c7d | 10 years ago |
Brian Ford | 3de10b6f41 | 10 years ago |
Brian Ford | 444398e9d4 | 10 years ago |
Brian Ford | d4edd956f6 | 10 years ago |
Brian Ford | b40377531e | 10 years ago |
Brian Ford | 706a63cc51 | 10 years ago |
Brian Ford | 499c82d5a6 | 10 years ago |
Brian Ford | 1162787cb0 | 10 years ago |
Brian Ford | d6463b1e2f | 10 years ago |
Brian Ford | 5c676f9959 | 10 years ago |
Brian Ford | 556b6d0e0b | 10 years ago |
Brian Ford | decec2685d | 10 years ago |
Brian Ford | 6911e08b73 | 10 years ago |
Brian Ford | f1b460e45a | 10 years ago |
Brian Ford | 156bf99c1f | 10 years ago |
Carlo s A. Guillen | 97380e8fcc | 10 years ago |
Erin Altenhof-Long | 03bef161b5 | 10 years ago |
Erin Altenhof-Long | c432fb6930 | 10 years ago |
Erin Altenhof-Long | b86d7a6955 | 10 years ago |
Erin Altenhof-Long | 952a4a4c73 | 10 years ago |
Carlo s A. Guillen | 0130c9dc8e | 10 years ago |
Erin Altenhof-Long | 6a0ae94f4d | 10 years ago |
Erin Altenhof-Long | df5bf33762 | 10 years ago |
Erin Altenhof-Long | 9bcaec14ee | 10 years ago |
Erin Altenhof-Long | d826dadf39 | 10 years ago |
Carlos Guillen | f06a989a78 | 10 years ago |
Carlos Guillen | 3b55f063e5 | 10 years ago |
Erin Altenhof-Long | 85606bb78c | 10 years ago |
Erin Altenhof-Long | d0da55123f | 10 years ago |
Erin Altenhof-Long | e1cd70d98e | 10 years ago |
Carlo s A. Guillen | b94c14e5aa | 10 years ago |
Carlo s A. Guillen | fe231271b7 | 10 years ago |
Carlo s A. Guillen | e81023bac7 | 10 years ago |
Carlo s A. Guillen | 2ebab66b13 | 10 years ago |
Carlo s A. Guillen | f3703f423f | 10 years ago |
Brian Ford | 036a6cf310 | 10 years ago |
Brian Ford | 99ea8b5225 | 10 years ago |
Brian Ford | 4a85f99679 | 10 years ago |
Brian Ford | 0c1edd6e58 | 10 years ago |
Brian Ford | cd2a9e495d | 11 years ago |
Frost | a68c9b7724 | 11 years ago |
Brian Ford | 4741ed559f | 11 years ago |
Brian Ford | 1cb6beacb7 | 11 years ago |
Brian Ford | febbc8c478 | 11 years ago |
Brian Ford | 253337e55f | 11 years ago |
Brian Ford | 514eddcda9 | 11 years ago |
Brian Ford | b581e188d1 | 11 years ago |
Brian Ford | 8e7ae1e968 | 11 years ago |
Frost | 5089764c36 | 11 years ago |
Frost | da9dc6d740 | 11 years ago |
Frost | 01560fe7b4 | 11 years ago |
Brian Ford | 989749f4d3 | 11 years ago |
Brian Ford | 7421f9196f | 11 years ago |
Brian Ford | f81ebb4701 | 11 years ago |
Brian Ford | 1ce75f5922 | 11 years ago |
Brian Ford | e4a68061ae | 11 years ago |
Brian Ford | 13d23765b2 | 11 years ago |
Brian Ford | 032b3a6ac6 | 11 years ago |
Brian Ford | 9be0ba8955 | 11 years ago |
Brian Ford | 704ac4dfbb | 11 years ago |
Brian Ford | ee422a1957 | 11 years ago |
Brian Ford | 6cfcaa8545 | 11 years ago |
Brian Ford | b2c7ac52a4 | 11 years ago |
Brian Ford | 2512c63e97 | 11 years ago |
Brian Ford | d4f851dae3 | 11 years ago |
Brian Ford | 04d7a1bbd1 | 11 years ago |
Brian Ford | 49a130c53b | 11 years ago |
Brian Ford | a5ecad55fd | 11 years ago |
Brian Ford | c002d055ef | 11 years ago |
Brian Ford | d468c20139 | 11 years ago |
Bert Willems | 354fa54100 | 11 years ago |
Brian Ford | b76af07ea0 | 12 years ago |
Brian Ford | ce96288504 | 12 years ago |
Brian Ford | 12f640924b | 12 years ago |
Brian Ford | bae0b604ca | 12 years ago |
Brian Ford | 3f5a2e5f57 | 12 years ago |
Brian Ford | 9bb1ebb37d | 12 years ago |
Brian Ford | 898fe826ba | 12 years ago |
Brian Ford | 2da618fdd7 | 12 years ago |
Brian Ford | d0fa314191 | 12 years ago |
Brian Ford | d8b2c2ef6f | 12 years ago |
Brian Ford | 4b584ec328 | 12 years ago |
Brian Ford | 24bcb46492 | 12 years ago |
Brian Ford | c5db59b348 | 12 years ago |
Brian Ford | 4682d4baf2 | 12 years ago |
Brian Ford | 3800d92d71 | 12 years ago |
Brian Ford | 61e8981c89 | 12 years ago |
@ -0,0 +1,8 @@
|
|||||||
|
batarang-release-*.zip
|
||||||
|
*.build.js
|
||||||
|
hint.bundle.js
|
||||||
|
.idea
|
||||||
|
bower_components
|
||||||
|
node_modules
|
||||||
|
coverage/
|
||||||
|
package/
|
@ -0,0 +1,21 @@
|
|||||||
|
language: node_js
|
||||||
|
node_js:
|
||||||
|
- "0.10"
|
||||||
|
|
||||||
|
env:
|
||||||
|
global:
|
||||||
|
- BROWSER_PROVIDER_READY_FILE=/tmp/sauce-connect-ready
|
||||||
|
- LOGS_DIR=/tmp/angular-hint-build/logs
|
||||||
|
- SAUCE_USERNAME=angular-ci
|
||||||
|
- SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
||||||
|
|
||||||
|
install:
|
||||||
|
- mkdir -p $LOGS_DIR
|
||||||
|
- ./scripts/sauce_connect_setup.sh
|
||||||
|
- npm install
|
||||||
|
- npm install -g gulp
|
||||||
|
- npm install -g karma-cli
|
||||||
|
- ./scripts/wait_for_browser_provider.sh
|
||||||
|
|
||||||
|
script:
|
||||||
|
- ./scripts/test_on_sauce.sh
|
@ -0,0 +1,36 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
This document describes how to contribute to Batarang
|
||||||
|
|
||||||
|
## Installing from Source
|
||||||
|
|
||||||
|
1. Clone the repository: `git clone git://github.com/angular/angularjs-batarang`
|
||||||
|
2. Install the npm dependencies `npm install`
|
||||||
|
3. Build the inject script: `npm run build`
|
||||||
|
4. Navigate to `chrome://chrome/extensions/` and enable Developer Mode.
|
||||||
|
5. Choose "Load unpacked extension"
|
||||||
|
6. In the dialog, open the directory you just cloned.
|
||||||
|
|
||||||
|
## Running the tests
|
||||||
|
|
||||||
|
## Packaging a release
|
||||||
|
|
||||||
|
I (@btford) will do this periodically, but I'm adding these instructions here
|
||||||
|
for posterity.
|
||||||
|
|
||||||
|
1. Edit the version number in `manifest.json` and `package.json` with the new version.
|
||||||
|
2. Run `gulp zip`
|
||||||
|
3. `git add package.json manifest.json dist/`
|
||||||
|
4. `git commit -m "v1.2.3"`
|
||||||
|
5. `git tag v1.2.3`
|
||||||
|
6. `git push upstream master && git push upstream --tags`
|
||||||
|
7. Upload `batarang-v1.2.3.zip` to Web Store via the [Web Store Dashboard](https://chrome.google.com/webstore/developer/dashboard)
|
||||||
|
|
||||||
|
## Layout
|
||||||
|
|
||||||
|
The `panel` directory contains...
|
||||||
|
`panel/components` contains self-contained directives and services.
|
||||||
|
|
||||||
|
## Testing Batarang manually
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
# Batarang FAQ
|
|
||||||
|
|
||||||
### How do I measure a directive's performance?
|
|
||||||
If your directive uses `$watch`, you should be able to see the watch expression wherever your directive is used.
|
|
||||||
|
|
||||||
### My $watch functions show up as just "function ()" in the performance tab
|
|
||||||
Use named functions for $watch:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
scope.$watch(function checkIfSomethingChanged() {
|
|
||||||
// ...
|
|
||||||
}, function whenThatChanges(newValue, oldValue) {
|
|
||||||
// ...
|
|
||||||
});
|
|
||||||
```
|
|
@ -1,61 +1,23 @@
|
|||||||
# AngularJS Batarang
|
# AngularJS Batarang
|
||||||
|
[![Build Status](https://travis-ci.org/angular/angularjs-batarang.svg)](https://travis-ci.org/angular/angularjs-batarang)
|
||||||
|
|
||||||
## Installing from the Web Store
|
## Installing from the Web Store
|
||||||
|
|
||||||
https://chrome.google.com/webstore/detail/ighdmehidhipcmcojjgiloacoafjmpfk
|
https://chrome.google.com/webstore/detail/ighdmehidhipcmcojjgiloacoafjmpfk
|
||||||
|
|
||||||
## Installing from Source
|
## Installing Previous Versions
|
||||||
|
|
||||||
1. Clone the repository: `git clone git://github.com/angular/angularjs-batarang`
|
|
||||||
2. Navigate to `chrome://chrome/extensions/` and enable Developer Mode.
|
|
||||||
3. Choose "Load unpacked extension"
|
|
||||||
4. Open the directory you just cloned (should open with Chrome, otherwise try dragging/dropping the file into Chrome) and follow the prompts to install.
|
|
||||||
|
|
||||||
## Screencast
|
|
||||||
|
|
||||||
Check out [this screencast](http://www.youtube.com/embed/q-7mhcHXSfM) that walks you through the Batarang's features.
|
|
||||||
|
|
||||||
## Using the Batarang
|
|
||||||
First, navigate Chrome Canary to the AngularJS application that you want to debug. [Open the Developer Tools](https://developers.google.com/chrome-developer-tools/docs/overview#access). There should be an AngularJS icon. Click on it to open the AngularJS Batarang.
|
|
||||||
|
|
||||||
<!-- HELP TAB -->
|
|
||||||
|
|
||||||
In order to begin using the Batarang you need to click the "enable" checkbox. This will cause the application's tab to refresh, and the Batarang to begin collecting perfomance and debug information about the inspected app.
|
|
||||||
|
|
||||||
The Batarang has five tabs: Model, Performance, Dependencies, Options, and Help.
|
|
||||||
|
|
||||||
### Models
|
|
||||||
![Batarang screenshot](https://github.com/angular/angularjs-batarang/raw/master/img/models.png)
|
|
||||||
|
|
||||||
Starting at the top of this tab, there is the root selection. If the application has only one `ng-app` declaration (as most applications do) then you will not see the option to change roots.
|
|
||||||
|
|
||||||
Below that is a tree showing how scopes are nested, and which models are attached to them. Clicking on a scope name will take you to the Elements tab, and show you the DOM element associated with that scope. Models and methods attached to each scope are listed with bullet points on the tree. Just the name of methods attached to a scope are shown. Models with a simple value and complex objects are shown as JSON. You can edit either, and the changes will be reflected in the application being debugged.
|
1. Download and extract one of the files from the [Batarang releases page on GitHub](https://github.com/angular/angularjs-batarang/releases)
|
||||||
|
1. Navigate to `chrome://chrome/extensions/` in Chrome
|
||||||
|
1. If you've installed Batarang from the web store, disable or remove that version
|
||||||
|
1. On the top right, check the checkbox for "Developer mode"
|
||||||
|
1. Click "Load unpacked extension..."
|
||||||
|
1. Select the directory where you extracted the extension
|
||||||
|
1. Close and re-open any inspected tabs
|
||||||
|
|
||||||
### Performance
|
## Installing from Source
|
||||||
![Batarang performance tab screenshot](https://github.com/angular/angularjs-batarang/raw/master/img/perf.png)
|
|
||||||
|
|
||||||
The performance tab must be enabled separately because it causes code to be injected into AngularJS to track and report performance metrics. There is also an option to output performance metrics to the console.
|
|
||||||
|
|
||||||
Below that is a tree of watched expressions, showing which expressions are attached to which scopes. Much like the model tree, you can collapse sections by clicking on "toggle" and you can inspect the element that a scope is attached to by clicking on the scope name.
|
|
||||||
|
|
||||||
Underneath that is a graph showing the relative performance of all of the application's expressions. This graph will update as you interact with the application.
|
|
||||||
|
|
||||||
### Dependencies
|
|
||||||
![Batarang dependencies tab screenshot](https://github.com/angular/angularjs-batarang/raw/master/img/deps.png)
|
|
||||||
|
|
||||||
The dependencies tab shows a visualization of the application's dependencies. When you hover over a service name, services that depend on the hovered service turn green, and those the hovered service depend on turn red.
|
|
||||||
|
|
||||||
### Options
|
|
||||||
![Batarang options tab screenshot](https://github.com/angular/angularjs-batarang/raw/master/img/options.png)
|
|
||||||
|
|
||||||
Last, there is the options tab. The options tab has three checkboxes: one for "show applications," "show scopes," and "show bindings." Each of these options, when enabled, highlights the respective feature of the application being debugged; scopes will have a red outline, and bindings will have a blue outline, and applications a green outline.
|
|
||||||
|
|
||||||
### Elements
|
|
||||||
![Batarang console screenshot](https://github.com/angular/angularjs-batarang/raw/master/img/inspect.png)
|
|
||||||
|
|
||||||
The Batarang also hooks into some of the existing features of the Chrome developer tools. For AngularJS applications, there is now a properties pane on in the Elements tab. Much like the model tree in the AngularJS tab, you can use this to inspect the models attached to a given element's scope.
|
|
||||||
|
|
||||||
### Console
|
See the [instructions in the contributing guide](https://github.com/angular/angularjs-batarang/blob/master/CONTRIBUTING.md#installing-from-source)
|
||||||
![Batarang console screenshot](https://github.com/angular/angularjs-batarang/raw/master/img/console.png)
|
|
||||||
|
|
||||||
The Batarang exposes some convenient features to the Chrome developer tools console. To access the scope of an element selected in the Elements tab of the developer tools, in console, you can type `$scope`. If you change value of some model on `$scope` and want to have this change reflected in the running application, you need to call `$scope.$apply()` after making the change.
|
## License
|
||||||
|
MIT
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
<html>
|
|
||||||
<body>
|
|
||||||
<script src="js/background.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* karma.conf.js and karma.es5.conf.js optionally load this
|
||||||
|
*/
|
||||||
|
|
||||||
|
var CUSTOM_LAUNCHERS = {
|
||||||
|
'SL_Chrome': {
|
||||||
|
base: 'SauceLabs',
|
||||||
|
browserName: 'chrome',
|
||||||
|
version: '35'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = function(options) {
|
||||||
|
options.sauceLabs = {
|
||||||
|
testName: 'AngularJS Batarang Unit Tests',
|
||||||
|
startConnect: true
|
||||||
|
};
|
||||||
|
options.customLaunchers = CUSTOM_LAUNCHERS;
|
||||||
|
options.browsers = Object.keys(CUSTOM_LAUNCHERS);
|
||||||
|
options.reporters = ['dots', 'saucelabs'];
|
||||||
|
};
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* karma.conf.js optionally loads this
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = function(options) {
|
||||||
|
if (!isTravis()) {
|
||||||
|
return;
|
||||||
|
} else if (!options.sauceLabs) {
|
||||||
|
throw new Error('This should be loaded after karma.sauce config');
|
||||||
|
}
|
||||||
|
options.sauceLabs.build = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||||
|
options.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||||
|
options.sauceLabs.startConnect = false;
|
||||||
|
|
||||||
|
// TODO(vojta): remove once SauceLabs supports websockets.
|
||||||
|
// This speeds up the capturing a bit, as browsers don't even try to use websocket.
|
||||||
|
options.transports = ['xhr-polling'];
|
||||||
|
|
||||||
|
options.singleRun = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
function isTravis() {
|
||||||
|
return !!process.env.TRAVIS;
|
||||||
|
}
|
@ -1,815 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap Responsive v2.0.4
|
|
||||||
*
|
|
||||||
* Copyright 2012 Twitter, Inc
|
|
||||||
* Licensed under the Apache License v2.0
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
|
||||||
*/
|
|
||||||
|
|
||||||
.clearfix {
|
|
||||||
*zoom: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearfix:before,
|
|
||||||
.clearfix:after {
|
|
||||||
display: table;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
|
|
||||||
.clearfix:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hide-text {
|
|
||||||
font: 0/0 a;
|
|
||||||
color: transparent;
|
|
||||||
text-shadow: none;
|
|
||||||
background-color: transparent;
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-block-level {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 28px;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-ms-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.visible-phone {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.visible-tablet {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-desktop {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
.visible-phone {
|
|
||||||
display: inherit !important;
|
|
||||||
}
|
|
||||||
.hidden-phone {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.hidden-desktop {
|
|
||||||
display: inherit !important;
|
|
||||||
}
|
|
||||||
.visible-desktop {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) and (max-width: 979px) {
|
|
||||||
.visible-tablet {
|
|
||||||
display: inherit !important;
|
|
||||||
}
|
|
||||||
.hidden-tablet {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
.hidden-desktop {
|
|
||||||
display: inherit !important;
|
|
||||||
}
|
|
||||||
.visible-desktop {
|
|
||||||
display: none !important ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
|
||||||
.nav-collapse {
|
|
||||||
-webkit-transform: translate3d(0, 0, 0);
|
|
||||||
}
|
|
||||||
.page-header h1 small {
|
|
||||||
display: block;
|
|
||||||
line-height: 18px;
|
|
||||||
}
|
|
||||||
input[type="checkbox"],
|
|
||||||
input[type="radio"] {
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
}
|
|
||||||
.form-horizontal .control-group > label {
|
|
||||||
float: none;
|
|
||||||
width: auto;
|
|
||||||
padding-top: 0;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.form-horizontal .controls {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.form-horizontal .control-list {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
.form-horizontal .form-actions {
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
.modal {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
right: 10px;
|
|
||||||
left: 10px;
|
|
||||||
width: auto;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
.modal.fade.in {
|
|
||||||
top: auto;
|
|
||||||
}
|
|
||||||
.modal-header .close {
|
|
||||||
padding: 10px;
|
|
||||||
margin: -10px;
|
|
||||||
}
|
|
||||||
.carousel-caption {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
|
||||||
body {
|
|
||||||
padding-right: 20px;
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
.navbar-fixed-top,
|
|
||||||
.navbar-fixed-bottom {
|
|
||||||
margin-right: -20px;
|
|
||||||
margin-left: -20px;
|
|
||||||
}
|
|
||||||
.container-fluid {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.dl-horizontal dt {
|
|
||||||
float: none;
|
|
||||||
width: auto;
|
|
||||||
clear: none;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.dl-horizontal dd {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
.row-fluid {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.row,
|
|
||||||
.thumbnails {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
[class*="span"],
|
|
||||||
.row-fluid [class*="span"] {
|
|
||||||
display: block;
|
|
||||||
float: none;
|
|
||||||
width: auto;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.input-large,
|
|
||||||
.input-xlarge,
|
|
||||||
.input-xxlarge,
|
|
||||||
input[class*="span"],
|
|
||||||
select[class*="span"],
|
|
||||||
textarea[class*="span"],
|
|
||||||
.uneditable-input {
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 28px;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-ms-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.input-prepend input,
|
|
||||||
.input-append input,
|
|
||||||
.input-prepend input[class*="span"],
|
|
||||||
.input-append input[class*="span"] {
|
|
||||||
display: inline-block;
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px) and (max-width: 979px) {
|
|
||||||
.row {
|
|
||||||
margin-left: -20px;
|
|
||||||
*zoom: 1;
|
|
||||||
}
|
|
||||||
.row:before,
|
|
||||||
.row:after {
|
|
||||||
display: table;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.row:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
[class*="span"] {
|
|
||||||
float: left;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
.container,
|
|
||||||
.navbar-fixed-top .container,
|
|
||||||
.navbar-fixed-bottom .container {
|
|
||||||
width: 724px;
|
|
||||||
}
|
|
||||||
.span12 {
|
|
||||||
width: 724px;
|
|
||||||
}
|
|
||||||
.span11 {
|
|
||||||
width: 662px;
|
|
||||||
}
|
|
||||||
.span10 {
|
|
||||||
width: 600px;
|
|
||||||
}
|
|
||||||
.span9 {
|
|
||||||
width: 538px;
|
|
||||||
}
|
|
||||||
.span8 {
|
|
||||||
width: 476px;
|
|
||||||
}
|
|
||||||
.span7 {
|
|
||||||
width: 414px;
|
|
||||||
}
|
|
||||||
.span6 {
|
|
||||||
width: 352px;
|
|
||||||
}
|
|
||||||
.span5 {
|
|
||||||
width: 290px;
|
|
||||||
}
|
|
||||||
.span4 {
|
|
||||||
width: 228px;
|
|
||||||
}
|
|
||||||
.span3 {
|
|
||||||
width: 166px;
|
|
||||||
}
|
|
||||||
.span2 {
|
|
||||||
width: 104px;
|
|
||||||
}
|
|
||||||
.span1 {
|
|
||||||
width: 42px;
|
|
||||||
}
|
|
||||||
.offset12 {
|
|
||||||
margin-left: 764px;
|
|
||||||
}
|
|
||||||
.offset11 {
|
|
||||||
margin-left: 702px;
|
|
||||||
}
|
|
||||||
.offset10 {
|
|
||||||
margin-left: 640px;
|
|
||||||
}
|
|
||||||
.offset9 {
|
|
||||||
margin-left: 578px;
|
|
||||||
}
|
|
||||||
.offset8 {
|
|
||||||
margin-left: 516px;
|
|
||||||
}
|
|
||||||
.offset7 {
|
|
||||||
margin-left: 454px;
|
|
||||||
}
|
|
||||||
.offset6 {
|
|
||||||
margin-left: 392px;
|
|
||||||
}
|
|
||||||
.offset5 {
|
|
||||||
margin-left: 330px;
|
|
||||||
}
|
|
||||||
.offset4 {
|
|
||||||
margin-left: 268px;
|
|
||||||
}
|
|
||||||
.offset3 {
|
|
||||||
margin-left: 206px;
|
|
||||||
}
|
|
||||||
.offset2 {
|
|
||||||
margin-left: 144px;
|
|
||||||
}
|
|
||||||
.offset1 {
|
|
||||||
margin-left: 82px;
|
|
||||||
}
|
|
||||||
.row-fluid {
|
|
||||||
width: 100%;
|
|
||||||
*zoom: 1;
|
|
||||||
}
|
|
||||||
.row-fluid:before,
|
|
||||||
.row-fluid:after {
|
|
||||||
display: table;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.row-fluid:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
.row-fluid [class*="span"] {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 28px;
|
|
||||||
margin-left: 2.762430939%;
|
|
||||||
*margin-left: 2.709239449638298%;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-ms-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.row-fluid [class*="span"]:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.row-fluid .span12 {
|
|
||||||
width: 99.999999993%;
|
|
||||||
*width: 99.9468085036383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span11 {
|
|
||||||
width: 91.436464082%;
|
|
||||||
*width: 91.38327259263829%;
|
|
||||||
}
|
|
||||||
.row-fluid .span10 {
|
|
||||||
width: 82.87292817100001%;
|
|
||||||
*width: 82.8197366816383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span9 {
|
|
||||||
width: 74.30939226%;
|
|
||||||
*width: 74.25620077063829%;
|
|
||||||
}
|
|
||||||
.row-fluid .span8 {
|
|
||||||
width: 65.74585634900001%;
|
|
||||||
*width: 65.6926648596383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span7 {
|
|
||||||
width: 57.182320438000005%;
|
|
||||||
*width: 57.129128948638304%;
|
|
||||||
}
|
|
||||||
.row-fluid .span6 {
|
|
||||||
width: 48.618784527%;
|
|
||||||
*width: 48.5655930376383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span5 {
|
|
||||||
width: 40.055248616%;
|
|
||||||
*width: 40.0020571266383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span4 {
|
|
||||||
width: 31.491712705%;
|
|
||||||
*width: 31.4385212156383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span3 {
|
|
||||||
width: 22.928176794%;
|
|
||||||
*width: 22.874985304638297%;
|
|
||||||
}
|
|
||||||
.row-fluid .span2 {
|
|
||||||
width: 14.364640883%;
|
|
||||||
*width: 14.311449393638298%;
|
|
||||||
}
|
|
||||||
.row-fluid .span1 {
|
|
||||||
width: 5.801104972%;
|
|
||||||
*width: 5.747913482638298%;
|
|
||||||
}
|
|
||||||
input,
|
|
||||||
textarea,
|
|
||||||
.uneditable-input {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
input.span12,
|
|
||||||
textarea.span12,
|
|
||||||
.uneditable-input.span12 {
|
|
||||||
width: 714px;
|
|
||||||
}
|
|
||||||
input.span11,
|
|
||||||
textarea.span11,
|
|
||||||
.uneditable-input.span11 {
|
|
||||||
width: 652px;
|
|
||||||
}
|
|
||||||
input.span10,
|
|
||||||
textarea.span10,
|
|
||||||
.uneditable-input.span10 {
|
|
||||||
width: 590px;
|
|
||||||
}
|
|
||||||
input.span9,
|
|
||||||
textarea.span9,
|
|
||||||
.uneditable-input.span9 {
|
|
||||||
width: 528px;
|
|
||||||
}
|
|
||||||
input.span8,
|
|
||||||
textarea.span8,
|
|
||||||
.uneditable-input.span8 {
|
|
||||||
width: 466px;
|
|
||||||
}
|
|
||||||
input.span7,
|
|
||||||
textarea.span7,
|
|
||||||
.uneditable-input.span7 {
|
|
||||||
width: 404px;
|
|
||||||
}
|
|
||||||
input.span6,
|
|
||||||
textarea.span6,
|
|
||||||
.uneditable-input.span6 {
|
|
||||||
width: 342px;
|
|
||||||
}
|
|
||||||
input.span5,
|
|
||||||
textarea.span5,
|
|
||||||
.uneditable-input.span5 {
|
|
||||||
width: 280px;
|
|
||||||
}
|
|
||||||
input.span4,
|
|
||||||
textarea.span4,
|
|
||||||
.uneditable-input.span4 {
|
|
||||||
width: 218px;
|
|
||||||
}
|
|
||||||
input.span3,
|
|
||||||
textarea.span3,
|
|
||||||
.uneditable-input.span3 {
|
|
||||||
width: 156px;
|
|
||||||
}
|
|
||||||
input.span2,
|
|
||||||
textarea.span2,
|
|
||||||
.uneditable-input.span2 {
|
|
||||||
width: 94px;
|
|
||||||
}
|
|
||||||
input.span1,
|
|
||||||
textarea.span1,
|
|
||||||
.uneditable-input.span1 {
|
|
||||||
width: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1200px) {
|
|
||||||
.row {
|
|
||||||
margin-left: -30px;
|
|
||||||
*zoom: 1;
|
|
||||||
}
|
|
||||||
.row:before,
|
|
||||||
.row:after {
|
|
||||||
display: table;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.row:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
[class*="span"] {
|
|
||||||
float: left;
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
.container,
|
|
||||||
.navbar-fixed-top .container,
|
|
||||||
.navbar-fixed-bottom .container {
|
|
||||||
width: 1170px;
|
|
||||||
}
|
|
||||||
.span12 {
|
|
||||||
width: 1170px;
|
|
||||||
}
|
|
||||||
.span11 {
|
|
||||||
width: 1070px;
|
|
||||||
}
|
|
||||||
.span10 {
|
|
||||||
width: 970px;
|
|
||||||
}
|
|
||||||
.span9 {
|
|
||||||
width: 870px;
|
|
||||||
}
|
|
||||||
.span8 {
|
|
||||||
width: 770px;
|
|
||||||
}
|
|
||||||
.span7 {
|
|
||||||
width: 670px;
|
|
||||||
}
|
|
||||||
.span6 {
|
|
||||||
width: 570px;
|
|
||||||
}
|
|
||||||
.span5 {
|
|
||||||
width: 470px;
|
|
||||||
}
|
|
||||||
.span4 {
|
|
||||||
width: 370px;
|
|
||||||
}
|
|
||||||
.span3 {
|
|
||||||
width: 270px;
|
|
||||||
}
|
|
||||||
.span2 {
|
|
||||||
width: 170px;
|
|
||||||
}
|
|
||||||
.span1 {
|
|
||||||
width: 70px;
|
|
||||||
}
|
|
||||||
.offset12 {
|
|
||||||
margin-left: 1230px;
|
|
||||||
}
|
|
||||||
.offset11 {
|
|
||||||
margin-left: 1130px;
|
|
||||||
}
|
|
||||||
.offset10 {
|
|
||||||
margin-left: 1030px;
|
|
||||||
}
|
|
||||||
.offset9 {
|
|
||||||
margin-left: 930px;
|
|
||||||
}
|
|
||||||
.offset8 {
|
|
||||||
margin-left: 830px;
|
|
||||||
}
|
|
||||||
.offset7 {
|
|
||||||
margin-left: 730px;
|
|
||||||
}
|
|
||||||
.offset6 {
|
|
||||||
margin-left: 630px;
|
|
||||||
}
|
|
||||||
.offset5 {
|
|
||||||
margin-left: 530px;
|
|
||||||
}
|
|
||||||
.offset4 {
|
|
||||||
margin-left: 430px;
|
|
||||||
}
|
|
||||||
.offset3 {
|
|
||||||
margin-left: 330px;
|
|
||||||
}
|
|
||||||
.offset2 {
|
|
||||||
margin-left: 230px;
|
|
||||||
}
|
|
||||||
.offset1 {
|
|
||||||
margin-left: 130px;
|
|
||||||
}
|
|
||||||
.row-fluid {
|
|
||||||
width: 100%;
|
|
||||||
*zoom: 1;
|
|
||||||
}
|
|
||||||
.row-fluid:before,
|
|
||||||
.row-fluid:after {
|
|
||||||
display: table;
|
|
||||||
content: "";
|
|
||||||
}
|
|
||||||
.row-fluid:after {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
.row-fluid [class*="span"] {
|
|
||||||
display: block;
|
|
||||||
float: left;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 28px;
|
|
||||||
margin-left: 2.564102564%;
|
|
||||||
*margin-left: 2.510911074638298%;
|
|
||||||
-webkit-box-sizing: border-box;
|
|
||||||
-moz-box-sizing: border-box;
|
|
||||||
-ms-box-sizing: border-box;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.row-fluid [class*="span"]:first-child {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.row-fluid .span12 {
|
|
||||||
width: 100%;
|
|
||||||
*width: 99.94680851063829%;
|
|
||||||
}
|
|
||||||
.row-fluid .span11 {
|
|
||||||
width: 91.45299145300001%;
|
|
||||||
*width: 91.3997999636383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span10 {
|
|
||||||
width: 82.905982906%;
|
|
||||||
*width: 82.8527914166383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span9 {
|
|
||||||
width: 74.358974359%;
|
|
||||||
*width: 74.30578286963829%;
|
|
||||||
}
|
|
||||||
.row-fluid .span8 {
|
|
||||||
width: 65.81196581200001%;
|
|
||||||
*width: 65.7587743226383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span7 {
|
|
||||||
width: 57.264957265%;
|
|
||||||
*width: 57.2117657756383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span6 {
|
|
||||||
width: 48.717948718%;
|
|
||||||
*width: 48.6647572286383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span5 {
|
|
||||||
width: 40.170940171000005%;
|
|
||||||
*width: 40.117748681638304%;
|
|
||||||
}
|
|
||||||
.row-fluid .span4 {
|
|
||||||
width: 31.623931624%;
|
|
||||||
*width: 31.5707401346383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span3 {
|
|
||||||
width: 23.076923077%;
|
|
||||||
*width: 23.0237315876383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span2 {
|
|
||||||
width: 14.529914530000001%;
|
|
||||||
*width: 14.4767230406383%;
|
|
||||||
}
|
|
||||||
.row-fluid .span1 {
|
|
||||||
width: 5.982905983%;
|
|
||||||
*width: 5.929714493638298%;
|
|
||||||
}
|
|
||||||
input,
|
|
||||||
textarea,
|
|
||||||
.uneditable-input {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
input.span12,
|
|
||||||
textarea.span12,
|
|
||||||
.uneditable-input.span12 {
|
|
||||||
width: 1160px;
|
|
||||||
}
|
|
||||||
input.span11,
|
|
||||||
textarea.span11,
|
|
||||||
.uneditable-input.span11 {
|
|
||||||
width: 1060px;
|
|
||||||
}
|
|
||||||
input.span10,
|
|
||||||
textarea.span10,
|
|
||||||
.uneditable-input.span10 {
|
|
||||||
width: 960px;
|
|
||||||
}
|
|
||||||
input.span9,
|
|
||||||
textarea.span9,
|
|
||||||
.uneditable-input.span9 {
|
|
||||||
width: 860px;
|
|
||||||
}
|
|
||||||
input.span8,
|
|
||||||
textarea.span8,
|
|
||||||
.uneditable-input.span8 {
|
|
||||||
width: 760px;
|
|
||||||
}
|
|
||||||
input.span7,
|
|
||||||
textarea.span7,
|
|
||||||
.uneditable-input.span7 {
|
|
||||||
width: 660px;
|
|
||||||
}
|
|
||||||
input.span6,
|
|
||||||
textarea.span6,
|
|
||||||
.uneditable-input.span6 {
|
|
||||||
width: 560px;
|
|
||||||
}
|
|
||||||
input.span5,
|
|
||||||
textarea.span5,
|
|
||||||
.uneditable-input.span5 {
|
|
||||||
width: 460px;
|
|
||||||
}
|
|
||||||
input.span4,
|
|
||||||
textarea.span4,
|
|
||||||
.uneditable-input.span4 {
|
|
||||||
width: 360px;
|
|
||||||
}
|
|
||||||
input.span3,
|
|
||||||
textarea.span3,
|
|
||||||
.uneditable-input.span3 {
|
|
||||||
width: 260px;
|
|
||||||
}
|
|
||||||
input.span2,
|
|
||||||
textarea.span2,
|
|
||||||
.uneditable-input.span2 {
|
|
||||||
width: 160px;
|
|
||||||
}
|
|
||||||
input.span1,
|
|
||||||
textarea.span1,
|
|
||||||
.uneditable-input.span1 {
|
|
||||||
width: 60px;
|
|
||||||
}
|
|
||||||
.thumbnails {
|
|
||||||
margin-left: -30px;
|
|
||||||
}
|
|
||||||
.thumbnails > li {
|
|
||||||
margin-left: 30px;
|
|
||||||
}
|
|
||||||
.row-fluid .thumbnails {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 979px) {
|
|
||||||
body {
|
|
||||||
padding-top: 0;
|
|
||||||
}
|
|
||||||
.navbar-fixed-top,
|
|
||||||
.navbar-fixed-bottom {
|
|
||||||
position: static;
|
|
||||||
}
|
|
||||||
.navbar-fixed-top {
|
|
||||||
margin-bottom: 18px;
|
|
||||||
}
|
|
||||||
.navbar-fixed-bottom {
|
|
||||||
margin-top: 18px;
|
|
||||||
}
|
|
||||||
.navbar-fixed-top .navbar-inner,
|
|
||||||
.navbar-fixed-bottom .navbar-inner {
|
|
||||||
padding: 5px;
|
|
||||||
}
|
|
||||||
.navbar .container {
|
|
||||||
width: auto;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.navbar .brand {
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-left: 10px;
|
|
||||||
margin: 0 0 0 -5px;
|
|
||||||
}
|
|
||||||
.nav-collapse {
|
|
||||||
clear: both;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav {
|
|
||||||
float: none;
|
|
||||||
margin: 0 0 9px;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav > li {
|
|
||||||
float: none;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav > li > a {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav > .divider-vertical {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav .nav-header {
|
|
||||||
color: #999999;
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav > li > a,
|
|
||||||
.nav-collapse .dropdown-menu a {
|
|
||||||
padding: 6px 15px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #999999;
|
|
||||||
-webkit-border-radius: 3px;
|
|
||||||
-moz-border-radius: 3px;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
.nav-collapse .btn {
|
|
||||||
padding: 4px 10px 4px;
|
|
||||||
font-weight: normal;
|
|
||||||
-webkit-border-radius: 4px;
|
|
||||||
-moz-border-radius: 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
.nav-collapse .dropdown-menu li + li a {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
.nav-collapse .nav > li > a:hover,
|
|
||||||
.nav-collapse .dropdown-menu a:hover {
|
|
||||||
background-color: #222222;
|
|
||||||
}
|
|
||||||
.nav-collapse.in .btn-group {
|
|
||||||
padding: 0;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
.nav-collapse .dropdown-menu {
|
|
||||||
position: static;
|
|
||||||
top: auto;
|
|
||||||
left: auto;
|
|
||||||
display: block;
|
|
||||||
float: none;
|
|
||||||
max-width: none;
|
|
||||||
padding: 0;
|
|
||||||
margin: 0 15px;
|
|
||||||
background-color: transparent;
|
|
||||||
border: none;
|
|
||||||
-webkit-border-radius: 0;
|
|
||||||
-moz-border-radius: 0;
|
|
||||||
border-radius: 0;
|
|
||||||
-webkit-box-shadow: none;
|
|
||||||
-moz-box-shadow: none;
|
|
||||||
box-shadow: none;
|
|
||||||
}
|
|
||||||
.nav-collapse .dropdown-menu:before,
|
|
||||||
.nav-collapse .dropdown-menu:after {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.nav-collapse .dropdown-menu .divider {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.nav-collapse .navbar-form,
|
|
||||||
.nav-collapse .navbar-search {
|
|
||||||
float: none;
|
|
||||||
padding: 9px 15px;
|
|
||||||
margin: 9px 0;
|
|
||||||
border-top: 1px solid #222222;
|
|
||||||
border-bottom: 1px solid #222222;
|
|
||||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
||||||
-moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
.navbar .nav-collapse .nav.pull-right {
|
|
||||||
float: none;
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
.nav-collapse,
|
|
||||||
.nav-collapse.collapse {
|
|
||||||
height: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.navbar .btn-navbar {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
.navbar-static .navbar-inner {
|
|
||||||
padding-right: 10px;
|
|
||||||
padding-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 980px) {
|
|
||||||
.nav-collapse.collapse {
|
|
||||||
height: auto !important;
|
|
||||||
overflow: visible !important;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
path.arc {
|
|
||||||
fill: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node {
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node:hover {
|
|
||||||
fill: #1f77b4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link {
|
|
||||||
fill: none;
|
|
||||||
stroke: #1f77b4;
|
|
||||||
stroke-opacity: .4;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link.source, .link.target {
|
|
||||||
stroke-opacity: 1;
|
|
||||||
stroke-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node.target {
|
|
||||||
fill: #d62728 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link.source {
|
|
||||||
stroke: #d62728;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node.source {
|
|
||||||
fill: #2ca02c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.link.target {
|
|
||||||
stroke: #2ca02c;
|
|
||||||
}
|
|
||||||
|
|
||||||
d3 {
|
|
||||||
display: block;
|
|
||||||
max-width: 600px;
|
|
||||||
height: 100%;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg text {
|
|
||||||
font-size: 1.4em;
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
.col {
|
|
||||||
float: left;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
.col-2 {
|
|
||||||
float: left;
|
|
||||||
width: 400px;
|
|
||||||
}
|
|
||||||
.scope-branch {
|
|
||||||
margin-left: 30px;
|
|
||||||
background-color: rgba(0,0,0,0.06);
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
margin: 10px;
|
|
||||||
}
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Slider widget style based on jquery-ui-bootstrap
|
|
||||||
* http://addyosmani.github.com/jquery-ui-bootstrap
|
|
||||||
*/
|
|
||||||
|
|
||||||
.ui-slider {
|
|
||||||
position: relative;
|
|
||||||
text-align: left;
|
|
||||||
height: .8em;
|
|
||||||
|
|
||||||
border-radius: 4px;
|
|
||||||
border: 1px solid #aaaaaa;
|
|
||||||
|
|
||||||
background: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-slider .ui-slider-handle {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 2;
|
|
||||||
width: 1.2em;
|
|
||||||
height: 1.2em;
|
|
||||||
top: -.3em;
|
|
||||||
margin-left: -.6em;
|
|
||||||
cursor: default;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
background-color: #e6e6e6;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-image: -webkit-linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);
|
|
||||||
/*background-image: linear-gradient(#ffffff, #ffffff 25%, #e6e6e6);*/
|
|
||||||
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: normal;
|
|
||||||
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-bottom-color: #bbb;
|
|
||||||
|
|
||||||
-webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
||||||
|
|
||||||
-webkit-transition: 0.1s linear background-image;
|
|
||||||
transition: 0.1s linear background-image;
|
|
||||||
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ui-slider .ui-slider-range {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
height: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
font-size: .7em;
|
|
||||||
display: block;
|
|
||||||
border: 0;
|
|
||||||
|
|
||||||
background-position: 0 0;
|
|
||||||
background-color: #0064cd;
|
|
||||||
background-repeat: repeat-x;
|
|
||||||
background-image: -webkit-linear-gradient(top, #049cdb, #0064cd);
|
|
||||||
/*background-image: linear-gradient(top, #049cdb, #0064cd);*/
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
<html>
|
<html>
|
||||||
<body>
|
<body>
|
||||||
<script src="js/devtoolsBackground.js"></script>
|
<script src="devtoolsBackground.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
@ -0,0 +1,29 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>yo</title>
|
||||||
|
</head>
|
||||||
|
<body ng-app="myApp">
|
||||||
|
<div ng-controller="myKontroller">
|
||||||
|
<h1>name: {{name}}</h1>
|
||||||
|
<input ng-model="name">
|
||||||
|
<h1>complex.a.b.c: {{complex.a.b.c}}</h1>
|
||||||
|
<input ng-model="complex.a.b.c">
|
||||||
|
<p>scope: {{$id}}</p>
|
||||||
|
<ul>
|
||||||
|
<li ng-repeat="num in items">{{num}}</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<script src="../node_modules/angular/angular.js"></script>
|
||||||
|
<script>
|
||||||
|
angular.module('myApp', []).
|
||||||
|
controller('myKontroller', function ($scope) {
|
||||||
|
$scope.name = 'hey';
|
||||||
|
$scope.items = [1, 2, 3];
|
||||||
|
$scope.complex = { a: { b: { c: 'yo' }}};
|
||||||
|
var thing = 'document';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -0,0 +1,42 @@
|
|||||||
|
var gulp = require('gulp');
|
||||||
|
var source = require('vinyl-source-stream');
|
||||||
|
var browserify = require('browserify');
|
||||||
|
var zip = require('gulp-zip');
|
||||||
|
|
||||||
|
var main = require('./package.json').main;
|
||||||
|
// TODO: make sure manifest version === package.json version === bower.json version
|
||||||
|
var version = require('./manifest.json').version;
|
||||||
|
|
||||||
|
gulp.task('watch', function(){
|
||||||
|
gulp.watch(['hint.js', '!./dist/*.js'], ['browserify']);
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('browserify', function() {
|
||||||
|
var bundleStream = browserify('./' + main).bundle().pipe(source(main));
|
||||||
|
return bundleStream.pipe(gulp.dest('./dist'));
|
||||||
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
|
* I use this to make a zip for the chrome store
|
||||||
|
*/
|
||||||
|
gulp.task('package', ['browserify'], function () {
|
||||||
|
return gulp.src([
|
||||||
|
'./dist/**',
|
||||||
|
'./img/**',
|
||||||
|
'./panel/**',
|
||||||
|
'background.js',
|
||||||
|
'devtoolsBackground.*',
|
||||||
|
'inject.js',
|
||||||
|
'manifest.json',
|
||||||
|
'./node_modules/angular/angular.js'
|
||||||
|
], {base: '.'})
|
||||||
|
.pipe(gulp.dest('./package'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('zip', ['package'], function () {
|
||||||
|
return gulp.src('package/**')
|
||||||
|
.pipe(zip('batarang-' + version + '.zip'))
|
||||||
|
.pipe(gulp.dest('.'));
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('default', ['browserify']);
|
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Batarang instrumentation
|
||||||
|
*
|
||||||
|
* This gets loaded into the context of the app you are inspecting
|
||||||
|
*/
|
||||||
|
require('./loader.js');
|
||||||
|
require('angular-hint');
|
||||||
|
|
||||||
|
angular.hint.onMessage = function (moduleName, message, messageType, category) {
|
||||||
|
if (!message) {
|
||||||
|
message = moduleName;
|
||||||
|
moduleName = 'Unknown'
|
||||||
|
}
|
||||||
|
if (typeof messageType === 'undefined') {
|
||||||
|
messageType = 1;
|
||||||
|
}
|
||||||
|
sendMessage({
|
||||||
|
module: moduleName,
|
||||||
|
message: message,
|
||||||
|
severity: messageType,
|
||||||
|
category: category
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
Before Width: | Height: | Size: 235 KiB |
Before Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 8.6 KiB |
Before Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 248 KiB |
Before Width: | Height: | Size: 102 KiB |
Before Width: | Height: | Size: 87 KiB |
Before Width: | Height: | Size: 200 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,28 @@
|
|||||||
|
if (document.cookie.indexOf('__ngDebug=true') != -1) {
|
||||||
|
bootstrapHint();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootstrapHint () {
|
||||||
|
chrome.extension.sendMessage('refresh');
|
||||||
|
|
||||||
|
var html = document.getElementsByTagName('html')[0];
|
||||||
|
|
||||||
|
var eventProxyElement = document.createElement('div');
|
||||||
|
eventProxyElement.id = '__ngBatarangElement';
|
||||||
|
eventProxyElement.style.display = 'none';
|
||||||
|
html.appendChild(eventProxyElement);
|
||||||
|
|
||||||
|
// inject into the application context from the content script context
|
||||||
|
|
||||||
|
var script = window.document.createElement('script');
|
||||||
|
script.src = chrome.extension.getURL('dist/hint.js');
|
||||||
|
|
||||||
|
eventProxyElement.addEventListener('batarangDataEvent', function () {
|
||||||
|
var eventData = eventProxyElement.innerText;
|
||||||
|
chrome.extension.sendMessage(eventData);
|
||||||
|
});
|
||||||
|
|
||||||
|
html.setAttribute('ng-hint', '');
|
||||||
|
|
||||||
|
html.appendChild(script);
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
|
|
||||||
// notify of page refreshes
|
|
||||||
chrome.extension.onConnect.addListener(function(port) {
|
|
||||||
port.onMessage.addListener(function (msg) {
|
|
||||||
if (msg.action === 'register') {
|
|
||||||
var respond = function (tabId, changeInfo, tab) {
|
|
||||||
if (tabId !== msg.inspectedTabId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
port.postMessage('refresh');
|
|
||||||
};
|
|
||||||
|
|
||||||
chrome.tabs.onUpdated.addListener(respond);
|
|
||||||
port.onDisconnect.addListener(function () {
|
|
||||||
chrome.tabs.onUpdated.removeListener(respond);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,9 +0,0 @@
|
|||||||
panelApp.controller('DepsCtrl', function DepsCtrl($scope, appDeps, poll) {
|
|
||||||
$scope.$on('poll', function () {
|
|
||||||
appDeps.get(function (deps) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.deps = deps;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,40 +0,0 @@
|
|||||||
|
|
||||||
panelApp.controller('ModelCtrl', function ModelCtrl($scope, appContext, appModel, poll) {
|
|
||||||
|
|
||||||
$scope.inspect = function () {
|
|
||||||
appContext.inspect(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();
|
|
||||||
}, {
|
|
||||||
name: this.key,
|
|
||||||
value: JSON.parse(this.item)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.roots = [];
|
|
||||||
$scope.selectedRoot = null;
|
|
||||||
|
|
||||||
$scope.$on('poll', function () {
|
|
||||||
appModel.getRootScopes(function (rootScopes) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.roots = rootScopes;
|
|
||||||
if ($scope.roots.length === 0) {
|
|
||||||
$scope.selectedRoot = null;
|
|
||||||
} else if (!$scope.selectedRoot) {
|
|
||||||
$scope.selectedRoot = $scope.roots[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
appModel.getModelTree($scope.selectedRoot, function (tree) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.tree = tree;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,40 +0,0 @@
|
|||||||
panelApp.controller('OptionsCtrl', function OptionsCtrl($scope, appInfo, appHighlight) {
|
|
||||||
|
|
||||||
$scope.debugger = {
|
|
||||||
scopes: false,
|
|
||||||
bindings: false,
|
|
||||||
app: false
|
|
||||||
};
|
|
||||||
|
|
||||||
['scopes', 'bindings', 'app'].forEach(function (thing) {
|
|
||||||
$scope.$watch('debugger.' + thing, function (val) {
|
|
||||||
appHighlight[thing](val);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
appInfo.getAngularVersion(function (version) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.version = version;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
appInfo.getAngularSrc(function (status) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
switch(status) {
|
|
||||||
case 'good':
|
|
||||||
$scope.status = 'success';
|
|
||||||
$scope.explain = 'CDN detected';
|
|
||||||
break;
|
|
||||||
case 'bad':
|
|
||||||
$scope.status = 'important';
|
|
||||||
$scope.explain = 'You are using the old code.angularjs.org links, which are slow! You should switch to the new CDN link. See <a target="_blank" href="http://blog.angularjs.org/2012/07/angularjs-now-hosted-on-google-cdn.html">this post</a> for more info';
|
|
||||||
break;
|
|
||||||
case 'info':
|
|
||||||
$scope.status = 'info';
|
|
||||||
$scope.explain = 'You may want to use the CDN-hosted AngularJS files. See <a target="_blank" href="http://blog.angularjs.org/2012/07/angularjs-now-hosted-on-google-cdn.html">this post</a> for more info';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,47 +0,0 @@
|
|||||||
panelApp.controller('PerfCtrl', function PerfCtrl($scope, appContext, appPerf, appModel, appWatch, filesystem, poll) {
|
|
||||||
|
|
||||||
$scope.histogram = [];
|
|
||||||
|
|
||||||
$scope.roots = [];
|
|
||||||
|
|
||||||
$scope.min = 0;
|
|
||||||
$scope.max = 100;
|
|
||||||
|
|
||||||
$scope.clearHistogram = function () {
|
|
||||||
appPerf.clear();
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.exportData = function () {
|
|
||||||
filesystem.exportJSON('file.json', $scope.histogram);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.$watch('log', function (newVal, oldVal) {
|
|
||||||
appContext.setLog(newVal);
|
|
||||||
});
|
|
||||||
|
|
||||||
$scope.inspect = function () {
|
|
||||||
appContext.inspect(this.val.id);
|
|
||||||
};
|
|
||||||
|
|
||||||
$scope.$on('poll', function () {
|
|
||||||
appPerf.get(function (histogram) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.histogram = histogram;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
appModel.getRootScopes(function (rootScopes) {
|
|
||||||
$scope.$apply(function () {
|
|
||||||
$scope.roots = rootScopes;
|
|
||||||
if ($scope.roots.length === 0) {
|
|
||||||
$scope.selectedRoot = null;
|
|
||||||
} else if (!$scope.selectedRoot) {
|
|
||||||
$scope.selectedRoot = $scope.roots[0];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
appWatch.getWatchTree($scope.selectedRoot, function (tree) {
|
|
||||||
$scope.tree = tree;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,274 +0,0 @@
|
|||||||
// D3 visualization
|
|
||||||
// TODO: D3 as a service
|
|
||||||
panelApp.directive('batD3', function () {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
terminal: true,
|
|
||||||
scope: {
|
|
||||||
val: '=val'
|
|
||||||
},
|
|
||||||
link: function (scope, element, attrs) {
|
|
||||||
// Based on code from: http://mbostock.github.com/d3/talk/20111116/bundle.html
|
|
||||||
|
|
||||||
// Initialize Element
|
|
||||||
// ------------------
|
|
||||||
var div = d3.select(element[0]);
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
// ---------
|
|
||||||
var w = 600,
|
|
||||||
h = 600,
|
|
||||||
rx = w / 2,
|
|
||||||
ry = h / 2,
|
|
||||||
m0,
|
|
||||||
rotate = 0;
|
|
||||||
|
|
||||||
// Helpers
|
|
||||||
// -------
|
|
||||||
|
|
||||||
// generate element ids that do not have '$'
|
|
||||||
var sanitize = function (key) {
|
|
||||||
return key.replace('$', 'dollar')
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: refactor the data transformation to make it faster
|
|
||||||
// For instance, build up the ideal structure in inject/degug.js
|
|
||||||
var packages = {
|
|
||||||
// Lazily construct the package hierarchy from class names.
|
|
||||||
root: function(classes) {
|
|
||||||
var map = {};
|
|
||||||
|
|
||||||
// add "classes" with no dependencies
|
|
||||||
var exist = {},
|
|
||||||
toAdd = [];
|
|
||||||
classes.forEach(function (cl) {
|
|
||||||
exist[cl.name] = true;
|
|
||||||
});
|
|
||||||
classes.forEach(function (cl) {
|
|
||||||
cl.imports.forEach(function (im) {
|
|
||||||
if (!exist[im]) {
|
|
||||||
toAdd.push(im);
|
|
||||||
exist[im] = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
toAdd.forEach(function (a) {
|
|
||||||
classes.push({
|
|
||||||
name: a,
|
|
||||||
imports: []
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function find(name, data) {
|
|
||||||
var node = map[name], i;
|
|
||||||
if (!node) {
|
|
||||||
node = map[name] = data || {name: name, children: []};
|
|
||||||
if (name.length) {
|
|
||||||
node.parent = find(name.substring(0, i = name.lastIndexOf(".")));
|
|
||||||
node.parent.children.push(node);
|
|
||||||
node.key = name.substring(i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
classes.forEach(function(d) {
|
|
||||||
find(d.name, d);
|
|
||||||
});
|
|
||||||
|
|
||||||
return map[""];
|
|
||||||
},
|
|
||||||
|
|
||||||
// Return a list of imports for the given array of nodes.
|
|
||||||
imports: function(nodes) {
|
|
||||||
var map = {},
|
|
||||||
imports = [];
|
|
||||||
|
|
||||||
// Compute a map from name to node.
|
|
||||||
nodes.forEach(function(d) {
|
|
||||||
map[d.name] = d;
|
|
||||||
});
|
|
||||||
|
|
||||||
// For each import, construct a link from the source to target node.
|
|
||||||
nodes.forEach(function(d) {
|
|
||||||
if (d.imports) d.imports.forEach(function(i) {
|
|
||||||
imports.push({source: map[d.name], target: map[i]});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return imports;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Instantiate and Style D3 Objects
|
|
||||||
// --------------------------------
|
|
||||||
|
|
||||||
var cluster = d3.layout.cluster().
|
|
||||||
size([360, ry - 120]).
|
|
||||||
sort(function(a, b) { return d3.ascending(a.key, b.key); });
|
|
||||||
|
|
||||||
var bundle = d3.layout.bundle();
|
|
||||||
|
|
||||||
var line = d3.svg.line.radial().
|
|
||||||
interpolate("bundle").
|
|
||||||
tension(.85).
|
|
||||||
radius(function(d) { return d.y; }).
|
|
||||||
angle(function(d) { return d.x / 180 * Math.PI; });
|
|
||||||
|
|
||||||
var svg = div.append("svg:svg").
|
|
||||||
attr("preserveAspectRatio", "xMinYMin meet").
|
|
||||||
attr("viewBox", [0, 0, w, h].join(' ')).
|
|
||||||
attr("height", h).
|
|
||||||
append("svg:g").
|
|
||||||
attr("transform", "translate(" + rx + "," + ry + ")");
|
|
||||||
|
|
||||||
// Render the data whenever "val" changes
|
|
||||||
// --------------------------------------
|
|
||||||
scope.$watch('val', function (newVal, oldVal) {
|
|
||||||
var classes;
|
|
||||||
|
|
||||||
if (!newVal || newVal.length === 0) {
|
|
||||||
svg.selectAll('*').remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oldVal && oldVal.length === newVal.length) {
|
|
||||||
var changed = false;
|
|
||||||
for (i = 0; i < oldVal.length; i++) {
|
|
||||||
if (oldVal[i].name !== newVal[i].name || newVal[i].imports.length !== oldVal[i].imports.length) {
|
|
||||||
changed = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!changed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
classes = newVal.slice(0);
|
|
||||||
classes.sort(function (a, b) {
|
|
||||||
return .5 - (a.name < b.name);
|
|
||||||
});
|
|
||||||
|
|
||||||
svg.selectAll('*').remove();
|
|
||||||
|
|
||||||
svg.append("svg:path")
|
|
||||||
.attr("class", "arc")
|
|
||||||
.attr("d", d3.svg.arc().outerRadius(ry - 120).innerRadius(0).startAngle(0).endAngle(2 * Math.PI))
|
|
||||||
.on("mousedown", mousedown);
|
|
||||||
|
|
||||||
var nodes = cluster.nodes(packages.root(classes)),
|
|
||||||
links = packages.imports(nodes),
|
|
||||||
splines = bundle(links);
|
|
||||||
|
|
||||||
var path = svg.selectAll("path.link")
|
|
||||||
.data(links)
|
|
||||||
.enter().append("svg:path")
|
|
||||||
.attr("class", function(d) { return "link source-" + sanitize(d.source.key) + " target-" + sanitize(d.target.key); })
|
|
||||||
.attr("d", function(d, i) { return line(splines[i]); });
|
|
||||||
|
|
||||||
svg.selectAll("g.node")
|
|
||||||
.data(nodes.filter(function(n) { return !n.children; }))
|
|
||||||
.enter().append("svg:g")
|
|
||||||
.attr("class", "node")
|
|
||||||
.attr("id", function(d) { return "node-" + sanitize(d.key); })
|
|
||||||
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; })
|
|
||||||
.append("svg:text")
|
|
||||||
.attr("dx", function(d) { return d.x < 180 ? 8 : -8; })
|
|
||||||
.attr("dy", ".31em")
|
|
||||||
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; })
|
|
||||||
.attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; })
|
|
||||||
.text(function(d) { return d.key; })
|
|
||||||
.on("mouseover", mouseover)
|
|
||||||
.on("mouseout", mouseout);
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
d3.select("input[type=range]").on("change", function() {
|
|
||||||
line.tension(this.value / 100);
|
|
||||||
path.attr("d", function(d, i) { return line(splines[i]); });
|
|
||||||
});
|
|
||||||
*/
|
|
||||||
|
|
||||||
//TODO: decide where to attach these events
|
|
||||||
|
|
||||||
/*
|
|
||||||
d3.select(window)
|
|
||||||
.on("mousemove", mousemove)
|
|
||||||
.on("mouseup", mouseup);
|
|
||||||
*/
|
|
||||||
|
|
||||||
function mouse(e) {
|
|
||||||
return [e.pageX - rx, e.pageY - ry];
|
|
||||||
}
|
|
||||||
|
|
||||||
function mousedown() {
|
|
||||||
m0 = mouse(d3.event);
|
|
||||||
d3.event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function mousemove() {
|
|
||||||
if (m0) {
|
|
||||||
var m1 = mouse(d3.event),
|
|
||||||
dm = Math.atan2(cross(m0, m1), dot(m0, m1)) * 180 / Math.PI;
|
|
||||||
div.style("-webkit-transform", "translate3d(0," + (ry - rx) + "px,0)rotate3d(0,0,0," + dm + "deg)translate3d(0," + (rx - ry) + "px,0)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mouseup() {
|
|
||||||
if (m0) {
|
|
||||||
var m1 = mouse(d3.event),
|
|
||||||
dm = Math.atan2(cross(m0, m1), dot(m0, m1)) * 180 / Math.PI;
|
|
||||||
|
|
||||||
rotate += dm;
|
|
||||||
if (rotate > 360) rotate -= 360;
|
|
||||||
else if (rotate < 0) rotate += 360;
|
|
||||||
m0 = null;
|
|
||||||
|
|
||||||
div.style("-webkit-transform", "rotate3d(0,0,0,0deg)");
|
|
||||||
|
|
||||||
svg
|
|
||||||
.attr("transform", "translate(" + rx + "," + ry + ")rotate(" + rotate + ")")
|
|
||||||
.selectAll("g.node text")
|
|
||||||
.attr("dx", function(d) { return (d.x + rotate) % 360 < 180 ? 8 : -8; })
|
|
||||||
.attr("text-anchor", function(d) { return (d.x + rotate) % 360 < 180 ? "start" : "end"; })
|
|
||||||
.attr("transform", function(d) { return (d.x + rotate) % 360 < 180 ? null : "rotate(180)"; });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mouseover(d) {
|
|
||||||
svg.selectAll("path.link.target-" + sanitize(d.key))
|
|
||||||
.classed("target", true)
|
|
||||||
.each(updateNodes("source", true));
|
|
||||||
|
|
||||||
svg.selectAll("path.link.source-" + sanitize(d.key))
|
|
||||||
.classed("source", true)
|
|
||||||
.each(updateNodes("target", true));
|
|
||||||
}
|
|
||||||
|
|
||||||
function mouseout(d) {
|
|
||||||
svg.selectAll("path.link.source-" + sanitize(d.key))
|
|
||||||
.classed("source", false)
|
|
||||||
.each(updateNodes("target", false));
|
|
||||||
|
|
||||||
svg.selectAll("path.link.target-" + sanitize(d.key))
|
|
||||||
.classed("target", false)
|
|
||||||
.each(updateNodes("source", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateNodes(name, value) {
|
|
||||||
return function(d) {
|
|
||||||
if (value) this.parentNode.appendChild(this);
|
|
||||||
svg.select("#node-" + sanitize(d[name].key)).classed(name, value);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function cross(a, b) {
|
|
||||||
return a[0] * b[1] - a[1] * b[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function dot(a, b) {
|
|
||||||
return a[0] * b[0] + a[1] * b[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,41 +0,0 @@
|
|||||||
panelApp.directive('batJsonTree', function($compile) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
terminal: true,
|
|
||||||
scope: {
|
|
||||||
val: '='
|
|
||||||
//edit: '=',
|
|
||||||
},
|
|
||||||
link: function (scope, element, attrs) {
|
|
||||||
// this is more complicated then it should be
|
|
||||||
// see: https://github.com/angular/angular.js/issues/898
|
|
||||||
|
|
||||||
var buildDom = function (object) {
|
|
||||||
var html = '';
|
|
||||||
if (object == undefined) {
|
|
||||||
html += 'null';
|
|
||||||
} 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>';
|
|
||||||
} else if (object instanceof Object) {
|
|
||||||
for (prop in object) {
|
|
||||||
if (object.hasOwnProperty(prop)) {
|
|
||||||
html += '<div class="scope-branch">' + prop + ': ' + buildDom(object[prop]) + '</div>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
html += '<span>' + object.toString() + '</span>';
|
|
||||||
}
|
|
||||||
return html;
|
|
||||||
};
|
|
||||||
|
|
||||||
scope.$watch('val', function (newVal, oldVal) {
|
|
||||||
element.html(buildDom(newVal));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,43 +0,0 @@
|
|||||||
// range slider
|
|
||||||
panelApp.directive('batSlider', function($compile) {
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
terminal: true,
|
|
||||||
scope: {
|
|
||||||
minimum: '=minimum',
|
|
||||||
maximum: '=maximum'
|
|
||||||
},
|
|
||||||
link: function (scope, element, attrs) {
|
|
||||||
|
|
||||||
var dom = $('<div class="ui-slider">' +
|
|
||||||
'<a class="ui-slider-handle" href></a>' +
|
|
||||||
'<a class="ui-slider-handle" href></a>' +
|
|
||||||
'<div class="ui-slider-range"></div>' +
|
|
||||||
'</div>');
|
|
||||||
|
|
||||||
element.append(dom);
|
|
||||||
|
|
||||||
$compile(element.contents())(scope.$new());
|
|
||||||
|
|
||||||
dom.slider({
|
|
||||||
range: true,
|
|
||||||
values: [0, 100],
|
|
||||||
slide: function () {
|
|
||||||
var min = $(this).slider('values', 0);
|
|
||||||
var max = $(this).slider('values', 1);
|
|
||||||
scope.minimum = min;
|
|
||||||
scope.maximum = max;
|
|
||||||
scope.$apply();
|
|
||||||
},
|
|
||||||
stop: function () {
|
|
||||||
var min = $(this).slider('values', 0);
|
|
||||||
var max = $(this).slider('values', 1);
|
|
||||||
scope.minimum = min;
|
|
||||||
scope.maximum = max;
|
|
||||||
scope.$apply();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,43 +0,0 @@
|
|||||||
// watchers tree
|
|
||||||
panelApp.directive('batWatcherTree', function($compile) {
|
|
||||||
|
|
||||||
// make toggle settings persist across $compile
|
|
||||||
var scopeState = {};
|
|
||||||
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
terminal: true,
|
|
||||||
scope: {
|
|
||||||
val: '=val',
|
|
||||||
inspect: '=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> | ' +
|
|
||||||
'<a href ng-click="scopeState[val.id] = !scopeState[val.id]">toggle</a>' +
|
|
||||||
'<div ng-hide="scopeState[val.id]">' +
|
|
||||||
'<ul>' +
|
|
||||||
'<li ng-repeat="item in val.watchers">' +
|
|
||||||
'<a href ng-hide="item.split(\'\n\').length < 2" ng-click="showState = !showState">toggle</a> ' +
|
|
||||||
'<code ng-hide="showState && item.split(\'\n\').length > 1">{{item | first}}</code>' +
|
|
||||||
'<pre ng-hide="!showState || item.split(\'\n\').length < 2">' +
|
|
||||||
'{{item}}' +
|
|
||||||
'</pre>' +
|
|
||||||
'</li>' +
|
|
||||||
'</ul>' +
|
|
||||||
'<div ng-repeat="child in val.children">' +
|
|
||||||
'<bat-watcher-tree val="child" inspect="inspect"></bat-watcher-tree>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>' +
|
|
||||||
'</div>');
|
|
||||||
|
|
||||||
var childScope = scope.$new();
|
|
||||||
childScope.scopeState = scopeState;
|
|
||||||
|
|
||||||
$compile(element.contents())(childScope);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,6 +0,0 @@
|
|||||||
// returns the first line of a multi-line string
|
|
||||||
panelApp.filter('first', function () {
|
|
||||||
return function (input, output) {
|
|
||||||
return input.split("\n")[0];
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,6 +0,0 @@
|
|||||||
// returns the number's first 4 decimals
|
|
||||||
panelApp.filter('precision', function () {
|
|
||||||
return function (input, output) {
|
|
||||||
return input.toPrecision(4);
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,20 +0,0 @@
|
|||||||
// Sort watchers by time
|
|
||||||
// Used by the performance tab
|
|
||||||
panelApp.filter('sortByTime', function () {
|
|
||||||
return function (input, min, max) {
|
|
||||||
var copy = input.slice(0);
|
|
||||||
|
|
||||||
copy = copy.sort(function (a, b) {
|
|
||||||
return b.time - a.time;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (typeof min !== 'number' || typeof max !== 'number') {
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
var start = Math.floor(input.length * min/100);
|
|
||||||
var end = Math.ceil(input.length * max/100) - start;
|
|
||||||
|
|
||||||
return copy.splice(start, end);
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,532 +0,0 @@
|
|||||||
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) {
|
|
||||||
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 (Object.prototype.toString.apply(value) === '[object Array]') {
|
|
||||||
nu = [];
|
|
||||||
for (i = 0; i < value.length; i += 1) {
|
|
||||||
nu[i] = derez(value[i], path + '[' + i + ']');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu = {};
|
|
||||||
for (name in value) {
|
|
||||||
if (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;
|
|
||||||
};
|
|
||||||
}());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
},
|
|
||||||
|
|
||||||
getScopeTree: function (id) {
|
|
||||||
if (debug.rootScopeDirty[id] === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var traverse = function (sc) {
|
|
||||||
var tree = {
|
|
||||||
id: sc.$id,
|
|
||||||
locals: debug.scopes[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.rootScopeDirty[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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 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 --> model objects
|
|
||||||
scopes: {},
|
|
||||||
|
|
||||||
// map of $ids --> refs to root scopes
|
|
||||||
rootScopes: {},
|
|
||||||
|
|
||||||
// map of $ids --> bools
|
|
||||||
rootScopeDirty: {},
|
|
||||||
|
|
||||||
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 (typeof definition === 'object') {
|
|
||||||
// it should have a $get
|
|
||||||
var tempGet = definition.$get;
|
|
||||||
definition.$get = 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.scopes[thatScope.$id] = getScopeLocals(thatScope);
|
|
||||||
//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;
|
|
||||||
debug.rootScopeDirty[thatScope.$root.$id] = true;
|
|
||||||
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.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create empty watchers array for this scope
|
|
||||||
if (!debug.watchers[ret.$id]) {
|
|
||||||
debug.watchers[ret.$id] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
debug.rootScopeDirty[ret.$root.$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();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
var panelApp = angular.module('panelApp', []);
|
|
@ -1,101 +0,0 @@
|
|||||||
// Service for running code in the context of the application being debugged
|
|
||||||
panelApp.factory('appContext', function (chromeExtension) {
|
|
||||||
|
|
||||||
// Public API
|
|
||||||
// ==========
|
|
||||||
return {
|
|
||||||
// TODO: Fix selection of scope
|
|
||||||
// https://github.com/angular/angularjs-batarang/issues/6
|
|
||||||
executeOnScope: function(scopeId, fn, args, cb) {
|
|
||||||
if (typeof args === 'function') {
|
|
||||||
cb = args;
|
|
||||||
args = {};
|
|
||||||
} else if (!args) {
|
|
||||||
args = {};
|
|
||||||
}
|
|
||||||
args.scopeId = scopeId;
|
|
||||||
args.fn = fn.toString();
|
|
||||||
|
|
||||||
chromeExtension.eval("function (window, args) {" +
|
|
||||||
"var elts = window.document.getElementsByClassName('ng-scope'), i;" +
|
|
||||||
"for (i = 0; i < elts.length; i++) {" +
|
|
||||||
"(function (elt) {" +
|
|
||||||
"var $scope = window.angular.element(elt).scope();" +
|
|
||||||
"if ($scope.$id === args.scopeId) {" +
|
|
||||||
"(" + args.fn + "($scope, elt, args));" +
|
|
||||||
"}" +
|
|
||||||
"}(elts[i]));" +
|
|
||||||
"}" +
|
|
||||||
"}", args, cb);
|
|
||||||
},
|
|
||||||
|
|
||||||
refresh: function (cb) {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
window.document.location.reload();
|
|
||||||
}, cb);
|
|
||||||
},
|
|
||||||
|
|
||||||
inspect: function (scopeId) {
|
|
||||||
this.executeOnScope(scopeId, function (scope, elt) {
|
|
||||||
inspect(elt);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
// --------
|
|
||||||
|
|
||||||
// takes a bool
|
|
||||||
setDebug: function (setting) {
|
|
||||||
if (setting) {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
window.document.cookie = '__ngDebug=true;';
|
|
||||||
window.document.location.reload();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
window.document.cookie = '__ngDebug=false;';
|
|
||||||
window.document.location.reload();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getDebug: function (cb) {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
return document.cookie.indexOf('__ngDebug=true') !== -1;
|
|
||||||
}, cb);
|
|
||||||
},
|
|
||||||
|
|
||||||
// takes a bool
|
|
||||||
setLog: function (setting) {
|
|
||||||
setting = !!setting;
|
|
||||||
chromeExtension.eval('function (window) {' +
|
|
||||||
'window.__ngDebug.log = ' + setting.toString() + ';' +
|
|
||||||
'}');
|
|
||||||
},
|
|
||||||
|
|
||||||
// Registering events
|
|
||||||
// ------------------
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
port.postMessage({
|
|
||||||
action: 'register',
|
|
||||||
inspectedTabId: chrome.devtools.inspectedWindow.tabId
|
|
||||||
});
|
|
||||||
port.onMessage.addListener(function(msg) {
|
|
||||||
if (msg === 'refresh') {
|
|
||||||
cb();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
port.onDisconnect.addListener(function (a) {
|
|
||||||
console.log(a);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,23 +0,0 @@
|
|||||||
// Service for injecting CSS into the application
|
|
||||||
panelApp.factory('appCss', function (chromeExtension) {
|
|
||||||
return {
|
|
||||||
addCssRule: function (args) {
|
|
||||||
chromeExtension.eval(function (window, args) {
|
|
||||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
|
||||||
styleSheet.insertRule(args.selector + '{' + args.style + '}', styleSheet.cssRules.length);
|
|
||||||
}, args);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeCssRule: function (args) {
|
|
||||||
chromeExtension.eval(function (window, args) {
|
|
||||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
|
||||||
var i;
|
|
||||||
for (i = styleSheet.cssRules.length - 1; i >= 0; i -= 1) {
|
|
||||||
if (styleSheet.cssRules[i].cssText === args.selector + ' { ' + args.style + '; }') {
|
|
||||||
styleSheet.deleteRule(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, args);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,26 +0,0 @@
|
|||||||
// Service for retrieving and caching application dependencies
|
|
||||||
panelApp.factory('appDeps', function (chromeExtension, appContext) {
|
|
||||||
|
|
||||||
var _depsCache = [];
|
|
||||||
|
|
||||||
// clear cache on page refresh
|
|
||||||
appContext.watchRefresh(function () {
|
|
||||||
_depsCache = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
get: function (callback) {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
if (window.__ngDebug) {
|
|
||||||
return window.__ngDebug.getDeps();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (data) {
|
|
||||||
if (data) {
|
|
||||||
_depsCache = data;
|
|
||||||
}
|
|
||||||
callback(_depsCache);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,39 +0,0 @@
|
|||||||
// Service for highlighting parts of the application
|
|
||||||
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
|
|
||||||
var styles = {
|
|
||||||
scopes: {
|
|
||||||
selector: '.ng-scope',
|
|
||||||
style: 'border: 1px solid red'
|
|
||||||
},
|
|
||||||
bindings: {
|
|
||||||
selector: '.ng-binding',
|
|
||||||
style: 'border: 1px solid blue'
|
|
||||||
},
|
|
||||||
app: {
|
|
||||||
selector: '[ng-app]',
|
|
||||||
style: 'border: 1px solid green'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var api = {};
|
|
||||||
|
|
||||||
for (i in styles) {
|
|
||||||
if (styles.hasOwnProperty(i)) {
|
|
||||||
// create closure around "styles"
|
|
||||||
(function () {
|
|
||||||
var style = styles[i];
|
|
||||||
api[i] = function (setting) {
|
|
||||||
if (setting) {
|
|
||||||
appCss.addCssRule(style);
|
|
||||||
} else {
|
|
||||||
appCss.removeCssRule(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return api;
|
|
||||||
});
|
|
@ -1,67 +0,0 @@
|
|||||||
// Service for running code in the context of the application being debugged
|
|
||||||
panelApp.factory('appInfo', function (chromeExtension, appContext) {
|
|
||||||
|
|
||||||
var _versionCache = null,
|
|
||||||
_srcCache = null;
|
|
||||||
|
|
||||||
// clear cache on page refresh
|
|
||||||
appContext.watchRefresh(function () {
|
|
||||||
_versionCache = null;
|
|
||||||
_srcCache = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
getAngularVersion: function (callback) {
|
|
||||||
if (_versionCache) {
|
|
||||||
setTimeout(function () {
|
|
||||||
callback(_versionCache);
|
|
||||||
}, 0);
|
|
||||||
} else {
|
|
||||||
chromeExtension.eval(function () {
|
|
||||||
return window.angular.version.full +
|
|
||||||
' ' +
|
|
||||||
window.angular.version.codeName;
|
|
||||||
}, function (data) {
|
|
||||||
_versionCache = data;
|
|
||||||
callback(_versionCache);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getAngularSrc: function (callback) {
|
|
||||||
if (_srcCache) {
|
|
||||||
setTimeout(function () {
|
|
||||||
callback(_srcCache);
|
|
||||||
}, 0);
|
|
||||||
} else {
|
|
||||||
chromeExtension.eval(function (window, args) {
|
|
||||||
if (!window.angular) {
|
|
||||||
return 'info';
|
|
||||||
}
|
|
||||||
var elts = window.angular.element('script[src]');
|
|
||||||
var re = /\/angular(-\d+(\.(\d+))+(rc)?)?(\.min)?\.js$/;
|
|
||||||
var elt;
|
|
||||||
for (i = 0; i < elts.length; i++) {
|
|
||||||
elt = elts[i];
|
|
||||||
if (re.exec(elt.src)) {
|
|
||||||
if (elt.src.indexOf('code.angularjs.org') !== -1) {
|
|
||||||
return 'error';
|
|
||||||
} else if (elt.src.indexOf('ajax.googleapis.com') !== -1) {
|
|
||||||
return 'good';
|
|
||||||
} else {
|
|
||||||
return 'info';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'info';
|
|
||||||
}, function (src) {
|
|
||||||
if (src) {
|
|
||||||
_srcCache = src;
|
|
||||||
}
|
|
||||||
callback(_srcCache);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
// Service for highlighting parts of the application
|
|
||||||
panelApp.factory('appInspect', function (chromeExtension) {
|
|
||||||
return {
|
|
||||||
enable: function () {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
var angular = window.angular;
|
|
||||||
var popover = angular.element('<div style="position: fixed; left: 10px; top: 10px; z-index: 9999; background-color: white; padding: 10px;"></div>');
|
|
||||||
angular.element(window.document.body).append(popover);
|
|
||||||
angular.element('.ng-scope').
|
|
||||||
on('mouseover', function () {
|
|
||||||
var thisElt = this;
|
|
||||||
var thisScope = angular.element(this).scope();
|
|
||||||
var models = {};
|
|
||||||
for (prop in thisScope) {
|
|
||||||
if (thisScope.hasOwnProperty(prop) && prop !== 'this' && prop[0] !== '$') {
|
|
||||||
models[prop] = thisScope[prop];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var str = JSON.stringify(models);
|
|
||||||
console.log(str);
|
|
||||||
//console.log(thisScope);
|
|
||||||
popover.html(str);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,44 +0,0 @@
|
|||||||
// Service for running code in the context of the application being debugged
|
|
||||||
panelApp.factory('appModel', function (chromeExtension, appContext) {
|
|
||||||
|
|
||||||
var _scopeCache = {},
|
|
||||||
_rootScopeCache = [];
|
|
||||||
|
|
||||||
|
|
||||||
// clear cache on page refresh
|
|
||||||
appContext.watchRefresh(function () {
|
|
||||||
_scopeCache = {};
|
|
||||||
_rootScopeCache = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
getRootScopes: function (callback) {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
if (!window.__ngDebug) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return window.__ngDebug.getRootScopeIds();
|
|
||||||
},
|
|
||||||
function (data) {
|
|
||||||
if (data) {
|
|
||||||
_rootScopeCache = data;
|
|
||||||
}
|
|
||||||
callback(_rootScopeCache);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getModelTree: function (id, callback) {
|
|
||||||
if (!id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
chromeExtension.eval(function (window, args) {
|
|
||||||
return window.__ngDebug.getScopeTree(args.id);
|
|
||||||
}, {id: id}, function (tree) {
|
|
||||||
if (tree) {
|
|
||||||
_scopeCache[id] = tree;
|
|
||||||
}
|
|
||||||
callback(_scopeCache[id]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,62 +0,0 @@
|
|||||||
// Service for retrieving and caching performance data
|
|
||||||
panelApp.factory('appPerf', function (chromeExtension, appContext) {
|
|
||||||
|
|
||||||
var _histogramCache = [],
|
|
||||||
_watchNameToPerf = {},
|
|
||||||
_totalCache = 0;
|
|
||||||
|
|
||||||
var clear = function () {
|
|
||||||
_histogramCache = [];
|
|
||||||
_watchNameToPerf = {};
|
|
||||||
_totalCache = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// clear cache on page refresh
|
|
||||||
appContext.watchRefresh(function () {
|
|
||||||
clear();
|
|
||||||
});
|
|
||||||
|
|
||||||
var getHistogramData = function (callback) {
|
|
||||||
chromeExtension.eval(function (window) {
|
|
||||||
if (!window.__ngDebug) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return window.__ngDebug.getWatchPerf();
|
|
||||||
},
|
|
||||||
function (data) {
|
|
||||||
if (data && data.length) {
|
|
||||||
updateHistogram(data);
|
|
||||||
}
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var updateHistogram = function (data) {
|
|
||||||
data.forEach(function (info) {
|
|
||||||
_totalCache += info.time;
|
|
||||||
|
|
||||||
if (_watchNameToPerf[info.name]) {
|
|
||||||
_watchNameToPerf[info.name].time += info.time;
|
|
||||||
} else {
|
|
||||||
_watchNameToPerf[info.name] = info;
|
|
||||||
_histogramCache.push(info);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// recalculate all percentages
|
|
||||||
_histogramCache.forEach(function (item) {
|
|
||||||
item.percent = (100 * item.time / _totalCache).toPrecision(3);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// Public API
|
|
||||||
// ==========
|
|
||||||
return {
|
|
||||||
get: function (callback) {
|
|
||||||
getHistogramData(function () {
|
|
||||||
callback(_histogramCache);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
clear: clear
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,22 +0,0 @@
|
|||||||
// Service for running code in the context of the application being debugged
|
|
||||||
panelApp.factory('appWatch', function (chromeExtension) {
|
|
||||||
|
|
||||||
var _watchCache = {};
|
|
||||||
|
|
||||||
// Public API
|
|
||||||
// ==========
|
|
||||||
return {
|
|
||||||
|
|
||||||
getWatchTree: function (id, callback) {
|
|
||||||
chromeExtension.eval("function (window, args) {" +
|
|
||||||
"return window.__ngDebug.getWatchTree(args.id);" +
|
|
||||||
"}", {id: id}, function (tree) {
|
|
||||||
if (tree) {
|
|
||||||
_watchCache[id] = tree;
|
|
||||||
}
|
|
||||||
callback(_watchCache[id]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,27 +0,0 @@
|
|||||||
// abstraction layer for Chrome Extension APIs
|
|
||||||
panelApp.value('chromeExtension', {
|
|
||||||
sendRequest: function (requestName, cb) {
|
|
||||||
chrome.extension.sendRequest({
|
|
||||||
script: requestName,
|
|
||||||
tab: chrome.devtools.inspectedWindow.tabId
|
|
||||||
}, cb || function () {});
|
|
||||||
},
|
|
||||||
|
|
||||||
// evaluates in the context of a window
|
|
||||||
//written because I don't like the API for chrome.devtools.inspectedWindow.eval;
|
|
||||||
// passing strings instead of functions are gross.
|
|
||||||
eval: function (fn, args, cb) {
|
|
||||||
// with two args
|
|
||||||
if (!cb && typeof args === 'function') {
|
|
||||||
cb = args;
|
|
||||||
args = {};
|
|
||||||
} else if (!args) {
|
|
||||||
args = {};
|
|
||||||
}
|
|
||||||
chrome.devtools.inspectedWindow.eval('(' +
|
|
||||||
fn.toString() +
|
|
||||||
'(window, ' +
|
|
||||||
JSON.stringify(args) +
|
|
||||||
'));', cb);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,30 +0,0 @@
|
|||||||
// Service for exporting as JSON
|
|
||||||
panelApp.factory('filesystem', function(chromeExtension) {
|
|
||||||
|
|
||||||
// taken from:
|
|
||||||
// http://html5-demos.appspot.com/static/html5storage/index.html#slide59
|
|
||||||
|
|
||||||
// TODO: error handlers?
|
|
||||||
|
|
||||||
return {
|
|
||||||
exportJSON: function (name, data) {
|
|
||||||
//TODO: file size/limits? 1024*1024
|
|
||||||
window.webkitRequestFileSystem(window.TEMPORARY, 1024*1024, function (fs) {
|
|
||||||
fs.root.getFile(name + '.json', {create: true}, function (fileEntry) {
|
|
||||||
fileEntry.createWriter(function(fileWriter) {
|
|
||||||
|
|
||||||
var blob = new Blob([ JSON.stringify(data) ], { type: 'text/plain' });
|
|
||||||
|
|
||||||
fileWriter.onwriteend = function () {
|
|
||||||
// navigate to file, will download
|
|
||||||
//location.href = fileEntry.toURL();
|
|
||||||
window.open(fileEntry.toURL());
|
|
||||||
};
|
|
||||||
|
|
||||||
fileWriter.write(blob);
|
|
||||||
}, function() {});
|
|
||||||
}, function() {});
|
|
||||||
}, function() {});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
@ -1,11 +0,0 @@
|
|||||||
// Service for broadcasting poll events
|
|
||||||
panelApp.factory('poll', function ($rootScope) {
|
|
||||||
|
|
||||||
setInterval(function () {
|
|
||||||
$rootScope.$broadcast('poll');
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
return {
|
|
||||||
setInterval: function (int) {}
|
|
||||||
};
|
|
||||||
});
|
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This karma conf tests just the panel app
|
||||||
|
*/
|
||||||
|
|
||||||
|
var sauceConfig = require('./config/karma.sauce.conf');
|
||||||
|
var travisConfig = require('./config/karma.travis.conf');
|
||||||
|
|
||||||
|
module.exports = function(config) {
|
||||||
|
var options = {
|
||||||
|
frameworks: ['browserify', 'jasmine'],
|
||||||
|
files: [
|
||||||
|
'node_modules/angular/angular.js',
|
||||||
|
'node_modules/angular-mocks/angular-mocks.js',
|
||||||
|
'panel/app.js',
|
||||||
|
'panel/**/*.js',
|
||||||
|
'panel/**/*.spec.js'
|
||||||
|
],
|
||||||
|
exclude: [],
|
||||||
|
preprocessors: {
|
||||||
|
'hint.js': [ 'browserify' ]
|
||||||
|
},
|
||||||
|
browsers: ['Chrome'],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.argv.indexOf('--sauce') > -1) {
|
||||||
|
sauceConfig(options);
|
||||||
|
travisConfig(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
config.set(options);
|
||||||
|
};
|
@ -0,0 +1,432 @@
|
|||||||
|
/**
|
||||||
|
* @license AngularJS v1.3.0-build.3042+sha.76e57a7
|
||||||
|
* (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
|
* License: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* This object provides a utility for producing rich Error messages within
|
||||||
|
* Angular. It can be called as follows:
|
||||||
|
*
|
||||||
|
* var exampleMinErr = minErr('example');
|
||||||
|
* throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
|
||||||
|
*
|
||||||
|
* The above creates an instance of minErr in the example namespace. The
|
||||||
|
* resulting error will have a namespaced error code of example.one. The
|
||||||
|
* resulting error will replace {0} with the value of foo, and {1} with the
|
||||||
|
* value of bar. The object is not restricted in the number of arguments it can
|
||||||
|
* take.
|
||||||
|
*
|
||||||
|
* If fewer arguments are specified than necessary for interpolation, the extra
|
||||||
|
* interpolation markers will be preserved in the final string.
|
||||||
|
*
|
||||||
|
* Since data will be parsed statically during a build step, some restrictions
|
||||||
|
* are applied with respect to how minErr instances are created and called.
|
||||||
|
* Instances should have names of the form namespaceMinErr for a minErr created
|
||||||
|
* using minErr('namespace') . Error codes, namespaces and template strings
|
||||||
|
* should all be static strings, not variables or general expressions.
|
||||||
|
*
|
||||||
|
* @param {string} module The namespace to use for the new minErr instance.
|
||||||
|
* @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
|
||||||
|
*/
|
||||||
|
|
||||||
|
function minErr(module) {
|
||||||
|
return function () {
|
||||||
|
var code = arguments[0],
|
||||||
|
prefix = '[' + (module ? module + ':' : '') + code + '] ',
|
||||||
|
template = arguments[1],
|
||||||
|
templateArgs = arguments,
|
||||||
|
stringify = function (obj) {
|
||||||
|
if (typeof obj === 'function') {
|
||||||
|
return obj.toString().replace(/ \{[\s\S]*$/, '');
|
||||||
|
} else if (typeof obj === 'undefined') {
|
||||||
|
return 'undefined';
|
||||||
|
} else if (typeof obj !== 'string') {
|
||||||
|
return JSON.stringify(obj);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
message, i;
|
||||||
|
|
||||||
|
message = prefix + template.replace(/\{\d+\}/g, function (match) {
|
||||||
|
var index = +match.slice(1, -1), arg;
|
||||||
|
|
||||||
|
if (index + 2 < templateArgs.length) {
|
||||||
|
arg = templateArgs[index + 2];
|
||||||
|
if (typeof arg === 'function') {
|
||||||
|
return arg.toString().replace(/ ?\{[\s\S]*$/, '');
|
||||||
|
} else if (typeof arg === 'undefined') {
|
||||||
|
return 'undefined';
|
||||||
|
} else if (typeof arg !== 'string') {
|
||||||
|
return toJson(arg);
|
||||||
|
}
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
return match;
|
||||||
|
});
|
||||||
|
|
||||||
|
message = message + '\nhttp://errors.angularjs.org/1.3.0-build.3042+sha.76e57a7/' +
|
||||||
|
(module ? module + '/' : '') + code;
|
||||||
|
for (i = 2; i < arguments.length; i++) {
|
||||||
|
message = message + (i == 2 ? '?' : '&') + 'p' + (i-2) + '=' +
|
||||||
|
encodeURIComponent(stringify(arguments[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error(message);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc type
|
||||||
|
* @name angular.Module
|
||||||
|
* @module ng
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Interface for configuring angular {@link angular.module modules}.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function setupModuleLoader(window) {
|
||||||
|
|
||||||
|
var $injectorMinErr = minErr('$injector');
|
||||||
|
var ngMinErr = minErr('ng');
|
||||||
|
|
||||||
|
function ensure(obj, name, factory) {
|
||||||
|
return obj[name] || (obj[name] = factory());
|
||||||
|
}
|
||||||
|
|
||||||
|
var angular = ensure(window, 'angular', Object);
|
||||||
|
|
||||||
|
// We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
|
||||||
|
angular.$$minErr = angular.$$minErr || minErr;
|
||||||
|
|
||||||
|
return ensure(angular, 'module', function() {
|
||||||
|
/** @type {Object.<string, angular.Module>} */
|
||||||
|
var modules = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc function
|
||||||
|
* @name angular.module
|
||||||
|
* @module ng
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* The `angular.module` is a global place for creating, registering and retrieving Angular
|
||||||
|
* modules.
|
||||||
|
* All modules (angular core or 3rd party) that should be available to an application must be
|
||||||
|
* registered using this mechanism.
|
||||||
|
*
|
||||||
|
* When passed two or more arguments, a new module is created. If passed only one argument, an
|
||||||
|
* existing module (the name passed as the first argument to `module`) is retrieved.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* # Module
|
||||||
|
*
|
||||||
|
* A module is a collection of services, directives, controllers, filters, and configuration information.
|
||||||
|
* `angular.module` is used to configure the {@link auto.$injector $injector}.
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* // Create a new module
|
||||||
|
* var myModule = angular.module('myModule', []);
|
||||||
|
*
|
||||||
|
* // register a new service
|
||||||
|
* myModule.value('appName', 'MyCoolApp');
|
||||||
|
*
|
||||||
|
* // configure existing services inside initialization blocks.
|
||||||
|
* myModule.config(['$locationProvider', function($locationProvider) {
|
||||||
|
* // Configure existing providers
|
||||||
|
* $locationProvider.hashPrefix('!');
|
||||||
|
* }]);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Then you can create an injector and load your modules like this:
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* var injector = angular.injector(['ng', 'myModule'])
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* However it's more likely that you'll just use
|
||||||
|
* {@link ng.directive:ngApp ngApp} or
|
||||||
|
* {@link angular.bootstrap} to simplify this process for you.
|
||||||
|
*
|
||||||
|
* @param {!string} name The name of the module to create or retrieve.
|
||||||
|
* @param {!Array.<string>=} requires If specified then new module is being created. If
|
||||||
|
* unspecified then the module is being retrieved for further configuration.
|
||||||
|
* @param {Function=} configFn Optional configuration function for the module. Same as
|
||||||
|
* {@link angular.Module#config Module#config()}.
|
||||||
|
* @returns {module} new module with the {@link angular.Module} api.
|
||||||
|
*/
|
||||||
|
return function module(name, requires, configFn) {
|
||||||
|
var assertNotHasOwnProperty = function(name, context) {
|
||||||
|
if (name === 'hasOwnProperty') {
|
||||||
|
throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assertNotHasOwnProperty(name, 'module');
|
||||||
|
if (requires && modules.hasOwnProperty(name)) {
|
||||||
|
modules[name] = null;
|
||||||
|
}
|
||||||
|
return ensure(modules, name, function() {
|
||||||
|
if (!requires) {
|
||||||
|
throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
|
||||||
|
"the module name or forgot to load it. If registering a module ensure that you " +
|
||||||
|
"specify the dependencies as the second argument.", name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {!Array.<Array.<*>>} */
|
||||||
|
var invokeQueue = [];
|
||||||
|
|
||||||
|
/** @type {!Array.<Function>} */
|
||||||
|
var configBlocks = [];
|
||||||
|
|
||||||
|
/** @type {!Array.<Function>} */
|
||||||
|
var runBlocks = [];
|
||||||
|
|
||||||
|
var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
|
||||||
|
|
||||||
|
/** @type {angular.Module} */
|
||||||
|
var moduleInstance = {
|
||||||
|
// Private state
|
||||||
|
_configBlocks: [],
|
||||||
|
_runBlocks: runBlocks,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc property
|
||||||
|
* @name angular.Module#requires
|
||||||
|
* @module ng
|
||||||
|
* @returns {Array.<string>} List of module names which must be loaded before this module.
|
||||||
|
* @description
|
||||||
|
* Holds the list of modules which the injector will load before the current module is
|
||||||
|
* loaded.
|
||||||
|
*/
|
||||||
|
requires: requires,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc property
|
||||||
|
* @name angular.Module#name
|
||||||
|
* @module ng
|
||||||
|
* @returns {string} Name of the module.
|
||||||
|
* @description
|
||||||
|
*/
|
||||||
|
name: name,
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#provider
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name service name
|
||||||
|
* @param {Function} providerType Construction function for creating new instance of the
|
||||||
|
* service.
|
||||||
|
* @description
|
||||||
|
* See {@link auto.$provide#provider $provide.provider()}.
|
||||||
|
*/
|
||||||
|
provider: invokeLater('$provide', 'provider'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#factory
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name service name
|
||||||
|
* @param {Function} providerFunction Function for creating new instance of the service.
|
||||||
|
* @description
|
||||||
|
* See {@link auto.$provide#factory $provide.factory()}.
|
||||||
|
*/
|
||||||
|
factory: invokeLater('$provide', 'factory'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#service
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name service name
|
||||||
|
* @param {Function} constructor A constructor function that will be instantiated.
|
||||||
|
* @description
|
||||||
|
* See {@link auto.$provide#service $provide.service()}.
|
||||||
|
*/
|
||||||
|
service: invokeLater('$provide', 'service'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#value
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name service name
|
||||||
|
* @param {*} object Service instance object.
|
||||||
|
* @description
|
||||||
|
* See {@link auto.$provide#value $provide.value()}.
|
||||||
|
*/
|
||||||
|
value: invokeLater('$provide', 'value'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#constant
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name constant name
|
||||||
|
* @param {*} object Constant value.
|
||||||
|
* @description
|
||||||
|
* Because the constant are fixed, they get applied before other provide methods.
|
||||||
|
* See {@link auto.$provide#constant $provide.constant()}.
|
||||||
|
*/
|
||||||
|
constant: invokeLater('$provide', 'constant', 'unshift'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#animation
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name animation name
|
||||||
|
* @param {Function} animationFactory Factory function for creating new instance of an
|
||||||
|
* animation.
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Defines an animation hook that can be later used with
|
||||||
|
* {@link ngAnimate.$animate $animate} service and directives that use this service.
|
||||||
|
*
|
||||||
|
* ```js
|
||||||
|
* module.animation('.animation-name', function($inject1, $inject2) {
|
||||||
|
* return {
|
||||||
|
* eventName : function(element, done) {
|
||||||
|
* //code to run the animation
|
||||||
|
* //once complete, then run done()
|
||||||
|
* return function cancellationFunction(element) {
|
||||||
|
* //code to cancel the animation
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* See {@link ngAnimate.$animateProvider#register $animateProvider.register()} and
|
||||||
|
* {@link ngAnimate ngAnimate module} for more information.
|
||||||
|
*/
|
||||||
|
animation: invokeLater('$animateProvider', 'register'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#filter
|
||||||
|
* @module ng
|
||||||
|
* @param {string} name Filter name.
|
||||||
|
* @param {Function} filterFactory Factory function for creating new instance of filter.
|
||||||
|
* @description
|
||||||
|
* See {@link ng.$filterProvider#register $filterProvider.register()}.
|
||||||
|
*/
|
||||||
|
filter: invokeLater('$filterProvider', 'register'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#controller
|
||||||
|
* @module ng
|
||||||
|
* @param {string|Object} name Controller name, or an object map of controllers where the
|
||||||
|
* keys are the names and the values are the constructors.
|
||||||
|
* @param {Function} constructor Controller constructor function.
|
||||||
|
* @description
|
||||||
|
* See {@link ng.$controllerProvider#register $controllerProvider.register()}.
|
||||||
|
*/
|
||||||
|
controller: invokeLater('$controllerProvider', 'register'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#directive
|
||||||
|
* @module ng
|
||||||
|
* @param {string|Object} name Directive name, or an object map of directives where the
|
||||||
|
* keys are the names and the values are the factories.
|
||||||
|
* @param {Function} directiveFactory Factory function for creating new instance of
|
||||||
|
* directives.
|
||||||
|
* @description
|
||||||
|
* See {@link ng.$compileProvider#directive $compileProvider.directive()}.
|
||||||
|
*/
|
||||||
|
directive: invokeLater('$compileProvider', 'directive'),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#config
|
||||||
|
* @module ng
|
||||||
|
* @param {Function} configFn Execute this function on module load. Useful for service
|
||||||
|
* configuration.
|
||||||
|
* @description
|
||||||
|
* Use this method to register work which needs to be performed on module loading.
|
||||||
|
* For more about how to configure services, see
|
||||||
|
* {@link providers#providers_provider-recipe Provider Recipe}.
|
||||||
|
*/
|
||||||
|
config: config,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ngdoc method
|
||||||
|
* @name angular.Module#run
|
||||||
|
* @module ng
|
||||||
|
* @param {Function} initializationFn Execute this function after injector creation.
|
||||||
|
* Useful for application initialization.
|
||||||
|
* @description
|
||||||
|
* Use this method to register work which should be performed when the injector is done
|
||||||
|
* loading all modules.
|
||||||
|
*/
|
||||||
|
run: function(block) {
|
||||||
|
runBlocks.push(block);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ANGULAR HINT ALTERATION
|
||||||
|
* To make this loader compatible with apps that are running
|
||||||
|
* both Angular 1.2 and 1.3, the loader must handle 1.3 applications
|
||||||
|
* that expect to initialize their config blocks after all providers
|
||||||
|
* are registered. Hence, the configBlocks are added to the end
|
||||||
|
* of the exisiting invokeQueue.
|
||||||
|
*/
|
||||||
|
Object.defineProperty(moduleInstance, '_invokeQueue', {
|
||||||
|
get: function() {
|
||||||
|
return invokeQueue.concat(configBlocks);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (configFn) {
|
||||||
|
config(configFn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return moduleInstance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} provider
|
||||||
|
* @param {string} method
|
||||||
|
* @param {String=} insertMethod
|
||||||
|
* @returns {angular.Module}
|
||||||
|
*/
|
||||||
|
function invokeLater(provider, method, insertMethod, queue) {
|
||||||
|
if (!queue) queue = invokeQueue;
|
||||||
|
return function() {
|
||||||
|
queue[insertMethod || 'push']([provider, method, arguments]);
|
||||||
|
return moduleInstance;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setupModuleLoader(window);
|
||||||
|
})(window);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closure compiler type information
|
||||||
|
*
|
||||||
|
* @typedef { {
|
||||||
|
* requires: !Array.<string>,
|
||||||
|
* invokeQueue: !Array.<Array.<*>>,
|
||||||
|
*
|
||||||
|
* service: function(string, Function):angular.Module,
|
||||||
|
* factory: function(string, Function):angular.Module,
|
||||||
|
* value: function(string, *):angular.Module,
|
||||||
|
*
|
||||||
|
* filter: function(string, Function):angular.Module,
|
||||||
|
*
|
||||||
|
* init: function(Function):angular.Module
|
||||||
|
* } }
|
||||||
|
*/
|
||||||
|
angular.Module;
|
||||||
|
|
@ -1,22 +1,44 @@
|
|||||||
{
|
{
|
||||||
"name": "AngularJS Batarang",
|
"name": "AngularJS Batarang",
|
||||||
"version": "0.3.2",
|
"version": "0.7.4",
|
||||||
"description": "Extends the Developer Tools, adding tools for debugging and profiling AngularJS applications.",
|
"description": "Extends the Developer Tools, adding tools for debugging and profiling AngularJS applications.",
|
||||||
"background": {
|
|
||||||
"page": "background.html"
|
|
||||||
},
|
|
||||||
"devtools_page": "devtoolsBackground.html",
|
"devtools_page": "devtoolsBackground.html",
|
||||||
"manifest_version": 2,
|
"manifest_version": 2,
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"tabs",
|
"tabs",
|
||||||
"<all_urls>"
|
"<all_urls>"
|
||||||
],
|
],
|
||||||
|
"icons": {
|
||||||
|
"16": "img/webstore-icon.png",
|
||||||
|
"48": "img/webstore-icon.png",
|
||||||
|
"128": "img/webstore-icon.png"
|
||||||
|
},
|
||||||
|
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["<all_urls>"],
|
"matches": ["<all_urls>"],
|
||||||
"js": ["js/inject/debug.js"],
|
"js": ["inject.js"],
|
||||||
"run_at": "document_start"
|
"run_at": "document_start"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
"page_action": {
|
||||||
|
"default_icon": {
|
||||||
|
"19": "img/icon19.png",
|
||||||
|
"38": "img/icon38.png"
|
||||||
|
},
|
||||||
|
|
||||||
|
"default_title": "AngularJS Super-Powered"
|
||||||
|
},
|
||||||
|
|
||||||
|
"web_accessible_resources": [
|
||||||
|
"dist/hint.js"
|
||||||
|
],
|
||||||
"minimum_chrome_version": "21.0.1180.57"
|
"minimum_chrome_version": "21.0.1180.57"
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"name": "angularjs-batarang",
|
||||||
|
"version": "0.7.4",
|
||||||
|
"description": "chrome extension for inspecting angular apps",
|
||||||
|
"main": "hint.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"angular-mocks": "^1.3.6",
|
||||||
|
"gulp-zip": "^2.0.2",
|
||||||
|
"karma-bro": "^0.6.0",
|
||||||
|
"karma-chrome-launcher": "^0.1.4",
|
||||||
|
"karma-sauce-launcher": "^0.2.9",
|
||||||
|
"karma-jasmine": "^0.1.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"angular": "^1.3.6",
|
||||||
|
"angular-hint": "~0.0.0",
|
||||||
|
"browserify": "^5.9.1",
|
||||||
|
"gulp": "^3.8.7",
|
||||||
|
"vinyl-source-stream": "^0.1.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "gulp"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/angular/angularjs-batarang.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"angular",
|
||||||
|
"angularjs",
|
||||||
|
"chrome",
|
||||||
|
"extension"
|
||||||
|
],
|
||||||
|
"author": "Brian Ford <btford@umich.edu>",
|
||||||
|
"license": "MIT",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/angular/angularjs-batarang/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/angular/angularjs-batarang"
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html ng-csp ng-app="panelApp">
|
|
||||||
<head>
|
|
||||||
<link rel="stylesheet" href="css/bootstrap.css">
|
|
||||||
<link rel="stylesheet" href="css/bootstrap-responsive.css">
|
|
||||||
<link rel="stylesheet" href="css/d3.css">
|
|
||||||
<link rel="stylesheet" href="css/panel.css">
|
|
||||||
|
|
||||||
<!-- libs -->
|
|
||||||
<script src="js/lib/angular.js"></script>
|
|
||||||
<script src="js/lib/jquery-1.7.2.min.js"></script>
|
|
||||||
<script src="js/lib/jquery-ui-1.8.21.custom.min.js"></script>
|
|
||||||
<script src="js/lib/d3.js"></script>
|
|
||||||
<script src="js/lib/d3.layout.js"></script>
|
|
||||||
|
|
||||||
<script src="js/panelApp.js"></script>
|
|
||||||
|
|
||||||
<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/slider.js"></script>
|
|
||||||
<script src="js/directives/tabs.js"></script>
|
|
||||||
<script src="js/directives/watcherTree.js"></script>
|
|
||||||
|
|
||||||
<script src="js/filters/first.js"></script>
|
|
||||||
<script src="js/filters/precision.js"></script>
|
|
||||||
<script src="js/filters/sortByTime.js"></script>
|
|
||||||
|
|
||||||
<script src="js/services/appContext.js"></script>
|
|
||||||
<script src="js/services/appCss.js"></script>
|
|
||||||
<script src="js/services/appDeps.js"></script>
|
|
||||||
<script src="js/services/appHighlight.js"></script>
|
|
||||||
<script src="js/services/appInfo.js"></script>
|
|
||||||
<script src="js/services/appModel.js"></script>
|
|
||||||
<script src="js/services/appPerf.js"></script>
|
|
||||||
<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>
|
|
||||||
<script src="js/controllers/OptionsCtrl.js"></script>
|
|
||||||
<script src="js/controllers/PerfCtrl.js"></script>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<bat-tabs>
|
|
||||||
<bat-pane title="Models" src="panes/model.html"></bat-pane>
|
|
||||||
<bat-pane title="Performance" src="panes/perf.html"></bat-pane>
|
|
||||||
<bat-pane title="Dependencies" src="panes/deps.html"></bat-pane>
|
|
||||||
<bat-pane title="Options" src="panes/options.html"></bat-pane>
|
|
||||||
<bat-pane title="Help" src="panes/help.html"></bat-pane>
|
|
||||||
</bat-tabs>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -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;
|
||||||
|
}
|
@ -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="../node_modules/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>
|
@ -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]);
|
@ -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('');
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,131 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('batarang.inspected-app', []).
|
||||||
|
service('inspectedApp', ['$rootScope', '$q', inspectedAppService]);
|
||||||
|
|
||||||
|
function inspectedAppService($rootScope, $q) {
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.assign = function (scopeId, path, value) {
|
||||||
|
return invokeAngularHintMethod('assign', scopeId, path, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.enableInstrumentation = function (setting) {
|
||||||
|
setting = !!setting;
|
||||||
|
chrome.devtools.inspectedWindow.eval(
|
||||||
|
"(function () {" +
|
||||||
|
"var prev = document.cookie.indexOf('__ngDebug=true') !== -1;" +
|
||||||
|
"if (prev !== " + setting + ") {" +
|
||||||
|
"window.document.cookie = '__ngDebug=" + setting + ";';" +
|
||||||
|
"window.document.location.reload();" +
|
||||||
|
"}" +
|
||||||
|
"}())"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getInstrumentationStatus = function () {
|
||||||
|
return $q(function(resolve, reject) {
|
||||||
|
chrome.devtools.inspectedWindow.eval(
|
||||||
|
"document.cookie.indexOf('__ngDebug=true') !== -1", resolve);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* sets window.$scope to the scope of the given id
|
||||||
|
*/
|
||||||
|
this.inspectScope = function (scopeId) {
|
||||||
|
return invokeAngularHintMethod('inspectScope', scopeId);
|
||||||
|
};
|
||||||
|
|
||||||
|
function invokeAngularHintMethod(method, scopeId, path, value) {
|
||||||
|
var args = [parseInt(scopeId, 10), path || ''].
|
||||||
|
map(JSON.stringify).
|
||||||
|
concat(value ? [value] : []).
|
||||||
|
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();
|
||||||
|
$rootScope.$broadcast('refresh');
|
||||||
|
} else if (typeof msg === 'string') {
|
||||||
|
var hint = JSON.parse(msg);
|
||||||
|
onHintMessage(hint);
|
||||||
|
} else if (typeof msg === 'object') {
|
||||||
|
onHintMessage(msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
port.onDisconnect.addListener(function (a) {
|
||||||
|
console.log(a);
|
||||||
|
});
|
||||||
|
|
||||||
|
function onHintMessage(hint) {
|
||||||
|
if (hint.message) {
|
||||||
|
hints.push(hint);
|
||||||
|
} else if (hint.event) {
|
||||||
|
if (hint.event === 'hydrate') {
|
||||||
|
Object.keys(hint.data.scopes).forEach(function (scopeId) {
|
||||||
|
scopes[scopeId] = hint.data.scopes[scopeId];
|
||||||
|
});
|
||||||
|
hint.data.hints.forEach(function (hint) {
|
||||||
|
hints.push(hint);
|
||||||
|
});
|
||||||
|
} else if (hint.event === 'scope:new') {
|
||||||
|
addNewScope(hint);
|
||||||
|
} else if (hint.id && scopes[hint.id]) {
|
||||||
|
var scope = scopes[hint.id];
|
||||||
|
if (hint.event === 'scope:destroy') {
|
||||||
|
if (scope.parent) {
|
||||||
|
scope.parent.children.splice(scope.parent.children.indexOf(child), 1);
|
||||||
|
}
|
||||||
|
delete scopes[hint.id];
|
||||||
|
} else if (hint.event === 'model:change') {
|
||||||
|
scope.models[hint.path] = (typeof hint.value === 'undefined') ?
|
||||||
|
undefined : JSON.parse(hint.value);
|
||||||
|
} else if (hint.event === 'scope:link') {
|
||||||
|
scope.descriptor = hint.descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$rootScope.$broadcast(hint.event, hint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRefreshMessage() {
|
||||||
|
clear(scopes);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear (obj) {
|
||||||
|
Object.keys(obj).forEach(function (key) {
|
||||||
|
delete obj[key];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('inspectedApp', function() {
|
||||||
|
var inspectedApp, port;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
module('batarang.inspected-app')
|
||||||
|
window.chrome = createMockChrome();
|
||||||
|
inject(function(_inspectedApp_) {
|
||||||
|
inspectedApp = _inspectedApp_;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when instantiated', function () {
|
||||||
|
it('should post a message with the inspected tabId', function () {
|
||||||
|
expect(port.postMessage).
|
||||||
|
toHaveBeenCalledWith(window.chrome.devtools.inspectedWindow.tabId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('messaging', function () {
|
||||||
|
it('should track hints', inject(function ($browser) {
|
||||||
|
port.onMessage.trigger(JSON.stringify({ message: 'hi' }));
|
||||||
|
$browser.defer.flush();
|
||||||
|
expect(inspectedApp.hints).toEqual([{message: 'hi'}]);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should track new scopes', inject(function ($browser) {
|
||||||
|
port.onMessage.trigger(JSON.stringify({ event: 'scope:new', child: 1 }));
|
||||||
|
$browser.defer.flush();
|
||||||
|
|
||||||
|
expect(inspectedApp.scopes).toEqual({ 1: { parent: undefined, children: [], models: {} } });
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should track updates to scope descriptors', inject(function ($browser) {
|
||||||
|
port.onMessage.trigger(JSON.stringify({ event: 'scope:new', child: 1 }));
|
||||||
|
port.onMessage.trigger(JSON.stringify({ event: 'scope:link', id: 1, descriptor: 'pasta' }));
|
||||||
|
$browser.defer.flush();
|
||||||
|
|
||||||
|
expect(inspectedApp.scopes[1].descriptor).toBe('pasta');
|
||||||
|
}));
|
||||||
|
})
|
||||||
|
|
||||||
|
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,"")');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('inspectScope', function () {
|
||||||
|
it('should call chrome devtools APIs', function() {
|
||||||
|
inspectedApp.inspectScope(2);
|
||||||
|
expect(chrome.devtools.inspectedWindow.eval).toHaveBeenCalledWith('angular.hint.inspectScope(2,"")');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createMockChrome() {
|
||||||
|
return {
|
||||||
|
extension: {
|
||||||
|
connect: function () {
|
||||||
|
return port = createMockSocket();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
devtools: {
|
||||||
|
inspectedWindow: {
|
||||||
|
tabId: 1,
|
||||||
|
eval: jasmine.createSpy('inspectedWindowEval')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function createListenerSpy(name) {
|
||||||
|
var symbol = '_' + name;
|
||||||
|
|
||||||
|
var listener = {
|
||||||
|
addListener: function (fn) {
|
||||||
|
listener[symbol].push(fn);
|
||||||
|
},
|
||||||
|
removeListener: function (fn) {
|
||||||
|
listener[symbol].splice(fn, 1);
|
||||||
|
},
|
||||||
|
trigger: function () {
|
||||||
|
var args = arguments;
|
||||||
|
listener[symbol].forEach(function (fn) {
|
||||||
|
fn.apply(listener, args);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
listener[symbol] = [];
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMockSocket() {
|
||||||
|
return {
|
||||||
|
onMessage: createListenerSpy('messageFunction'),
|
||||||
|
postMessage: jasmine.createSpy('postMessageFunction'),
|
||||||
|
onDisconnect: createListenerSpy('onDisconnect')
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/* 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,
|
||||||
|
.console-formatted-boolean {
|
||||||
|
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;
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
angular.module('batarang.json-tree', []).
|
||||||
|
directive('batJsonTree', [batJsonTreeDirective]);
|
||||||
|
|
||||||
|
var BAT_JSON_TREE_TEMPLATE = '<div class="properties-tree"></div>';
|
||||||
|
|
||||||
|
var ENTER_KEY = 13,
|
||||||
|
EXIT_KEY = 27;
|
||||||
|
|
||||||
|
var BAT_JSON_TREE_UNEDITABLE = [
|
||||||
|
'$id',
|
||||||
|
|
||||||
|
// managed by ngRepeat
|
||||||
|
'$first',
|
||||||
|
'$last',
|
||||||
|
'$index',
|
||||||
|
'$even',
|
||||||
|
'$odd'
|
||||||
|
];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: remove dependency on inspectedApp service
|
||||||
|
*/
|
||||||
|
function batJsonTreeDirective() {
|
||||||
|
return {
|
||||||
|
restrict: 'E',
|
||||||
|
terminal: true,
|
||||||
|
scope: {
|
||||||
|
batInspect: '&',
|
||||||
|
batAssign: '&',
|
||||||
|
batModel: '='
|
||||||
|
},
|
||||||
|
link: jsonTreeLinkFn
|
||||||
|
};
|
||||||
|
|
||||||
|
function jsonTreeLinkFn(scope, element, attrs) {
|
||||||
|
var root = angular.element(BAT_JSON_TREE_TEMPLATE);
|
||||||
|
element.append(root);
|
||||||
|
|
||||||
|
var branches = {
|
||||||
|
'': root
|
||||||
|
};
|
||||||
|
|
||||||
|
scope.$watch('batModel', function (val) {
|
||||||
|
if (!val) {
|
||||||
|
root = angular.element(BAT_JSON_TREE_TEMPLATE);
|
||||||
|
element.html('');
|
||||||
|
element.append(root);
|
||||||
|
branches = {
|
||||||
|
'': root
|
||||||
|
};
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
if (Number.isNaN(parseInt(key, 10))) {
|
||||||
|
fullPath += '.' + key;
|
||||||
|
} else {
|
||||||
|
fullPath += '[' + key + ']';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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 () {
|
||||||
|
scope.batInspect({ path: 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 + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test this
|
||||||
|
// some properties (like $id) shouldn't be edited
|
||||||
|
if (BAT_JSON_TREE_UNEDITABLE.indexOf(fullPath) > -1) {
|
||||||
|
childElt = angular.element(
|
||||||
|
'<span class="console-formatted-' + (typeof val) + '">' +
|
||||||
|
val +
|
||||||
|
'</span>');
|
||||||
|
} else {
|
||||||
|
childElt = angular.element(
|
||||||
|
'<span contentEditable="true" class="console-formatted-' + (typeof val) + '">' +
|
||||||
|
val +
|
||||||
|
'</span>');
|
||||||
|
|
||||||
|
// TODO: test this
|
||||||
|
childElt.on('keydown', function (ev) {
|
||||||
|
if (ev.keyCode === ENTER_KEY || ev.keyCode === EXIT_KEY) {
|
||||||
|
ev.preventDefault();
|
||||||
|
childElt[0].blur();
|
||||||
|
doAssign();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: test this
|
||||||
|
childElt.on('blur', doAssign);
|
||||||
|
|
||||||
|
function doAssign() {
|
||||||
|
scope.batAssign({
|
||||||
|
path: fullPath,
|
||||||
|
value: childElt.text()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('batJsonTree', function () {
|
||||||
|
|
||||||
|
var $compile, $rootScope, element;
|
||||||
|
|
||||||
|
beforeEach(module('batarang.json-tree'));
|
||||||
|
beforeEach(inject(function (_$compile_, _$rootScope_) {
|
||||||
|
$compile = _$compile_;
|
||||||
|
$rootScope = _$rootScope_;
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not throw on an undefined model', function () {
|
||||||
|
expect(compileTree).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render a simple model', function () {
|
||||||
|
$rootScope.data = {
|
||||||
|
'': { '$id': 1 }
|
||||||
|
};
|
||||||
|
compileTree();
|
||||||
|
expect(element.text()).toBe('$id: 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
function compileTree() {
|
||||||
|
element = compile('<bat-json-tree bat-model="data"></bat-json-tree>');
|
||||||
|
$rootScope.$apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
function compile(template) {
|
||||||
|
return $compile(template)($rootScope);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,130 @@
|
|||||||
|
|
||||||
|
/* 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 {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bat-scope-tree .selected {
|
||||||
|
font-weight: bold;
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
*/
|
@ -0,0 +1,49 @@
|
|||||||
|
<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 class="sidebar-tree-item
|
||||||
|
profile-launcher-view-tree-item">
|
||||||
|
<img class="icon">
|
||||||
|
<div class="status"></div>
|
||||||
|
<div class="titles no-subtitle">
|
||||||
|
<span class="title ng-binding">Enable <input type="checkbox" ng-model="enabled"></span>
|
||||||
|
<span class="subtitle"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
<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>
|
@ -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');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
@ -0,0 +1,28 @@
|
|||||||
|
<div class="sidebar-pane-stack audit-result-view fill visible" ng-controller="HintController">
|
||||||
|
|
||||||
|
<div></div>
|
||||||
|
|
||||||
|
<div class="sidebar-pane-title expanded"
|
||||||
|
ng-repeat-start="(groupName, group) in groupedHints">{{groupName}}
|
||||||
|
<div class="sidebar-pane-toolbar"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sidebar-pane visible"
|
||||||
|
ng-repeat-end
|
||||||
|
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>
|
@ -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;
|
||||||
|
if (!$scope.groupedHints[moduleName]) {
|
||||||
|
$scope.groupedHints[moduleName] = {};
|
||||||
|
}
|
||||||
|
if (!$scope.groupedHints[moduleName][category]) {
|
||||||
|
$scope.groupedHints[moduleName][category] = [];
|
||||||
|
}
|
||||||
|
$scope.groupedHints[moduleName][category].push(hint);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
<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
|
||||||
|
bat-model="scopes[inspectedScope].models"
|
||||||
|
bat-inspect="inspect(path)"
|
||||||
|
bat-assign="assign(path, value)"></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>
|
@ -0,0 +1,42 @@
|
|||||||
|
'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.inspect = function (path) {
|
||||||
|
inspectedApp.watch($scope.inspectedScope, path);
|
||||||
|
};
|
||||||
|
$scope.assign = function (path, value) {
|
||||||
|
inspectedApp.assign($scope.inspectedScope, path, value);
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.inspectedScope = null;
|
||||||
|
|
||||||
|
$scope.$on('refresh', function () {
|
||||||
|
$scope.inspectedScope = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// expand models the fist time we inspect a scope
|
||||||
|
var cancelWatch = $scope.$watch('inspectedScope', function (newScope) {
|
||||||
|
if (newScope) {
|
||||||
|
$scope.modelsExpanded = true;
|
||||||
|
cancelWatch();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$scope.$on('inspected-scope:change', function (ev, data) {
|
||||||
|
inspectScope(data.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
function inspectScope(scopeId) {
|
||||||
|
$scope.watch(scopeId);
|
||||||
|
$scope.inspectedScope = scopeId;
|
||||||
|
inspectedApp.inspectScope(scopeId);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
<div ng-controller="DepsCtrl">
|
|
||||||
<div class="span12">
|
|
||||||
<h2>Service Dependencies</h2>
|
|
||||||
<div class="well">
|
|
||||||
<bat-d3 val="deps"></bat-d3>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,45 +0,0 @@
|
|||||||
<p>In order to begin using the Batarang you need to click the "enable" checkbox. This will cause the application's tab to refresh, and the Batarang to begin collecting perfomance and debug information about the inspected app.</p>
|
|
||||||
|
|
||||||
<p>The Batarang has five tabs: Model, Performance, Dependencies, Options, and Help.</p>
|
|
||||||
|
|
||||||
<h3>Models</h3>
|
|
||||||
|
|
||||||
<p><img src="/img/models.png" alt="Batarang screenshot" title="" /></p>
|
|
||||||
|
|
||||||
<p>Starting at the top of this tab, there is the root selection. If the application has only one <code>ng-app</code> declaration (as most applications do) then you will not see the option to change roots.</p>
|
|
||||||
|
|
||||||
<p>Below that is a tree showing how scopes are nested, and which models are attached to them. Clicking on a scope name will take you to the Elements tab, and show you the DOM element associated with that scope. Models and methods attached to each scope are listed with bullet points on the tree. Just the name of methods attached to a scope are shown. Models with a simple value and complex objects are shown as JSON. You can edit either, and the changes will be reflected in the application being debugged.</p>
|
|
||||||
|
|
||||||
<h3>Performance</h3>
|
|
||||||
|
|
||||||
<p><img src="/img/perf.png" alt="Batarang performance tab screenshot" title="" /></p>
|
|
||||||
|
|
||||||
<p>The performance tab must be enabled separately because it causes code to be injected into AngularJS to track and report performance metrics. There is also an option to output performance metrics to the console.</p>
|
|
||||||
|
|
||||||
<p>Below that is a tree of watched expressions, showing which expressions are attached to which scopes. Much like the model tree, you can collapse sections by clicking on "toggle" and you can inspect the element that a scope is attached to by clicking on the scope name.</p>
|
|
||||||
|
|
||||||
<p>Underneath that is a graph showing the relative performance of all of the application's expressions. This graph will update as you interact with the application.</p>
|
|
||||||
|
|
||||||
<h3>Dependencies</h3>
|
|
||||||
|
|
||||||
<p><img src="/img/deps.png" alt="Batarang dependencies tab screenshot" title="" /></p>
|
|
||||||
|
|
||||||
<p>The dependencies tab shows a visualization of the application's dependencies. When you hover over a service name, services that depend on the hovered service turn green, and those the hovered service depend on turn red.</p>
|
|
||||||
|
|
||||||
<h3>Options</h3>
|
|
||||||
|
|
||||||
<p><img src="/img/options.png" alt="Batarang options tab screenshot" title="" /></p>
|
|
||||||
|
|
||||||
<p>Last, there is the options tab. The options tab has three checkboxes: one for "show applications," "show scopes," and "show bindings." Each of these options, when enabled, highlights the respective feature of the application being debugged; scopes will have a red outline, and bindings will have a blue outline, and applications a green outline.</p>
|
|
||||||
|
|
||||||
<h3>Elements</h3>
|
|
||||||
|
|
||||||
<p><img src="/img/inspect.png" alt="Batarang console screenshot" title="" /></p>
|
|
||||||
|
|
||||||
<p>The Batarang also hooks into some of the existing features of the Chrome developer tools. For AngularJS applications, there is now a properties pane on in the Elements tab. Much like the model tree in the AngularJS tab, you can use this to inspect the models attached to a given element's scope.</p>
|
|
||||||
|
|
||||||
<h3>Console</h3>
|
|
||||||
|
|
||||||
<p><img src="/img/console.png" alt="Batarang console screenshot" title="" /></p>
|
|
||||||
|
|
||||||
<p>The Batarang exposes some convenient features to the Chrome developer tools console. To access the scope of an element selected in the Elements tab of the developer tools, in console, you can type <code>$scope</code>. If you change value of some model on <code>$scope</code> and want to have this change reflected in the running application, you need to call <code>$scope.$apply()</code> after making the change.</p>
|
|
@ -1,13 +0,0 @@
|
|||||||
<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>
|
|
||||||
<pre>
|
|
||||||
<bat-model-tree val="tree" inspect="inspect" edit="edit"></bat-model-tree>
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
@ -1,30 +0,0 @@
|
|||||||
<div ng-controller="OptionsCtrl">
|
|
||||||
|
|
||||||
<div class="span6">
|
|
||||||
<h2>Options</h2>
|
|
||||||
<form class="well">
|
|
||||||
|
|
||||||
<label class="checkbox" for="app">
|
|
||||||
<input type="checkbox" ng-model="debugger.app" id="app"> Show applications
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="checkbox" for="bindings">
|
|
||||||
<input type="checkbox" ng-model="debugger.bindings" id="bindings"> Show bindings
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label class="checkbox" for="scopes">
|
|
||||||
<input type="checkbox" ng-model="debugger.scopes" id="scopes"> Show scopes
|
|
||||||
</label>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="span6">
|
|
||||||
<h2>Info</h2>
|
|
||||||
<div class="well">
|
|
||||||
<p>Angular version: {{version}}</p>
|
|
||||||
<p>Angular CDN status: <span class="label" ng-class="'label-' + status" ng-bind-html-unsafe="explain"></span></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
@ -1,48 +0,0 @@
|
|||||||
<div ng-controller="PerfCtrl">
|
|
||||||
|
|
||||||
<h2>Performance</h2>
|
|
||||||
<form class="well form-inline" class="row-fluid">
|
|
||||||
<label class="checkbox span4" for="log">
|
|
||||||
<input type="checkbox" ng-model="log" id="log"> Log to console
|
|
||||||
</label>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div class="row-fluid">
|
|
||||||
<div class="span6">
|
|
||||||
<h3>Watch Tree</h3>
|
|
||||||
<div class="well well-top" style="height: 400px; overflow-y: auto;">
|
|
||||||
<bat-watcher-tree val="tree" inspect="inspect"></bat-watcher-tree>
|
|
||||||
</div>
|
|
||||||
<div class="well well-bottom">
|
|
||||||
<label for="select-root" ng-hide="roots.length <= 1">Root
|
|
||||||
<select id="select-root" ng-options="p for p in roots" ng-model="selectedRoot"></select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="span6">
|
|
||||||
<h3>Watch Expressions</h3>
|
|
||||||
|
|
||||||
<div class="well well-top" style="height: 400px; overflow-y: auto;">
|
|
||||||
<div ng-repeat="watch in histogram|sortByTime:min:max">
|
|
||||||
<span style="font-family: monospace;">{{watch.name | first}} </span>
|
|
||||||
<span> | {{watch.percent}}% | {{watch.time | precision}}ms</span>
|
|
||||||
<div class="progress">
|
|
||||||
<div ng-style="{width: (watch.percent) + '%'}" class= "bar">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="well well-bottom">
|
|
||||||
<form class="form-inline">
|
|
||||||
<label>Filter expressions</label>
|
|
||||||
<bat-slider minimum="min" maximum="max"></bat-slider>
|
|
||||||
</form>
|
|
||||||
<button class="btn btn-success" ng-click="exportData()"><i class="icon-download-alt icon-white"></i> Save Data as JSON</button>
|
|
||||||
<button class="btn btn-danger" ng-click="clearHistogram()">Clear Data</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -0,0 +1,29 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Switch a dependency to git repo.
|
||||||
|
# Remove the NPM package and link it to a repo in parent directory.
|
||||||
|
DEP_NAME=$1
|
||||||
|
SCRIPT_DIR=$(dirname $0)
|
||||||
|
cd $SCRIPT_DIR/..
|
||||||
|
|
||||||
|
if [ -L ./node_modules/$DEP_NAME ]; then
|
||||||
|
echo "$DEP_NAME is already a symlink"
|
||||||
|
else
|
||||||
|
PKG_INFO=($($SCRIPT_DIR/read-pkg-url.js ./node_modules/$DEP_NAME/package.json))
|
||||||
|
URL=${PKG_INFO[0]}
|
||||||
|
DIR_NAME=${PKG_INFO[1]}
|
||||||
|
|
||||||
|
echo "Switching $DEP_NAME"
|
||||||
|
rm -rf ./node_modules/$DEP_NAME
|
||||||
|
|
||||||
|
if [ -d ../$DIR_NAME ]; then
|
||||||
|
echo "Repo already cloned in ../$DIR_NAME"
|
||||||
|
else
|
||||||
|
cd ..
|
||||||
|
git clone $URL $DIR_NAME
|
||||||
|
cd -
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Link ./node_modules/$DEP_NAME -> ../$DIR_NAME"
|
||||||
|
ln -s ../../$DIR_NAME ./node_modules/$DEP_NAME
|
||||||
|
fi
|
@ -0,0 +1,15 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Switch a dependency to NPM.
|
||||||
|
# Remove the symlink and install from NPM.
|
||||||
|
|
||||||
|
DEP_NAME=$1
|
||||||
|
SCRIPT_DIR=$(dirname $0)
|
||||||
|
cd $SCRIPT_DIR/..
|
||||||
|
|
||||||
|
if [ ! -L ./node_modules/$DEP_NAME ]; then
|
||||||
|
echo "$DEP_NAME is not a symlink"
|
||||||
|
else
|
||||||
|
rm ./node_modules/$DEP_NAME
|
||||||
|
npm install $DEP_NAME
|
||||||
|
fi
|
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
LOG_FILES=$LOGS_DIR/*
|
||||||
|
|
||||||
|
for FILE in $LOG_FILES; do
|
||||||
|
echo -e "\n\n\n"
|
||||||
|
echo "=================================================================="
|
||||||
|
echo " $FILE"
|
||||||
|
echo "=================================================================="
|
||||||
|
cat $FILE
|
||||||
|
done
|
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var fs = require('fs');
|
||||||
|
var pkg = JSON.parse(fs.readFileSync(process.argv[2]));
|
||||||
|
var url = pkg.repository.url;
|
||||||
|
var dirname = url.replace(/^.*\//, '').replace(/\.git$/, '');
|
||||||
|
|
||||||
|
console.log(url + ' ' + dirname);
|
@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Setup and start Sauce Connect for your TravisCI build
|
||||||
|
# This script requires your .travis.yml to include the following two private env variables:
|
||||||
|
# SAUCE_USERNAME
|
||||||
|
# SAUCE_ACCESS_KEY
|
||||||
|
# Follow the steps at https://saucelabs.com/opensource/travis to set that up.
|
||||||
|
#
|
||||||
|
# Curl and run this script as part of your .travis.yml before_script section:
|
||||||
|
# before_script:
|
||||||
|
# - curl https://gist.github.com/santiycr/5139565/raw/sauce_connect_setup.sh | bash
|
||||||
|
|
||||||
|
CONNECT_URL="https://saucelabs.com/downloads/sc-4.3-linux.tar.gz"
|
||||||
|
CONNECT_DIR="/tmp/sauce-connect-$RANDOM"
|
||||||
|
CONNECT_DOWNLOAD="sc-latest-linux.tar.gz"
|
||||||
|
|
||||||
|
CONNECT_LOG="$LOGS_DIR/sauce-connect"
|
||||||
|
CONNECT_STDOUT="$LOGS_DIR/sauce-connect.stdout"
|
||||||
|
CONNECT_STDERR="$LOGS_DIR/sauce-connect.stderr"
|
||||||
|
|
||||||
|
# Get Connect and start it
|
||||||
|
mkdir -p $CONNECT_DIR
|
||||||
|
cd $CONNECT_DIR
|
||||||
|
curl $CONNECT_URL -o $CONNECT_DOWNLOAD 2> /dev/null 1> /dev/null
|
||||||
|
mkdir sauce-connect
|
||||||
|
tar --extract --file=$CONNECT_DOWNLOAD --strip-components=1 --directory=sauce-connect > /dev/null
|
||||||
|
rm $CONNECT_DOWNLOAD
|
||||||
|
|
||||||
|
SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||||
|
|
||||||
|
|
||||||
|
ARGS=""
|
||||||
|
|
||||||
|
# Set tunnel-id only on Travis, to make local testing easier.
|
||||||
|
if [ ! -z "$TRAVIS_JOB_NUMBER" ]; then
|
||||||
|
ARGS="$ARGS --tunnel-identifier $TRAVIS_JOB_NUMBER"
|
||||||
|
fi
|
||||||
|
if [ ! -z "$BROWSER_PROVIDER_READY_FILE" ]; then
|
||||||
|
ARGS="$ARGS --readyfile $BROWSER_PROVIDER_READY_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "Starting Sauce Connect in the background, logging into:"
|
||||||
|
echo " $CONNECT_LOG"
|
||||||
|
echo " $CONNECT_STDOUT"
|
||||||
|
echo " $CONNECT_STDERR"
|
||||||
|
sauce-connect/bin/sc -u $SAUCE_USERNAME -k $SAUCE_ACCESS_KEY -v $ARGS \
|
||||||
|
--logfile $CONNECT_LOG 2> $CONNECT_STDERR 1> $CONNECT_STDOUT &
|
@ -0,0 +1,8 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
SCRIPT_DIR=$(dirname $0)
|
||||||
|
cd $SCRIPT_DIR/..
|
||||||
|
|
||||||
|
SAUCE_ACCESS_KEY=`echo $SAUCE_ACCESS_KEY | rev`
|
||||||
|
|
||||||
|
gulp
|
||||||
|
karma start --sauce
|