├── breach.png
├── breach.icns
├── .gitignore
├── controls
├── assets
│ ├── hero.jpg
│ ├── breach.png
│ ├── favicon.png
│ ├── fonts
│ │ ├── Roboto-Bold.ttf
│ │ ├── Roboto-Thin.ttf
│ │ ├── Roboto-Black.ttf
│ │ ├── Roboto-Italic.ttf
│ │ ├── Roboto-Light.ttf
│ │ ├── Roboto-Medium.ttf
│ │ ├── Roboto-Regular.ttf
│ │ ├── Roboto-BlackItalic.ttf
│ │ ├── Roboto-BoldItalic.ttf
│ │ ├── Roboto-LightItalic.ttf
│ │ ├── Roboto-MediumItalic.ttf
│ │ ├── Roboto-ThinItalic.ttf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.ttf
│ │ ├── RobotoCondensed-Bold.ttf
│ │ ├── RobotoCondensed-Light.ttf
│ │ ├── fontawesome-webfont.woff
│ │ ├── RobotoCondensed-Italic.ttf
│ │ ├── RobotoCondensed-Regular.ttf
│ │ ├── RobotoCondensed-BoldItalic.ttf
│ │ ├── RobotoCondensed-LightItalic.ttf
│ │ └── LICENSE.txt
│ ├── onboarding-step0.png
│ ├── onboarding-step2.png
│ ├── onboarding-step3-box.png
│ ├── onboarding-step3-tabs.png
│ └── onboarding-step4-rightclick.png
├── css
│ └── main.css
├── lib
│ ├── nprogress.css
│ ├── angular-route-1.3.0-beta.13.min.js
│ ├── nprogress.js
│ ├── normalize-2.1.2.css
│ └── async-0.9.0.min.js
├── modules
│ ├── partials
│ │ ├── out.html
│ │ └── modules.html
│ ├── css
│ │ ├── out.css
│ │ └── modules.css
│ ├── index.html
│ └── js
│ │ ├── app.js
│ │ ├── out_c.js
│ │ └── modules_c.js
├── splash
│ ├── partials
│ │ ├── splash.html
│ │ └── onboarding.html
│ ├── css
│ │ ├── splash.css
│ │ └── onboarding.css
│ ├── js
│ │ ├── app.js
│ │ ├── splash_c.js
│ │ └── onboarding_c.js
│ └── index.html
└── js
│ ├── bind_s.js
│ ├── filters.js
│ ├── socket_s.js
│ ├── modules_s.js
│ └── req_s.js
├── dist
├── linux_wrapper.sh
├── darwin_restart.sh
├── LINUX_README.md
├── linux.js
└── darwin.js
├── Makefile
├── module
├── index.js
├── package.json
└── lib
│ ├── common.js
│ └── module.js
├── LICENSE
├── ReadMe.md
├── package.json
├── NOTES
├── index.js
├── openpgp.store
└── openpgp-public-keys
└── lib
├── session.js
├── core_store.js
├── common.js
├── session_manager.js
├── core_ui.js
└── core_controls.js
/breach.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/breach.png
--------------------------------------------------------------------------------
/breach.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/breach.icns
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .*.swp
3 | *~
4 | npm-debug.log
5 | out
6 | .DS_Store
7 |
--------------------------------------------------------------------------------
/controls/assets/hero.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/hero.jpg
--------------------------------------------------------------------------------
/controls/assets/breach.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/breach.png
--------------------------------------------------------------------------------
/controls/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/favicon.png
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Bold.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Thin.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Thin.ttf
--------------------------------------------------------------------------------
/controls/assets/onboarding-step0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/onboarding-step0.png
--------------------------------------------------------------------------------
/controls/assets/onboarding-step2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/onboarding-step2.png
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Black.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Black.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Italic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Medium.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-Regular.ttf
--------------------------------------------------------------------------------
/controls/assets/onboarding-step3-box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/onboarding-step3-box.png
--------------------------------------------------------------------------------
/controls/assets/onboarding-step3-tabs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/onboarding-step3-tabs.png
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-BlackItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-BlackItalic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-BoldItalic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-LightItalic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-MediumItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-MediumItalic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/Roboto-ThinItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/Roboto-ThinItalic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/controls/assets/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/RobotoCondensed-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/RobotoCondensed-Bold.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/RobotoCondensed-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/RobotoCondensed-Light.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/controls/assets/onboarding-step4-rightclick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/onboarding-step4-rightclick.png
--------------------------------------------------------------------------------
/controls/assets/fonts/RobotoCondensed-Italic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/RobotoCondensed-Italic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/RobotoCondensed-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/RobotoCondensed-Regular.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/RobotoCondensed-BoldItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/RobotoCondensed-BoldItalic.ttf
--------------------------------------------------------------------------------
/controls/assets/fonts/RobotoCondensed-LightItalic.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spolu/breach_core/HEAD/controls/assets/fonts/RobotoCondensed-LightItalic.ttf
--------------------------------------------------------------------------------
/dist/linux_wrapper.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | export SRC_DIR=$(cd "$(dirname "$0")"; pwd)
4 |
5 | $SRC_DIR/__AUTO_UPDATE_BUNDLE__/exo_browser/exo_browser \
6 | --raw $SRC_DIR/__AUTO_UPDATE_BUNDLE__/breach_core
7 |
--------------------------------------------------------------------------------
/controls/css/main.css:
--------------------------------------------------------------------------------
1 | html {
2 | margin-top: 0px;
3 | font: 12px 300 "Roboto", Arial;
4 | color: hsl(72, 4%, 90%);
5 | height: 100%;
6 | -webkit-font-smoothing: antialiased;
7 | }
8 |
9 | body {
10 | background-color: hsl(72, 4%, 24%);
11 | height: 100%;
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/controls/lib/nprogress.css:
--------------------------------------------------------------------------------
1 | /* Make clicks pass-through */
2 | #nprogress {
3 | pointer-events: none;
4 | }
5 |
6 | #nprogress .bar {
7 | background: #29d;
8 |
9 | position: fixed;
10 | z-index: 100;
11 | bottom: 0;
12 | left: 0;
13 |
14 | width: 100%;
15 | height: 2px;
16 | }
17 |
18 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | EXO_BROWSER="__DUMMY__"
2 | ARCH="x64"
3 |
4 | clean:
5 | rm -rf node_modules
6 | rm -rf out
7 |
8 | install: clean
9 | npm install
10 |
11 | dist_linux: install
12 | node dist/linux.js $(ARCH) $(EXO_BROWSER)
13 | dist_darwin: install
14 | node dist/darwin.js $(ARCH) $(EXO_BROWSER)
15 |
16 |
17 | .PHONY: clean install dist_linux dist_darwin
18 |
--------------------------------------------------------------------------------
/controls/modules/partials/out.html:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/controls/splash/partials/splash.html:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/controls/js/bind_s.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: bind_s.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2013-08-12 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | angular.module('breach.services').
14 | factory('_bind', function() {
15 | return function(host, attr, promise) {
16 | promise.then(function(data) {
17 | host[attr] = data;
18 | });
19 | };
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/module/index.js:
--------------------------------------------------------------------------------
1 | var common = require('./lib/common.js');
2 |
3 | module.exports = require('./lib/module.js').module({});
4 | module.exports.common = common;
5 |
6 | // SAFETY NET (kills the process and the spawns)
7 | process.on('uncaughtException', function (err) {
8 | common.fatal(err);
9 | });
10 |
11 | var sig_handler = function() {
12 | common.exit(0);
13 | };
14 | process.on('SIGHUP', sig_handler);
15 | process.on('SIGINT', sig_handler);
16 | process.on('SIGQUIT', sig_handler);
17 | process.on('SIGABRT', sig_handler);
18 | process.on('SIGTERM', sig_handler);
19 |
20 |
--------------------------------------------------------------------------------
/module/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "breach_module",
3 | "description": "Breach Module Library",
4 | "author": {
5 | "name": "Stanislas Polu",
6 | "email": "polu.stanislas@gmail.com",
7 | "url": "http://github.com/spolu"
8 | },
9 | "version": "0.3.22-alpha.6",
10 | "dependencies": {
11 | },
12 | "repository" : {
13 | "type" : "git",
14 | "url" : "http://github.com/breach/breach_core.git"
15 | },
16 | "bugs": {
17 | "url": "https://github.com/breach/breach_core/issues"
18 | },
19 | "licenses": [
20 | {
21 | "type": "MIT",
22 | "url": "https://github.com/breach/breach_core/raw/master/LICENSE"
23 | }
24 | ],
25 | "main": "./index.js",
26 | "engines": {
27 | "exo_browser": "0.6.x"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/controls/modules/css/out.css:
--------------------------------------------------------------------------------
1 | .out {
2 | position: absolute;
3 | background-color: black;
4 | left: 0px;
5 | right: 0px;
6 | top: 0px;
7 | bottom: 0px;
8 | }
9 |
10 | .out .header {
11 | position: absolute;
12 | background-color: hsl(0, 0%, 20%);
13 | color: white;
14 | top: 0px;
15 | left: 0px;
16 | right: 0px;
17 | padding: 10px;
18 | height: 15px;
19 | }
20 |
21 | .out .header a {
22 | color: hsl(198, 75%, 55%);
23 | text-decoration: none;
24 | }
25 |
26 | .out .header .module {
27 | font-weight: bold;
28 | }
29 |
30 | .out .header .file {
31 | font-style: italic;
32 | }
33 |
34 | .out .wrapper {
35 | position: absolute;
36 | left: 0px;
37 | right: 0px;
38 | top: 35px;
39 | bottom: 0px;
40 | overflow-y: scroll;
41 | }
42 |
43 | .out .wrapper pre {
44 | color: hsl(200, 50%, 90%);
45 | }
46 |
--------------------------------------------------------------------------------
/controls/js/filters.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: filters_d.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2013-08-14 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | // ### truncate
14 | // ```
15 | // @text {string} text to truncate
16 | // @length {number} max length [optional, default: 10]
17 | // @end {string} end string [optional, default: '...']
18 | // ```
19 | angular.module('breach.filters').
20 | filter('truncate', function() {
21 | return function (text, length, end) {
22 | if (isNaN(length))
23 | length = 10;
24 | if (end === undefined)
25 | end = "...";
26 | if (text.length <= length) {
27 | return text;
28 | }
29 | else {
30 | return String(text).substring(0, length-end.length) + end;
31 | }
32 | };
33 | });
34 |
35 |
--------------------------------------------------------------------------------
/controls/js/socket_s.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: socket_s.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-26 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | //
14 | // ## Socket IO service
15 | //
16 | angular.module('breach.services').
17 | factory('_socket', function($rootScope) {
18 | var socket = io.connect();
19 | return {
20 | on: function (eventName, callback) {
21 | socket.on(eventName, function () {
22 | var args = arguments;
23 | $rootScope.$apply(function () {
24 | callback.apply(socket, args);
25 | });
26 | });
27 | },
28 | emit: function (eventName, data, callback) {
29 | socket.emit(eventName, data, function () {
30 | var args = arguments;
31 | $rootScope.$apply(function () {
32 | if (callback) {
33 | callback.apply(socket, args);
34 | }
35 | });
36 | })
37 | }
38 | };
39 | });
40 |
--------------------------------------------------------------------------------
/controls/splash/css/splash.css:
--------------------------------------------------------------------------------
1 | .splash {
2 | opacity: 0;
3 | transition: opacity 1s;
4 | }
5 |
6 | .splash .modules {
7 | position:absolute;
8 | bottom: 15px;
9 | width: 100%;
10 | text-align: right;
11 | font-family: "Courier New", Courier, monospace;
12 | }
13 |
14 | .splash .modules a {
15 | display: block;
16 | color: white;
17 | text-decoration: none;
18 | margin-right: 20px;
19 | opacity: 0.5;
20 | }
21 |
22 | .splash .modules a:hover {
23 | opacity: 1;
24 | }
25 |
26 | .splash .wrapper {
27 | position: absolute;
28 | left: 0px;
29 | right: 0px;
30 | text-align: center;
31 | top: 43%;
32 | }
33 |
34 | .splash .wrapper .logo {
35 | background-size: cover;
36 | background-image: url('../../assets/breach.png');
37 | display:inline-block;
38 | width: 50px;
39 | height: 50px;
40 | opacity: 0.8;
41 | }
42 |
43 | .splash .wrapper .logo:hover {
44 | opacity: 1;
45 | }
46 |
47 | .splash .meta {
48 | position:absolute;
49 | bottom: 15px;
50 | left: 20px;
51 | }
52 |
53 | .splash .meta a {
54 | color: white;
55 | text-decoration: none;
56 | opacity: 0.5;
57 | }
58 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Stanislas Polu
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | ### Breach: A Browser for the HTML5 Era
2 |
3 | **Modular**
4 | Everything in the browser is a module, a web-app running in its own process.
5 | Construct your own browsing experience by selecting the right modules for you.
6 |
7 | **Hackable**
8 | Want vertical tabs? Write some JS & CSS! Customised autocomplete engine? JS!
9 | Every behavior is programmatic and exposed through APIs.
10 |
11 | **Open source**
12 | The entire technology stack is open source.
13 | Modify existing modules and you can create your own to extend the behavior of Breach.
14 |
15 |
16 | ### Getting Involved
17 |
18 | - Homepage: [http://breach.cc](http://breach.cc)
19 | - Mailing list: [breach-dev@googlegroups.com](https://groups.google.com/d/forum/breach-dev)
20 | - IRC Channel: #breach on Freenode
21 |
22 | You can find a list of Modules available or under developement here: [List of Modules](https://github.com/breach/breach_core/wiki/List-of-modules)
23 |
24 | ### Runing Breach on Linux
25 |
26 | See instructions here: [Running Breach on Linux](https://github.com/breach/breach_core/wiki/Running-Breach-on-Linux)
27 |
28 | ### Support the fun
29 |
30 |
31 | DogeCoin: `D5yw7dP4XqE2vEDVwwRxyH6VtuTdCHEwGP`
32 |
--------------------------------------------------------------------------------
/controls/js/modules_s.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: modules_s.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-17 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | //
14 | // ## Module list & commands
15 | //
16 | angular.module('breach.services').
17 | factory('_modules', function(_req, _bind) {
18 | var _modules = {
19 | list: function() {
20 | return _req.get('/modules/list');
21 | },
22 | add: function(path) {
23 | return _req.post('/modules/add', { path: path });
24 | },
25 | install: function(path) {
26 | return _req.post('/modules/install', { path: path });
27 | },
28 | remove: function(path) {
29 | return _req.post('/modules/remove', { path: path });
30 | },
31 | update: function(path) {
32 | return _req.post('/modules/update', { path: path });
33 | },
34 | run: function(path) {
35 | return _req.post('/modules/run', { path: path });
36 | },
37 | kill: function(path) {
38 | return _req.post('/modules/kill', { path: path });
39 | }
40 | };
41 |
42 | return _modules;
43 | });
44 |
45 |
--------------------------------------------------------------------------------
/controls/splash/js/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [splash] app.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-19 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | //
14 | // ## App Module
15 | //
16 | angular.module('breach', ['ngRoute',
17 | 'breach.services',
18 | 'breach.directives',
19 | 'breach.filters']).
20 | config(['$routeProvider', function($routeProvider) {
21 | $routeProvider.
22 | when('/',
23 | { templateUrl: 'partials/splash.html',
24 | controller: SplashCtrl }).
25 | when('/onboarding',
26 | { templateUrl: 'partials/onboarding.html',
27 | controller: OnBoardingCtrl }).
28 | otherwise({ redirectTo: '/' });
29 | }]);
30 |
31 | angular.module('breach.directives', []);
32 | angular.module('breach.filters', []);
33 | angular.module('breach.services', []);
34 |
35 |
36 | //
37 | // ### TopCtrl
38 | // Initializations goes here as well as global objects
39 | //
40 | function TopCtrl($scope, $location, $rootScope, $window, $timeout, $filter,
41 | _bind) {
42 | };
43 |
44 |
--------------------------------------------------------------------------------
/dist/darwin_restart.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # this bash wrapper is used by the auto-udpate mechanism to restart the entire
4 | # process after the update was installed. It makes sure that:
5 | # - we wait for the older process to shutdown entirely
6 | # - we disable quarantine of the newly downloaded package
7 |
8 | destination=$1
9 |
10 | # Wait until all processes from within the bundle are closed
11 | echo -n "Waiting for bundled processes to close..."
12 | while [ $(
13 | # List all processes, filtering out this process
14 | processes=$(echo "$(ps ax)" | grep -v "$0")
15 |
16 | # Escape the destination into a regexp that matches it
17 | regexp=$(echo "$destination" | sed 's/[^[:alnum:]_-]/\\&/g')
18 |
19 | # Filters entries matching the regexp, and do some magic to preserve the trailing newline
20 | matches=$(echo "$processes" | awk "/$regexp/ { print \$1 }"; echo .)
21 | matches=${matches%.}
22 |
23 | # Count matches
24 | printf "%s" "$matches" | wc -l
25 | ) -gt 0 ]
26 | do
27 | echo -n .
28 | sleep 1
29 | done
30 | echo
31 |
32 | echo "Make sure destination is not quarantined..."
33 | xattr -d com.apple.quarantine "$destination"
34 |
35 | # (Re)launch the destination bundle
36 | echo "Relaunching bundle..."
37 | open "$destination"
38 |
39 | echo "Done."
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "breach_core",
3 | "description": "Breach Core Module",
4 | "author": {
5 | "name": "Stanislas Polu",
6 | "email": "polu.stanislas@gmail.com",
7 | "url": "http://github.com/spolu"
8 | },
9 | "version": "0.3.22-alpha.6",
10 | "dependencies": {
11 | "exo_browser": "0.6.x",
12 | "gig.fs": "0.2.x",
13 | "async": "0.9.x",
14 | "mkdirp": "0.3.x",
15 | "octonode": "0.6.x",
16 | "fs-extra": "0.8.x",
17 | "npm": "1.4.x",
18 | "semver": "2.2.x",
19 | "tar": "0.1.x",
20 | "request": "2.36.x",
21 | "express": "4.0.x",
22 | "body-parser": "1.0.x",
23 | "method-override": "1.0.x",
24 | "socket.io": "1.0.x",
25 | "openpgp": "0.6.x",
26 | "tail": "0.3.x"
27 | },
28 | "repository" : {
29 | "type" : "git",
30 | "url" : "http://github.com/breach/breach_core.git"
31 | },
32 | "bugs": {
33 | "url": "https://github.com/breach/breach_core/issues"
34 | },
35 | "licenses": [
36 | {
37 | "type": "MIT",
38 | "url": "https://github.com/breach/breach_core/raw/master/LICENSE"
39 | }
40 | ],
41 | "main": "./index.js",
42 | "engines": {
43 | "exo_browser": "0.6.x"
44 | },
45 | "auto_update": {
46 | "url": "https://data.breach.cc/update"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/controls/modules/css/modules.css:
--------------------------------------------------------------------------------
1 | .modules {
2 | text-align: center;
3 | }
4 |
5 | .modules a {
6 | color: hsl(198, 65%, 25%);
7 | }
8 |
9 | .modules > .container {
10 | display:inline-block;
11 | text-align: left;
12 | margin-top: 100px;
13 | }
14 |
15 | .modules table {
16 | margin-top: 20px;
17 | color: black;
18 | width: 100%;
19 | background-color: hsl(72, 4%, 24%);
20 | }
21 |
22 | .modules table tr.separator {
23 | }
24 | .modules table tr.separator td {
25 | background-color: hsl(72, 4%, 24%);
26 | }
27 |
28 | .modules table th {
29 | background-color: hsl(72, 4%, 42%);
30 | border-bottom: 3px solid hsl(0, 0%, 85%);
31 | height: 25px;
32 | padding-left: 5px;
33 | line-height: 25px;
34 | }
35 |
36 | .modules table td {
37 | height: 25px;
38 | padding-left: 5px;
39 | padding-right: 5px;
40 | background-color: hsl(0, 0%, 97%);
41 | }
42 |
43 | .modules table td.green {
44 | color: hsl(100, 80%, 40%);
45 | }
46 | .modules table td.orange {
47 | color: orange;
48 | }
49 | .modules table td.orange a {
50 | color: orange;
51 | }
52 | .modules table td.red {
53 | color: hsl(0, 80%, 40%);
54 | }
55 |
56 | .modules table input.text {
57 | width: 100%;
58 | }
59 |
60 | .modules .footer {
61 | margin-top: 20px;
62 | }
63 |
64 | .modules .footer .path {
65 | color: hsl(72, 4%, 60%);
66 | }
67 |
68 | .modules .footer .onboarding {
69 | float: right;
70 | color: hsl(198, 75%, 35%);
71 | text-decoration: none;
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/controls/modules/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Breach::Modules
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/controls/splash/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{title}}
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/controls/js/req_s.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: bind_s.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2013-08-12 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | angular.module('breach.services').
14 | factory('_req', function($http, $q, $rootScope) {
15 |
16 | function go(httpPromise) {
17 | $rootScope.$broadcast('loading', true);
18 | var d = $q.defer();
19 | httpPromise
20 | .success(function(data, status, headers, config) {
21 | $rootScope.$broadcast('loading', false);
22 | d.resolve(data);
23 | })
24 | .error(function(data, status, headers, config) {
25 | $rootScope.$broadcast('loading', false);
26 | if(!data || !data.error) {
27 | $rootScope.$broadcast('error', data);
28 | return d.reject(data);
29 | }
30 | else {
31 | var error = new Error(data.error.message);
32 | error.name = data.error.name;
33 | $rootScope.$broadcast('error', data.error);
34 | return d.reject(data.error.message);
35 | }
36 | });
37 | return d.promise;
38 | };
39 |
40 | return {
41 | 'get': function(url, config) {
42 | return go($http.get(url, config));
43 | },
44 | 'post': function(url, data, config) {
45 | return go($http.post(url, data, config));
46 | },
47 | 'put': function(url, data, config) {
48 | return go($http.put(url, data, config));
49 | },
50 | 'del': function(url, config) {
51 | return go($http.delete(url, config));
52 | }
53 | };
54 | });
55 |
--------------------------------------------------------------------------------
/controls/splash/js/splash_c.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [splash] splash_c.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-19 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | //
14 | // ### SplashCtrl
15 | // Controller to manage the splash screen
16 | //
17 | function SplashCtrl($scope, $location, $rootScope, $window, $timeout, $sce,
18 | _bind, _req, _socket) {
19 |
20 | /****************************************************************************/
21 | /* INITIALIZATION */
22 | /****************************************************************************/
23 | /* Handhsaking [modules] */
24 | _socket.emit('handshake', 'splash');
25 | _socket.emit('handshake', 'modules');
26 |
27 | /* Handhsaking [splash] */
28 | _socket.on('splash', function(state) {
29 | //console.log('========================================');
30 | //console.log(JSON.stringify(state, null, 2));
31 | //console.log('----------------------------------------');
32 | $scope.splash = state;
33 | });
34 |
35 | _socket.on('modules', function(state) {
36 | //console.log('========================================');
37 | //console.log(JSON.stringify(state, null, 2));
38 | //console.log('----------------------------------------');
39 | $scope.modules = state;
40 | if($scope.modules.length === 0) {
41 | $location.path("/onboarding");
42 | }
43 | else {
44 | $('.splash').css({ opacity: 1 });
45 | }
46 | });
47 |
48 | };
49 |
50 |
--------------------------------------------------------------------------------
/controls/modules/js/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [modules] app.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-17 spolu Modules/Out/Config routing
10 | * - 2014-06-06 spolu Enhanced modules output
11 | * - 2014-06-02 spolu Fix install/restart action
12 | * - 2014-05-29 spolu Support for modules output
13 | * - 2014-05-23 spolu Use socket.io
14 | * - 2014-04-17 spolu Creation
15 | */
16 | 'use strict';
17 |
18 | //
19 | // ## App Module
20 | //
21 | angular.module('breach', ['ngRoute',
22 | 'breach.services',
23 | 'breach.directives',
24 | 'breach.filters']).
25 | config(['$routeProvider', function($routeProvider) {
26 | $routeProvider.
27 | when('/',
28 | { templateUrl: 'partials/modules.html',
29 | controller: ModulesCtrl }).
30 | when('/out/:name',
31 | { templateUrl: '/modules/partials/out.html',
32 | controller: OutCtrl }).
33 | /*
34 | when('/:name/config',
35 | { templateUrl: '/modules/partials/config.html',
36 | controller: ConfigCtrl }).
37 | */
38 | otherwise({ redirectTo: '/' });
39 | }]);
40 |
41 | angular.module('breach.directives', []);
42 | angular.module('breach.filters', []);
43 | angular.module('breach.services', []);
44 |
45 |
46 | //
47 | // ### TopCtrl
48 | // Initializations goes here as well as global objects
49 | //
50 | function TopCtrl($scope, $location, $rootScope, $window, $timeout, $filter,
51 | _bind) {
52 | };
53 |
54 |
--------------------------------------------------------------------------------
/controls/modules/js/out_c.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [module] out_c.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-07-03 spolu Scroll to bottom + cleaer on restart #49
10 | * - 2014-07-02 spolu Fix mixed module out #48
11 | * - 2014-06-17 spolu Creation
12 | */
13 | 'use strict';
14 |
15 | //
16 | // ## App Module
17 | //
18 | angular.module('breach', ['breach.services',
19 | 'breach.directives',
20 | 'breach.filters']);
21 |
22 | //
23 | // ### OutCtrl
24 | // Controller to manage modules display
25 | //
26 | function OutCtrl($scope, $location, $rootScope, $window, $timeout, $routeParams,
27 | _bind, _modules, _req, _socket) {
28 |
29 | /****************************************************************************/
30 | /* INITIALIZATION */
31 | /****************************************************************************/
32 | /* Handhsaking */
33 | _socket.emit('tail', $routeParams.name);
34 |
35 | _socket.on('module', function(module) {
36 | $scope.module = module;
37 | $window.document.title = 'out::' + module.name;
38 | });
39 |
40 | $scope.data = '';
41 | _socket.on('chunk', function(chunk) {
42 | /* The same socket may receive events from multiple modules. */
43 | if($scope.module.name === chunk.module) {
44 | $scope.data += chunk.data;
45 | }
46 | /* Basically the socket stays open. TODO(spolu): FixMe */
47 | if($timeout && $('.wrapper') && $('.wrapper')[0]) {
48 | $timeout(function() {
49 | $('.wrapper').scrollTop($('.wrapper')[0].scrollHeight);
50 | });
51 | }
52 | });
53 |
54 | /****************************************************************************/
55 | /* COMMANDS */
56 | /****************************************************************************/
57 |
58 | $scope.modules_restart = function(path) {
59 | /* On restart we clear the log buffer. */
60 | $scope.data = '';
61 | async.series([
62 | function(cb_) {
63 | _modules.kill(path).then(function(data) {
64 | return cb_();
65 | });
66 | },
67 | function(cb_) {
68 | _modules.run(path).then(function(data) {
69 | return cb_();
70 | });
71 | },
72 | ], function(err) {
73 | });
74 | };
75 | };
76 |
77 |
--------------------------------------------------------------------------------
/dist/LINUX_README.md:
--------------------------------------------------------------------------------
1 | ## Breach README
2 |
3 | Breach currently requires some coaxing to run under most linux
4 | distributions. There are two known issues that you may run into --
5 | both occur immediately after running breach like so:
6 |
7 | ```
8 | ./breach
9 | ```
10 |
11 | Solutions to the issues are outlined below.
12 |
13 | ### `libudev.so.0` problem
14 |
15 | `libudev0` has been removed from many recent distributions, which
16 | stops Breach from running out of the box. One fix is to make a
17 | symbolic link pointing to your libudev.so.1 file:
18 |
19 | ```
20 | sudo ln -sf /lib/$(arch)-linux-gnu/libudev.so.1 /lib/$(arch)-linux-gnu/libudev.so.0
21 | ```
22 |
23 | A more dangerous fix is to replace the reference to libudev.so.0 in the
24 | exo_browser executable with a reference to libudev.so.1. You can
25 | do that with the following sed command (it's safest to back up the file first):
26 |
27 | ```
28 | sed -i 's/udev\.so\.0/udev.so.1/g' __AUTO_UPDATE_BUNDLE__/exo_browser/exo_browser
29 | ```
30 |
31 | If you need more information, see these [solutions for missing libudev.so.0](https://github.com/rogerwang/node-webkit/wiki/The-solution-of-lacking-libudev.so.0).
32 |
33 | ### SUID Sandbox problem
34 |
35 | Breach is based on the Chromium content module which requires a
36 | SUID sandbox process to chroot the renderer processes on linux.
37 | Running Breach out of the box on linux will probably result in
38 | the following error:
39 |
40 | ```
41 | [4590:4590:0721/143932:27331338145:FATAL:browser_main_loop.cc(172)]
42 | Running without the SUID sandbox!
43 | See https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment
44 | for more information on developing with the sandbox on.
45 | Aborted (core dumped)
46 | ```
47 |
48 | To properly handle the access to the sandbox, you need to either
49 | point Breach to an existing `chrome-sandbox`, or install one and
50 | point there.
51 |
52 | **Pointing Breach to an existing `chrome-sandbox`** If you have
53 | chrome installed, you can point Breach to the SUID sandbox
54 | you've already got. The path could be a number of things, but
55 | you probably want one of the two following environment variable
56 | settings:
57 | `export CHROME_DEVEL_SANDBOX=/opt/google/chrome/chrome-sandbox`, or
58 | `export CHROME_DEVEL_SANDBOX=/usr/local/sbin/chrome-devel-sandbox`
59 |
60 | **Build your own `chrome-devel-sandbox`** The instructions provided
61 | in the [LinuxSUIDSandboxDevelopment](https://code.google.com/p/chromium/wiki/LinuxSUIDSandboxDevelopment)
62 | chromium dev page will tell you how to build the sandbox, which you
63 | can then point to. You will have to have the chromium source code
64 | checked out and ready to build.
65 |
--------------------------------------------------------------------------------
/NOTES:
--------------------------------------------------------------------------------
1 | /******************************************************************************/
2 | /* TODO */
3 | /******************************************************************************/
4 |
5 | >>>v0.x<<<
6 |
7 | TODO(spolu): Session Management (when not logged)
8 |
9 | TODO(spolu): Onboarding (Networked, no module yet)
10 | TODO(spolu): InMemory Sessions (default modules?)
11 | TODO(spolu): Two phase initialization for modules (`init`, then `start`)
12 | TODO(spolu): Session Management API (to expose to modules)
13 |
14 |
15 | DONE:
16 |
17 | >>v0.3<<
18 | - GiG.fs 0.3x integration / local table only (alpha)
19 | - Basic Web Interface for Modules (Install, Remove)
20 | - Splash Screen (loading state + link to module manager)
21 | - `core_ui` for all things UI-related of `core` (serving pages)
22 | - Integrate GiG.fs w/ `module_manager`
23 | - Build Session Management on top of GiG.fs
24 |
25 | >>v0.2<<
26 | - Expose core API and Implement Stack as a module
27 | - FMA
28 | + Core RPC-CALL Broadcast
29 | + Name-based communication for modules
30 | + Module management API
31 | + Module retrieval (local)
32 | + Add running status to module list
33 | + Command Line interface for module Management (Dev)
34 | - Remove factory -> common
35 | - ExoBrowser: Run nodeJS only
36 | - ExoBrowser: Merge 1700 and fix DevTools
37 | - Rename repo into `breach_core`
38 |
39 | >>>v0.1.4<<
40 | - Connect to free port (multiple instances)
41 | - Find in Page feedback Impl
42 |
43 | >>v0.1<<
44 | - Sessions
45 | - Basic local CookieStore
46 | - :exit
47 | - NProgress
48 | - Keyboard shortcuts MacOSX
49 | - More conventional Keyboard shortcuts
50 | - Design (flatter + lighter)
51 | - Box get emptied while pages are loading (#3)
52 |
53 |
54 | /******************************************************************************/
55 | /* MODULES WORK */
56 | /******************************************************************************/
57 |
58 | TODO(spolu): Authentication
59 | TODO(spolu): Distributed Storage
60 | TODO(spolu): Inspector ?
61 | TODO(spolu): Flash ?
62 |
63 | /******************************************************************************/
64 | /* FMA MODULE DEFINITION */
65 | /******************************************************************************/
66 |
67 | A module is identified by its location. Module may be:
68 | - local: `local:~/src/breach/mod_stack`
69 | - retrieved from github: `github:breach/mod_test#v0.5.2`
70 |
71 | The branch part of github modules is optional and is automatically replaced
72 | by latest version or #master if not specified.
73 |
74 | breach --module install github:breach/mod_test#v0.2.1
75 | breach --module remove github:breach/mod_test
76 | breach --module list
77 | breach --module info github.com/breach/mod_test
78 |
79 | Then modules are referenced by names. Two modules with the same name cannot
80 | be installed at the same time.
81 |
82 |
83 | /******************************************************************************/
84 | /* NOTES(spolu) */
85 | /******************************************************************************/
86 |
87 | - Hooks in startup process?
88 | - Hooks in cookie storage?
89 | - Module should advertise their interfaces?
90 | - How does module collaborate to provide an interface?
91 | - Existing examples?
92 |
93 | - advertise `id`
94 | - rpc_call + reconciliation
95 |
96 | CORE_MODULE
97 | > maintains a list of frames for a given browser window
98 | > has a notion of identity
99 | > exposes distributed storage
100 |
101 |
--------------------------------------------------------------------------------
/controls/modules/js/modules_c.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [modules] modules_c.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-17 spolu Creation
10 | */
11 | 'use strict';
12 |
13 | //
14 | // ### ModulesCtrl
15 | // Controller to manage modules display
16 | //
17 | function ModulesCtrl($scope, $location, $rootScope, $window, $timeout, $sce,
18 | _bind, _modules, _req, _socket) {
19 |
20 | /****************************************************************************/
21 | /* INITIALIZATION */
22 | /****************************************************************************/
23 | /* Handhsaking [modules] */
24 | _socket.on('modules', function(state) {
25 | //console.log('========================================');
26 | //console.log(JSON.stringify(state, null, 2));
27 | //console.log('----------------------------------------');
28 | $scope.modules = state;
29 | });
30 |
31 | /* Handhsaking [about] */
32 | _socket.on('about', function(state) {
33 | //console.log('========================================');
34 | //console.log(JSON.stringify(state, null, 2));
35 | //console.log('----------------------------------------');
36 | $scope.about = state;
37 | });
38 |
39 | _socket.emit('handshake', 'modules');
40 | _socket.emit('handshake', 'about');
41 |
42 | $window.document.title = 'Breach::Modules';
43 |
44 | /****************************************************************************/
45 | /* COMMANDS */
46 | /****************************************************************************/
47 | $scope.modules_install = function() {
48 | var path = $scope.install_path;
49 | $scope.install_path = '';
50 | async.waterfall([
51 | function(cb_) {
52 | _modules.add(path).then(function(data) {
53 | return cb_(null, data.module);
54 | });
55 | },
56 | function(module, cb_) {
57 | _modules.install(module.path).then(function(data) {
58 | return cb_(null, data.module);
59 | });
60 | },
61 | function(module, cb_) {
62 | _modules.run(module.path).then(function(data) {
63 | return cb_(null, data.module);
64 | });
65 | }
66 | ], function(err) {
67 | });
68 | };
69 |
70 | $scope.modules_remove = function(path) {
71 | _modules.remove(path).then(function(data) {
72 | });
73 | };
74 |
75 | $scope.modules_update = function(path) {
76 | _modules.update(path).then(function(data) {
77 | });
78 | };
79 |
80 | $scope.modules_kill = function(path) {
81 | _modules.kill(path).then(function(data) {
82 | });
83 | };
84 | $scope.modules_run = function(path) {
85 | _modules.run(path).then(function(data) {
86 | });
87 | };
88 |
89 | $scope.modules_restart = function(path) {
90 | async.series([
91 | function(cb_) {
92 | _modules.kill(path).then(function(data) {
93 | return cb_();
94 | });
95 | },
96 | function(cb_) {
97 | _modules.run(path).then(function(data) {
98 | return cb_();
99 | });
100 | },
101 | ], function(err) {
102 | });
103 | };
104 |
105 | $scope.about_install = function() {
106 | _req.post('/about/install', {}).then(function(data) {
107 | });
108 | };
109 | };
110 |
111 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: index.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-14 spolu [GiG.fs 0.2.x] local only
10 | * - 2014-04-17 spolu Removed monolithic session
11 | * - 2014-04-15 spolu Try outs with session_manager
12 | * - 2014-01-17 spolu Removed express, exposed module
13 | * - 2013-11-14 spolu FMA refactoring
14 | * - 2013-09-06 spolu Exp1 process.exit on session kill
15 | * - 2013-08-11 spolu Creation
16 | */
17 | "use strict"
18 |
19 | var common = require('./lib/common.js');
20 | var async = require('async');
21 |
22 | /******************************************************************************/
23 | /* HELPERS */
24 | /******************************************************************************/
25 |
26 | /* This is a hack. But process.nextTick can sleep if there is no event on the */
27 | /* content API part so we replace it here by a setTimeout that will be called */
28 | /* TODO(spolu): find a better solution */
29 | process.nextTick = setTimeout
30 |
31 | /******************************************************************************/
32 | /* MAIN RUN MODES */
33 | /******************************************************************************/
34 | // ### breach_start
35 | //
36 | // Starts breach and its modules for the local session
37 | var breach_start = function() {
38 | common.log.out('[index] Breach v' + require('./package.json').version +
39 | ' Starting...');
40 |
41 | var args = process.argv;
42 | args.forEach(function(a) {
43 | if(a === '--debug') {
44 | common.DEBUG = true;
45 | }
46 | if(a === '--msg-log') {
47 | common.MSG_LOG = true;
48 | }
49 | if(a === '--msg-dump') {
50 | common.MSG_DUMP = true;
51 | }
52 | });
53 |
54 | /* TODO(spolu): Useful for debugging on OSX. Maybe integrate it in */
55 | /* module_manager. */
56 | /*
57 | var fs = require('fs');
58 | var util = require('util');
59 | var stdout = fs.createWriteStream('/Users/spolu/breach.stdout', { flags: 'a' })
60 | var fun = console.log;
61 | console.log = function(d) {
62 | stdout.write(util.format(d) + '\n');
63 | fun(d);
64 | };
65 | */
66 |
67 | common.auto_updater = require('./lib/auto_updater.js').auto_updater({});
68 | common.auto_updater.init();
69 |
70 | common.session_manager = require('./lib/session_manager.js').session_manager({
71 | off_the_record: false
72 | });
73 | async.waterfall([
74 | common.session_manager.init,
75 | common.session_manager.list_sessions,
76 | function(sessions, cb_) {
77 | if(Object.keys(sessions).length === 0) {
78 | common.session_manager.new_session(false, 'Alpha Session', cb_);
79 | }
80 | else {
81 | return cb_(null, Object.keys(sessions)[0]);
82 | }
83 | },
84 | common.session_manager.open_session
85 | ], function(err) {
86 | if(err) {
87 | common.fatal(err);
88 | }
89 | common.log.out('[index] Startup Complete');
90 | });
91 | };
92 |
93 |
94 |
95 | /******************************************************************************/
96 | /* INITIALIZATION */
97 | /******************************************************************************/
98 | breach_start();
99 |
100 |
101 | // SAFETY NET (kills the process and the spawns)
102 | process.on('uncaughtException', function (err) {
103 | common.fatal(err);
104 | });
105 |
106 | var sig_handler = function() {
107 | common.exit(0);
108 | };
109 | process.on('SIGHUP', sig_handler);
110 | process.on('SIGINT', sig_handler);
111 | process.on('SIGQUIT', sig_handler);
112 | process.on('SIGABRT', sig_handler);
113 | process.on('SIGTERM', sig_handler);
114 |
--------------------------------------------------------------------------------
/controls/modules/partials/modules.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Core
8 |
9 |
10 | Breach
11 | v{{about.version}}
12 | running
13 |
14 |
15 | up to date
16 |
17 |
18 | need update (v{{about.update.version}})
19 |
20 |
21 | install & restart
22 |
23 |
24 | install manually
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | Modules
36 |
37 |
38 | {{m.type}}
39 | {{m.name}}
40 | v{{m.version}}
41 | {{m.owner}}
42 | {{m.tag}}
43 |
44 | installing
45 |
46 |
47 | running
48 |
49 |
50 | stopped
51 |
52 |
53 |
54 | up to date
55 |
56 |
57 | need restart
58 |
59 |
60 |
61 | kill
62 | run
63 |
64 |
65 | restart
66 |
67 |
68 | update
70 |
71 |
72 | remove
73 |
74 |
75 | out
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | Install Modules
85 |
86 |
87 |
88 |
91 |
92 |
93 | install
94 |
95 |
96 |
97 |
98 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/controls/lib/angular-route-1.3.0-beta.13.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | AngularJS v1.3.0-beta.13
3 | (c) 2010-2014 Google, Inc. http://angularjs.org
4 | License: MIT
5 | */
6 | (function(n,e,A){'use strict';function x(s,g,k){return{restrict:"ECA",terminal:!0,priority:400,transclude:"element",link:function(a,b,c,f,w){function y(){p&&(p.remove(),p=null);h&&(h.$destroy(),h=null);l&&(k.leave(l,function(){p=null}),p=l,l=null)}function v(){var c=s.current&&s.current.locals;if(e.isDefined(c&&c.$template)){var c=a.$new(),d=s.current;l=w(c,function(d){k.enter(d,null,l||b,function(){!e.isDefined(t)||t&&!a.$eval(t)||g()});y()});h=d.scope=c;h.$emit("$viewContentLoaded");h.$eval(u)}else y()}
7 | var h,l,p,t=c.autoscroll,u=c.onload||"";a.$on("$routeChangeSuccess",v);v()}}}function z(e,g,k){return{restrict:"ECA",priority:-400,link:function(a,b){var c=k.current,f=c.locals;b.html(f.$template);var w=e(b.contents());c.controller&&(f.$scope=a,f=g(c.controller,f),c.controllerAs&&(a[c.controllerAs]=f),b.data("$ngControllerController",f),b.children().data("$ngControllerController",f));w(a)}}}n=e.module("ngRoute",["ng"]).provider("$route",function(){function s(a,b){return e.extend(new (e.extend(function(){},
8 | {prototype:a})),b)}function g(a,e){var c=e.caseInsensitiveMatch,f={originalPath:a,regexp:a},k=f.keys=[];a=a.replace(/([().])/g,"\\$1").replace(/(\/)?:(\w+)([\?\*])?/g,function(a,e,c,b){a="?"===b?b:null;b="*"===b?b:null;k.push({name:c,optional:!!a});e=e||"";return""+(a?"":e)+"(?:"+(a?e:"")+(b&&"(.+?)"||"([^/]+)")+(a||"")+")"+(a||"")}).replace(/([\/$\*])/g,"\\$1");f.regexp=RegExp("^"+a+"$",c?"i":"");return f}var k={};this.when=function(a,b){k[a]=e.extend({reloadOnSearch:!0},b,a&&g(a,b));if(a){var c=
9 | "/"==a[a.length-1]?a.substr(0,a.length-1):a+"/";k[c]=e.extend({redirectTo:a},g(c,b))}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache","$sce",function(a,b,c,f,g,n,v,h){function l(){var d=p(),m=r.current;if(d&&m&&d.$$route===m.$$route&&e.equals(d.pathParams,m.pathParams)&&!d.reloadOnSearch&&!u)m.params=d.params,e.copy(m.params,c),a.$broadcast("$routeUpdate",m);else if(d||m)u=!1,a.$broadcast("$routeChangeStart",
10 | d,m),(r.current=d)&&d.redirectTo&&(e.isString(d.redirectTo)?b.path(t(d.redirectTo,d.params)).search(d.params).replace():b.url(d.redirectTo(d.pathParams,b.path(),b.search())).replace()),f.when(d).then(function(){if(d){var a=e.extend({},d.resolve),b,c;e.forEach(a,function(d,b){a[b]=e.isString(d)?g.get(d):g.invoke(d,null,null,b)});e.isDefined(b=d.template)?e.isFunction(b)&&(b=b(d.params)):e.isDefined(c=d.templateUrl)&&(e.isFunction(c)&&(c=c(d.params)),c=h.getTrustedResourceUrl(c),e.isDefined(c)&&(d.loadedTemplateUrl=
11 | c,b=n.get(c,{cache:v}).then(function(a){return a.data})));e.isDefined(b)&&(a.$template=b);return f.all(a)}}).then(function(b){d==r.current&&(d&&(d.locals=b,e.copy(d.params,c)),a.$broadcast("$routeChangeSuccess",d,m))},function(b){d==r.current&&a.$broadcast("$routeChangeError",d,m,b)})}function p(){var a,c;e.forEach(k,function(f,k){var q;if(q=!c){var g=b.path();q=f.keys;var l={};if(f.regexp)if(g=f.regexp.exec(g)){for(var h=1,p=g.length;h 0) {
32 | $scope.modules.forEach(function(m) {
33 | if(m.name === 'mod_strip') {
34 | if(m.installing) {
35 | if(m.install_status === 'dependencies') {
36 | $scope.step1_download_done = true;
37 | }
38 | }
39 | }
40 | });
41 | }
42 | });
43 |
44 | /****************************************************************************/
45 | /* STEPS */
46 | /****************************************************************************/
47 | $scope.done_step0 = function() {
48 |
49 | $scope.step1_error = null;
50 | var to_install = ['mod_strip', 'mod_stats'];
51 |
52 | /* Detection of current state. */
53 | var module = {};
54 |
55 | if($scope.modules && $scope.modules.length > 0) {
56 | $scope.modules.forEach(function(m) {
57 | if(to_install.indexOf(m.name) !== -1) {
58 | module[m.name] = m;
59 | }
60 | });
61 | }
62 |
63 | /* Install to_install modules */
64 | async.series([
65 | function(cb_) {
66 | async.each(to_install, function(m, cb_) {
67 | if(module[m]) {
68 | return cb_();
69 | }
70 | _modules.add('github:breach/' + m).then(function(data) {
71 | module[m] = data.module;
72 | return cb_();
73 | }, function(reason) { return cb_(reason); });
74 | }, cb_);
75 | },
76 | function(cb_) {
77 | $scope.step1_add_done = true;
78 | async.each(to_install, function(m, cb_) {
79 | _modules.install(module[m].path).then(function(data) {
80 | return cb_();
81 | }, function(reason) { return cb_(reason); });
82 | }, cb_);
83 | },
84 | function(cb_) {
85 | $scope.step1_dependencies_done = true;
86 | async.each(to_install, function(m, cb_) {
87 | if(module[m].running) {
88 | return cb_();
89 | }
90 | _modules.run(module[m].path).then(function(data) {
91 | return cb_();
92 | }, function(reason) { return cb_(reason); });
93 | }, cb_);
94 | }
95 | ], function(err) {
96 | if(err) {
97 | $scope.step1_error = err.split(' at')[0];
98 | }
99 | else {
100 | $scope.step1_run_done = true;
101 | }
102 | });
103 |
104 | $('.onboarding').addClass('step-1');
105 | $('.onboarding').removeClass('step-0');
106 | };
107 |
108 | $scope.done_step1 = function() {
109 | $('.onboarding').addClass('step-2');
110 | $('.onboarding').removeClass('step-1');
111 | };
112 |
113 | $scope.done_step2 = function() {
114 | $('.onboarding').addClass('step-3');
115 | $('.onboarding').removeClass('step-2');
116 | };
117 |
118 | $scope.done_step3 = function() {
119 | $('.onboarding').addClass('step-4');
120 | $('.onboarding').removeClass('step-3');
121 | };
122 |
123 | $scope.done_step4 = function() {
124 | $location.path('/');
125 | };
126 | };
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/openpgp.store/openpgp-public-keys:
--------------------------------------------------------------------------------
1 | ["-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: Keybase OpenPGP v0.1.17\r\nComment: https://keybase.io/breach\r\n\r\nxsFNBFOBJSIBEADlN1ZvdOhhulGCpZvB/JZc3GbIsolGfTbBkBZFIbVEgLwr1QlJ\r\nAJJhyb0bNxmBb4V/0JPTSVrScurvjIWg0zM6jHYDK9TdxLfeLWhc3yjQ0sw91MR6\r\nuugZvdYKBocpWc0y7etQ7/LB81IHr2o1e3aIf1OmR5Hj/vIcsHibVDyoDGj9RYvl\r\n+op9bNw4/NMYPTVAsisCdtisR20E525Kd7WM0HWrnivfaZhcmiMKqomo2CitkqZS\r\ndBLzwzvyJGA0bNDrOFDxNR/QzGh6oehcVRZMqag5RZljmv4i5WntMXxDmQ+azWRG\r\nLFMTwL9lYwk4dTzh7J5Ym7HAjUxAiHbHL4nW3d1Te4RHFv5OXcTiQ1phssp273Ml\r\nBNS1/uGab0l1/A8GK1DT7DS88ecg42/CiV56YraTPbYm8+n3fn0niuGE8uvs1fA4\r\ntC7BZwtikm3Ulkh+YJeiUNxtJV5/htD6WsJNNS98KPi61th8L052e3wy9OWx9pgT\r\nvIsyhF9E3j6wx61TMNEwUU4TKCWu3sHhr9m6TgtwrGcQAz6jS7QGFuSgEwGqaiPA\r\nlW3A70oVKOx9v7vUM1T+M1BFskpKZx6UV7tefGat6Lb+3Wet2NcBWsWVw/UzzYk5\r\n0Qh2Xhk7AmsRdZjwdh4t1spmRZSr6+n3aSUzPyTWpt2V37fUCU69n0ngHQARAQAB\r\nzSVrZXliYXNlLmlvL2JyZWFjaCA8YnJlYWNoQGtleWJhc2UuaW8+wsFtBBMBCgAX\r\nBQJTgSUiAhsvAwsJBwMVCggCHgECF4AACgkQvvp73IxmBMpuFQ//TcMXW8kJy2xO\r\nqWxM5nfoJmJnKjnlSY+Qh+rlnlg3dKXpj7PCHodrBsmo6AN2d0yR4/5ORXBvHyEs\r\ngkFjCqCHKomsObbiv1NwiAfgO0l7vb7T9ngg9mB69lMGmMaOCTjGldS9CDUdhWAx\r\nAmMuDBqutvcgFkp/hMAr3YsHeCsfrYNxgEQvQvViCBR+UYfKHm43GWHMWD8f4B9N\r\na/a34cmzW9yC2Ksm1ri7j+zhZBShFwTzwPIKGolnbKgBOgFE8+a43u9olf4qGWJA\r\n/V0RxSucOqRBTXC+ZThNrJhNyjY7Kc1Pi8me6psO77pK3g0d37m2shMwIzbQ+QR5\r\njpkZaXBMoXfcQPd4RMLP7zAgDGSxEb5IhcLIF7TmAHvuQAYFx7i8N6u9GkcrJnAp\r\nAL1AI/BepWhl9IKwNDJzwXNgSHs5HQptSoaOJocpHBS1zHKSiNGQSqlPQUn50hkX\r\nFgcpJH/ZsH52PuAJ9gCxaQrT/oBtWVplGzigrM8Z4V2YmyTOoHfJQkP40lEDiMla\r\nWJDh8n0V0QTbB6fxVNzG925ef6H5UlPhVXM1q0Qn/bLoUjxslTl+LR+094UWDQ+r\r\nfXOoO8Msh9T3ElBjYlekDIXBLoL5MBJyBpjhZwl8/jcDOqLwMXYmoaiDIDkpjCSj\r\nk9wumK6I5pSN28yznrN+HD5ifI+9n+fOwE0EU4ElIgEIAM6xhAGtKpWTBvei54cu\r\n+uKGQ/PmyxLSWBWtxrbJW1f8X9DU3pCSeAEUJcVMLBoinfVqeY2qnbu864rmYocl\r\n1CaAgN48844pJ/aUow26SSQrBID4IVf86ENxkzh5G02S9H7C3thVQN5jJ5zRPjEN\r\nuFoB2q4fX7PCpnGsX0nJ5FpaIl6wDQ2bRA8DlyEa9MxOgckv5hYqCKoxZYBXnRjr\r\nr7JcHorQji28I+Tz65OVrCFdUMEQ/JSTswvZY2PScUkb+KwMU3uZ4l+6jNtfvIwa\r\n/GLh+VTugtVQce+ESp7Mf5ft7bQ7FTpZX/8XaXbsDzvbgwIxyapkeMfTWWBnCHNf\r\nUncAEQEAAcLChAQYAQoADwUCU4ElIgUJDwmcAAIbAgEpCRC++nvcjGYEysBdIAQZ\r\nAQoABgUCU4ElIgAKCRC7ssQKdilXcWslB/sEUOQuNS7CbCmTi2aFovuRXZ3ZqT12\r\nqlgbBDOtlVjZtjDqV0AvC4wOM/NqTr7Wz7JdvMapzCCwUk4nIcGzs37A8U9NVB6r\r\nibx5lKYwZQyiloUmeof0dplPvoLV7fMHgK4W999rQxfAtf9/Hq4JYtM6NqvEURTZ\r\nrhgAJKxkJnTV+HTL6sACB+etoRXYfC0kpKHdLZEMEdQ906DizbTpFXwPPlH20scj\r\nN3xg4/pO5nBfalmPk9splhN9haexzJOo2XEt7anNgz8ABQED4HNCariQauvdf7c2\r\nDGIYAH+aTUtmamxoLnndRTMpshF9+QeWdum8whH6V9betzSTx+skj9gN5MQQALZv\r\npDy510dbpivSVi0zT+7/HsY+72cr9RSok7ksHGITR4RHuLSmnAI/aL2p9Ssbb8RB\r\n6mbTiX9AmNm3iETu4uIqxh1Ihr/1J4tG0yAW8DeXeD3V5y2ik/NsHg3f2GOjo3AB\r\nRS9pGfsE4hy/UU7sv8TA4+rD8O1MJFjlJMqk6TgeawqRjYAmq8c+9Y/9hfbNxYoQ\r\nRKxILLxSfnz/sRU6HuIRDxJEWC7RcA1N4MYyec+z7jWq8C0edw1GTIprKyU5RyCq\r\nyv46H2sAMmuiBwvE2NKdGrdCXq6NmfcbdkUUbnuYqRJlJxGTMVH5B7rnArqf0Q33\r\nvYQrVo/FDhneLflVl+vM2YoqnZS9WC9tdqZ0cyLCzHGn47HxxdmrHeSo5g41PKlL\r\nf+9VEz/4dD1/njP0HcgZdj5qVqa4nBOPmOw7V7VCqEtM7fvsGa1T1dTko/srwdzQ\r\niv7EKuO1N6swTUdaPfDUzX9/lCEjCyXC4fS0BTQWLKsTvPoPBEpy5ZWl+V8SnDh0\r\nLmKfR9WZO5/kh+rOR5S60hFB6nZAl+VnS22/JW7XW7jb9svfWGoXI0Jba0QBGLfc\r\npGMvxjUHeC4IbBa7YmEmmi+TdRx83i8U9T0shqWamZmf9aAFqVxfdjpmnGoyq0GN\r\n5x3mea6W/GRNM18LYeieSAXZfs14uj59xsqv4XvyzsBNBFOBJSIBCADKod3x0MrF\r\nqv1oAzdjuOHDpfVQyWkwy5Jjx+XEymefu+eLNq93Jn43eTn7pBgdGIwvD4XG3cKh\r\niy/6B89t8LUnzdGoTdLQAgb4uibIAeXEMkO91cTnpcDZ5WBX+abpVWdPUrPMcNfQ\r\npP1h0puf/bnqg8Pph9kwq/af/FT9iMoA/PfrYxlagrtlEArxRoUMs4sSLVOQvFhR\r\nj3Zhh1Pr1Mgbb9rV99fDqAT+CDldGj/Kjap+aYuMIJlowfTyW71qw0yzZRiQOUHP\r\n7RlD/YnHXLmm4vJ4orlsZzJZQOmjpu5DKm53eASy+86IBhIadG5hFsbv4FjXv+Ot\r\ni8+s3LfZlSMbABEBAAHCwoQEGAEKAA8FAlOBJSIFCQ8JnAACGwwBKQkQvvp73Ixm\r\nBMrAXSAEGQEKAAYFAlOBJSIACgkQ1xuwH3BbS2qNvgf/XhiNC48wlookeJKDp7nz\r\nr4DaNDy9QjFibttCJQEkILZ3rK0Kfq3UXpwWcRpyRlLhEbbX2fK5Sth+g83Kqzsc\r\nFql3TlCfOHBCYnXCmZbcxor1DJl00zOSwp9NIF6Re7enJhsFhIY3tQH6B3OVvuQj\r\njkSdYGcCsxsCIalKIs7Ye92hG3x2DXgcPfe17mmpsKMdU9Ryftr8CXl0LINMTnFk\r\npOgsYMCG29l7ukTKK5r+cEFOE1zALMlNp3vuq4j3/7NTvMyMIB/ZOAUmjFjX6TJZ\r\nz5v+SS6ALTXWS++L/CwR2wVHx0lwdo4/xEu/ea8UX33CLxqwbVRwJqhzm/iS9+YV\r\n/A1PD/912gBKP8E2NCVtgJV9vCqUYuYJaRXgc5OH9bhQxvSVHqJqiiRdRRBUUcGf\r\nU7uB++xVGK6hdHog6oOWdkDdjXFb/VKTwYtFwe7v1mR4lJRrp0CedZzAI2/+Ru5s\r\nSHtRaPEtXXHj5r5v3ne4TFDUU+onzeuUsRgWoTEjtVGwKfkBzyuHKRQ1mZ2MJk28\r\np4S+9tb41Ca4bcMb2Fh2C8q8/0Tx1LOPniZ65ua7DE1wLzco8zOIoMdY8/uFuaWv\r\nZc92/sJHT5Hj+zBGS1TkTrcMOYjbYn8ldpQ8p5K1Bk8EEdKr47c66WlKBeiW2G5Q\r\nyhGUUGfMymrXNzdNf+dMHc/YWA7LYd6Jb/pzAqKU1Inw49JHf0a4bXV3HyeyvVGy\r\nHVVKrrqC9O9DD3cbJjFZm7JQKJ2iYgYMS8eyxf4kGWVt/PGAQDDaVUQzAnNkjXl8\r\nuLqecMlx/GWCWakkTOlXSYcz2oHgRsYY/ptSY5OwCgW/O9XB8pMRCaOlMzoOye6U\r\nA/Trjmxwne1/kBhuFlahMf92Ib3/tQ28Mrhcbu3Xb6cTGXrfXn3zAth4Y0LjDPyw\r\nDVg1POVX0Wt9taYJDzfGPcND/36eZMLxdA6K/7EABxf/gcq+1mRRzs7peCDlNYrK\r\nUDox9p6dHsux0PZney7VA/8aHJr3N/H+h+2GXuRNIvu8a7WIrA==\r\n=pJx2\r\n-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n"]
2 |
--------------------------------------------------------------------------------
/lib/session.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: session.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-14 spolu [GiG.fs 0.2.x] local only
10 | * - 2014-04-11 spolu GiG.fs Integration
11 | * - 2014-01-17 spolu New Init/RunModules API
12 | * - 2013-11-14 spolu FMA refactoring
13 | * - 2013-10-21 spolu Cookie Store integration for v0.1
14 | * - 2013-09-05 spolu Fix exo_browser/#56
15 | * - 2013-08-12 spolu Creation
16 | */
17 | "use strict"
18 |
19 | var events = require('events');
20 | var async = require('async');
21 | var path = require('path');
22 | var mkdirp = require('mkdirp');
23 |
24 | var api = require('exo_browser');
25 | var common = require('./common.js');
26 |
27 | // ## session
28 | //
29 | // The session takes three arguments.
30 | // - `session_id` generated by the session_manager
31 | // - `gig` to use as data store for the session. This `gig` can be in-memory.
32 | // If `null`, an in-memory gig is created.
33 | // - `off_the_record` whether the browser is authorised to cache data locally.
34 | //
35 | // ```
36 | // @emits `ready`
37 | // @spec { session_id, gig, off_the_record }
38 | // ```
39 | var session = function(spec, my) {
40 | var _super = {};
41 | my = my || {};
42 | spec = spec || {};
43 |
44 | my.session_id = spec.session_id || 'NO_SESSION_ID';
45 |
46 | my.off_the_record = spec.off_the_record || false;
47 | my.data_path = my.off_the_record ? null :
48 | path.join(api.data_path('breach'), 'sessions', my.session_id);
49 |
50 | my.gig = spec.gig || require('gig.fs').gig({
51 | local_table: {
52 | 'core': [ { storage_path: null } ],
53 | 'modules': [ { storage_path: null } ]
54 | }
55 | });
56 | my.core_module = null;
57 | my.module_manager = null;
58 |
59 | //
60 | // _public_
61 | //
62 | var init; /* init(cb_); */
63 | var kill; /* kill(cb_); */
64 |
65 | var run_modules; /* run_modules(cb_); */
66 |
67 | //
68 | // _private_
69 | //
70 |
71 | //
72 | // #### _that_
73 | //
74 | var that = new events.EventEmitter();
75 |
76 | /****************************************************************************/
77 | /* PUBLIC METHODS */
78 | /****************************************************************************/
79 | // ### init
80 | //
81 | // Initialializes this session. The initialization must not go on network as
82 | // we need to display as fast as possible the session window (exo_browser)
83 | // and show the blank loading page.
84 | // ```
85 | // @cb_ {function(err, session)} asynchronous callback
86 | // ```
87 | init = function(cb_) {
88 | async.series([
89 | function(cb_) {
90 | if(!my.off_the_record) {
91 | mkdirp(my.data_path, cb_);
92 | }
93 | else {
94 | return cb_();
95 | }
96 | },
97 | function(cb_) {
98 | my.module_manager = require('./module_manager.js').module_manager({
99 | session: that
100 | });
101 | my.module_manager.init(cb_);
102 | }
103 | ], function(err) {
104 | return cb_(err, that);
105 | });
106 | };
107 |
108 | // ### run_modules
109 | //
110 | // Starts the core_module and all installed modules
111 | // ```
112 | // @cb_ {function(err)} asynchronous callback
113 | // ```
114 | run_modules = function(cb_) {
115 | async.series([
116 | function(cb_) {
117 | my.core_module = require('./core_module.js').core_module({
118 | session: that
119 | });
120 | my.core_module.init(cb_);
121 | },
122 | function(cb_) {
123 | my.module_manager.list(function(err, modules) {
124 | if(err) {
125 | return cb_(err);
126 | }
127 | async.each(modules, function(m, cb_) {
128 | my.module_manager.run_module(m.path, cb_);
129 | }, cb_);
130 | });
131 | },
132 | function(cb_) {
133 | that.emit('ready');
134 | return cb_();
135 | }
136 | ], cb_);
137 | };
138 |
139 | // ### kill
140 | //
141 | // Kills this session as well as the underlying exo_browser
142 | // ```
143 | // @cb_ {function(err)} asynchronous callback
144 | // ```
145 | kill = function(cb_) {
146 | common.log.out('[session] {' + my.session_id + '} KILL');
147 | async.series([
148 | function(cb_) {
149 | my.module_manager.kill(cb_);
150 | },
151 | function(cb_) {
152 | if(my.core_module) {
153 | my.core_module.kill(cb_);
154 | }
155 | else {
156 | return cb_();
157 | }
158 | },
159 | function(cb_) {
160 | setTimeout(function() {
161 | that.emit('kill');
162 | });
163 | return cb_();
164 | }
165 | ], cb_);
166 | };
167 |
168 | common.method(that, 'init', init, _super);
169 | common.method(that, 'kill', kill, _super);
170 |
171 | common.method(that, 'run_modules', run_modules, _super);
172 |
173 | common.getter(that, 'session_id', my, 'session_id');
174 | common.getter(that, 'gig', my, 'gig');
175 | common.getter(that, 'off_the_record', my, 'off_the_record');
176 | common.getter(that, 'data_path', my, 'data_path');
177 |
178 | common.getter(that, 'module_manager', my, 'module_manager');
179 |
180 | return that;
181 | };
182 |
183 | exports.session = session;
184 |
--------------------------------------------------------------------------------
/controls/splash/css/onboarding.css:
--------------------------------------------------------------------------------
1 | .onboarding {
2 | position: absolute;
3 | width: 100%;
4 | height: 100%;
5 | }
6 |
7 | .onboarding .step {
8 | width: 400px;
9 | height: 500px;
10 |
11 | margin-top: 100px;
12 | margin-left: auto;
13 | margin-right: auto;
14 |
15 | background-color: hsl(38, 36%, 96%);
16 |
17 | display: none;
18 | text-align: center;
19 |
20 | opacity: 0;
21 | transition: opacity 1s;
22 | }
23 |
24 | .onboarding.step-0 #step-0 {
25 | display: block;
26 | opacity: 1;
27 | }
28 | .onboarding.step-1 #step-1 {
29 | display: block;
30 | opacity: 1;
31 | }
32 | .onboarding.step-2 #step-2 {
33 | display: block;
34 | opacity: 1;
35 | }
36 | .onboarding.step-3 #step-3 {
37 | display: block;
38 | opacity: 1;
39 | }
40 | .onboarding.step-4 #step-4 {
41 | display: block;
42 | opacity: 1;
43 | }
44 |
45 | .onboarding .step .wrapper {
46 | position: absolute;
47 | width: 400px;
48 | }
49 |
50 | .onboarding .step .header {
51 | position: absolute;
52 | top: 0px;
53 | height: 40px;
54 | width: 360px;
55 | text-align: center;
56 | line-height: 40px;
57 | font-weight: bold;
58 | color: white;
59 | background-color: hsl(72, 4%, 42%);
60 | border-bottom: 3px solid hsl(0, 0%, 85%);
61 | padding-left: 40px;
62 | }
63 |
64 | .onboarding .step .header .progress {
65 | opacity: 0.5;
66 | float: right;
67 | margin-right: 15px;
68 | }
69 |
70 | .onboarding .step .body {
71 | position: absolute;
72 | top: 40px;
73 | height: 380px;
74 | width: 400px;
75 |
76 | padding-top: 10px;
77 | }
78 |
79 | .onboarding .step .footer {
80 | position: absolute;
81 | width: 400px;
82 | top: 420px;
83 | height: 80px;
84 | }
85 |
86 | .onboarding .step img {
87 | margin-top: 30px;
88 | margin-bottom: 20px;
89 | width: 300px;
90 | }
91 |
92 | .onboarding .step p {
93 | text-align: justify;
94 | display: inline-block;
95 | padding-left: 20px;
96 | padding-right: 20px;
97 | color: black;
98 | font-weight: 300;
99 | width: 360px;
100 | }
101 |
102 | .onboarding .step .path {
103 | background-color: hsl(0, 0%, 90%);
104 | font-weight: bold;
105 | color: hsl(0, 0%, 20%);
106 | padding: 2px;
107 | border-radius: 2px;
108 | }
109 |
110 | .onboarding .button {
111 | bottom: 20px;
112 |
113 | display: inline-block;
114 | height: 30px;
115 | background-color: hsl(198, 75%, 52%);
116 | font-weight: bold;
117 | color: white;
118 | text-align: center;
119 | font-size: 1.1em;
120 | line-height: 30px;
121 |
122 | margin-left: auto;
123 | margin-right: auto;
124 |
125 | margin-top: 20px;
126 | padding-left: 7px;
127 | padding-right: 7px;
128 | padding-top: 3px;
129 |
130 | cursor: pointer;
131 | }
132 |
133 | .onboarding .support {
134 | display: block;
135 | text-align: left;
136 | margin-left: auto;
137 | margin-right: auto;
138 | margin-top: 20px;
139 | font-size: 0.9em;
140 | text-align: center;
141 | width: 400px;
142 | }
143 |
144 | /******************************************************************************/
145 | /* STEP 1 */
146 | /******************************************************************************/
147 | .onboarding .step#step-1 .loading {
148 | width: 320px;
149 |
150 | font-family: "Courier New", Courier, "Lucida Sans Typewriter", "Lucida Typewriter", monospace;
151 | font-size: 1.2em;
152 |
153 | margin-top: 40px;
154 | margin-bottom: 40px;
155 | margin-left: 30px;
156 |
157 | padding-left: 20px;
158 | padding-top: 10px;
159 | padding-bottom: 10px;
160 |
161 | text-align: left;
162 |
163 | color: black;
164 | min-height: 64px;
165 | background-color: hsl(0, 0%, 100%);
166 | }
167 |
168 | .onboarding .step#step-1 .loading .action {
169 | width: 250px;
170 | }
171 |
172 | .onboarding .step#step-1 .loading .ok {
173 | color: green;
174 | font-weight: bold;
175 | }
176 |
177 | .onboarding .step#step-1 .loading .error {
178 | color: red;
179 | margin-top: 10px;
180 | font-size: 0.8em;
181 | }
182 | .onboarding .step#step-1 .loading .error .details {
183 | margin-top: 10px;
184 | color: grey;
185 | }
186 | .onboarding .step#step-1 .button {
187 | background-color: hsl(79, 99%, 37%);
188 | }
189 |
190 | /******************************************************************************/
191 | /* STEP 2 */
192 | /******************************************************************************/
193 | .onboarding .step#step-2 .header {
194 | }
195 |
196 | .onboarding .step#step-2 .button {
197 | background-color: hsl(205, 13%, 81%);
198 | color: hsl(0, 0%, 20%);
199 | }
200 |
201 | /******************************************************************************/
202 | /* STEP 3 */
203 | /******************************************************************************/
204 | .onboarding .step#step-3 img {
205 | margin-top: 10px;
206 | margin-bottom: 10px;
207 | width: 300px;
208 | }
209 |
210 | .onboarding .step#step-3 .top {
211 | margin-bottom: 20px;
212 | }
213 |
214 |
215 |
216 | /******************************************************************************/
217 | /* STEP 4 */
218 | /******************************************************************************/
219 | .onboarding .step#step-4 img {
220 | margin-top: 10px;
221 | margin-bottom: 10px;
222 | width: 300px;
223 | }
224 |
225 | .onboarding .step#step-4 .button {
226 | background-color: hsl(79, 99%, 37%);
227 | }
228 |
--------------------------------------------------------------------------------
/lib/core_store.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: core_store.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-18 spolu Creation
10 | */
11 | "use strict"
12 |
13 | var common = require('./common.js');
14 | var async = require('async');
15 | var api = require('exo_browser');
16 | var vm = require('vm');
17 |
18 | // ## core_store
19 | //
20 | // Breach `core` module store implementation.
21 | //
22 | // The `core_store` object is in charge of exposing data storage capabilities to
23 | // the modules. It relies on the session gigfs `module` channel. The module name
24 | // is prepended to any path used by a module.
25 | //
26 | // ```
27 | // @spec { core_module, session }
28 | // @inherits {}
29 | // ```
30 | var core_store = function(spec, my) {
31 | var _super = {};
32 | my = my || {};
33 | spec = spec || {};
34 |
35 | my.core_module = spec.core_module;
36 | my.session = spec.session;
37 |
38 |
39 | //
40 | // #### _public_
41 | //
42 | var init; /* init(cb_); */
43 | var kill; /* kill(cb_); */
44 |
45 | var store_register; /* store_register(src, args, cb_); */
46 | var store_get; /* store_get(src, args, cb_); */
47 | var store_push; /* store_push(src, args, cb_); */
48 |
49 | //
50 | // #### _private_
51 | //
52 |
53 | //
54 | // #### _that_
55 | //
56 | var that = {};
57 |
58 | /****************************************************************************/
59 | /* PRIVATE HELPERS */
60 | /****************************************************************************/
61 |
62 | /****************************************************************************/
63 | /* EXPOSED PROCEDURES */
64 | /****************************************************************************/
65 | // ### store_register
66 | //
67 | // Registers a new type for the calling module. The type is prepended with the
68 | // module name so that type do not conflict across modules.
69 | //
70 | // Additionally we're running untrusted code coming from the modules here but
71 | // as the modules have full access to the system for now, this does not really
72 | // matter. The code is nonetheless encapsulated in a `vm` as of now.
73 | // /* TODO(spolu): Check/Elaborate security model here */
74 | // ```
75 | // @src {string} the source module
76 | // @args {object} { type, reduce } reduce is the code of the reduce function
77 | // @cb_ {function(err, res)}
78 | // ```
79 | store_register = function(src, args, cb_) {
80 | if(typeof args.type !== 'string') {
81 | return cb_(common.err('Invalid `type`: ' + args.type,
82 | 'core_store:invalid_type'));
83 | }
84 | if(typeof args.reduce !== 'string') {
85 | return cb_(common.err('Invalid `reduce`: ' + args.reduce,
86 | 'core_store:invalid_reduce'));
87 | }
88 |
89 | var type = 'modules:' + src + ':' + args.type;
90 |
91 | my.session.gig().register(type, function(oplog) {
92 | var sandbox = { oplog: oplog };
93 | var code = 'value = (' + args.reduce + ')(oplog);';
94 | vm.runInNewContext(code, sandbox);
95 | return sandbox.value || null;
96 | });
97 |
98 | return cb_();
99 | };
100 |
101 | // ### store_get
102 | //
103 | // Retrieves the value for the given type and path for the calling module. The
104 | // registered reducer provided by the module is used to compute the value.
105 | // ```
106 | // @src {string} the source module
107 | // @args {object} { type, path } the type and path to retrieve
108 | // @cb_ {function(err, res)}
109 | // ```
110 | store_get = function(src, args, cb_) {
111 | if(typeof args.type !== 'string') {
112 | return cb_(common.err('Invalid `type`: ' + args.type,
113 | 'core_store:invalid_type'));
114 | }
115 | if(typeof args.path !== 'string') {
116 | return cb_(common.err('Invalid `path`: ' + args.path,
117 | 'core_store:invalid_path'));
118 | }
119 |
120 | var type = 'modules:' + src + ':' + args.type;
121 | var path = require('path').join(my.session.session_id(), src, args.path);
122 |
123 | my.session.gig().get('modules', type, path, cb_);
124 | };
125 |
126 | // ### store_push
127 | //
128 | // Pushes a new payload on the gigfs oplog associated with this type and path.
129 | // ```
130 | // @src {string} the source module
131 | // @args {object} { type, path, payload } the type, path and payload to push
132 | // @cb_ {function(err, res)}
133 | // ```
134 | store_push = function(src, args, cb_) {
135 | if(typeof args.type !== 'string') {
136 | return cb_(common.err('Invalid `type`: ' + args.type,
137 | 'core_store:invalid_type'));
138 | }
139 | if(typeof args.path !== 'string') {
140 | return cb_(common.err('Invalid `path`: ' + args.path,
141 | 'core_store:invalid_path'));
142 | }
143 |
144 | var type = 'modules:' + src + ':' + args.type;
145 | var path = require('path').join(my.session.session_id(), src, args.path);
146 | var payload = args.payload || null;
147 |
148 | my.session.gig().push('modules', type, path, payload, cb_);
149 | };
150 |
151 | /****************************************************************************/
152 | /* INITIALIZATION */
153 | /****************************************************************************/
154 | // ### init
155 | //
156 | // Initializes the core store module
157 | // ```
158 | // @cb_ {function(err)} asynchronous callback
159 | // ```
160 | init = function(cb_) {
161 | return cb_();
162 | };
163 |
164 | // ### kill
165 | //
166 | // Kills the core store module
167 | // ```
168 | // @cb_ {function(err)} asynchronous callback
169 | // ```
170 | kill = function(cb_) {
171 | /* TODO(spolu): unregister types. */
172 | return cb_();
173 | };
174 |
175 | common.method(that, 'init', init, _super);
176 | common.method(that, 'kill', kill, _super);
177 |
178 | common.method(that, 'store_register', store_register, _super);
179 | common.method(that, 'store_get', store_get, _super);
180 | common.method(that, 'store_push', store_push, _super);
181 |
182 | return that;
183 | };
184 |
185 | exports.core_store = core_store;
186 |
--------------------------------------------------------------------------------
/dist/linux.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [dist] linux.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-27 spolu Signature generation
10 | * - 2014-05-15 spolu Creation
11 | */
12 | "use strict"
13 |
14 | var common = require('../lib/common.js');
15 | var async = require('async');
16 | var mkdirp = require('mkdirp');
17 | var fs = require('fs-extra');
18 | var npm = require('npm');
19 | var path = require('path');
20 |
21 | var module_path = path.join(__dirname, '..');
22 | var package_json = require(path.join(module_path, 'package.json'));
23 |
24 | var base_name;
25 | var out_path;
26 | var tmp_dist_path;
27 | var out_dist_path;
28 |
29 | async.series([
30 | function(cb_) {
31 | if(!process.argv[2] && !process.argv[3]) {
32 | return cb_(common.err('Usage: linux.js arch path/to/exo_browser.tar.gz',
33 | 'dist_linux:missing_exo_browser_dist'));
34 | }
35 | common.log.out('Making `linux` distribution for v' + package_json.version);
36 | common.log.out('Using breach_core: ' + module_path);
37 | common.log.out('Using arch: ' + process.argv[2]);
38 | common.log.out('Using ExoBrowser: ' + process.argv[3]);
39 |
40 | base_name = 'breach-v' + package_json.version + '-' +
41 | 'linux' + '-' + process.argv[2];
42 | out_path = path.join(process.cwd(), 'out');
43 | tmp_dist_path = path.join('/tmp', 'breach.linux.dist');
44 | out_dist_path = path.join(out_path, base_name);
45 |
46 | return cb_();
47 | },
48 | function(cb_) {
49 | fs.remove(out_path, cb_);
50 | },
51 | function(cb_) {
52 | fs.remove(tmp_dist_path, cb_);
53 | },
54 |
55 | /* Create tmp dist path and copy local module there. */
56 | function(cb_) {
57 | common.log.out('Creating `tmp_dist_path`: ' + tmp_dist_path);
58 | mkdirp(path.join(tmp_dist_path, '__AUTO_UPDATE_BUNDLE__'), cb_);
59 | },
60 | function(cb_) {
61 | fs.copy(module_path,
62 | path.join(tmp_dist_path, '__AUTO_UPDATE_BUNDLE__', 'breach_core'), cb_);
63 | },
64 | function(cb_) {
65 | fs.remove(path.join(tmp_dist_path, '__AUTO_UPDATE_BUNDLE__', 'breach_core', '.git'), cb_);
66 | },
67 | function(cb_) {
68 | mkdirp(path.join(tmp_dist_path, '__AUTO_UPDATE_BUNDLE__', 'exo_browser'), cb_);
69 | },
70 |
71 | /* Extract exo_browser in dist path */
72 | function(cb_) {
73 | common.log.out('Extracting ExoBrowser: ' + process.argv[3]);
74 | var tar = require('child_process').spawn('tar',
75 | ['xfz', process.argv[3],
76 | '-C', path.join(tmp_dist_path, '__AUTO_UPDATE_BUNDLE__', 'exo_browser'),
77 | '--strip', '1']);
78 | tar.stdout.on('data', function (data) {
79 | console.log('stdout: ' + data);
80 | });
81 | tar.stderr.on('data', function (data) {
82 | console.log('stderr: ' + data);
83 | });
84 | tar.on('close', function (code) {
85 | if(code !== 0) {
86 | return cb_(common.err('Extraction failed with code: ' + code,
87 | 'auto_updater:failed_extraction'));
88 |
89 | }
90 | return cb_();
91 | });
92 | },
93 |
94 | /* Copy linux wrapper. */
95 | function(cb_) {
96 | common.log.out('Injecting `linux_wrapper` and `breach`');
97 | fs.copy(path.join(__dirname, './linux_wrapper.sh'),
98 | path.join(tmp_dist_path, 'breach'), cb_);
99 | },
100 | function(cb_) {
101 | fs.chmod(path.join(tmp_dist_path, 'breach'), '755', cb_);
102 | },
103 | /* Copy linux README. */
104 | function(cb_) {
105 | fs.copy(path.join(__dirname, './LINUX_README'),
106 | path.join(tmp_dist_path, 'README'), cb_);
107 | },
108 |
109 | /* copy. */
110 | function(cb_) {
111 | mkdirp(out_path, cb_);
112 | },
113 | function(cb_) {
114 | fs.rename(tmp_dist_path, out_dist_path, cb_);
115 | },
116 |
117 | /* tar */
118 | function(cb_) {
119 | common.log.out('Compressing Breach: ' + base_name + '.tar.gz');
120 | var tar = require('child_process').spawn('tar',
121 | ['cfz', base_name + '.tar.gz', base_name], {
122 | cwd: out_path
123 | });
124 | tar.stdout.on('data', function(data) {
125 | console.log('stdout: ' + data);
126 | });
127 | tar.stderr.on('data', function(data) {
128 | console.log('stderr: ' + data);
129 | });
130 | tar.on('close', function(code) {
131 | if(code !== 0) {
132 | return cb_(common.err('Extraction failed with code: ' + code,
133 | 'auto_updater:failed_extraction'));
134 |
135 | }
136 | return cb_();
137 | });
138 | },
139 |
140 | /* Generate sha1sum. */
141 | function(cb_) {
142 | common.log.out('Hash generation: ' + base_name + '.tar.gz.sha1sum');
143 | var sha1sum = require('child_process').spawn('sha1sum',
144 | [base_name + '.tar.gz'], {
145 | cwd: out_path
146 | });
147 | var out = fs.createWriteStream(path.join(out_path,
148 | base_name + '.tar.gz.sha1sum'));
149 | sha1sum.stdout.on('data', function(data) {
150 | out.write(data);
151 | });
152 | sha1sum.stderr.on('data', function(data) {
153 | console.log('stderr: ' + data);
154 | });
155 | sha1sum.on('close', function(code) {
156 | out.end();
157 | if(code !== 0) {
158 | return cb_(common.err('`sha1sum` failed with code: ' + code,
159 | 'auto_updater:failed_sha1sum'));
160 |
161 | }
162 | return cb_();
163 | });
164 | },
165 |
166 | /* Generate signature. */
167 | /* Warning: `breach` private key required for actual release. */
168 | function(cb_) {
169 | common.log.out('Signature generation: ' + base_name + '.tar.gz.sha1sum.asc');
170 | var gpg = require('child_process').spawn('gpg',
171 | ['--armor', '--clearsign', base_name + '.tar.gz.sha1sum'], {
172 | cwd: out_path
173 | });
174 | gpg.stdout.on('data', function(data) {
175 | console.log('stdout: ' + data);
176 | });
177 | gpg.stderr.on('data', function(data) {
178 | console.log('stderr: ' + data);
179 | });
180 | gpg.on('close', function(code) {
181 | if(code !== 0) {
182 | return cb_(common.err('`gpg` failed with code: ' + code,
183 | 'auto_updater:failed_gpg'));
184 |
185 | }
186 | return cb_();
187 | });
188 | }
189 |
190 | ], function(err) {
191 | if(err) {
192 | common.fatal(err);
193 | }
194 | process.exit(0);
195 | });
196 |
197 |
--------------------------------------------------------------------------------
/module/lib/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Breach: common.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-06-02 spolu Added `exit` method
10 | * - 2013-12-15 spolu Creation
11 | */
12 | "use strict";
13 |
14 | var util = require('util');
15 | var events = require('events');
16 |
17 | /******************************************************************************/
18 | /* CROCKFORD */
19 | /******************************************************************************/
20 |
21 | // ### method
22 | //
23 | // Adds a method to the current object denoted by that and preserves _super
24 | // implementation (see Crockford)
25 | // ```
26 | // @that {object} object to extend
27 | // @name {string} the method name
28 | // @method {function} the method
29 | // @_super {object} parent object for functional inheritence
30 | // ```
31 | exports.method = function(that, name, method, _super) {
32 | if(_super) {
33 | var m = that[name];
34 | _super[name] = function() {
35 | return m.apply(that, arguments);
36 | };
37 | }
38 | that[name] = method;
39 | };
40 |
41 | // ### getter
42 | //
43 | // Generates a getter on obj for key
44 | // ```
45 | // @that {object} object to extend
46 | // @name {string} the getter name
47 | // @obj {object} the object targeted by the getter
48 | // @key {string} the key to get on obj
49 | // ```
50 | exports.getter = function(that, name, obj, prop) {
51 | var getter = function() {
52 | return obj[prop];
53 | };
54 | that[name] = getter;
55 | };
56 |
57 | // ### setter
58 | //
59 | // Generates a getter on obj for key
60 | // ```
61 | // @that {object} object to extend
62 | // @name {string} the getter name
63 | // @obj {object} the object targeted by the getter
64 | // @key {string} the key to get on obj
65 | // ```
66 | exports.setter = function(that, name, obj, prop) {
67 | var setter = function (arg) {
68 | obj[prop] = arg;
69 | return that;
70 | };
71 | that['set' + name.substring(0, 1).toUpperCase() + name.substring(1)] = setter;
72 | that['set' + '_' + name] = setter;
73 | };
74 |
75 | // ### responds
76 | //
77 | // Tests whether the object responds to the given method name
78 | // ```
79 | // @that {object} object to test
80 | // @name {string} the method/getter/setter name
81 | // ```
82 | exports.responds = function(that, name) {
83 | return (that[name] && typeof that[name] === 'function');
84 | };
85 |
86 |
87 | /******************************************************************************/
88 | /* HELPERS */
89 | /******************************************************************************/
90 |
91 | // #### once
92 | //
93 | // Returns a function that will call the underlying function only once
94 | // whether it is called once or multiple times
95 | // ```
96 | // @fn {function} function to call once
97 | // ```
98 | exports.once = function(fn) {
99 | if(fn === void 0 || fn === null || typeof fn !== 'function')
100 | throw new TypeError();
101 |
102 | var done = false;
103 | return function() {
104 | if(!done) {
105 | args = Array.prototype.slice.call(arguments);
106 | done = true;
107 | fn.apply(null, args);
108 | }
109 | };
110 | };
111 |
112 | // ### remove
113 | //
114 | // Removes the element e from the Array, using the JS '===' equality
115 | // ```
116 | // @that {array} the array to operate on
117 | // @e {object} element to remove from the array
118 | // ```
119 | exports.remove = function(that, e) {
120 | "use strict";
121 |
122 | if(that === void 0 || that === null || !Array.isArray(that))
123 | throw new TypeError();
124 |
125 | for(var i = that.length - 1; i >= 0; i--)
126 | if(e === that[i]) that.splice(i, 1);
127 | };
128 |
129 | // ### clamp
130 | //
131 | // Clamp a given integer to a specified range and pad
132 | // ```
133 | // @v {number} value
134 | // @min {number} minimum
135 | // @max {number} maximum
136 | // ```
137 | exports.clamp = function(v, min, max) {
138 | if (v < min)
139 | return min;
140 | if (v > max)
141 | return max;
142 | return v;
143 | };
144 |
145 | // ### lpad
146 | //
147 | // Pads a string to the provided length with ' ' or opt_ch
148 | // ```
149 | // @str {string} string to pad
150 | // @length {number} pad to length character
151 | // @opt_ch {string} [optional] character
152 | // ```
153 | exports.lpad = function(str, length, opt_ch) {
154 | str = String(str);
155 | opt_ch = opt_ch || ' ';
156 |
157 | while (str.length < length)
158 | str = opt_ch + str;
159 |
160 | return str;
161 | };
162 |
163 | // ### zpad
164 | //
165 | // Pads a string to the provided length with zeroes
166 | // ```
167 | // @str {string} string to pad
168 | // @length {number} pad to length character
169 | // ```
170 | exports.zpad = function(str, length) {
171 | return exports.lpad(str, length, '0');
172 | };
173 |
174 |
175 | /******************************************************************************/
176 | /* LOGGING AND ERROR REPORTING */
177 | /******************************************************************************/
178 |
179 | var log = function(str, debug, error) {
180 | var pre = '[' + new Date().toISOString() + '] ';
181 | //pre += (my.name ? '{' + my.name.toUpperCase() + '} ' : '');
182 | pre += (debug ? 'DEBUG: ' : '');
183 | str.toString().split('\n').forEach(function(line) {
184 | if(error)
185 | console.error(pre + line)
186 | else if(debug)
187 | util.debug(pre + line);
188 | else
189 | console.log(pre + line);
190 | });
191 | };
192 |
193 | exports.DEBUG = false;
194 |
195 | // ### log
196 | //
197 | // Logging helpers. Object based on the `log` function including 4 logging
198 | // functions: `out`, `error`, `debug`, `info`
199 | // ```
200 | // @str {string|error} the string or error to log
201 | // ```
202 | exports.log = {
203 | out: function(str) {
204 | log(str);
205 | },
206 | error: function(err) {
207 | if(typeof err === 'object') {
208 | log('*********************************************', false, true);
209 | log('ERROR: ' + err.message);
210 | log('*********************************************', false, true);
211 | log(err.stack);
212 | log('---------------------------------------------', false, true);
213 | }
214 | else {
215 | log('*********************************************', false, true);
216 | log('ERROR: ' + JSON.stringify(err));
217 | log('*********************************************', false, true);
218 | log('---------------------------------------------', false, true);
219 | }
220 | },
221 | debug: function(str) {
222 | if(exports.DEBUG)
223 | log(str, true);
224 | },
225 | info: function(str) {
226 | util.print(str + '\n');
227 | }
228 | };
229 |
230 | // ### exit
231 | //
232 | // Makes sure to kill all subprocesses
233 | // ``
234 | // @code {number} exit code
235 | // ```
236 | exports.exit = function(code) {
237 | try {
238 | process.kill('-' + process.pid);
239 | }
240 | finally {
241 | process.exit(code);
242 | }
243 | };
244 |
245 | // ### fatal
246 | //
247 | // Prints out the error and exits the process while killing all sub processes
248 | // ```
249 | // @err {error}
250 | // ```
251 | exports.fatal = function(err) {
252 | exports.log.error(err);
253 | exports.exit(1);
254 | };
255 |
256 | // ### err
257 | //
258 | // Generates a proper error with name set
259 | // ```
260 | // @msg {string} the error message
261 | // @name {string} the error name
262 | // ```
263 | exports.err = function(msg, name) {
264 | var err = new Error(msg);
265 | err.name = name || 'CommonError';
266 | return err;
267 | };
268 |
269 |
--------------------------------------------------------------------------------
/controls/splash/partials/onboarding.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 | Breach is a modular browser. All functionalities in Breach are
13 | provided by interchangeable modules.
14 |
15 |
16 | Since you don't have any modules running yet, Breach can't do much,
17 | so let's add `mod_strip` , a module that
18 | provides a basic tab strip and URL box support.
19 |
20 | If you're a developer, you can clone it, hack it or simply create
21 | your own modules from scratch!
22 |
23 |
24 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 | Adding module...
44 |
45 | [OK]
46 |
47 |
48 |
49 | Downloading `mod_strip`...
50 |
51 | [OK]
52 |
53 |
54 |
55 | Installing dependencies...
56 |
57 | [OK]
58 |
59 |
60 |
61 | Running...
62 |
63 | [OK]
64 |
65 |
66 |
67 |
68 |
69 | Damn! An error occurred.
70 |
71 | {{step1_error}}
72 |
73 |
74 |
75 |
76 | Adding `github:breach/mod_strip` . This may take
77 | a few seconds.
78 |
79 |
80 | (Breach is downloading the module from GitHub and installing all its
81 | required nodeJS dependencies)
82 |
83 |
84 | FYI: Breach modules can be addressed by:
85 | - a GitHub repository: `github:breach/mod_strip`
86 | - a local directory path: `local:~/src/spolu/mod_test`
87 |
88 |
89 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
105 |
106 |
107 |
108 | By default, Breach will be gathering and sending anonymized usage
109 | statistics to help us detect issues and required improvements.
110 |
111 |
112 | The stats only include generic event types (no URLs, no personal data)
113 | and they are sent to Google Analytics.
114 |
115 |
116 | Obviously, usage stats aggregation is also provided by a module! You can
117 | opt-in or opt-out at any time simply by adding or removing the module
118 | `breach/mod_stats`
119 |
120 |
121 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
138 |
139 |
140 | `mod_strip` comes with a few experimental
141 | features we hope you'll enjoy:
142 |
143 |
144 | Stacked Tabs
145 | The most recently opened tab is always displayed first to keep you focused
146 | on what you're doing.
147 |
148 |
149 |
150 | Tab Filtering
151 | Whenever you start typing in the URL box, only the tabs matching your query
152 | are shown.
153 |
154 |
155 |
156 | Color Sniffing
157 | Each tab extracts the color of its webpage... to keep things visual!
158 |
159 |
160 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
176 |
177 |
178 | Module management
179 | At any time you can right click and choose `Configure modules` to get
180 | access to the module manager.
181 |
182 |
183 |
184 | From there, you can add or remove modules as well as apply updates whenever
185 | they are available.
186 |
187 |
188 |
189 | It's only the beginning.
190 | We hope to build around Breach a community of developers and users
191 | who enjoy crafting, testing and using new innovative ways to browse
192 | the Web.
193 | You'll be defining the future of Breach by trying out new modules
194 | and hacking on it.
195 |
196 |
197 | Developer or designer?
198 | Make sure to visit breach.cc/hack
199 | and let us know what you're building!
200 |
201 |
202 |
208 |
209 |
210 |
211 |
212 | If you are experiencing any issue, please send us an email to dev@breach.cc
213 |
214 |
215 |
216 |
--------------------------------------------------------------------------------
/dist/darwin.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: [dist] darwin.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-16 spolu Creation
10 | */
11 | "use strict"
12 |
13 | var common = require('../lib/common.js');
14 | var async = require('async');
15 | var mkdirp = require('mkdirp');
16 | var fs = require('fs-extra');
17 | var npm = require('npm');
18 | var path = require('path');
19 |
20 | var module_path = path.join(__dirname, '..');
21 | var package_json = require(path.join(module_path, 'package.json'));
22 |
23 | var base_name;
24 | var out_path;
25 | var tmp_dist_path;
26 | var out_dist_path;
27 |
28 | async.series([
29 | function(cb_) {
30 | if(!process.argv[2] && !process.argv[3]) {
31 | return cb_(common.err('Usage: darwin.js arch path/to/exo_browser.tar.gz',
32 | 'dist_linux:missing_exo_browser_dist'));
33 | }
34 | common.log.out('Making `darwin` distribution for v' + package_json.version);
35 | common.log.out('Using breach_core: ' + module_path);
36 | common.log.out('Using arch: ' + process.argv[2]);
37 | common.log.out('Using ExoBrowser: ' + process.argv[3]);
38 |
39 | base_name = 'breach-v' + package_json.version + '-' +
40 | 'darwin' + '-' + process.argv[2];
41 | out_path = path.join(process.cwd(), 'out');
42 | tmp_dist_path = path.join('/tmp', 'breach.darwin.dist');
43 | out_dist_path = path.join(out_path, base_name);
44 |
45 | return cb_();
46 | },
47 | function(cb_) {
48 | fs.remove(out_path, cb_);
49 | },
50 | function(cb_) {
51 | fs.remove(tmp_dist_path, cb_);
52 | },
53 |
54 | /* Extract exo_browser in dist path */
55 | function(cb_) {
56 | common.log.out('Creating `tmp_dist_path`: ' + tmp_dist_path);
57 | mkdirp(tmp_dist_path, cb_);
58 | },
59 | function(cb_) {
60 | common.log.out('Extracting ExoBrowser: ' + process.argv[3]);
61 | var tar = require('child_process').spawn('tar',
62 | ['xfz', process.argv[3], '-C', tmp_dist_path, '--strip', '1']);
63 | tar.stdout.on('data', function (data) {
64 | console.log('stdout: ' + data);
65 | });
66 | tar.stderr.on('data', function (data) {
67 | console.log('stderr: ' + data);
68 | });
69 | tar.on('close', function (code) {
70 | if(code !== 0) {
71 | return cb_(common.err('Extraction failed with code: ' + code,
72 | 'auto_updater:failed_extraction'));
73 |
74 | }
75 | return cb_();
76 | });
77 | },
78 | function(cb_) {
79 | fs.rename(path.join(tmp_dist_path, 'ExoBrowser.app'),
80 | path.join(tmp_dist_path, 'Breach.app'), cb_);
81 | },
82 |
83 | /* Swap shell/ with content of breach_core/. */
84 | function(cb_) {
85 | common.log.out('Injecting `breach_core` as `shell`');
86 | fs.remove(path.join(tmp_dist_path,
87 | 'Breach.app', 'Contents', 'Resources', 'shell'), cb_);
88 | },
89 | function(cb_) {
90 | fs.copy(module_path,
91 | path.join(tmp_dist_path,
92 | 'Breach.app', 'Contents', 'Resources', 'shell'), cb_);
93 | },
94 | function(cb_) {
95 | fs.remove(path.join(tmp_dist_path,
96 | 'Breach.app', 'Contents', 'Resources', 'shell', '.git'), cb_);
97 | },
98 |
99 | /* Update app.icns */
100 | function(cb_) {
101 | common.log.out('Updating `app.icns`');
102 | fs.remove(path.join(tmp_dist_path,
103 | 'Breach.app', 'Contents', 'Resources', 'app.icns'), cb_);
104 | },
105 | function(cb_) {
106 | fs.rename(path.join(tmp_dist_path,
107 | 'Breach.app', 'Contents', 'Resources',
108 | 'shell', 'breach.icns'),
109 | path.join(tmp_dist_path,
110 | 'Breach.app', 'Contents', 'Resources',
111 | 'app.icns'),
112 | cb_);
113 | },
114 |
115 | /* Update Info.plist */
116 | function(cb_) {
117 | common.log.out('Updating `Info.plist`');
118 | var info_path = path.join(tmp_dist_path,
119 | 'Breach.app', 'Contents', 'Info.plist');
120 | var sed = require('child_process').spawn('sed',
121 | ['-i', '', 's/ExoBrowser/Breach/g', info_path]);
122 | sed.stdout.on('data', function (data) {
123 | console.log('stdout: ' + data);
124 | });
125 | sed.stderr.on('data', function (data) {
126 | console.log('stderr: ' + data);
127 | });
128 | sed.on('close', function (code) {
129 | if(code !== 0) {
130 | return cb_(common.err('Sed replacement failed with code: ' + code,
131 | 'auto_updater:failed_replacement'));
132 |
133 | }
134 | return cb_();
135 | });
136 | },
137 | function(cb_) {
138 | fs.rename(path.join(tmp_dist_path, 'Breach.app', 'Contents', 'MacOs', 'ExoBrowser'),
139 | path.join(tmp_dist_path, 'Breach.app', 'Contents', 'MacOs', 'Breach'), cb_)
140 | },
141 |
142 | /* copy. */
143 | function(cb_) {
144 | mkdirp(out_path, cb_);
145 | },
146 | function(cb_) {
147 | fs.rename(tmp_dist_path, out_dist_path, cb_);
148 | },
149 |
150 | /* tar. */
151 | function(cb_) {
152 | common.log.out('Compressing Breach: ' + base_name + '.tar.gz');
153 | var tar = require('child_process').spawn('tar',
154 | ['cfz', base_name + '.tar.gz', base_name], {
155 | cwd: out_path
156 | });
157 | tar.stdout.on('data', function (data) {
158 | console.log('stdout: ' + data);
159 | });
160 | tar.stderr.on('data', function (data) {
161 | console.log('stderr: ' + data);
162 | });
163 | tar.on('close', function (code) {
164 | if(code !== 0) {
165 | return cb_(common.err('Extraction failed with code: ' + code,
166 | 'auto_updater:failed_extraction'));
167 |
168 | }
169 | return cb_();
170 | });
171 | },
172 |
173 | /* Generate sha1sum. */
174 | function(cb_) {
175 | common.log.out('Hash generation: ' + base_name + '.tar.gz.sha1sum');
176 | var sha1sum = require('child_process').spawn('shasum',
177 | ['--algorithm', '1', base_name + '.tar.gz'], {
178 | cwd: out_path
179 | });
180 | var out = fs.createWriteStream(path.join(out_path,
181 | base_name + '.tar.gz.sha1sum'));
182 | sha1sum.stdout.on('data', function(data) {
183 | out.write(data);
184 | });
185 | sha1sum.stderr.on('data', function(data) {
186 | console.log('stderr: ' + data);
187 | });
188 | sha1sum.on('close', function(code) {
189 | out.end();
190 | if(code !== 0) {
191 | return cb_(common.err('`sha1sum` failed with code: ' + code,
192 | 'auto_updater:failed_sha1sum'));
193 |
194 | }
195 | return cb_();
196 | });
197 | },
198 |
199 | /* Generate signature. */
200 | /* Warning: `breach` private key required for actual release. */
201 | function(cb_) {
202 | common.log.out('Signature generation: ' + base_name + '.tar.gz.sha1sum.asc');
203 | var gpg = require('child_process').spawn('gpg',
204 | ['--armor', '--clearsign', base_name + '.tar.gz.sha1sum'], {
205 | cwd: out_path
206 | });
207 | gpg.stdout.on('data', function(data) {
208 | console.log('stdout: ' + data);
209 | });
210 | gpg.stderr.on('data', function(data) {
211 | console.log('stderr: ' + data);
212 | });
213 | gpg.on('close', function(code) {
214 | if(code !== 0) {
215 | return cb_(common.err('`gpg` failed with code: ' + code,
216 | 'auto_updater:failed_gpg'));
217 |
218 | }
219 | return cb_();
220 | });
221 | }
222 |
223 | ], function(err) {
224 | if(err) {
225 | common.fatal(err);
226 | }
227 | process.exit(0);
228 | });
229 |
230 |
--------------------------------------------------------------------------------
/controls/lib/nprogress.js:
--------------------------------------------------------------------------------
1 | /*! NProgress (c) 2013, Rico Sta. Cruz
2 | * http://ricostacruz.com/nprogress */
3 |
4 | ;(function(factory) {
5 |
6 | if (typeof module === 'function') {
7 | module.exports = factory(this.jQuery || require('jquery'));
8 | } else if (typeof define === 'function' && define.amd) {
9 | define(['jquery'], function($) {
10 | return factory($);
11 | });
12 | } else {
13 | this.NProgress = factory(this.jQuery);
14 | }
15 |
16 | })(function($) {
17 | var NProgress = {};
18 |
19 | NProgress.version = '0.1.2';
20 |
21 | var Settings = NProgress.settings = {
22 | minimum: 0.08,
23 | easing: 'ease',
24 | positionUsing: '',
25 | speed: 200,
26 | trickle: true,
27 | trickleRate: 0.02,
28 | trickleSpeed: 800,
29 | showSpinner: true,
30 | template: ''
31 | };
32 |
33 | /**
34 | * Updates configuration.
35 | *
36 | * NProgress.configure({
37 | * minimum: 0.1
38 | * });
39 | */
40 | NProgress.configure = function(options) {
41 | $.extend(Settings, options);
42 | return this;
43 | };
44 |
45 | /**
46 | * Last number.
47 | */
48 |
49 | NProgress.status = null;
50 |
51 | /**
52 | * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
53 | *
54 | * NProgress.set(0.4);
55 | * NProgress.set(1.0);
56 | */
57 |
58 | NProgress.set = function(n) {
59 | var started = NProgress.isStarted();
60 |
61 | n = clamp(n, Settings.minimum, 1);
62 | NProgress.status = (n === 1 ? null : n);
63 |
64 | var $progress = NProgress.render(!started),
65 | $bar = $progress.find('[role="bar"]'),
66 | speed = Settings.speed,
67 | ease = Settings.easing;
68 |
69 | $progress[0].offsetWidth; /* Repaint */
70 |
71 | $progress.queue(function(next) {
72 | // Set positionUsing if it hasn't already been set
73 | if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();
74 |
75 | // Add transition
76 | $bar.css(barPositionCSS(n, speed, ease));
77 |
78 | if (n === 1) {
79 | // Fade out
80 | $progress.css({ transition: 'none', opacity: 1 });
81 | $progress[0].offsetWidth; /* Repaint */
82 |
83 | setTimeout(function() {
84 | $progress.css({ transition: 'all '+speed+'ms linear', opacity: 0 });
85 | setTimeout(function() {
86 | NProgress.remove();
87 | next();
88 | }, speed);
89 | }, speed);
90 | } else {
91 | setTimeout(next, speed);
92 | }
93 | });
94 |
95 | return this;
96 | };
97 |
98 | NProgress.isStarted = function() {
99 | return typeof NProgress.status === 'number';
100 | };
101 |
102 | /**
103 | * Shows the progress bar.
104 | * This is the same as setting the status to 0%, except that it doesn't go backwards.
105 | *
106 | * NProgress.start();
107 | *
108 | */
109 | NProgress.start = function() {
110 | if (!NProgress.status) NProgress.set(0);
111 |
112 | var work = function() {
113 | setTimeout(function() {
114 | if (!NProgress.status) return;
115 | NProgress.trickle();
116 | work();
117 | }, Settings.trickleSpeed);
118 | };
119 |
120 | if (Settings.trickle) work();
121 |
122 | return this;
123 | };
124 |
125 | /**
126 | * Hides the progress bar.
127 | * This is the *sort of* the same as setting the status to 100%, with the
128 | * difference being `done()` makes some placebo effect of some realistic motion.
129 | *
130 | * NProgress.done();
131 | *
132 | * If `true` is passed, it will show the progress bar even if its hidden.
133 | *
134 | * NProgress.done(true);
135 | */
136 |
137 | NProgress.done = function(force) {
138 | if (!force && !NProgress.status) return this;
139 |
140 | return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
141 | };
142 |
143 | /**
144 | * Increments by a random amount.
145 | */
146 |
147 | NProgress.inc = function(amount) {
148 | var n = NProgress.status;
149 |
150 | if (!n) {
151 | return NProgress.start();
152 | } else {
153 | if (typeof amount !== 'number') {
154 | amount = (1 - n) * clamp(Math.random() * n, 0.1, 0.95);
155 | }
156 |
157 | n = clamp(n + amount, 0, 0.994);
158 | return NProgress.set(n);
159 | }
160 | };
161 |
162 | NProgress.trickle = function() {
163 | return NProgress.inc(Math.random() * Settings.trickleRate);
164 | };
165 |
166 | /**
167 | * Waits for all supplied jQuery promises and
168 | * increases the progress as the promises resolve.
169 | *
170 | * @param $promise jQUery Promise
171 | */
172 | (function() {
173 | var initial = 0, current = 0;
174 |
175 | NProgress.promise = function($promise) {
176 | if (!$promise || $promise.state() == "resolved") {
177 | return this;
178 | }
179 |
180 | if (current == 0) {
181 | NProgress.start();
182 | }
183 |
184 | initial++;
185 | current++;
186 |
187 | $promise.always(function() {
188 | current--;
189 | if (current == 0) {
190 | initial = 0;
191 | NProgress.done();
192 | } else {
193 | NProgress.set((initial - current) / initial);
194 | }
195 | });
196 |
197 | return this;
198 | };
199 |
200 | })();
201 |
202 | /**
203 | * (Internal) renders the progress bar markup based on the `template`
204 | * setting.
205 | */
206 |
207 | NProgress.render = function(fromStart) {
208 | if (NProgress.isRendered()) return $("#nprogress");
209 | $('html').addClass('nprogress-busy');
210 |
211 | var $el = $("")
212 | .html(Settings.template);
213 |
214 | var perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0);
215 |
216 | $el.find('[role="bar"]').css({
217 | transition: 'all 0 linear',
218 | transform: 'translate3d('+perc+'%,0,0)'
219 | });
220 |
221 | if (!Settings.showSpinner)
222 | $el.find('[role="spinner"]').remove();
223 |
224 | $el.appendTo(document.body);
225 |
226 | return $el;
227 | };
228 |
229 | /**
230 | * Removes the element. Opposite of render().
231 | */
232 |
233 | NProgress.remove = function() {
234 | $('html').removeClass('nprogress-busy');
235 | $('#nprogress').remove();
236 | };
237 |
238 | /**
239 | * Checks if the progress bar is rendered.
240 | */
241 |
242 | NProgress.isRendered = function() {
243 | return ($("#nprogress").length > 0);
244 | };
245 |
246 | /**
247 | * Determine which positioning CSS rule to use.
248 | */
249 |
250 | NProgress.getPositioningCSS = function() {
251 | // Sniff on document.body.style
252 | var bodyStyle = document.body.style;
253 |
254 | // Sniff prefixes
255 | var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
256 | ('MozTransform' in bodyStyle) ? 'Moz' :
257 | ('msTransform' in bodyStyle) ? 'ms' :
258 | ('OTransform' in bodyStyle) ? 'O' : '';
259 |
260 | if (vendorPrefix + 'Perspective' in bodyStyle) {
261 | // Modern browsers with 3D support, e.g. Webkit, IE10
262 | return 'translate3d';
263 | } else if (vendorPrefix + 'Transform' in bodyStyle) {
264 | // Browsers without 3D support, e.g. IE9
265 | return 'translate';
266 | } else {
267 | // Browsers without translate() support, e.g. IE7-8
268 | return 'margin';
269 | }
270 | };
271 |
272 | /**
273 | * Helpers
274 | */
275 |
276 | function clamp(n, min, max) {
277 | if (n < min) return min;
278 | if (n > max) return max;
279 | return n;
280 | }
281 |
282 | /**
283 | * (Internal) converts a percentage (`0..1`) to a bar translateX
284 | * percentage (`-100%..0%`).
285 | */
286 |
287 | function toBarPerc(n) {
288 | return (-1 + n) * 100;
289 | }
290 |
291 |
292 | /**
293 | * (Internal) returns the correct CSS for changing the bar's
294 | * position given an n percentage, and speed and ease from Settings
295 | */
296 |
297 | function barPositionCSS(n, speed, ease) {
298 | var barCSS;
299 |
300 | if (Settings.positionUsing === 'translate3d') {
301 | barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
302 | } else if (Settings.positionUsing === 'translate') {
303 | barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
304 | } else {
305 | barCSS = { 'margin-left': toBarPerc(n)+'%' };
306 | }
307 |
308 | barCSS.transition = 'all '+speed+'ms '+ease;
309 |
310 | return barCSS;
311 | }
312 |
313 | return NProgress;
314 | });
315 |
316 |
--------------------------------------------------------------------------------
/lib/common.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Breach: common.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-16 spolu Added `exit` method
10 | * - 2014-01-14 spolu Added `hash` method
11 | * - 2013-12-15 spolu Creation
12 | */
13 | "use strict";
14 |
15 | var util = require('util');
16 | var events = require('events');
17 | var crypto = require('crypto');
18 |
19 | /******************************************************************************/
20 | /* GLOBAL CONFIG */
21 | /******************************************************************************/
22 |
23 | exports.DEBUG = false;
24 | exports.MSG_LOGGING = false;
25 | exports.MSG_DUMP = false;
26 |
27 | /******************************************************************************/
28 | /* CROCKFORD */
29 | /******************************************************************************/
30 |
31 | // ### method
32 | //
33 | // Adds a method to the current object denoted by that and preserves _super
34 | // implementation (see Crockford)
35 | // ```
36 | // @that {object} object to extend
37 | // @name {string} the method name
38 | // @method {function} the method
39 | // @_super {object} parent object for functional inheritance
40 | // ```
41 | exports.method = function(that, name, method, _super) {
42 | if(_super) {
43 | var m = that[name];
44 | _super[name] = function() {
45 | return m.apply(that, arguments);
46 | };
47 | }
48 | that[name] = method;
49 | };
50 |
51 | // ### getter
52 | //
53 | // Generates a getter on obj for key
54 | // ```
55 | // @that {object} object to extend
56 | // @name {string} the getter name
57 | // @obj {object} the object targeted by the getter
58 | // @key {string} the key to get on obj
59 | // ```
60 | exports.getter = function(that, name, obj, prop) {
61 | var getter = function() {
62 | return obj[prop];
63 | };
64 | that[name] = getter;
65 | };
66 |
67 | // ### setter
68 | //
69 | // Generates a getter on obj for key
70 | // ```
71 | // @that {object} object to extend
72 | // @name {string} the getter name
73 | // @obj {object} the object targeted by the getter
74 | // @key {string} the key to get on obj
75 | // ```
76 | exports.setter = function(that, name, obj, prop) {
77 | var setter = function (arg) {
78 | obj[prop] = arg;
79 | return that;
80 | };
81 | that['set' + name.substring(0, 1).toUpperCase() + name.substring(1)] = setter;
82 | that['set' + '_' + name] = setter;
83 | };
84 |
85 | // ### responds
86 | //
87 | // Tests whether the object responds to the given method name
88 | // ```
89 | // @that {object} object to test
90 | // @name {string} the method/getter/setter name
91 | // ```
92 | exports.responds = function(that, name) {
93 | return (that[name] && typeof that[name] === 'function');
94 | };
95 |
96 |
97 | /******************************************************************************/
98 | /* HELPERS */
99 | /******************************************************************************/
100 |
101 | // #### once
102 | //
103 | // Returns a function that will call the underlying function only once
104 | // whether it is called once or multiple times
105 | // ```
106 | // @fn {function} function to call once
107 | // ```
108 | exports.once = function(fn) {
109 | if(fn === void 0 || fn === null || typeof fn !== 'function')
110 | throw new TypeError();
111 |
112 | var done = false;
113 | return function() {
114 | if(!done) {
115 | args = Array.prototype.slice.call(arguments);
116 | done = true;
117 | fn.apply(null, args);
118 | }
119 | };
120 | };
121 |
122 | // ### remove
123 | //
124 | // Removes the element e from the Array, using the JS '===' equality
125 | // ```
126 | // @that {array} the array to operate on
127 | // @e {object} element to remove from the array
128 | // @only_one {boolean} remove only one
129 | // ```
130 | exports.remove = function(that, e, only_one) {
131 | "use strict";
132 |
133 | if(that === void 0 || that === null || !Array.isArray(that))
134 | throw new TypeError();
135 |
136 | for(var i = that.length - 1; i >= 0; i--) {
137 | if(e === that[i]) {
138 | that.splice(i, 1);
139 | if(only_one) return;
140 | }
141 | }
142 | };
143 |
144 | // ### clamp
145 | //
146 | // Clamp a given integer to a specified range and pad
147 | // ```
148 | // @v {number} value
149 | // @min {number} minimum
150 | // @max {number} maximum
151 | // ```
152 | exports.clamp = function(v, min, max) {
153 | if (v < min)
154 | return min;
155 | if (v > max)
156 | return max;
157 | return v;
158 | };
159 |
160 | // ### lpad
161 | //
162 | // Pads a string to the provided length with ' ' or opt_ch
163 | // ```
164 | // @str {string} string to pad
165 | // @length {number} pad to length character
166 | // @opt_ch {string} [optional] character
167 | // ```
168 | exports.lpad = function(str, length, opt_ch) {
169 | str = String(str);
170 | opt_ch = opt_ch || ' ';
171 |
172 | while (str.length < length)
173 | str = opt_ch + str;
174 |
175 | return str;
176 | };
177 |
178 | // ### rpad
179 | //
180 | // Pads a string to the provided length with ' ' or opt_ch
181 | // ```
182 | // @str {string} string to pad
183 | // @length {number} pad to length character
184 | // @opt_ch {string} [optional] character
185 | // ```
186 | exports.rpad = function(str, length, opt_ch) {
187 | str = String(str);
188 | opt_ch = opt_ch || ' ';
189 |
190 | while (str.length < length)
191 | str += opt_ch;
192 |
193 | return str.substr(0, length);
194 | };
195 |
196 | // ### zpad
197 | //
198 | // Pads a string to the provided length with zeroes
199 | // ```
200 | // @str {string} string to pad
201 | // @length {number} pad to length character
202 | // ```
203 | exports.zpad = function(str, length) {
204 | return exports.lpad(str, length, '0');
205 | };
206 |
207 | // ### hash
208 | //
209 | // Computes a hash value from the given strings
210 | // ```
211 | // @strings {array} an array of string used to update HMAC
212 | // @encoding {string} the encoding used [optional] (default: 'hex')
213 | // @return {string} the hash value
214 | // ```
215 | //
216 | exports.hash = function(strings, encoding) {
217 | encoding = encoding || 'hex';
218 | var hash = crypto.createHmac('sha1', '');
219 | strings.forEach(function(update) {
220 | hash.update(new Buffer(update.toString()));
221 | });
222 | return hash.digest(encoding);
223 | };
224 |
225 |
226 | /******************************************************************************/
227 | /* LOGGING AND ERROR REPORTING */
228 | /******************************************************************************/
229 |
230 | var log = function(str, debug, error) {
231 | var pre = '[' + new Date().toISOString() + '] ';
232 | //pre += (my.name ? '{' + my.name.toUpperCase() + '} ' : '');
233 | pre += (debug ? 'DEBUG: ' : '');
234 | str.toString().split('\n').forEach(function(line) {
235 | if(error)
236 | console.error(pre + line)
237 | else if(debug)
238 | console.log(pre + line);
239 | else
240 | console.log(pre + line);
241 | });
242 | };
243 |
244 |
245 | // ### log
246 | //
247 | // Logging helpers. Object based on the `log` function including 4 logging
248 | // functions: `out`, `error`, `debug`, `info`
249 | // ```
250 | // @str {string|error} the string or error to log
251 | // ```
252 | exports.log = {
253 | out: function(str) {
254 | log(str);
255 | },
256 | error: function(err) {
257 | if(typeof err === 'object') {
258 | log('*********************************************', false, true);
259 | log('ERROR: ' + err.message);
260 | log('*********************************************', false, true);
261 | log(err.stack);
262 | log('---------------------------------------------', false, true);
263 | }
264 | else {
265 | log('*********************************************', false, true);
266 | log('ERROR: ' + JSON.stringify(err));
267 | log('*********************************************', false, true);
268 | log('---------------------------------------------', false, true);
269 | }
270 | },
271 | debug: function(str) {
272 | if(exports.DEBUG)
273 | log(str, true);
274 | },
275 | info: function(str) {
276 | util.print(str + '\n');
277 | }
278 | };
279 |
280 | // ### exit
281 | //
282 | // Makes sure to kill all subprocesses
283 | // ``
284 | // @code {number} exit code
285 | // ```
286 | exports.exit = function(code) {
287 | try {
288 | process.kill('-' + process.pid);
289 | }
290 | finally {
291 | process.exit(code);
292 | }
293 | };
294 |
295 | // ### fatal
296 | //
297 | // Prints out the error and exits the process while killing all sub processes
298 | // ```
299 | // @err {error}
300 | // ```
301 | exports.fatal = function(err) {
302 | exports.log.error(err);
303 | exports.exit(1);
304 | };
305 |
306 | // ### err
307 | //
308 | // Generates a proper error with name set
309 | // ```
310 | // @msg {string} the error message
311 | // @name {string} the error name
312 | // ```
313 | exports.err = function(msg, name) {
314 | var err = new Error(msg);
315 | err.name = name || 'CommonError';
316 | return err;
317 | };
318 |
319 |
--------------------------------------------------------------------------------
/controls/lib/normalize-2.1.2.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.1.2 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /**
8 | * Correct `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | main,
20 | nav,
21 | section,
22 | summary {
23 | display: block;
24 | }
25 |
26 | /**
27 | * Correct `inline-block` display not defined in IE 8/9.
28 | */
29 |
30 | audio,
31 | canvas,
32 | video {
33 | display: inline-block;
34 | }
35 |
36 | /**
37 | * Prevent modern browsers from displaying `audio` without controls.
38 | * Remove excess height in iOS 5 devices.
39 | */
40 |
41 | audio:not([controls]) {
42 | display: none;
43 | height: 0;
44 | }
45 |
46 | /**
47 | * Address styling not present in IE 8/9.
48 | */
49 |
50 | [hidden] {
51 | display: none;
52 | }
53 |
54 | /* ==========================================================================
55 | Base
56 | ========================================================================== */
57 |
58 | /**
59 | * 1. Set default font family to sans-serif.
60 | * 2. Prevent iOS text size adjust after orientation change, without disabling
61 | * user zoom.
62 | */
63 |
64 | html {
65 | font-family: sans-serif; /* 1 */
66 | -ms-text-size-adjust: 100%; /* 2 */
67 | -webkit-text-size-adjust: 100%; /* 2 */
68 | }
69 |
70 | /**
71 | * Remove default margin.
72 | */
73 |
74 | body {
75 | margin: 0;
76 | }
77 |
78 | /* ==========================================================================
79 | Links
80 | ========================================================================== */
81 |
82 | /**
83 | * Address `outline` inconsistency between Chrome and other browsers.
84 | */
85 |
86 | a:focus {
87 | outline: thin dotted;
88 | }
89 |
90 | /**
91 | * Improve readability when focused and also mouse hovered in all browsers.
92 | */
93 |
94 | a:active,
95 | a:hover {
96 | outline: 0;
97 | }
98 |
99 | /* ==========================================================================
100 | Typography
101 | ========================================================================== */
102 |
103 | /**
104 | * Address variable `h1` font-size and margin within `section` and `article`
105 | * contexts in Firefox 4+, Safari 5, and Chrome.
106 | */
107 |
108 | h1 {
109 | font-size: 2em;
110 | margin: 0.67em 0;
111 | }
112 |
113 | /**
114 | * Address styling not present in IE 8/9, Safari 5, and Chrome.
115 | */
116 |
117 | abbr[title] {
118 | border-bottom: 1px dotted;
119 | }
120 |
121 | /**
122 | * Address style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
123 | */
124 |
125 | b,
126 | strong {
127 | font-weight: bold;
128 | }
129 |
130 | /**
131 | * Address styling not present in Safari 5 and Chrome.
132 | */
133 |
134 | dfn {
135 | font-style: italic;
136 | }
137 |
138 | /**
139 | * Address differences between Firefox and other browsers.
140 | */
141 |
142 | hr {
143 | -moz-box-sizing: content-box;
144 | box-sizing: content-box;
145 | height: 0;
146 | }
147 |
148 | /**
149 | * Address styling not present in IE 8/9.
150 | */
151 |
152 | mark {
153 | background: #ff0;
154 | color: #000;
155 | }
156 |
157 | /**
158 | * Correct font family set oddly in Safari 5 and Chrome.
159 | */
160 |
161 | code,
162 | kbd,
163 | pre,
164 | samp {
165 | font-family: monospace, serif;
166 | font-size: 1em;
167 | }
168 |
169 | /**
170 | * Improve readability of pre-formatted text in all browsers.
171 | */
172 |
173 | pre {
174 | white-space: pre-wrap;
175 | }
176 |
177 | /**
178 | * Set consistent quote types.
179 | */
180 |
181 | q {
182 | quotes: "\201C" "\201D" "\2018" "\2019";
183 | }
184 |
185 | /**
186 | * Address inconsistent and variable font size in all browsers.
187 | */
188 |
189 | small {
190 | font-size: 80%;
191 | }
192 |
193 | /**
194 | * Prevent `sub` and `sup` affecting `line-height` in all browsers.
195 | */
196 |
197 | sub,
198 | sup {
199 | font-size: 75%;
200 | line-height: 0;
201 | position: relative;
202 | vertical-align: baseline;
203 | }
204 |
205 | sup {
206 | top: -0.5em;
207 | }
208 |
209 | sub {
210 | bottom: -0.25em;
211 | }
212 |
213 | /* ==========================================================================
214 | Embedded content
215 | ========================================================================== */
216 |
217 | /**
218 | * Remove border when inside `a` element in IE 8/9.
219 | */
220 |
221 | img {
222 | border: 0;
223 | }
224 |
225 | /**
226 | * Correct overflow displayed oddly in IE 9.
227 | */
228 |
229 | svg:not(:root) {
230 | overflow: hidden;
231 | }
232 |
233 | /* ==========================================================================
234 | Figures
235 | ========================================================================== */
236 |
237 | /**
238 | * Address margin not present in IE 8/9 and Safari 5.
239 | */
240 |
241 | figure {
242 | margin: 0;
243 | }
244 |
245 | /* ==========================================================================
246 | Forms
247 | ========================================================================== */
248 |
249 | /**
250 | * Define consistent border, margin, and padding.
251 | */
252 |
253 | fieldset {
254 | border: 1px solid #c0c0c0;
255 | margin: 0 2px;
256 | padding: 0.35em 0.625em 0.75em;
257 | }
258 |
259 | /**
260 | * 1. Correct `color` not being inherited in IE 8/9.
261 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
262 | */
263 |
264 | legend {
265 | border: 0; /* 1 */
266 | padding: 0; /* 2 */
267 | }
268 |
269 | /**
270 | * 1. Correct font family not being inherited in all browsers.
271 | * 2. Correct font size not being inherited in all browsers.
272 | * 3. Address margins set differently in Firefox 4+, Safari 5, and Chrome.
273 | */
274 |
275 | button,
276 | input,
277 | select,
278 | textarea {
279 | font-family: inherit; /* 1 */
280 | font-size: 100%; /* 2 */
281 | margin: 0; /* 3 */
282 | }
283 |
284 | /**
285 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in
286 | * the UA stylesheet.
287 | */
288 |
289 | button,
290 | input {
291 | line-height: normal;
292 | }
293 |
294 | /**
295 | * Address inconsistent `text-transform` inheritance for `button` and `select`.
296 | * All other form control elements do not inherit `text-transform` values.
297 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 8+.
298 | * Correct `select` style inheritance in Firefox 4+ and Opera.
299 | */
300 |
301 | button,
302 | select {
303 | text-transform: none;
304 | }
305 |
306 | /**
307 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
308 | * and `video` controls.
309 | * 2. Correct inability to style clickable `input` types in iOS.
310 | * 3. Improve usability and consistency of cursor style between image-type
311 | * `input` and others.
312 | */
313 |
314 | button,
315 | html input[type="button"], /* 1 */
316 | input[type="reset"],
317 | input[type="submit"] {
318 | -webkit-appearance: button; /* 2 */
319 | cursor: pointer; /* 3 */
320 | }
321 |
322 | /**
323 | * Re-set default cursor for disabled elements.
324 | */
325 |
326 | button[disabled],
327 | html input[disabled] {
328 | cursor: default;
329 | }
330 |
331 | /**
332 | * 1. Address box sizing set to `content-box` in IE 8/9.
333 | * 2. Remove excess padding in IE 8/9.
334 | */
335 |
336 | input[type="checkbox"],
337 | input[type="radio"] {
338 | box-sizing: border-box; /* 1 */
339 | padding: 0; /* 2 */
340 | }
341 |
342 | /**
343 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome.
344 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome
345 | * (include `-moz` to future-proof).
346 | */
347 |
348 | input[type="search"] {
349 | -webkit-appearance: textfield; /* 1 */
350 | -moz-box-sizing: content-box;
351 | -webkit-box-sizing: content-box; /* 2 */
352 | box-sizing: content-box;
353 | }
354 |
355 | /**
356 | * Remove inner padding and search cancel button in Safari 5 and Chrome
357 | * on OS X.
358 | */
359 |
360 | input[type="search"]::-webkit-search-cancel-button,
361 | input[type="search"]::-webkit-search-decoration {
362 | -webkit-appearance: none;
363 | }
364 |
365 | /**
366 | * Remove inner padding and border in Firefox 4+.
367 | */
368 |
369 | button::-moz-focus-inner,
370 | input::-moz-focus-inner {
371 | border: 0;
372 | padding: 0;
373 | }
374 |
375 | /**
376 | * 1. Remove default vertical scrollbar in IE 8/9.
377 | * 2. Improve readability and alignment in all browsers.
378 | */
379 |
380 | textarea {
381 | overflow: auto; /* 1 */
382 | vertical-align: top; /* 2 */
383 | }
384 |
385 | /* ==========================================================================
386 | Tables
387 | ========================================================================== */
388 |
389 | /**
390 | * Remove most spacing between table cells.
391 | */
392 |
393 | table {
394 | border-collapse: collapse;
395 | border-spacing: 0;
396 | }
397 |
--------------------------------------------------------------------------------
/controls/lib/async-0.9.0.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * async
3 | * https://github.com/caolan/async
4 | *
5 | * Copyright 2010-2014 Caolan McMahon
6 | * Released under the MIT license
7 | *//*jshint onevar: false, indent:4 *//*global setImmediate: false, setTimeout: false, console: false */(function(){function d(a){var c=!1;return function(){if(c)throw new Error("Callback was already called.");c=!0,a.apply(b,arguments)}}var a={},b,c;b=this,b!=null&&(c=b.async),a.noConflict=function(){return b.async=c,a};var e=Object.prototype.toString,f=Array.isArray||function(a){return e.call(a)==="[object Array]"},g=function(a,b){if(a.forEach)return a.forEach(b);for(var c=0;c
=a.length&&c())}c=c||function(){};if(!a.length)return c();var e=0;g(a,function(a){b(a,d(f))})},a.forEach=a.each,a.eachSeries=function(a,b,c){c=c||function(){};if(!a.length)return c();var d=0,e=function(){b(a[d],function(b){b?(c(b),c=function(){}):(d+=1,d>=a.length?c():e())})};e()},a.forEachSeries=a.eachSeries,a.eachLimit=function(a,b,c,d){var e=k(b);e.apply(null,[a,c,d])},a.forEachLimit=a.eachLimit;var k=function(a){return function(b,c,d){d=d||function(){};if(!b.length||a<=0)return d();var e=0,f=0,g=0;(function h(){if(e>=b.length)return d();while(g=b.length?d():h())})})()}},l=function(b){return function(){var c=Array.prototype.slice.call(arguments);return b.apply(null,[a.each].concat(c))}},m=function(a,b){return function(){var c=Array.prototype.slice.call(arguments);return b.apply(null,[k(a)].concat(c))}},n=function(b){return function(){var c=Array.prototype.slice.call(arguments);return b.apply(null,[a.eachSeries].concat(c))}},o=function(a,b,c,d){b=h(b,function(a,b){return{index:b,value:a}});if(!d)a(b,function(a,b){c(a.value,function(a){b(a)})});else{var e=[];a(b,function(a,b){c(a.value,function(c,d){e[a.index]=d,b(c)})},function(a){d(a,e)})}};a.map=l(o),a.mapSeries=n(o),a.mapLimit=function(a,b,c,d){return p(b)(a,c,d)};var p=function(a){return m(a,o)};a.reduce=function(b,c,d,e){a.eachSeries(b,function(a,b){d(c,a,function(a,d){c=d,b(a)})},function(a){e(a,c)})},a.inject=a.reduce,a.foldl=a.reduce,a.reduceRight=function(b,c,d,e){var f=h(b,function(a){return a}).reverse();a.reduce(f,c,d,e)},a.foldr=a.reduceRight;var q=function(a,b,c,d){var e=[];b=h(b,function(a,b){return{index:b,value:a}}),a(b,function(a,b){c(a.value,function(c){c&&e.push(a),b()})},function(a){d(h(e.sort(function(a,b){return a.index-b.index}),function(a){return a.value}))})};a.filter=l(q),a.filterSeries=n(q),a.select=a.filter,a.selectSeries=a.filterSeries;var r=function(a,b,c,d){var e=[];b=h(b,function(a,b){return{index:b,value:a}}),a(b,function(a,b){c(a.value,function(c){c||e.push(a),b()})},function(a){d(h(e.sort(function(a,b){return a.index-b.index}),function(a){return a.value}))})};a.reject=l(r),a.rejectSeries=n(r);var s=function(a,b,c,d){a(b,function(a,b){c(a,function(c){c?(d(a),d=function(){}):b()})},function(a){d()})};a.detect=l(s),a.detectSeries=n(s),a.some=function(b,c,d){a.each(b,function(a,b){c(a,function(a){a&&(d(!0),d=function(){}),b()})},function(a){d(!1)})},a.any=a.some,a.every=function(b,c,d){a.each(b,function(a,b){c(a,function(a){a||(d(!1),d=function(){}),b()})},function(a){d(!0)})},a.all=a.every,a.sortBy=function(b,c,d){a.map(b,function(a,b){c(a,function(c,d){c?b(c):b(null,{value:a,criteria:d})})},function(a,b){if(a)return d(a);var c=function(a,b){var c=a.criteria,d=b.criteria;return cd?1:0};d(null,h(b.sort(c),function(a){return a.value}))})},a.auto=function(b,c){c=c||function(){};var d=j(b),e=d.length;if(!e)return c();var h={},k=[],l=function(a){k.unshift(a)},m=function(a){for(var b=0;b>>1);c(b,a[f])>=0?d=f:e=f-1}return d}function h(b,c,h,i){b.started||(b.started=!0),f(c)||(c=[c]);if(c.length==0)return a.setImmediate(function(){b.drain&&b.drain()});g(c,function(c){var f={data:c,priority:h,callback:typeof i=="function"?i:null};b.tasks.splice(e(b.tasks,f,d)+1,0,f),b.saturated&&b.tasks.length===b.concurrency&&b.saturated(),a.setImmediate(b.process)})}var i=a.queue(b,c);return i.push=function(a,b,c){h(i,a,b,c)},delete i.unshift,i},a.cargo=function(b,c){var d=!1,e=[],i={tasks:e,payload:c,saturated:null,empty:null,drain:null,drained:!0,push:function(b,d){f(b)||(b=[b]),g(b,function(a){e.push({data:a,callback:typeof d=="function"?d:null}),i.drained=!1,i.saturated&&e.length===c&&i.saturated()}),a.setImmediate(i.process)},process:function j(){if(d)return;if(e.length===0){i.drain&&!i.drained&&i.drain(),i.drained=!0;return}var a=typeof c=="number"?e.splice(0,c):e.splice(0,e.length),f=h(a,function(a){return a.data});i.empty&&i.empty(),d=!0,b(f,function(){d=!1;var b=arguments;g(a,function(a){a.callback&&a.callback.apply(null,b)}),j()})},length:function(){return e.length},running:function(){return d}};return i};var v=function(a){return function(b){var c=Array.prototype.slice.call(arguments,1);b.apply(null,c.concat([function(b){var c=Array.prototype.slice.call(arguments,1);typeof console!="undefined"&&(b?console.error&&console.error(b):console[a]&&g(c,function(b){console[a](b)}))}]))}};a.log=v("log"),a.dir=v("dir"),a.memoize=function(b,c){var d={},e={};c=c||function(a){return a};var f=function(){var f=Array.prototype.slice.call(arguments),g=f.pop(),h=c.apply(null,f);h in d?a.nextTick(function(){g.apply(null,d[h])}):h in e?e[h].push(g):(e[h]=[g],b.apply(null,f.concat([function(){d[h]=arguments;var a=e[h];delete e[h];for(var b=0,c=a.length;b2){var d=Array.prototype.slice.call(arguments,2);return c.apply(this,d)}return c};a.applyEach=l(w),a.applyEachSeries=n(w),a.forever=function(a,b){function c(d){if(d){if(b)return b(d);throw d}a(c)}c()},typeof module!="undefined"&&module.exports?module.exports=a:typeof define!="undefined"&&define.amd?define([],function(){return a}):b.async=a})();
--------------------------------------------------------------------------------
/module/lib/module.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: module.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * 2013-12-02 spolu Creation
10 | */
11 | "use strict"
12 |
13 | var events = require('events');
14 | var common = require('./common.js');
15 |
16 | // ## module_proxy
17 | //
18 | // ```
19 | // @extends events.EventEmitter
20 | // @spec { name, send_message }
21 | // ```
22 | var module_proxy = function(spec, my) {
23 | var _super = {};
24 | my = my || {};
25 | spec = spec || {};
26 |
27 | my.name = spec.name || 'INVALID';
28 | my.send_message = spec.send_message;
29 |
30 | my.rpc_calls = {};
31 |
32 | //
33 | // ### _public_
34 | //
35 | var call; /* call(name, args, cb_); */
36 |
37 | //
38 | // ### _protected_
39 | //
40 | var rpc_reply; /* rpc_reply(err,res); */
41 |
42 | //
43 | // ### _that_
44 | //
45 | var that = new events.EventEmitter();
46 |
47 | /****************************************************************************/
48 | /* PROTECTED METHODS */
49 | /****************************************************************************/
50 | // ### rpc_reply
51 | //
52 | // Method called when an `rpc_reply` message is received.
53 | // ```
54 | // @oid {number} original message id for the `rpc_call`
55 | // @err {Error} Javascript error if an error occurred
56 | // @res {object} JSON serializable result object
57 | // ```
58 | rpc_reply = function(oid, err, res) {
59 | if(my.rpc_calls[oid]) {
60 | my.rpc_calls[oid](err, res);
61 | delete my.rpc_calls[oid];
62 | }
63 | };
64 |
65 | /****************************************************************************/
66 | /* PUBLIC METHODS */
67 | /****************************************************************************/
68 | // ###
69 | //
70 | // Calls a remote procedure. It generates a message and sends it through the
71 | // `send_message` method. When the reply is received, `rpc_reply` will be
72 | // triggered, and eventually the callback called
73 | // ```
74 | // @proc {string} the procedure name
75 | // @args {object} serializable JSON arguments
76 | // @cb_ {function(err, res)} the callback when the rpc completes
77 | // ```
78 | call = function(proc, args, cb_) {
79 | if(my.name === 'INVALID' || my.name === '__ALL__') {
80 | return cb_(common.err('Cannot use `call` on the wildcard proxy',
81 | 'breach_module:call_on_wildcard'));
82 | }
83 | var msg = {
84 | hdr: {
85 | typ: 'rpc_call'
86 | },
87 | dst: my.name,
88 | prc: proc,
89 | arg: args
90 | };
91 | var mid = my.send_message(msg);
92 | my.rpc_calls[mid] = cb_;
93 | return mid;
94 | };
95 |
96 | common.method(that, 'rpc_reply', rpc_reply, _super);
97 |
98 | common.method(that, 'call', call, _super);
99 |
100 | return that;
101 | };
102 |
103 |
104 |
105 |
106 | // ## module
107 | //
108 | // This object exposes the `breach` module API. It is automatically constructed
109 | // when the module is required and will start communicating with its parent
110 | // process, assuming it is running in a process spawned by Breach.
111 | //
112 | // ```
113 | // @spec {}
114 | // ```
115 | var module = function(spec, my) {
116 | var _super = {};
117 | my = my || {};
118 | spec = spec || {};
119 |
120 | my.VERSION = require('./../package.json').version;
121 |
122 | my.proxies = {};
123 | my.procedures = {};
124 | my.message_id = 0;
125 |
126 | //
127 | // #### _public_
128 | //
129 | var emit; /* emit(type, event); */
130 |
131 | var register; /* register(source, type); */
132 | var unregister; /* register(rid); */
133 |
134 | var expose; /* expose(name, proc(src, args, cb_(err, res))); */
135 | var remove; /* remove(name); */
136 |
137 | var module; /* module(name); */
138 |
139 | var init; /* init(cb_); */
140 |
141 | //
142 | // #### _private_
143 | //
144 | var send_message; /* send_message(msg); */
145 | var handle_message; /* handle_message(msg); */
146 |
147 | //
148 | // ### _that_
149 | //
150 | var that = {};
151 |
152 | /****************************************************************************/
153 | /* MESSAGE HANDLING */
154 | /****************************************************************************/
155 | // ### handle_message
156 | //
157 | // Handles an incoming message from the top process.
158 | // ```
159 | // @msg {object} incoming message
160 | // ```
161 | handle_message = function(msg) {
162 | if(!msg || !msg.hdr ||
163 | typeof msg.hdr.typ !== 'string' ||
164 | typeof msg.hdr.mid !== 'number' ||
165 | typeof msg.hdr.src !== 'string') {
166 | /* We ignore the message. */
167 | return;
168 | }
169 | //console.log('HANDLE MSG: ' + JSON.stringify(msg));
170 |
171 | switch(msg.hdr.typ) {
172 | /* `event` messages are received when the module actually registered */
173 | /* for this event class. We trust the core module code for registration */
174 | /* correctness and just emit that event on the associated module */
175 | /* object. */
176 | case 'event': {
177 | if(my.proxies[msg.hdr.src]) {
178 | my.proxies[msg.hdr.src].emit(msg.typ, msg.evt);
179 | }
180 | if(my.proxies['__ALL__']) {
181 | my.proxies['__ALL__'].emit(msg.typ, msg.evt);
182 | }
183 | break;
184 | }
185 | /* `rpc_call` messages are received when an other module wants to call */
186 | /* a local procedure previously exposed. */
187 | case 'rpc_call': {
188 | /* This is an helper function to reply to an `rpc_call` message. It */
189 | /* sets up the headers and store the error or result. */
190 | var rpc_reply = function(err, result) {
191 | msg.oid = msg.hdr.mid; delete msg.hdr.mid;
192 | msg.hdr.typ = 'rpc_reply';
193 | msg.dst = msg.hdr.src; delete msg.hdr.src;
194 | if(err) {
195 | msg.err = { nme: err.name, msg: err.message };
196 | }
197 | else {
198 | msg.res = result;
199 | }
200 | send_message(msg);
201 | };
202 |
203 | if(my.procedures[msg.prc]) {
204 | my.procedures[msg.prc](msg.hdr.src, msg.arg, rpc_reply);
205 | }
206 | else {
207 | rpc_reply(common.err('Procedure unknown: `' + msg.prc,
208 | 'breach_module:procedure_unknown'));
209 | }
210 | break;
211 | }
212 | /* `rpc_reply` messages are received when an `rpc_call` was previously */
213 | /* sent and has received an answer. */
214 | case 'rpc_reply': {
215 | if(my.proxies[msg.hdr.src]) {
216 | var err = null;
217 | if(msg.err) {
218 | err = common.err(msg.err.msg, msg.err.nme);
219 | }
220 | my.proxies[msg.hdr.src].rpc_reply(msg.oid, err, msg.res);
221 | }
222 | break;
223 | }
224 | }
225 | };
226 |
227 | // ### send_message
228 | //
229 | // Sends a message after setting the `message_id`. The message source will be
230 | // set by the core module. This methods returns the `message_id` used.
231 | // ```
232 | // @msg {object} the message object
233 | // @returns {number} the message id
234 | // ```
235 | send_message = function(msg) {
236 | if(!msg || !msg.hdr ||
237 | typeof msg.hdr.typ !== 'string') {
238 | /* We ignore the message. */
239 | return;
240 | }
241 | var mid = ++my.message_id;
242 | msg.hdr.mid = mid;
243 | msg.hdr.ver = my.VERSION;
244 | process.send(msg);
245 | return mid;
246 | };
247 |
248 | /****************************************************************************/
249 | /* PUBLIC METHODS */
250 | /****************************************************************************/
251 | // ### emit
252 | //
253 | // Emits an event for this module
254 | // ```
255 | // @type {string} the event type
256 | // @event {object} serializable JSON object
257 | // ```
258 | emit = function(type, event) {
259 | var msg = {
260 | hdr: {
261 | typ: 'event'
262 | },
263 | typ: type,
264 | evt: event
265 | };
266 | return send_message(msg);
267 | };
268 |
269 | // ### register
270 | //
271 | // Registers for remove events from a given module for a given type
272 | // ```
273 | // @source {string} a regexp string to test against module names [optional]
274 | // @type {string} a regexp string to test against event type [optional]
275 | // @returns {number} registration id
276 | // ```
277 | register = function(source, type) {
278 | var msg = {
279 | hdr: {
280 | typ: 'register'
281 | },
282 | src: source || '.*',
283 | typ: type || '.*'
284 | };
285 | return send_message(msg);
286 | };
287 |
288 | // ### unregister
289 | //
290 | // Unregisters a previously created registration by id
291 | // ```
292 | // @rid {number} registartion id
293 | // ```
294 | unregister = function(rid) {
295 | var msg = {
296 | hdr: {
297 | typ: 'unregister'
298 | },
299 | rid: rid
300 | };
301 | return send_message(msg);
302 | };
303 |
304 | // ### expose
305 | //
306 | // Exposes a new procedure to the other modules
307 | // ```
308 | // @name {string} the procedure call name
309 | // @proc {function(args, cb_(err, res))} the actual procedure
310 | // ```
311 | expose = function(name, proc) {
312 | console.log('EXPOSED: ' + name);
313 | my.procedures[name] = proc;
314 | };
315 |
316 | // ### remove
317 | //
318 | // Removes a previously exposed procedure
319 | // ```
320 | // @name {string} the procedure name to void
321 | // ```
322 | remove = function(name) {
323 | delete my.procedures[name];
324 | };
325 |
326 | // ### module
327 | //
328 | // Creates or retrieve the module proxy singleton
329 | // ```
330 | // @name {string} the module name
331 | // ```
332 | module = function(name) {
333 | if(!name) {
334 | name = '__ALL__';
335 | }
336 | if(!my.proxies[name]) {
337 | my.proxies[name] = module_proxy({
338 | name: name,
339 | send_message: send_message
340 | });
341 | }
342 | return my.proxies[name];
343 | };
344 |
345 | // ### init
346 | //
347 | // Inits the module system. Must be call before registering the `init` proc
348 | // which must be registered within the callback of that method. This lets the
349 | // module perform some work before installing the `init` proc.
350 | // ```
351 | // @cb_ {function(err)}
352 | // ```
353 | init = function(cb_) {
354 | /* Dummy `init` and `kill` procedures that should be overwritten by the */
355 | /* module implementation. */
356 | that.expose('init', function(src, args, cb_) {
357 | return cb_();
358 | });
359 | that.expose('kill', function(src, args, cb_) {
360 | process.nextTick(function() {
361 | process.exit(0);
362 | });
363 | return cb_();
364 | });
365 |
366 | process.on('message', function(msg) {
367 | handle_message(msg);
368 | });
369 |
370 | process.nextTick(function() {
371 | that.emit('internal:ready', {
372 | ver: my.VERSION
373 | });
374 | });
375 |
376 | return cb_();
377 | };
378 |
379 |
380 | common.method(that, 'emit', emit, _super);
381 |
382 | common.method(that, 'register', register, _super);
383 | common.method(that, 'unregister', unregister, _super);
384 |
385 | common.method(that, 'expose', expose, _super);
386 | common.method(that, 'remove', remove, _super);
387 |
388 | common.method(that, 'module', module, _super);
389 |
390 | common.method(that, 'init', init, _super);
391 |
392 | return that;
393 | };
394 |
395 | exports.module = module;
396 |
397 |
--------------------------------------------------------------------------------
/controls/assets/fonts/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/lib/session_manager.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: session_manager.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-05-14 spolu [GiG.fs 0.2.x] local only
10 | * - 2014-04-11 spolu Creation
11 | */
12 | "use strict"
13 |
14 | var events = require('events');
15 | var async = require('async');
16 | var api = require('exo_browser');
17 |
18 | var common = require('./common.js');
19 |
20 | // ## session_manager
21 | //
22 | // The session_manager is based on GiG.fs and lets the user retrieve its
23 | // session from any computer. It also lets the user start private sessions
24 | // that are not synchronized and do not leave any data locally.
25 | //
26 | // If the computer is trusted (the user computer), off_the_record can be set
27 | // to false to let the Content API cache content locally.
28 | //
29 | // The session manager takes three arguments:
30 | // - `off_the_record` whether this computer is trusted for local caching
31 | //
32 | // ```
33 | // @spec { off_the_record }
34 | // ```
35 | var session_manager = function(spec, my) {
36 | var _super = {};
37 | my = my || {};
38 | spec = spec || {};
39 |
40 | my.session_salt = 0;
41 |
42 | my.off_the_record = (typeof spec.off_the_record === 'undefined' ?
43 | true : spec.off_the_record);
44 |
45 | /* { session_id : { session_id, */
46 | /* name, */
47 | /* private, */
48 | /* session } } */
49 | my.sessions = {};
50 | my.gig = null;
51 |
52 | //
53 | // _public_
54 | //
55 | var list_sessions; /* list_sessions(cb_); */
56 | var open_session; /* open_session(session_id, cb_); */
57 | var close_session; /* close_session(session_id, cb_); */
58 | var new_session; /* new_session(priv, name, cb_); */
59 | var destroy_session; /* destroy_session(session_id, cb_); */
60 |
61 | var init; /* init(cb_); */
62 | var kill; /* kill(cb_); */
63 |
64 | //
65 | // _private_
66 | //
67 | var next_session_id; /* next_session_id(); */
68 | var gig_session_reducer; /* gig_session_reducer(oplog); */
69 |
70 | //
71 | // #### _that_
72 | //
73 | var that = new events.EventEmitter();
74 |
75 | /****************************************************************************/
76 | /* PRIVATE HELPERS */
77 | /****************************************************************************/
78 | // ### next_session_id
79 | //
80 | // Returns a freshly computed "kindof" unique session_id
81 | next_session_id = function() {
82 | return common.hash([
83 | ++my.session_salt,
84 | Date.now()
85 | ]).substr(0, 20);
86 | };
87 |
88 | // ### gig_session_reducer
89 | //
90 | // Reducer used with gig to store the `session_manager` state
91 | // ```
92 | // @oplog {array} the array of ops to reduce
93 | // ```
94 | gig_session_reducer = function(oplog) {
95 | /* Returns a dictionary of `session_id` to `session` object. */
96 | var value = {};
97 | oplog.forEach(function(op) {
98 | if(typeof op.value !== 'undefined') {
99 | value = op.value || {};
100 | }
101 | else if(op.payload) {
102 | switch(op.payload.type) {
103 | case 'new': {
104 | value[op.payload.session_id] = {
105 | session_id: op.payload.session_id,
106 | name: op.payload.name
107 | };
108 | break;
109 | }
110 | case 'open': {
111 | if(value[op.payload.session_id]) {
112 | value[op.payload.session_id].last_open = op.date;
113 | }
114 | break;
115 | }
116 | case 'destroy': {
117 | delete value[op.payload.session_id];
118 | }
119 | default: {
120 | break;
121 | }
122 | }
123 | }
124 | });
125 | return value;
126 | };
127 |
128 | /****************************************************************************/
129 | /* PUBLIC METHODS */
130 | /****************************************************************************/
131 | // ### list_sessions
132 | //
133 | // Lists all the sessions, running or idle accessible through gig.fs
134 | // ```
135 | // @cb_ {function(err, sessions)}
136 | // ```
137 | list_sessions = function(cb_) {
138 | my.gig.get('core', 'session', '/sessions', function(err, sessions) {
139 | if(err) {
140 | return cb_(err);
141 | }
142 | var res = {};
143 | Object.keys(sessions).forEach(function(session_id) {
144 | res[session_id] = {
145 | name: sessions[session_id].name,
146 | session_id: session_id
147 | };
148 | });
149 | Object.keys(my.sessions).forEach(function(session_id) {
150 | if(res[session_id]) {
151 | res[session_id].running = true;
152 | }
153 | else {
154 | res[session_id] = {
155 | session_id: session_id,
156 | private: my.sessions[session_id].private || false,
157 | name: my.sessions[session_id].name,
158 | running: true
159 | }
160 | }
161 | });
162 | return cb_(null, res);
163 | });
164 | };
165 |
166 | // ### open_session
167 | //
168 | // Retrieves the session list, check that the session exists and opens it. It
169 | // returns the actual session object.
170 | // ```
171 | // @session_id {string} the session_id
172 | // @cb_ {function(err, session)}
173 | // ```
174 | open_session = function(session_id, cb_) {
175 | var session = null;
176 | async.series([
177 | function(cb_) {
178 | my.gig.get('core', 'session', '/sessions', function(err, sessions) {
179 | if(err) {
180 | return cb_(err);
181 | }
182 | if(!sessions[session_id]) {
183 | return cb_(common.err('Unknown `session_id`: ' + session_id,
184 | 'session_manager:unknown_session_id'));
185 | }
186 | session = sessions[session_id];
187 | return cb_();
188 | });
189 | },
190 | function(cb_) {
191 | my.gig.push('core', 'session', '/sessions', {
192 | type: 'open',
193 | session_id: session_id
194 | }, cb_);
195 | },
196 | function(cb_) {
197 | my.sessions[session_id] = {
198 | session_id: session_id,
199 | private: false,
200 | name: session.name,
201 | session: require('./session.js').session({
202 | session_id: session_id,
203 | gig: my.gig,
204 | off_the_record: my.off_the_record
205 | })
206 | };
207 | my.sessions[session_id].session.on('kill', function() {
208 | delete my.sessions[session_id];
209 | if(Object.keys(my.sessions).length === 0) {
210 | common.log.out('No more active session. Exiting.');
211 | common.exit(0);
212 | }
213 | });
214 | return my.sessions[session_id].session.init(cb_);
215 | },
216 | function(cb_) {
217 | return my.sessions[session_id].session.run_modules(cb_);
218 | }
219 | ], function(err) {
220 | if(err) {
221 | return cb_(err);
222 | }
223 | return cb_(null, my.sessions[session_id].session);
224 | });
225 | };
226 |
227 | // ### close_session
228 | //
229 | // Closes the running session denoted by session_id
230 | // ```
231 | // @session_id {string} the session_id
232 | // @cb_ {function(err, session)}
233 | // ```
234 | close_session = function(session_id, cb_) {
235 | if(!my.sessions[session_id]) {
236 | return cb_(common.err('Unknown `session_id`: ' + session_id,
237 | 'session_manager:unknown_session_id'));
238 | }
239 | /* The object will get cleaned up by the handler on the `kill` event. */
240 | my.sessions[session_id].session.kill(cb_);
241 | };
242 |
243 | // ### new_session
244 | //
245 | // Creates a new session. This session can be private (not stored on GiG.fs)
246 | // and necessarily off_the_record with an in_memory GiG.fs client. Or if it's
247 | // not private, it's stored in the main GiG.fs.
248 | //
249 | // ```
250 | // @priv {boolean} is the session private?
251 | // @name {string} the session name
252 | // @cb_ {function(err, session_id)}
253 | // ```
254 | new_session = function(priv, name, cb_) {
255 | var session_id = next_session_id();
256 | if(priv) {
257 | async.series([
258 | function(cb_) {
259 | my.sessions[session_id] = {
260 | session_id: session_id,
261 | private: true,
262 | session: require('./lib/session.js').session({
263 | session_id: session_id,
264 | gig: null,
265 | off_the_record: true
266 | })
267 | };
268 | return cb_();
269 | },
270 | function(cb_) {
271 | return my.sessions[session_id].session.init(cb_);
272 | },
273 | function(cb_) {
274 | return my.sessions[session_id].session.run_modules(cb_);
275 | }
276 | ], function(err) {
277 | if(err) {
278 | return cb_(err);
279 | }
280 | return cb_(null, my.sessions[session_id].session);
281 | });
282 | }
283 | else {
284 | async.series([
285 | function(cb_) {
286 | my.gig.push('core', 'session', '/sessions', {
287 | type: 'new',
288 | session_id: session_id,
289 | name: name || null
290 | }, cb_);
291 | },
292 | ], function(err) {
293 | if(err) {
294 | return cb_(err);
295 | }
296 | return cb_(null, session_id);
297 | });
298 | }
299 | };
300 |
301 | // ### destroy_session
302 | //
303 | // Destroys a session as well as all the data related to it.
304 | //
305 | // ```
306 | // @session_id {string} the session_id
307 | // @cb_ {function(err)}
308 | // ```
309 | destroy_session = function(session_id, cb_) {
310 | var destroy = false;
311 | async.series([
312 | function(cb_) {
313 | if(my.sessions[session_id]) {
314 | close_session(session_id, cb_);
315 | }
316 | else {
317 | return cb_();
318 | }
319 | },
320 | function(cb_) {
321 | list_sessions(function(err, sessions) {
322 | if(err) {
323 | return cb_(err);
324 | }
325 | if(sessions[session_id]) {
326 | my.gig.push('core', 'session', '/sessions', {
327 | type: 'destroy',
328 | session_id: session_id,
329 | }, cb_);
330 | }
331 | else {
332 | return cb_();
333 | }
334 | });
335 | }
336 | /* TODO(spolu): all related data should also be destroyed (cookies, etc, */
337 | /* ...) which will require some work (API on gig.fs?). */
338 | ], cb_);
339 | };
340 |
341 | // ### init
342 | //
343 | // Initialializes the session manager
344 | // ```
345 | // @cb_ {function(err)} asynchronous callback
346 | // ```
347 | init = function(cb_) {
348 | /* In memory gigs are not accepted at initialization. An other static API */
349 | /* will be provided to open purely private session without "login". */
350 | var core_storage_path = my.off_the_record ? null :
351 | require('path').join(api.data_path('breach'), 'gig.fs', 'core');
352 | var modules_storage_path = my.off_the_record ? null :
353 | require('path').join(api.data_path('breach'), 'gig.fs', 'modules');
354 |
355 | my.gig = require('gig.fs').gig({
356 | local_table: {
357 | 'core': [ { storage_path: core_storage_path } ],
358 | 'modules': [ { storage_path: modules_storage_path } ]
359 | }
360 | });
361 |
362 | my.gig.register('session', gig_session_reducer);
363 |
364 | return my.gig.init(cb_);
365 | };
366 |
367 | // ### kill
368 | //
369 | // Stops and destroys the session_manager
370 | // ```
371 | // @cb_ {function(err)} asynchronous callback
372 | // ```
373 | kill = function(cb_) {
374 | async.series([
375 | function(cb_) {
376 | async.each(my.sessions, function(s, cb_) {
377 | s.kill(cb_);
378 | });
379 | },
380 | function(cb_) {
381 | my.gig.kill(cb_);
382 | }
383 | ], cb_);
384 | };
385 |
386 | common.method(that, 'list_sessions', list_sessions, _super);
387 | common.method(that, 'open_session', open_session, _super);
388 | common.method(that, 'close_session', close_session, _super);
389 | common.method(that, 'new_session', new_session, _super);
390 | common.method(that, 'destroy_session', destroy_session, _super);
391 |
392 | common.method(that, 'init', init, _super);
393 | common.method(that, 'kill', kill, _super);
394 |
395 | return that;
396 | };
397 |
398 | exports.session_manager = session_manager;
399 |
--------------------------------------------------------------------------------
/lib/core_ui.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: core_ui.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-07-02 spolu Fix mixed module out #48
10 | * - 2014-05-29 spolu Support for modules output
11 | * - 2014-05-28 spolu Socket.io integration, auto_update status
12 | * - 2014-04-18 spolu Server shutdown on `kill`
13 | * - 2014-04-17 spolu Creation
14 | */
15 | "use strict"
16 |
17 | var express = require('express');
18 | var async = require('async');
19 | var http = require('http');
20 | var fs = require('fs');
21 |
22 | var common = require('./common.js');
23 | var api = require('exo_browser');
24 |
25 |
26 | // ## core_ui
27 | //
28 | // Breach `core` module UI implementation
29 | //
30 | // The `core_ui` object is in charge of servicing html pages to serve as UI
31 | // for the `core_module`. This pages are served locally using express.
32 | // Eventual routes are handled here.
33 | //
34 | // This includes:
35 | // - A splash page displayed at startup (loading feedback)
36 | // - A module management page
37 | //
38 | // ```
39 | // @spec { core_module, session }
40 | // @inherits {}
41 | // ```
42 | var core_ui = function(spec, my) {
43 | var _super = {};
44 | my = my || {};
45 | spec = spec || {};
46 |
47 | my.core_module = spec.core_module;
48 | my.session = spec.session;
49 |
50 | my.app = null;
51 | my.sockets = {};
52 | my.is_ready = false;
53 | my.base_url = null;
54 |
55 | //
56 | // #### _public_
57 | //
58 | var init; /* init(cb_); */
59 | var kill; /* kill(cb_); */
60 |
61 | var url_for_ui; /* url_for_ui(type); */
62 | var ui_for_url; /* ui_for_url(url); */
63 |
64 | //
65 | // #### _private_
66 | //
67 | var socket_push; /* socket_push(); */
68 | var handshake; /* handshake(type, socket); */
69 | var tail; /* tail(module, socket); */
70 |
71 | var post_about_install; /* post_about_install(req, res, next); */
72 |
73 | var post_modules_cmd; /* post_modules_cmd(req, res, next); */
74 |
75 | //
76 | // #### _that_
77 | //
78 | var that = {};
79 |
80 | /****************************************************************************/
81 | /* PRIVATE HELPERS */
82 | /****************************************************************************/
83 | // ### socket_push
84 | //
85 | // Pushes the current state on the adequates sockets
86 | // ```
87 | // @type {string} the type of client (splash, ...)
88 | // ```
89 | socket_push = function(type) {
90 | var state = null;
91 | async.series([
92 | function(cb_) {
93 | if(type === 'splash') {
94 | state = {
95 | ready: my.is_ready
96 | };
97 | return cb_();
98 | }
99 | else if(type === 'about') {
100 | state = {
101 | update_ready: common.auto_updater.update_ready(),
102 | update_available: common.auto_updater.update_available(),
103 | version: require('./../package.json').version,
104 | update: common.auto_updater.update()
105 | };
106 | return cb_();
107 | }
108 | else if(type === 'modules') {
109 | my.session.module_manager().list(function(err, list) {
110 | if(err) {
111 | return cb_(err);
112 | }
113 | state = list;
114 | return cb_();
115 | });
116 | }
117 | }
118 | ], function(err) {
119 | if(err) {
120 | common.log.error(err);
121 | }
122 | else {
123 | if(my.sockets[type]) {
124 | my.sockets[type].forEach(function(s) {
125 | s.emit(type, state);
126 | });
127 | }
128 | }
129 | });
130 | };
131 |
132 | // ### handshake
133 | //
134 | // Called when a core ui client connected to the Socket
135 | // ```
136 | // @type {string} the type of client (splash, ...)
137 | // @socket {socket.io} the socket.io to connect with
138 | // ```
139 | handshake = function(type, socket) {
140 | common.log.out('[core_ui] HANDSHAKE: ' + type);
141 | my.sockets[type] = my.sockets[type] || [];
142 | my.sockets[type].unshift(socket);
143 | socket_push(type);
144 |
145 | socket.on('disconnect', function() {
146 | common.remove(my.sockets[type], socket, true);
147 | });
148 | };
149 |
150 | // ### tail
151 | //
152 | // Tails the module out to the socket
153 | // ```
154 | // @module {string} the module name
155 | // @socket {socket.io} the socket.io to connect with
156 | // ```
157 | tail = function(module, socket) {
158 | my.session.module_manager().list(function(err, list) {
159 | if(err) {
160 | common.log.error(err);
161 | }
162 | list.forEach(function(m) {
163 | if(m.name === module) {
164 | socket.emit('module', m);
165 |
166 | var r = fs.createReadStream(m.out, { encoding: 'utf8' });
167 | r.on('data', function(data) {
168 | socket.emit('chunk', {
169 | module: module,
170 | data: data
171 | });
172 | });
173 |
174 | var type = 'tail_' + module;
175 | my.sockets[type] = my.sockets[type] || [];
176 | if(my.sockets[type].indexOf(socket) !== -1) {
177 | return;
178 | }
179 | else {
180 | my.sockets[type].push(socket);
181 | }
182 |
183 | var t = new (require('tail').Tail)(m.out);
184 | t.on('error', function(err) {
185 | socket.emit('error', err);
186 | });
187 | t.on('line', function(data) {
188 | socket.emit('chunk', {
189 | module: module,
190 | data: data + '\n'
191 | });
192 | });
193 | socket.on('disconnect', function() {
194 | common.remove(my.sockets[type], socket, true);
195 | t.unwatch();
196 | });
197 | }
198 | });
199 | });
200 | };
201 |
202 |
203 | // ### post_about_install
204 | //
205 | // Express route to install new breach update
206 | // ```
207 | // POST /about/install_breach
208 | // ```
209 | post_about_install = function(req, res, next) {
210 | common.auto_updater.install_update(function(err) {
211 | if(err) {
212 | return next(err);
213 | }
214 | else {
215 | return res.json({
216 | ok: true,
217 | module: module
218 | });
219 | }
220 | });
221 | };
222 |
223 | // ### post_modules_cmd
224 | //
225 | // Express route to operate on a module
226 | // ```
227 | // POST /module/:cmd
228 | // ```
229 | post_modules_cmd = function(req, res, next) {
230 | var path = req.param('path');
231 | var cmd = req.param('cmd');
232 | async.waterfall([
233 | function(cb_) {
234 | switch(cmd) {
235 | case 'add': {
236 | my.session.module_manager().add(path, false, cb_);
237 | break;
238 | }
239 | case 'install': {
240 | my.session.module_manager().install(path, cb_);
241 | break;
242 | }
243 | case 'remove': {
244 | my.session.module_manager().remove(path, cb_);
245 | break;
246 | }
247 | case 'update': {
248 | my.session.module_manager().update(path, cb_);
249 | break;
250 | }
251 | case 'run': {
252 | my.session.module_manager().run_module(path, cb_);
253 | break;
254 | }
255 | case 'kill': {
256 | my.session.module_manager().kill_module(path, cb_);
257 | break;
258 | }
259 | default: {
260 | next(common.err('Invalid module `cmd`: ' + cmd,
261 | 'core_ui:invalid_module_cmd'));
262 | break;
263 | }
264 | }
265 | socket_push('modules');
266 | }
267 | ], function(err, module) {
268 | if(err) {
269 | return next(err);
270 | }
271 | socket_push('modules');
272 | return res.json({
273 | ok: true,
274 | module: module
275 | });
276 | });
277 | };
278 |
279 | /****************************************************************************/
280 | /* SOCKET EVENT HANDLERS */
281 | /****************************************************************************/
282 |
283 |
284 | /****************************************************************************/
285 | /* PUBLIC METHODS */
286 | /****************************************************************************/
287 | // ### url_for_route
288 | //
289 | // Returns a redirect url for the given ui type
290 | // ```
291 | // @type {string} ui type (`splash`, `modules`, ...)
292 | // ```
293 | url_for_ui = function(type) {
294 | switch(type) {
295 | case 'modules': {
296 | return my.base_url + 'modules/';
297 | }
298 | default: {
299 | return my.base_url + 'splash/';
300 | }
301 | }
302 | };
303 |
304 | // ### ui_for_url
305 | //
306 | // Returns whether the url is related to the ui core submodule
307 | // ```
308 | // @url {string} the url to test
309 | // ```
310 | ui_for_url = function(url) {
311 | url = require('url').parse(url);
312 | if((url.hostname === 'localhost' ||
313 | url.hostname === '127.0.0.1') &&
314 | url.port === my.port.toString()) {
315 | switch(url.pathname) {
316 | case '/modules/': {
317 | return 'modules';
318 | }
319 | case '/splash/': {
320 | return 'splash';
321 | }
322 | default: {
323 | return 'unknown';
324 | }
325 | }
326 | }
327 | return null;
328 | };
329 |
330 | /****************************************************************************/
331 | /* INITIALIZATION */
332 | /****************************************************************************/
333 | // ### init
334 | //
335 | // Initialializes the core ui module (spawns an HTTP server) and displays an
336 | // initial splash page.
337 | // ```
338 | // @cb_ {function(err)} asynchronous callback
339 | // ```
340 | init = function(cb_) {
341 | my.app = express();
342 | my.app.use('/', express.static(__dirname + '/../controls'));
343 | my.app.use(require('body-parser')());
344 | my.app.use(require('method-override')())
345 |
346 | my.app.post('/modules/:cmd', post_modules_cmd);
347 | my.app.post('/about/install', post_about_install);
348 |
349 | my.session.on('ready', function() {
350 | my.is_ready = true;
351 | socket_push('splash');
352 | });
353 |
354 | common.auto_updater.on('update_available', function() {
355 | socket_push('about');
356 | });
357 | common.auto_updater.on('update_ready', function() {
358 | socket_push('about');
359 | });
360 |
361 | my.session.module_manager().on('state_change', function(module) {
362 | socket_push('modules');
363 | });
364 | my.session.module_manager().on('update_ready', function() {
365 | socket_push('modules');
366 | });
367 |
368 | async.series([
369 | function(cb_) {
370 | my.http_srv = http.createServer(my.app).listen(0, '127.0.0.1');
371 | my.http_srv.on('listening', function() {
372 | my.port = my.http_srv.address().port;
373 | my.base_url = 'http://127.0.0.1:' + my.port + '/';
374 | common.log.out('[core_ui] HTTP Server started on `' + my.base_url + '`');
375 | return cb_();
376 | });
377 | },
378 | function(cb_) {
379 | var io = require('socket.io').listen(my.http_srv, {
380 | 'log level': 1
381 | });
382 | io.sockets.on('connection', function(socket) {
383 | socket.on('handshake', function(type) {
384 | handshake(type, socket);
385 | });
386 | socket.on('tail', function(module) {
387 | tail(module, socket);
388 | });
389 | });
390 | return cb_();
391 | },
392 | function(cb_) {
393 | /* We open the new tab as soon as the server is ready. */
394 | my.core_module.core_tabs().tabs_new('core', {
395 | visible: true,
396 | focus: true,
397 | id: '__NEW_TAB_ID__'
398 | }, cb_);
399 | }
400 | ], cb_);
401 | };
402 |
403 | // ### kill
404 | //
405 | // Kills the core ui module and shuts down the local server
406 | // ```
407 | // @cb_ {function(err)} asynchronous callback
408 | // ```
409 | kill = function(cb_) {
410 | common.log.out('[core_ui] KILL');
411 | my.http_srv.close(cb_);
412 | return cb_();
413 | };
414 |
415 | common.method(that, 'init', init, _super);
416 | common.method(that, 'kill', kill, _super);
417 |
418 | common.method(that, 'url_for_ui', url_for_ui, _super);
419 | common.method(that, 'ui_for_url', ui_for_url, _super);
420 |
421 | return that;
422 | };
423 |
424 | exports.core_ui = core_ui;
425 |
--------------------------------------------------------------------------------
/lib/core_controls.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Breach: core_controls.js
3 | *
4 | * Copyright (c) 2014, Stanislas Polu. All rights reserved.
5 | *
6 | * @author: spolu
7 | *
8 | * @log:
9 | * - 2014-01-15 spolu Creation
10 | */
11 | "use strict"
12 |
13 | var http = require('http');
14 |
15 | var common = require('./common.js');
16 | var async = require('async');
17 | var api = require('exo_browser');
18 |
19 |
20 | // ## core_controls
21 | //
22 | // Breach `core` module controls implementation.
23 | //
24 | // The `core_controls` object is in charge of tracking controls state and
25 | // exposing the `controls` API to other modules.
26 | //
27 | // ```
28 | // @spec { core_module, session }
29 | // @inherits {}
30 | // ```
31 | var core_controls = function(spec, my) {
32 | var _super = {};
33 | my = my || {};
34 | spec = spec || {};
35 |
36 | my.core_module = spec.core_module;
37 | my.session = spec.session;
38 |
39 | /* { id: { frame, */
40 | /* type, */
41 | /* id } } */
42 | my.controls = {};
43 |
44 | my.control_cnt = 0;
45 |
46 | //
47 | // #### _public_
48 | //
49 | var init; /* init(cb_); */
50 | var kill; /* kill(cb_); */
51 |
52 | var controls_set; /* controls_set(args, cb_); */
53 | var controls_unset; /* controls_unset(args, cb_); */
54 | var controls_dimension; /* controls_dimension(args, cb_); */
55 | var controls_focus; /* controls_focus(args, cb_); */
56 |
57 | //
58 | // #### _private_
59 | //
60 | var control_for_frame; /* control_for_frame(frame); */
61 | var install_context_menu; /* install_context_menu(type); */
62 |
63 | var frame_keyboard; /* frame_keyboard(frame, event); */
64 |
65 | //
66 | // #### _that_
67 | //
68 | var that = {};
69 |
70 | /****************************************************************************/
71 | /* PRIVATE HELPERS */
72 | /****************************************************************************/
73 | // ### control_for_frame
74 | //
75 | // Retrieves the control associated with the specified frame
76 | // ```
77 | // @frame {exo_frame} the frame to search for
78 | // ```
79 | control_for_frame = function(frame) {
80 | for(var type in my.controls) {
81 | if(my.controls.hasOwnProperty(type) &&
82 | my.controls[type].frame === frame) {
83 | return my.controls[type];
84 | }
85 | }
86 | return null;
87 | };
88 |
89 | // ### install_context_menu
90 | //
91 | // Installs the context menu handler for a given control
92 | // ```
93 | // @type {string} the control type
94 | // @id {string} the frame id
95 | // ```
96 | install_context_menu = function(type, id) {
97 | if(!my.controls[type]) {
98 | return;
99 | }
100 | my.controls[type].frame.set_context_menu_handler(function(params, cb_) {
101 | var final = [
102 | { item: 'Reload',
103 | trigger: function() {
104 | if(my.controls[type]) {
105 | my.controls[type].frame.reload(function(err) {
106 | if(err) {
107 | common.log.error(err);
108 | }
109 | });
110 | }
111 | } },
112 | { item: null },
113 | { item: 'Inspect Element',
114 | trigger: function() {
115 | if(my.controls[type]) {
116 | async.parallel({
117 | url: function(cb_) {
118 | my.core_module.exo_session().get_dev_tools_url(function(url) {
119 | return cb_(null, url);
120 | });
121 | },
122 | id: function(cb_) {
123 | my.controls[type].frame.dev_tools_get_id(function(id) {
124 | return cb_(null, id);
125 | });
126 | },
127 | element_at: function(cb_) {
128 | my.controls[type].frame.dev_tools_inspect_element_at(params.x,
129 | params.y, cb_);
130 | }
131 | }, function(err, res) {
132 | var dev_id = res.id;
133 | var url_p = require('url').parse(res.url);
134 | var json_url = 'http://' + url_p.hostname + ':' + url_p.port +
135 | '/json/list';
136 | http.get(json_url, function(res) {
137 | res.setEncoding('utf8');
138 | var data = '';
139 | res.on('data', function(chunk) {
140 | data += chunk;
141 | });
142 | res.on('end', function() {
143 | try {
144 | JSON.parse(data).forEach(function(dev) {
145 | if(dev.id === dev_id) {
146 | var url = 'http://' + url_p.hostname + ':' + url_p.port +
147 | dev.devtoolsFrontendUrl;
148 | common.log.out('[control] DEVTOOLS: ' + url);
149 | my.session.module_manager().core_emit('devtools',
150 | { devtools_url: url });
151 | }
152 | });
153 | }
154 | catch(err) { /* NOP */ }
155 | });
156 | }).on('error', function(err) { /* NOP */ });
157 | });
158 | }
159 | } },
160 | { item: 'Close DevTools',
161 | trigger: function() {
162 | my.session.module_manager().core_emit('devtools',
163 | { devtools_url: null });
164 | } }
165 | ];
166 | return cb_(null, final);
167 | });
168 | };
169 |
170 | /****************************************************************************/
171 | /* EXOBROWSER EVENTS */
172 | /****************************************************************************/
173 | // ### frame_keyboard
174 | //
175 | // Called when a keyboard event is sent to any frame in the ExoBrowser. We
176 | // filter for the frame we recognize and redistribute the event
177 | // ```
178 | // @frame {exo_frame} the target frame of the event
179 | // @event {object} the keyboard event
180 | // ```
181 | frame_keyboard = function(frame, event) {
182 | var c = control_for_frame(frame);
183 | if(c) {
184 | event.frame = {
185 | id: c.id,
186 | control_type: c.type,
187 | type: 'control'
188 | };
189 | my.session.module_manager().core_emit('controls:keyboard', event);
190 | }
191 | };
192 |
193 |
194 | /****************************************************************************/
195 | /* EXPOSED PROCEDURES */
196 | /****************************************************************************/
197 | // ### controls_set
198 | //
199 | // Sets a control for the specified control type.
200 | // Possible control types are: 'TOP', 'BOTTOM', 'LEFT', 'RIGHT'
201 | // ```
202 | // @src {string} source module
203 | // @args {object} { type, url, [dimension], [focus] }
204 | // @cb_ {function(err, res)}
205 | // ```
206 | controls_set = function(src, args, cb_) {
207 | if(!api[args.type + '_CONTROL']) {
208 | return cb_(common.err('Invalid control type: ' + args.type,
209 | 'core_controls:invalid_control_type'));
210 | }
211 | if(!args.url) {
212 | return cb_(common.err('Invalid URL: ' + args.url,
213 | 'core_controls:invalid_url'));
214 | }
215 | if(my.controls[args.type]) {
216 | return cb_(common.err('Control already set: ' + args.type,
217 | 'core_controls:control_already_set'));
218 | }
219 |
220 | var c = {
221 | frame: api.exo_frame({
222 | url: args.url,
223 | session: my.core_module.exo_session()
224 | }),
225 | id: common.hash([++my.control_cnt]),
226 | type: args.type
227 | };
228 | my.controls[args.type] = c;
229 | install_context_menu(args.type);
230 |
231 | async.series([
232 | function(cb_) {
233 | my.core_module.exo_browser().set_control(api[args.type + '_CONTROL'],
234 | c.frame, cb_);
235 | },
236 | function(cb_) {
237 | if(args.dimension) {
238 | my.core_module.exo_browser()
239 | .set_control_dimension(api[args.type + '_CONTROL'],
240 | args.dimension,
241 | cb_);
242 | }
243 | else {
244 | return cb_();
245 | }
246 | },
247 | function(cb_) {
248 | if(args.focus) {
249 | c.frame.focus();
250 | }
251 | else {
252 | return cb_();
253 | }
254 | },
255 | function(cb_) {
256 | /* TODO(spolu): adapt */
257 | //push();
258 | return cb_();
259 | }
260 | ], function(err) {
261 | return cb_(err, { id: c.id });
262 | });
263 | };
264 |
265 | // ### controls_unset
266 | //
267 | // Unset the control for the specified control type
268 | // Possible control types are: 'TOP', 'BOTTOM', 'LEFT', 'RIGHT'
269 | // ```
270 | // @src {string} source module
271 | // @args {object} { type }
272 | // @cb_ {function(err, res)}
273 | // ```
274 | controls_unset = function(src, args, cb_) {
275 | if(!api[args.type + '_CONTROL']) {
276 | return cb_(common.err('Invalid control type: ' + args.type,
277 | 'core_controls:invalid_control_type'));
278 | }
279 | var c = my.controls[args.type];
280 | if(!c) {
281 | return cb_(common.err('Control not set: ' + args.type,
282 | 'core_controls:control_not_set'));
283 | }
284 | async.series([
285 | function(cb_) {
286 | delete my.controls[args.type];
287 | my.core_module.exo_browser().unset_control(api[args.type + '_CONTROL'],
288 | cb_);
289 | },
290 | function(cb_) {
291 | return c.frame.kill(cb_);
292 | },
293 | function(cb_) {
294 | if(global.gc) global.gc();
295 | return cb_();
296 | }
297 | ], cb_);
298 | };
299 |
300 | // ### controls_dimension
301 | //
302 | // Set the control dimension for the specified control type
303 | // Possible control types are: 'TOP', 'BOTTOM', 'LEFT', 'RIGHT'
304 | // ```
305 | // @src {string} source module
306 | // @args {object} { type, dimension, focus }
307 | // @cb_ {function(err, res)}
308 | // ```
309 | controls_dimension = function(src, args, cb_) {
310 | if(!api[args.type + '_CONTROL']) {
311 | return cb_(common.err('Invalid control type: ' + args.type,
312 | 'core_controls:invalid_control_type'));
313 | }
314 | var c = my.controls[args.type];
315 | if(!c) {
316 | return cb_(common.err('Control not set: ' + args.type,
317 | 'core_controls:control_not_set'));
318 | }
319 | async.series([
320 | function(cb_) {
321 | my.core_module.exo_browser()
322 | .set_control_dimension(api[args.type + '_CONTROL'],
323 | args.dimension,
324 | cb_);
325 | },
326 | function(cb_) {
327 | if(args.focus) {
328 | return c.frame.focus(cb_);
329 | }
330 | else {
331 | return cb_();
332 | }
333 | }
334 | ], cb_);
335 | };
336 |
337 | // ### controls_focus
338 | //
339 | // Focuses the control for the specified control type
340 | // Possible control types are: 'TOP', 'BOTTOM', 'LEFT', 'RIGHT'
341 | // ```
342 | // @src {string} source module
343 | // @args {object} { type }
344 | // @cb_ {function(err, res)}
345 | // ```
346 | controls_focus = function(src, args, cb_) {
347 | if(!api[args.type + '_CONTROL']) {
348 | return cb_(common.err('Invalid control type: ' + args.type,
349 | 'core_controls:invalid_control_type'));
350 | }
351 | var c = my.controls[args.type];
352 | if(!c) {
353 | return cb_(common.err('Control not set: ' + args.type,
354 | 'core_controls:control_not_set'));
355 | }
356 | return c.frame.focus(cb_);
357 | };
358 |
359 |
360 | /****************************************************************************/
361 | /* INITIALIZATION */
362 | /****************************************************************************/
363 | // ### init
364 | //
365 | // Initializes the core controls module
366 | // ```
367 | // @cb_ {function(err)} asynchronous callback
368 | // ```
369 | init = function(cb_) {
370 | my.core_module.exo_browser().on('frame_keyboard',
371 | frame_keyboard);
372 | return cb_();
373 | };
374 |
375 | // ### kill
376 | //
377 | // Kills the core controls module and all associated controls
378 | // ```
379 | // @cb_ {function(err)} asynchronous callback
380 | // ```
381 | kill = function(cb_) {
382 | async.each(Object.keys(my.controls), function(type, cb_) {
383 | var c = my.controls[type];
384 | delete my.controls[type];
385 | my.core_module.exo_browser().unset_control(api[type + '_CONTROL'],
386 | function(err) {
387 | if(err) {
388 | /* We ignore the error as the browser may have already been */
389 | /* killed when we get here. */
390 | common.log.error(err);
391 | }
392 | return c.frame.kill(function(err) {
393 | if(err) {
394 | /* We ignore the error as the frames may have already been */
395 | /* killed when we get here. */
396 | common.log.error(err);
397 | }
398 | return cb_();
399 | });
400 | });
401 | }, function(err) {
402 | if(global.gc) global.gc();
403 | return cb_(err);
404 | });
405 | };
406 |
407 | common.method(that, 'init', init, _super);
408 | common.method(that, 'kill', kill, _super);
409 |
410 | common.method(that, 'controls_set', controls_set, _super);
411 | common.method(that, 'controls_unset', controls_unset, _super);
412 | common.method(that, 'controls_dimension', controls_dimension, _super);
413 | common.method(that, 'controls_focus', controls_focus, _super);
414 |
415 | return that;
416 | };
417 |
418 | exports.core_controls = core_controls;
419 |
--------------------------------------------------------------------------------