├── 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 |
2 |
3 | << 4 | {{module.name}} 5 | {{module.out}} 6 | [restart] 7 |
8 |
9 |
{{data}}
10 |
11 |
12 | -------------------------------------------------------------------------------- /controls/splash/partials/splash.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 |
7 |
8 | [> modules] 9 |
10 |
11 | dev@breach.cc | breach.cc/hack 12 |
13 |
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 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 20 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 46 | 49 | 52 | 53 | 56 | 59 | 60 | 64 | 67 | 71 | 74 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 92 | 95 | 96 |
Core
Breachv{{about.version}}running 15 | up to date 16 | 18 | need update (v{{about.update.version}}) 19 | 21 | install & restart 22 | 24 | install manually 25 |
Modules
{{m.type}}{{m.name}}v{{m.version}}{{m.owner}}{{m.tag}} 44 | installing 45 | 47 | running 48 | 50 | stopped 51 | 54 | up to date 55 | 57 | need restart 58 | 61 | kill 62 | run 63 | 65 | restart 66 | 68 | update 70 | 72 | remove 73 | 75 | out 76 |
Install Modules
88 |
89 | 90 |
91 |
93 | install 94 |
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 |
7 | Welcome!0/4 8 |
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 |
36 | Adding a module 37 | 1/4 38 |
39 |
40 |
41 | 42 | 43 | 44 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 59 | 60 | 61 | 62 | 65 | 66 |
Adding module... 45 | [OK] 46 |
Downloading `mod_strip`... 51 | [OK] 52 |
Installing dependencies... 57 | [OK] 58 |
Running... 63 | [OK] 64 |
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 |
102 | Usage statistics 103 | 2/4 104 |
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 |
135 | About `mod_strip` 136 | 3/4 137 |
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 |
173 | Finishing touches 174 | 4/4 175 |
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 | --------------------------------------------------------------------------------