├── .gitignore ├── .jshintrc ├── CHANGELOG.md ├── Gruntfile.js ├── LICENCE.txt ├── README.md ├── bower.json ├── client ├── app │ ├── app.config.js │ ├── app.module.js │ ├── app.routes.js │ ├── assets │ │ ├── css │ │ │ ├── bottom.css │ │ │ ├── main.css │ │ │ └── top.css │ │ ├── fonts │ │ │ ├── PT Sans Caption.ttf │ │ │ ├── PT Sans Narrow.ttf │ │ │ └── PT Sans.ttf │ │ └── img │ │ │ └── search_x.gif │ ├── controllers │ │ ├── main_controller.js │ │ └── toolbar_controller.js │ ├── factories │ │ ├── request.js │ │ └── socket.js │ ├── modules │ │ ├── app.behaviour │ │ │ ├── app.behaviour.module.js │ │ │ ├── assets │ │ │ │ ├── css │ │ │ │ │ ├── behaviour.css │ │ │ │ │ └── behaviour_search.css │ │ │ │ └── img │ │ │ │ │ ├── class-icon.svg │ │ │ │ │ ├── exception-icon.svg │ │ │ │ │ └── module-icon.svg │ │ │ ├── controllers │ │ │ │ ├── behaviour_controller.js │ │ │ │ └── behaviour_search_controller.js │ │ │ ├── factories │ │ │ │ └── behaviour.js │ │ │ ├── partials │ │ │ │ ├── _behaviour_item.html │ │ │ │ ├── _behaviour_list_box.html │ │ │ │ └── _search.html │ │ │ └── services │ │ │ │ └── behaviour_service.js │ │ ├── app.gem │ │ │ ├── app.gem.module.js │ │ │ ├── assets │ │ │ │ ├── css │ │ │ │ │ ├── gem.css │ │ │ │ │ └── gem_search.css │ │ │ │ └── img │ │ │ │ │ ├── gem-hover-icon.svg │ │ │ │ │ ├── gem-icon.svg │ │ │ │ │ └── package-icon.svg │ │ │ ├── controllers │ │ │ │ ├── gem_controller.js │ │ │ │ └── gem_search_controller.js │ │ │ ├── partials │ │ │ │ ├── _gem_item.html │ │ │ │ ├── _gem_list_box.html │ │ │ │ └── _search.html │ │ │ └── services │ │ │ │ └── gem_service.js │ │ ├── app.group │ │ │ ├── app.group.module.js │ │ │ ├── assets │ │ │ │ ├── css │ │ │ │ │ ├── group.css │ │ │ │ │ └── group_bar.css │ │ │ │ └── img │ │ │ │ │ ├── c-group-icon.svg │ │ │ │ │ └── ruby-group-icon.svg │ │ │ ├── controllers │ │ │ │ ├── group_bar_controller.js │ │ │ │ └── group_controller.js │ │ │ ├── partials │ │ │ │ ├── _group_bar.html │ │ │ │ ├── _group_list_box.html │ │ │ │ └── _groups.html │ │ │ └── services │ │ │ │ ├── group.js │ │ │ │ └── group_bar.js │ │ ├── app.method │ │ │ ├── app.method.module.js │ │ │ ├── assets │ │ │ │ └── css │ │ │ │ │ └── method.css │ │ │ ├── controllers │ │ │ │ └── method_controller.js │ │ │ ├── factories │ │ │ │ └── method_group.js │ │ │ ├── partials │ │ │ │ ├── _method_item.html │ │ │ │ └── _method_list_box.html │ │ │ └── services │ │ │ │ └── method_service.js │ │ ├── app.source │ │ │ ├── app.source.module.js │ │ │ ├── assets │ │ │ │ └── css │ │ │ │ │ └── source.css │ │ │ ├── controllers │ │ │ │ └── source_controller.js │ │ │ ├── directives │ │ │ │ └── gem_dependency.js │ │ │ ├── partials │ │ │ │ ├── _gem_dependency_directive.html │ │ │ │ └── _source.html │ │ │ └── services │ │ │ │ └── source_service.js │ │ └── vendor │ │ │ ├── marked │ │ │ ├── factories │ │ │ │ └── marked.js │ │ │ └── marked.module.js │ │ │ └── underscore │ │ │ ├── factories │ │ │ └── _.js │ │ │ └── underscore.module.js │ ├── services │ │ ├── callback_store_service.js │ │ ├── event_loop.js │ │ ├── init_service.js │ │ ├── message_dispatcher.js │ │ ├── source.js │ │ └── toolbar.js │ └── views │ │ └── main.html └── index.html ├── package.json ├── screenshots └── readme.png └── test ├── config └── karma.conf.js └── unit └── gem_controller_spec.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | bower_components 4 | .#* 5 | builds 6 | cache 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "curly": true, 4 | "browser": true, 5 | "strict": true, 6 | "debug": false, 7 | "boss": false, 8 | "expr": false, 9 | "loopfunc": false, 10 | "unused": true, 11 | "undef": true, 12 | "shadow": true, 13 | "singleGroups": true, 14 | "nonew": true, 15 | "nocomma": true, 16 | "maxdepth": 4, 17 | "bitwise": false, 18 | 19 | "globals": { 20 | "angular": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | System Browser Client 2 | ===================== 3 | 4 | ### v0.1.0 (July 20, 2015) 5 | 6 | * Initial release 7 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON("package.json"), 6 | dist: 'dist/', 7 | 8 | watch: { 9 | files: [ 10 | 'client/**/*.js', 11 | 'client/**/*.css', 12 | 'client/**/*.html', 13 | 'client/index.html', 14 | 'Gruntfile.js' 15 | ], 16 | tasks: ['concat', 'cssmin', 'copy'] 17 | }, 18 | 19 | concat: { 20 | dist: { 21 | src: [ 22 | 'node_modules/angular/angular.js', 23 | 'node_modules/angular-route/angular-route.js', 24 | 'node_modules/angular-scroll/angular-scroll.js', 25 | 26 | 'bower_components/raf/index.js', 27 | 'bower_components/angular-ui-layout/ui-layout.js', 28 | 'bower_components/angular-google-chart/ng-google-chart.js', 29 | 30 | 'client/app/app.module.js', 31 | 'client/app/app.routes.js', 32 | 'client/app/app.config.js', 33 | 34 | 'client/app/modules/vendor/**/*.module.js', 35 | 36 | 'client/app/**/*.js', 37 | ], 38 | 39 | dest: '<%= dist %>sb.js' 40 | } 41 | }, 42 | 43 | cssmin: { 44 | options: { 45 | shorthandCompacting: false, 46 | roundingPrecision: -1 47 | }, 48 | 49 | target: { 50 | files: { 51 | '<%= dist %>sb.css': [ 52 | 'node_modules/normalize.css/normalize.css', 53 | 'bower_components/angular-ui-layout/ui-layout.css', 54 | 55 | 'client/app/modules/**/assets/css/*', 56 | 'client/app/assets/css/top.css', 57 | 'client/app/assets/css/bottom.css', 58 | 'client/app/assets/css/main.css' 59 | ] 60 | } 61 | } 62 | }, 63 | 64 | copy: { 65 | main: { 66 | files: [ 67 | { 68 | expand: true, 69 | flatten: true, 70 | filter: 'isFile', 71 | src: [ 72 | 'client/app/views/*', 73 | 'client/app/assets/**/*', 74 | 'client/app/modules/**/assets/img/*', 75 | 'client/index.html' 76 | ], 77 | dest: '<%= dist %>' 78 | }, 79 | 80 | { 81 | expand: true, 82 | cwd: 'client/app', 83 | src: [ 84 | 'partials/*', 85 | 'modules/**/partials/*' 86 | ], 87 | dest: '<%= dist %>' 88 | } 89 | ] 90 | } 91 | }, 92 | 93 | nwjs: { 94 | options: { 95 | platforms: ['osx32', 'osx64', 'linux32', 'linux64'], 96 | buildDir: './builds' 97 | }, 98 | src: [ 99 | 'package.json', 100 | 101 | 'node_modules/marked/**/*', 102 | 'node_modules/underscore/**/*', 103 | 104 | 'dist/**/*' 105 | ] 106 | } 107 | }); 108 | 109 | grunt.loadNpmTasks('grunt-contrib-concat'); 110 | grunt.loadNpmTasks('grunt-contrib-watch'); 111 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 112 | grunt.loadNpmTasks('grunt-contrib-copy'); 113 | grunt.loadNpmTasks('grunt-nw-builder'); 114 | 115 | grunt.registerTask('compile', ['concat', 'cssmin', 'copy']); 116 | grunt.registerTask('default', ['compile', 'watch']); 117 | }; 118 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2015 Kyrylo Silin 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source distribution. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | System Browser Client 2 | == 3 | 4 | * [Repository](https://github.com/kyrylo/system_browser_client/) 5 | * [Server][srv] 6 | * [Video demonstration](https://youtu.be/CKDxMBC86zA) 7 | 8 | Description 9 | -- 10 | 11 | **NOTE:** this is beta software. It contains bugs and sometimes is unresponsive. 12 | 13 | System Browser Client is a desktop app for browsing Ruby code. Just click to get 14 | details about a module or class and explore its namespace or see the source of a 15 | method. 16 | 17 | ![System Browser Client](/screenshots/readme.png) 18 | 19 | The browser does not display information about behaviours in realtime. That 20 | is, if you launch the browser and define a class after it, the browser won't 21 | display it. 22 | 23 | Installation 24 | ------------ 25 | 26 | At the moment System Browser Client is not packaged, so you have to install it 27 | manually. Sorry about that. 28 | 29 | 1. Install the [server][srv] (`gem install system_browser`) 30 | 1. Select a suitable distribution for your operating system and download it 31 | * [OSX x32](https://www.dropbox.com/s/8n9d1sz2skau1d5/system-browser-client_osx32.zip?dl=1) 32 | * [OSX x64](https://www.dropbox.com/s/22l6mzmmztdcd2g/system-browser-client_osx64.zip?dl=1) 33 | * [GNU/Linux x32](https://www.dropbox.com/s/hnb70xmv9sitp45/system-browser-client_linux32.zip?dl=1) 34 | * [GNU/Linux x64](https://www.dropbox.com/s/kbn9r5dncb9bczw/system-browser-client_linux64.zip?dl=1) 35 | 1. Unzip the downloaded archive 36 | 1. Make sure the server can verify that the client is installed 37 | * For OSX drag the unzipped application to Applications folder 38 | * For GNU/Linux add the `system_browser` executable to your `$PATH` variable 39 | 1. The client is installed and you are ready to work with the server now 40 | 1. Important: do not run the client manually (the information will be missing) 41 | 42 | Quick test. Start IRB and copy-paste this: 43 | 44 | ```ruby 45 | require 'system_browser' 46 | SystemBrowser.start 47 | ``` 48 | 49 | Credits 50 | ------- 51 | 52 | * Big thanks to [@havenwood](https://github.com/havenwood) for helping me 53 | testing it on OSX 54 | 55 | Licence 56 | ------- 57 | 58 | The project uses the Zlib License. See LICENCE.txt file for more information. 59 | 60 | [srv]: https://github.com/kyrylo/system_browser_server/ 61 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "system_browser_client", 3 | "version": "0.0.0", 4 | "authors": [ 5 | "Kyrylo Silin " 6 | ], 7 | "license": "Zlib", 8 | "ignore": [ 9 | "**/.*", 10 | "node_modules", 11 | "bower_components", 12 | "test", 13 | "tests" 14 | ], 15 | "dependencies": { 16 | "angular-ui-layout": "bower", 17 | "ng-scrollbars": "~0.0.5", 18 | "angular-google-chart": "~0.0.11" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /client/app/app.config.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | })(window.global); 5 | -------------------------------------------------------------------------------- /client/app/app.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | var applicationModules = [ 5 | 'app.gem', 6 | 'app.behaviour', 7 | 'app.group', 8 | 'app.method', 9 | 'app.source' 10 | ]; 11 | 12 | var vendorModules = [ 13 | 'underscore', 14 | 'marked', 15 | 'ngRoute', 16 | 'ui.layout', 17 | 'duScroll', 18 | 'googlechart' 19 | ]; 20 | 21 | var appModules = applicationModules.concat(vendorModules); 22 | 23 | global.app = angular.module('systemBrowser', appModules); 24 | })(window.global, window.angular); 25 | -------------------------------------------------------------------------------- /client/app/app.routes.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | global.app.config(['$routeProvider', function($routeProvider) { 5 | $routeProvider 6 | .when('/', { 7 | controller: 'MainController', 8 | templateUrl: 'main.html' 9 | }) 10 | .otherwise({redirectTo: '/'}); 11 | }]); 12 | })(window.global); 13 | -------------------------------------------------------------------------------- /client/app/assets/css/bottom.css: -------------------------------------------------------------------------------- 1 | #bottom { 2 | width: 100vw; 3 | height: 50vh; 4 | overflow: hidden; 5 | } 6 | 7 | #bottom-content { 8 | height: 100%; 9 | overflow: hidden; 10 | } 11 | -------------------------------------------------------------------------------- /client/app/assets/css/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'PT Sans'; 3 | src: url('PT Sans.ttf') format('truetype'); 4 | } 5 | 6 | @font-face { 7 | font-family: 'PT Sans Caption'; 8 | src: url('PT Sans Caption.ttf') format('truetype'); 9 | } 10 | 11 | @font-face { 12 | font-family: 'PT Sans Narrow'; 13 | src: url('PT Sans Narrow.ttf') format('truetype'); 14 | } 15 | 16 | html, body { 17 | height: 100vh; 18 | } 19 | 20 | body { 21 | font-family: PT Sans; 22 | font-size: 80%; 23 | color: #272727; 24 | } 25 | 26 | pre { 27 | margin: 0; 28 | height: 100%; 29 | font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; 30 | } 31 | 32 | #layout { 33 | overflow: hidden; 34 | } 35 | 36 | .ui-splitbar { 37 | box-shadow: 0px -2px 2px rgba(0, 0, 0, 0.16); 38 | overflow: hidden; 39 | } 40 | 41 | .ui-splitbar:hover { 42 | box-shadow: 0 -3px 3px rgba(77, 11, 79, 0.25); 43 | transition: box-shadow 0.3s linear; 44 | } 45 | 46 | .ui-splitbar::before { 47 | content: "="; 48 | font-size: 125%; 49 | margin-top: 1px; 50 | font-family: "PT Mono"; 51 | color: #8b8b8b; 52 | } 53 | 54 | #main-toolbar { 55 | height: 23px; 56 | box-shadow: 0 2px 2px rgba(0, 0, 0, 0.16); 57 | z-index: 2; 58 | position: relative; 59 | } 60 | -------------------------------------------------------------------------------- /client/app/assets/css/top.css: -------------------------------------------------------------------------------- 1 | #top { 2 | width: 100vw; 3 | height: 50vh; 4 | overflow: auto; 5 | } 6 | 7 | .list-box { 8 | width: 25%; 9 | float: left; 10 | height: 100%; 11 | display: flex; 12 | flex-direction: column; 13 | } 14 | 15 | .list-box > div { 16 | height: 100%; 17 | margin-bottom: 3px; 18 | padding-top: 3px; 19 | } 20 | 21 | #gem-list-box .filter-input, 22 | #behaviour-list-box .filter-input { 23 | height: 22px; 24 | } 25 | 26 | #gem-list-box .filter-input input, 27 | #behaviour-list-box .filter-input input { 28 | width: calc(100% - 22.5px); 29 | border-top: none; 30 | border-left: none; 31 | border-right: 1px solid #E5E5E5; 32 | background-color: #F5F5F5; 33 | color: #35206B; 34 | box-shadow: 0px 2px 2px rgba(0,0,0,.17); 35 | border-bottom: 1px solid #C3C3C3; 36 | } 37 | 38 | #gem-list-box .filter-input input:focus, 39 | #behaviour-list-box .filter-input input:focus { 40 | background-color: #fff; 41 | } 42 | 43 | .list-box-content { 44 | overflow: auto; 45 | } 46 | 47 | .list-box-content { 48 | border-right: 1px solid #e5e5e5; 49 | } 50 | 51 | .list-box ul { 52 | list-style-type: none; 53 | padding: 0; 54 | margin: 0; 55 | float: left; 56 | display: table; 57 | width: calc(100% - 18px); 58 | line-height: 160%; 59 | } 60 | 61 | .list-box ul li.highlighted { 62 | background-color: #dadacf; 63 | } 64 | 65 | .list-box ul li { 66 | cursor: pointer; 67 | white-space: nowrap; 68 | display: table-row; 69 | width: calc(100% + 18px); 70 | float: left; 71 | transition: text-shadow 0.1s ease; 72 | } 73 | 74 | .list-box ul li.selected { 75 | background-color: #9763b0 !important; 76 | color: #fff; 77 | text-shadow: -1px -1px 0 rgba(55, 55, 55, 0.59) !important; 78 | } 79 | 80 | .list-box ul li:nth-child(odd) { 81 | background: #f5f5f5; 82 | } 83 | 84 | .list-box ul li:hover { 85 | background-color: #ead3ff; 86 | } 87 | 88 | .list-box-content .indent { 89 | visibility: visible; 90 | color: #ce93d0; 91 | } 92 | 93 | .list-box-content .selected .indent { 94 | visibility: visible; 95 | color: #d3b6d3; 96 | } 97 | 98 | .list-box-content .no-indent { 99 | visibility: hidden; 100 | } 101 | 102 | .list-box-content .display-name, 103 | .list-box-content .indent, 104 | .list-box-content .no-indent { 105 | white-space: pre; 106 | } 107 | 108 | .icon { 109 | height: 18px; 110 | width: 18px; 111 | float: left; 112 | position: relative; 113 | top: 2px; 114 | text-align: center; 115 | margin-left: 3px; 116 | } 117 | 118 | .icon i { 119 | width: 16px; 120 | height: 16px; 121 | float: left; 122 | } 123 | 124 | .list-box .filter-input input { 125 | z-index: 10; 126 | position: relative; 127 | } 128 | -------------------------------------------------------------------------------- /client/app/assets/fonts/PT Sans Caption.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyrylo/system_browser_client/e54ed63ba4d01a16ed98fef56af3f28b673b6839/client/app/assets/fonts/PT Sans Caption.ttf -------------------------------------------------------------------------------- /client/app/assets/fonts/PT Sans Narrow.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyrylo/system_browser_client/e54ed63ba4d01a16ed98fef56af3f28b673b6839/client/app/assets/fonts/PT Sans Narrow.ttf -------------------------------------------------------------------------------- /client/app/assets/fonts/PT Sans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyrylo/system_browser_client/e54ed63ba4d01a16ed98fef56af3f28b673b6839/client/app/assets/fonts/PT Sans.ttf -------------------------------------------------------------------------------- /client/app/assets/img/search_x.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyrylo/system_browser_client/e54ed63ba4d01a16ed98fef56af3f28b673b6839/client/app/assets/img/search_x.gif -------------------------------------------------------------------------------- /client/app/controllers/main_controller.js: -------------------------------------------------------------------------------- 1 | (function(global, process, require) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, $timeout, socket, eventLoop, 5 | initService) { 6 | var gui = require('nw.gui'); 7 | var win = gui.Window.get(); 8 | 9 | process.on('SIGINT', function() { 10 | console.log('Received SIGINT, bye-bye!'); 11 | gui.App.quit(); 12 | }); 13 | 14 | win.on('close', function() { 15 | console.log('Shutting down the client normally...'); 16 | 17 | if (socket.writable) { 18 | socket.end('FIN'); 19 | } 20 | 21 | this.close(true); 22 | }); 23 | 24 | socket.on('data', eventLoop.init()); 25 | 26 | $rootScope.$on('init', function() { 27 | $timeout(function() { 28 | initService.sendPid(process.pid); 29 | $scope.$broadcast('get:gem:all'); 30 | }); 31 | }); 32 | }; 33 | 34 | global.app.controller('MainController', [ 35 | '$scope', 36 | '$rootScope', 37 | '$timeout', 38 | 'socket', 39 | 'eventLoop', 40 | 'initService', 41 | controller]); 42 | })(window.global, window.process, window.require); 43 | -------------------------------------------------------------------------------- /client/app/controllers/toolbar_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, toolbar) { 5 | }; 6 | 7 | global.app.controller('ToolbarController', [ 8 | '$scope', 9 | '$rootScope', 10 | 'toolbar', 11 | controller]); 12 | })(window.global); 13 | -------------------------------------------------------------------------------- /client/app/factories/request.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var Request = function(action, resource, scope, other) { 5 | this.body = { 6 | callbackId: null, 7 | system_browser_server: { 8 | action: action, 9 | resource: resource, 10 | scope: scope, 11 | other: other 12 | } 13 | }; 14 | }; 15 | 16 | Request.prototype.constructor = Request; 17 | 18 | Request.prototype.to_json = function() { 19 | return JSON.stringify(this.body) + '\n'; 20 | }; 21 | 22 | Request.prototype.setCallbackId = function(callbackId) { 23 | this.body.callbackId = callbackId; 24 | }; 25 | 26 | var factory = function() { 27 | return Request; 28 | }; 29 | 30 | global.app.factory('Request', [ 31 | factory 32 | ]); 33 | })(window.global); 34 | -------------------------------------------------------------------------------- /client/app/factories/socket.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var net = require('net'); 5 | 6 | var socket = new net.Socket(); 7 | 8 | socket.connect(9696, '127.0.0.1', function() { 9 | console.log('Connected to 127.0.0.1:9696'); 10 | }); 11 | 12 | socket.send = function(request) { 13 | var defer = this.$q.defer(), 14 | callbackId = this.callbackStoreService.set(defer); 15 | 16 | request.setCallbackId(callbackId); 17 | socket.write(request.to_json()); 18 | 19 | return defer.promise; 20 | }; 21 | 22 | var factory = function($q, callbackStoreService) { 23 | socket.$q = $q; 24 | socket.callbackStoreService = callbackStoreService; 25 | 26 | return socket; 27 | }; 28 | 29 | global.app.factory('socket', [ 30 | '$q', 31 | 'callbackStoreService', 32 | factory 33 | ]); 34 | })(window.global); 35 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/app.behaviour.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.app.behaviour = angular.module('app.behaviour', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/assets/css/behaviour.css: -------------------------------------------------------------------------------- 1 | #behaviour-list-box .class-icon { 2 | background: url("class-icon.svg"); 3 | } 4 | 5 | #behaviour-list-box .module-icon { 6 | background: url("module-icon.svg"); 7 | } 8 | 9 | #behaviour-list-box .exception-icon { 10 | background: url("exception-icon.svg"); 11 | } 12 | 13 | #behaviour-list-box .superclass .classname { 14 | text-decoration: underline; 15 | } 16 | 17 | #behaviour-list-box .superclass .classname:hover { 18 | text-decoration: underline; 19 | color: #619da1; 20 | } 21 | 22 | #behaviour-list-box .superclass { 23 | color: #c5bec7; 24 | } 25 | 26 | #behaviour-list-box .selected .superclass { 27 | color: #ff95f2; 28 | } 29 | 30 | #behaviour-list-box .selected .classname:hover { 31 | color: #fcd7ff; 32 | } 33 | 34 | #behaviour-list-box ul li.no-behaviour { 35 | color: #99869d; 36 | cursor: default; 37 | } 38 | 39 | #behaviour-list-box ul li.no-behaviour:hover { 40 | background-color: #f5f5f5; 41 | } 42 | 43 | #behaviour-list-box > div { 44 | height: 100%; 45 | } 46 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/assets/css/behaviour_search.css: -------------------------------------------------------------------------------- 1 | #behaviour-list-box .filter-input { 2 | width: 100%; 3 | } 4 | 5 | #behaviour-list-box .clearable { 6 | background: #fff url("search_x.gif") no-repeat right -10px center; 7 | border: 1px solid #999; 8 | padding: 3px 18px 3px 4px; 9 | transition: background 0.18s; 10 | } 11 | 12 | #behaviour-list-box .clearable.x { 13 | background-position: right 5px center; 14 | } 15 | 16 | #behaviour-list-box .clearable.on-x { 17 | cursor: pointer; 18 | } 19 | 20 | #behaviour-list-box > div.with-search { 21 | height: calc(100% - 27px); 22 | } 23 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/assets/img/class-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 42 | 43 | 46 | 50 | 54 | 58 | 59 | 68 | 69 | 94 | 96 | 97 | 99 | image/svg+xml 100 | 102 | 103 | 104 | 105 | 106 | 111 | 117 | C 129 | C 141 | 142 | 143 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/assets/img/exception-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 42 | 43 | 46 | 50 | 54 | 58 | 59 | 68 | 69 | 94 | 96 | 97 | 99 | image/svg+xml 100 | 102 | 103 | 104 | 105 | 106 | 111 | 117 | E 129 | E 141 | E 153 | 154 | 155 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/assets/img/module-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 42 | 43 | 46 | 50 | 54 | 58 | 59 | 68 | 69 | 94 | 96 | 97 | 99 | image/svg+xml 100 | 102 | 103 | 104 | 105 | 106 | 111 | 117 | 119 | M 131 | M 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/controllers/behaviour_controller.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, $sce, $timeout, behaviourService, 5 | gemService, Behaviour, _) { 6 | // --- Public variables ---------------------------------------------------- 7 | 8 | $scope.behaviours = []; 9 | 10 | $scope.searchText = ''; 11 | 12 | // --- Private variables --------------------------------------------------- 13 | 14 | var noBehavioursMsg = 'No behaviours found'; 15 | 16 | // --- Private methods ----------------------------------------------------- 17 | 18 | var resetMethodState = function() { 19 | $rootScope.$broadcast('reset-methods'); 20 | $rootScope.$broadcast('reset-source'); 21 | }; 22 | 23 | var emptyBehaviour = function() { 24 | return [{displayName: noBehavioursMsg, noop: true}]; 25 | }; 26 | 27 | var scrollTo = function(behaviour) { 28 | var container = angular.element(document.getElementById('behaviour-container')); 29 | var li = angular.element(document.getElementById(behaviour.name)); 30 | 31 | if (li.length === 0) { 32 | return; 33 | } else { 34 | container.scrollTo(li, 50, 300); 35 | } 36 | }; 37 | 38 | var selectBehaviour = function(behaviour) { 39 | scrollTo(behaviour); 40 | behaviour.selected = true; 41 | }; 42 | 43 | var deselectBehaviour = function(behaviour) { 44 | behaviour.selected = false; 45 | }; 46 | 47 | var hideIndentation = function(behaviour) { 48 | if (behaviour.selected) { 49 | return; 50 | } 51 | 52 | behaviour.visibleIndentation = false; 53 | }; 54 | 55 | var showIndentation = function(behaviour) { 56 | behaviour.visibleIndentation = true; 57 | }; 58 | 59 | var findBehaviourByName = function(behaviourName) { 60 | var behaviour = _.find($scope.behaviours, function(behaviour) { 61 | if (behaviour.name === behaviourName) { 62 | return behaviour; 63 | } else { 64 | return false; 65 | } 66 | }); 67 | 68 | return behaviour; 69 | }; 70 | 71 | // --- Watchers ------------------------------------------------------------ 72 | 73 | $scope.$watch("behaviours", function (behs) { 74 | if (behs && behs.length !== 0) { 75 | var selected = _.find(behs, function(behaviour) { 76 | return behaviour.selected; 77 | }); 78 | 79 | if (selected) { 80 | $timeout(function() { 81 | scrollTo(selected); 82 | }); 83 | } 84 | } 85 | }); 86 | 87 | // --- Events -------------------------------------------------------------- 88 | 89 | $scope.$on('get:behaviour:all', function(_e, gem, selectGemDeferred) { 90 | behaviourService.getAllFrom(gem.name).then(function(behaviours) { 91 | resetMethodState(); 92 | 93 | if (behaviours.length === 0) { 94 | behaviours = emptyBehaviour(); 95 | } else { 96 | behaviours = _.sortBy(behaviours, 'name').map(function(rawB) { 97 | var behaviour = new Behaviour(rawB); 98 | behaviour.indent($sce); 99 | 100 | return behaviour; 101 | }); 102 | } 103 | 104 | $scope.behaviours = behaviours; 105 | selectGemDeferred.resolve(); 106 | }); 107 | }); 108 | 109 | $scope.$on('list-box:behaviour:selected', function() { 110 | $scope.behaviours.forEach(deselectBehaviour); 111 | }); 112 | 113 | $scope.$on('reset-behaviour', function() { 114 | $scope.behaviours = []; 115 | $rootScope.$broadcast('reset-source'); 116 | }); 117 | 118 | $scope.$on('search-change', function(_event, val) { 119 | $scope.searchText = val; 120 | }); 121 | 122 | // --- Public methods ------------------------------------------------------ 123 | 124 | $scope.showGroups = function(behaviour) { 125 | $rootScope.$broadcast('get:method:all', behaviour); 126 | }; 127 | 128 | $scope.selectBehaviour = function($event, behaviour) { 129 | if (behaviour.noop) { 130 | return; 131 | } 132 | 133 | $scope.$emit('list-box:behaviour:selected'); 134 | selectBehaviour(behaviour); 135 | 136 | $scope.behaviours.forEach(hideIndentation); 137 | showIndentation(behaviour); 138 | }; 139 | 140 | $scope.openSuperclass = function($event, superclass) { 141 | $event.stopPropagation(); 142 | 143 | gemService.getSelectedGem().then(function(gem) { 144 | behaviourService.openClass(superclass, gem).then(function(selectables) { 145 | gemService.selectGem(selectables.gem).then(function() { 146 | var behaviour = findBehaviourByName(selectables.behaviour); 147 | 148 | $scope.showGroups(behaviour); 149 | selectBehaviour(behaviour); 150 | }); 151 | }); 152 | }); 153 | }; 154 | 155 | $scope.needsSearch = function() { 156 | return $scope.behaviours.length > 15; 157 | }; 158 | 159 | $scope.showIndentation = showIndentation; 160 | 161 | $scope.hideIndentation = hideIndentation; 162 | }; 163 | 164 | global.app.behaviour.controller('BehaviourController', [ 165 | '$scope', 166 | '$rootScope', 167 | '$sce', 168 | '$timeout', 169 | 'behaviourService', 170 | 'gemService', 171 | 'Behaviour', 172 | '_', 173 | controller 174 | ]); 175 | })(window.global, window.angular); 176 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/controllers/behaviour_search_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope) { 5 | // --- Private methods ----------------------------------------------------- 6 | 7 | var resetSearchInput; 8 | (resetSearchInput = function() { 9 | $scope.search = { 10 | searchText: '', 11 | hoverX: false 12 | }; 13 | }).call(); 14 | 15 | // --- Watchers ------------------------------------------------------------ 16 | 17 | $scope.$watch('search.searchText', function(newVal) { 18 | $scope.$emit('search-change', newVal); 19 | }); 20 | 21 | // --- Public methods ------------------------------------------------------ 22 | 23 | $scope.searching = function() { 24 | return $scope.search.searchText.length > 0; 25 | }; 26 | 27 | $scope.onMouseMove = function($event) { 28 | if (!$scope.searching()) { 29 | return; 30 | } 31 | 32 | var el = $event.toElement, 33 | inputWidth = el.offsetWidth - 18, 34 | searchBarWidth = el.getBoundingClientRect().left; 35 | 36 | $scope.search.hoverX = inputWidth < $event.clientX - searchBarWidth; 37 | }; 38 | 39 | $scope.clearSearch = function($event) { 40 | if (!$scope.search.hoverX) { 41 | return; 42 | } 43 | 44 | $event.preventDefault(); 45 | resetSearchInput(); 46 | }; 47 | }; 48 | 49 | global.app.behaviour.controller('BehaviourSearchController', [ 50 | '$scope', 51 | controller 52 | ]); 53 | })(window.global); 54 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/factories/behaviour.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var Behaviour = function(behaviour) { 5 | this.selected = false; 6 | this.namespacesLen = 0; 7 | 8 | this.name = behaviour.name; 9 | this.isModule = behaviour.isModule; 10 | this.isException = behaviour.isException; 11 | this.name = behaviour.name; 12 | this.superclass = behaviour.superclass; 13 | 14 | this.setIcon(); 15 | 16 | if (this.name) { 17 | this.setDisplayName(); 18 | } 19 | }; 20 | 21 | Behaviour.prototype.constructor = Behaviour; 22 | 23 | Behaviour.prototype.setDisplayName = function() { 24 | var namespaces; 25 | 26 | if (/^[^A-Z]/.test(this.name)) { 27 | // A feeble attempt at supporting behaviours 28 | // with redefined #name 29 | this.displayName = this.name; 30 | } else { 31 | namespaces = this.name.split('::'); 32 | this.namespacesLen = namespaces.length; 33 | this.displayName = namespaces[this.namespacesLen - 1]; 34 | } 35 | }; 36 | 37 | Behaviour.prototype.setIcon = function() { 38 | var icon; 39 | 40 | if (this.isModule) { 41 | icon = 'module'; 42 | } else if (this.isException) { 43 | icon = 'exception'; 44 | } else { 45 | icon = 'class'; 46 | } 47 | 48 | this.icon = icon; 49 | }; 50 | 51 | Behaviour.prototype.indent = function($sce) { 52 | var indent = new Array(this.namespacesLen).join(' • '); 53 | this.indentation = $sce.trustAsHtml(indent); 54 | }; 55 | 56 | var factory = function() { 57 | return Behaviour; 58 | }; 59 | 60 | global.app.behaviour.factory('Behaviour', [factory]); 61 | })(window.global); 62 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/partials/_behaviour_item.html: -------------------------------------------------------------------------------- 1 |
  • 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 16 | {{ behaviour.displayName }} 17 | 18 | 19 | < {{ behaviour.superclass }} 20 | 21 |
  • 22 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/partials/_behaviour_list_box.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |
      6 | 7 |
    8 |
    9 |
    10 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/partials/_search.html: -------------------------------------------------------------------------------- 1 |
    2 | 9 |
    10 | -------------------------------------------------------------------------------- /client/app/modules/app.behaviour/services/behaviour_service.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var BehaviourService = function(socket, Request) { 5 | this.socket = socket; 6 | this.Request = Request; 7 | }; 8 | 9 | BehaviourService.prototype.constructor = BehaviourService; 10 | 11 | BehaviourService.prototype.getAllFrom = function(gemName) { 12 | var req = new this.Request('get', 'behaviour', gemName); 13 | return this.socket.send(req); 14 | }; 15 | 16 | BehaviourService.prototype.openClass = function(behaviour, gem) { 17 | var req = new this.Request('autoget', 'behaviour', behaviour, gem.name); 18 | return this.socket.send(req); 19 | }; 20 | 21 | global.app.behaviour.service('behaviourService', [ 22 | 'socket', 23 | 'Request', 24 | BehaviourService]); 25 | })(window.global); 26 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/app.gem.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.app.gem = angular.module('app.gem', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/assets/css/gem.css: -------------------------------------------------------------------------------- 1 | .ruby-icon { 2 | background: url("gem-icon.svg"); 3 | } 4 | 5 | .ruby-icon:hover { 6 | background: url("gem-hover-icon.svg"); 7 | } 8 | 9 | .package-icon { 10 | background: url("package-icon.svg"); 11 | } 12 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/assets/css/gem_search.css: -------------------------------------------------------------------------------- 1 | #gem-list-box .filter-input { 2 | width: 100%; 3 | } 4 | 5 | #gem-list-box .clearable { 6 | background: #fff url("search_x.gif") no-repeat right -10px center; 7 | border: 1px solid #999; 8 | padding: 3px 18px 3px 4px; 9 | transition: background 0.18s; 10 | } 11 | 12 | #gem-list-box .clearable.x { 13 | background-position: right 5px center; 14 | } 15 | 16 | #gem-list-box .clearable.on-x { 17 | cursor: pointer; 18 | } 19 | 20 | #gem-list-box > div { 21 | height: calc(100% - 27px); 22 | } 23 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/assets/img/gem-hover-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 39 | 49 | 52 | 56 | 60 | 64 | 68 | 69 | 77 | 81 | 82 | 90 | 94 | 95 | 103 | 107 | 108 | 116 | 120 | 121 | 129 | 133 | 134 | 142 | 146 | 147 | 155 | 159 | 160 | 168 | 172 | 173 | 181 | 185 | 186 | 194 | 198 | 199 | 200 | 225 | 227 | 228 | 230 | image/svg+xml 231 | 233 | 234 | 235 | 236 | 237 | 242 | 247 | 255 | 260 | 266 | 272 | 278 | 284 | 291 | 298 | 305 | 312 | 319 | 326 | 333 | 340 | 347 | 348 | 349 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/assets/img/gem-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 39 | 49 | 52 | 56 | 60 | 64 | 68 | 69 | 77 | 81 | 82 | 90 | 94 | 95 | 103 | 107 | 108 | 116 | 120 | 121 | 129 | 133 | 134 | 142 | 146 | 147 | 155 | 159 | 160 | 168 | 172 | 173 | 181 | 185 | 186 | 194 | 198 | 199 | 200 | 225 | 227 | 228 | 230 | image/svg+xml 231 | 233 | 234 | 235 | 236 | 237 | 242 | 247 | 255 | 260 | 266 | 272 | 278 | 284 | 291 | 298 | 305 | 312 | 319 | 326 | 333 | 340 | 347 | 348 | 349 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/assets/img/package-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 37 | 38 | 39 | 68 | 72 | 78 | 79 | 81 | 82 | 84 | image/svg+xml 85 | 87 | 88 | 89 | 90 | 91 | 96 | 98 | 113 | 119 | 123 | 128 | 133 | 134 | 139 | 144 | 149 | 154 | 159 | 164 | 169 | 174 | 180 | 186 | 192 | 198 | 204 | 210 | 216 | 222 | 228 | 234 | 240 | 246 | 247 | 248 | 249 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/controllers/gem_controller.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, $timeout, $q, gemService, _) { 5 | // --- Public variables ---------------------------------------------------- 6 | 7 | $scope.gems = []; 8 | 9 | $scope.searchText = ''; 10 | 11 | // --- Private methods ----------------------------------------------------- 12 | 13 | var selectGem = function(gem) { 14 | var container = angular.element(document.getElementById('gem-container')); 15 | var li = angular.element(document.getElementById(gem.name)); 16 | var promise = container.scrollTo(li, 50, 300); 17 | gem.selected = true; 18 | 19 | return promise; 20 | }; 21 | 22 | var deselectGem = function(gem) { 23 | gem.selected = false; 24 | }; 25 | 26 | var setIcon = function(gem, iconName) { 27 | gem.icon = iconName; 28 | }; 29 | 30 | var getDescription = function(gemName) { 31 | gemService.getDescription(gemName).then(function(gemspec) { 32 | $timeout(function() { 33 | $rootScope.$broadcast('show-gem-deps', gemspec); 34 | }); 35 | }); 36 | }; 37 | 38 | var findGemByName = function(gemName) { 39 | var gem = _.find($scope.gems, function(gem) { 40 | if (gem.name === gemName) { 41 | return gem; 42 | } else { 43 | return false; 44 | } 45 | }); 46 | 47 | return gem; 48 | }; 49 | 50 | // --- Events -------------------------------------------------------------- 51 | 52 | $scope.$on('get:gem:all', function() { 53 | gemService.getAll().then(function(gems) { 54 | var ruby_gems = gems.slice(2, gems.length).map(function(gem) { 55 | setIcon(gem, 'ruby'); 56 | return gem; 57 | }); 58 | 59 | var core_gems = gems.slice(0, 2).map(function(gem) { 60 | setIcon(gem, 'package'); 61 | deselectGem(gem); 62 | return gem; 63 | }); 64 | 65 | $scope.gems = core_gems.concat(_.sortBy(ruby_gems, 'name')); 66 | }); 67 | }); 68 | 69 | $scope.$on('select-gem', function(_event, deferred, gemName) { 70 | var gem = findGemByName(gemName); 71 | 72 | if (gem) { 73 | $scope.selectGem(gem, deferred); 74 | } else { 75 | throw new Error('gem not found: ' + gemName); 76 | } 77 | }); 78 | 79 | $scope.$on('get-selected-gem', function(_event, gemSelectedDeferred) { 80 | var selectedGem = _.find($scope.gems, function(gem) { 81 | return gem.selected; 82 | }); 83 | 84 | gemSelectedDeferred.resolve(selectedGem); 85 | }); 86 | 87 | $scope.$on('search-change', function(_event, val) { 88 | $scope.searchText = val; 89 | }); 90 | 91 | // --- Public methods ------------------------------------------------------ 92 | 93 | $scope.showBehaviours = function(gem) { 94 | var deferred = $q.defer(); 95 | 96 | $rootScope.$broadcast('get:behaviour:all', gem, deferred); 97 | 98 | return deferred.promise; 99 | }; 100 | 101 | $scope.selectGem = function(gem, selectGemDeferred) { 102 | $scope.gems.forEach(deselectGem); 103 | 104 | selectGem(gem).then(function() { 105 | $scope.showBehaviours(gem).then(function() { 106 | if (selectGemDeferred) { 107 | selectGemDeferred.resolve(); 108 | } 109 | }).then(function() { 110 | getDescription(gem.name); 111 | }); 112 | }); 113 | }; 114 | 115 | $scope.openGem = function(gem) { 116 | gemService.open(gem.name); 117 | }; 118 | }; 119 | 120 | global.app.gem.controller('GemController', [ 121 | '$scope', 122 | '$rootScope', 123 | '$timeout', 124 | '$q', 125 | 'gemService', 126 | '_', 127 | controller 128 | ]); 129 | })(window.global, window.angular); 130 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/controllers/gem_search_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope) { 5 | // --- Private methods ----------------------------------------------------- 6 | 7 | var resetSearchInput; 8 | (resetSearchInput = function() { 9 | $scope.search = { 10 | searchText: '', 11 | hoverX: false 12 | }; 13 | }).call(); 14 | 15 | // --- Watchers ------------------------------------------------------------ 16 | 17 | $scope.$watch('search.searchText', function(newVal) { 18 | $scope.$emit('search-change', newVal); 19 | }); 20 | 21 | // --- Public methods ------------------------------------------------------ 22 | 23 | $scope.searching = function() { 24 | return $scope.search.searchText.length > 0; 25 | }; 26 | 27 | $scope.onMouseMove = function($event) { 28 | if (!$scope.searching()) { 29 | return; 30 | } 31 | 32 | var el = $event.toElement, 33 | inputWidth = el.offsetWidth - 18, 34 | searchBarWidth = el.getBoundingClientRect().left; 35 | 36 | $scope.search.hoverX = inputWidth < $event.clientX - searchBarWidth; 37 | }; 38 | 39 | $scope.clearSearch = function($event) { 40 | if (!$scope.search.hoverX) { 41 | return; 42 | } 43 | 44 | $event.preventDefault(); 45 | resetSearchInput(); 46 | }; 47 | }; 48 | 49 | global.app.gem.controller('GemSearchController', [ 50 | '$scope', 51 | controller 52 | ]); 53 | })(window.global); 54 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/partials/_gem_item.html: -------------------------------------------------------------------------------- 1 |
  • 5 | 6 | 9 | 10 | {{ gem.name }} 11 |
  • 12 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/partials/_gem_list_box.html: -------------------------------------------------------------------------------- 1 |
    2 | 3 | 4 |
    5 |
      6 | 7 |
    8 |
    9 |
    10 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/partials/_search.html: -------------------------------------------------------------------------------- 1 |
    2 | 9 |
    10 | -------------------------------------------------------------------------------- /client/app/modules/app.gem/services/gem_service.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var gemService = function($rootScope, $q, socket, Request) { 5 | this.$rootScope = $rootScope; 6 | this.$q = $q; 7 | this.socket = socket; 8 | this.Request = Request; 9 | }; 10 | 11 | gemService.prototype.constructor = gemService; 12 | 13 | gemService.prototype.getAll = function() { 14 | var req = new this.Request('get', 'gem', 'all'); 15 | return this.socket.send(req); 16 | }; 17 | 18 | gemService.prototype.open = function(gemName) { 19 | var req = new this.Request('open', 'gem', gemName); 20 | this.socket.send(req); 21 | }; 22 | 23 | gemService.prototype.getDescription = function(gemName) { 24 | var req = new this.Request('description', 'gem', gemName); 25 | return this.socket.send(req); 26 | }; 27 | 28 | gemService.prototype.selectGem = function(gemName) { 29 | var deferred = this.$q.defer(); 30 | 31 | this.$rootScope.$broadcast('select-gem', deferred, gemName); 32 | 33 | return deferred.promise; 34 | }; 35 | 36 | gemService.prototype.getSelectedGem = function() { 37 | var deferred = this.$q.defer(); 38 | 39 | this.$rootScope.$broadcast('get-selected-gem', deferred); 40 | 41 | return deferred.promise; 42 | }; 43 | 44 | global.app.gem.service('gemService', [ 45 | '$rootScope', 46 | '$q', 47 | 'socket', 48 | 'Request', 49 | gemService 50 | ]); 51 | })(window.global); 52 | -------------------------------------------------------------------------------- /client/app/modules/app.group/app.group.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.app.group = angular.module('app.group', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/modules/app.group/assets/css/group.css: -------------------------------------------------------------------------------- 1 | .ruby-group-icon { 2 | background: url("ruby-group-icon.svg"); 3 | } 4 | 5 | .c-group-icon { 6 | background: url("c-group-icon.svg"); 7 | } 8 | -------------------------------------------------------------------------------- /client/app/modules/app.group/assets/css/group_bar.css: -------------------------------------------------------------------------------- 1 | #group-bar { 2 | display: block; 3 | width: 100%; 4 | float: left; 5 | border-bottom: 1px solid #e5e5e5; 6 | margin-top: 4px; 7 | white-space: nowrap; 8 | } 9 | 10 | #group-bar-content { 11 | margin-top: 5px; 12 | margin-left: 15px; 13 | } 14 | 15 | #group-bar .counters { 16 | float: left; 17 | text-transform: uppercase; 18 | font-size: 85%; 19 | padding-left: 25px; 20 | width: 72px; 21 | position: relative; 22 | top: -6px; 23 | } 24 | 25 | #group-bar .counters .method-count.class-side, 26 | #group-bar .counters .method-count.instance-side { 27 | font-weight: 700; 28 | clear: both; 29 | } 30 | 31 | #group-bar .counters .num { 32 | float: right; 33 | } 34 | 35 | #group-bar .squared-checkbox { 36 | width: 20px; 37 | position: relative; 38 | float: left; 39 | margin-top: 3px; 40 | margin-right: 6px; 41 | padding-bottom: 10px; 42 | } 43 | 44 | #group-bar .squared-checkbox label { 45 | width: 14px; 46 | height: 14px; 47 | cursor: pointer; 48 | position: absolute; 49 | top: 0; 50 | left: 0; 51 | background: #fff4ff; 52 | background: linear-gradient(to bottom,#fff4ff 0,#e5d7e3 40%,#bcadbe 100%); 53 | border-radius: 4px; 54 | box-shadow: inset 0px 1px 1px white, 0px 1px 3px rgba(0, 0, 0, 0.5); 55 | } 56 | 57 | #group-bar .squared-checkbox label:after { 58 | content: ''; 59 | width: 5px; 60 | height: 3px; 61 | position: absolute; 62 | top: 4px; 63 | left: 4px; 64 | border: 3px solid #722b86; 65 | border-top: none; 66 | border-right: none; 67 | background: transparent; 68 | opacity: 0; 69 | transform: rotate(-45deg); 70 | } 71 | 72 | #group-bar .squared-checkbox label:hover::after { 73 | opacity: 0.5; 74 | } 75 | 76 | #group-bar .squared-checkbox input[type=checkbox] { 77 | visibility: hidden; 78 | } 79 | 80 | #group-bar .squared-checkbox input[type=checkbox]:checked + label:after { 81 | opacity: 1; 82 | } 83 | 84 | #group-bar .squared-checkbox.methodless { 85 | color: #605d61; 86 | } 87 | 88 | #group-bar .squared-checkbox.methodless label { 89 | cursor: default; 90 | background: #ececec; 91 | box-shadow: inset 0 1px 1px #ffffff,0 1px 1px rgba(0, 0, 0, 0.31); 92 | } 93 | 94 | #group-bar .squared-checkbox.methodless label:after { 95 | display: none; 96 | } 97 | -------------------------------------------------------------------------------- /client/app/modules/app.group/assets/img/c-group-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 42 | 43 | 46 | 50 | 54 | 58 | 59 | 69 | 70 | 95 | 97 | 98 | 100 | image/svg+xml 101 | 103 | 104 | 105 | 106 | 107 | 112 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /client/app/modules/app.group/assets/img/ruby-group-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 38 | 42 | 43 | 46 | 50 | 54 | 58 | 59 | 69 | 70 | 95 | 97 | 98 | 100 | image/svg+xml 101 | 103 | 104 | 105 | 106 | 107 | 112 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /client/app/modules/app.group/controllers/group_bar_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, groupBar) { 5 | // --- Public variables ---------------------------------------------------- 6 | 7 | $scope.isClassSide = groupBar.classSide; 8 | 9 | $scope.hasMethodlessSide = false; 10 | 11 | // --- Private methods ----------------------------------------------------- 12 | 13 | var setSide = function() { 14 | $scope.isClassSide = groupBar.classSide; 15 | }; 16 | 17 | // --- Events -------------------------------------------------------------- 18 | 19 | $scope.$on('method-count:method', function(_event, count) { 20 | $scope.hasMethodlessSide = 21 | count.classMethods === 0 || count.instanceMethods === 0; 22 | $scope.classMethods = count.classMethods; 23 | $scope.instanceMethods = count.instanceMethods; 24 | }); 25 | 26 | $scope.$on('reset-groupbar', function() { 27 | groupBar.reset(); 28 | setSide(); 29 | }); 30 | 31 | $scope.$on('group_bar:show', function() { 32 | groupBar.show(); 33 | setSide(); 34 | }); 35 | 36 | // --- Public methods ------------------------------------------------------ 37 | 38 | $scope.isVisible = function() { 39 | return groupBar.visible; 40 | }; 41 | 42 | $scope.toggleMethodSide = function() { 43 | groupBar.toggleSide(); 44 | $rootScope.$broadcast('update-method-side'); 45 | }; 46 | }; 47 | 48 | global.app.group.controller('GroupBarController', [ 49 | '$scope', 50 | '$rootScope', 51 | 'groupBar', 52 | controller 53 | ]); 54 | })(window.global); 55 | -------------------------------------------------------------------------------- /client/app/modules/app.group/controllers/group_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, group, groupBar) { 5 | // --- Public variables ---------------------------------------------------- 6 | 7 | $scope.groups = []; 8 | 9 | // --- Private methods ----------------------------------------------------- 10 | 11 | var selectGroup = function(group) { 12 | group.selected = true; 13 | }; 14 | 15 | var deselectGroup = function(group) { 16 | group.selected = false; 17 | }; 18 | 19 | // --- Events -------------------------------------------------------------- 20 | 21 | $scope.$on('add:method-group', function(_event, methodGroup) { 22 | var groups = []; 23 | var ctx; 24 | 25 | if (methodGroup.classMethods().length === 0) { 26 | groupBar.setInstanceSide(); 27 | } else if (methodGroup.instanceMethods().length === 0) { 28 | groupBar.setClassSide(); 29 | } 30 | 31 | if (groupBar.classSide) { 32 | ctx = 'singleton'; 33 | } else { 34 | ctx = 'instance'; 35 | } 36 | 37 | if (methodGroup.anyPublicMethods(ctx)) { 38 | groups.push({name: group.labels.public}); 39 | } 40 | 41 | if (methodGroup.anyPrivateMethods(ctx)) { 42 | groups.push({name: group.labels.private}); 43 | } 44 | 45 | if (methodGroup.anyProtectedMethods(ctx)) { 46 | groups.push({name: group.labels.protected}); 47 | } 48 | 49 | if (methodGroup.anyCMethods(ctx)) { 50 | groups.push({name: group.labels.c, icon: 'c-group'}); 51 | } 52 | 53 | if (methodGroup.anyRubyMethods(ctx)) { 54 | groups.push({name: group.labels.rb, icon: 'ruby-group'}); 55 | } 56 | 57 | if (groups.length > 0) { 58 | groups.unshift({name: group.labels.all, selected: true}); 59 | } 60 | 61 | $rootScope.$broadcast('group_bar:show'); 62 | 63 | $scope.groups = groups; 64 | }); 65 | 66 | $scope.$on('reset-methods', function() { 67 | $scope.groups = []; 68 | $rootScope.$broadcast('reset-groupbar'); 69 | }); 70 | 71 | // --- Public methods ------------------------------------------------------ 72 | 73 | $scope.filterMethodsBy = function(group) { 74 | $rootScope.$broadcast('filter:method', group); 75 | }; 76 | 77 | $scope.selectGroup = function(group) { 78 | $scope.groups.forEach(deselectGroup); 79 | selectGroup(group); 80 | }; 81 | }; 82 | 83 | global.app.group.controller('GroupController', [ 84 | '$scope', 85 | '$rootScope', 86 | 'group', 87 | 'groupBar', 88 | controller 89 | ]); 90 | })(window.global); 91 | -------------------------------------------------------------------------------- /client/app/modules/app.group/partials/_group_bar.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 9 | 24 |
    25 |
    26 |
    27 | -------------------------------------------------------------------------------- /client/app/modules/app.group/partials/_group_list_box.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 |
    6 |
    7 | -------------------------------------------------------------------------------- /client/app/modules/app.group/partials/_groups.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /client/app/modules/app.group/services/group.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var Group = function() { 5 | this.labels = { 6 | all: '-- all --', 7 | public: 'public', 8 | private: 'private', 9 | protected: 'protected', 10 | c: 'C methods', 11 | rb: 'Ruby methods' 12 | }; 13 | }; 14 | 15 | Group.prototype.constructor = Group; 16 | 17 | global.app.group.service('group', [Group]); 18 | })(window.global); 19 | -------------------------------------------------------------------------------- /client/app/modules/app.group/services/group_bar.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var GroupBar = function() { 5 | this.reset(); 6 | }; 7 | 8 | GroupBar.prototype.constructor = GroupBar; 9 | 10 | GroupBar.prototype.reset = function() { 11 | this.hide(); 12 | this.setInstanceSide(); 13 | }; 14 | 15 | GroupBar.prototype.hide = function() { 16 | this.visible = false; 17 | }; 18 | 19 | GroupBar.prototype.show = function() { 20 | this.visible = true; 21 | }; 22 | 23 | GroupBar.prototype.toggleSide = function() { 24 | if (this.classSide) { 25 | this.setInstanceSide(); 26 | } else { 27 | this.setClassSide(); 28 | } 29 | }; 30 | 31 | GroupBar.prototype.setInstanceSide = function() { 32 | this.classSide = false; 33 | }; 34 | 35 | GroupBar.prototype.setClassSide = function() { 36 | this.classSide = true; 37 | }; 38 | 39 | global.app.service('groupBar', [ 40 | GroupBar 41 | ]); 42 | })(window.global); 43 | -------------------------------------------------------------------------------- /client/app/modules/app.method/app.method.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.app.method = angular.module('app.method', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/modules/app.method/assets/css/method.css: -------------------------------------------------------------------------------- 1 | #method-list-box .list-box-content { 2 | border-right: none; 3 | } 4 | 5 | #method-list-box ul li .item { 6 | padding-left: 18px; 7 | } 8 | 9 | #method-list-box ul li.c-method { 10 | color: #99869d; 11 | cursor: default; 12 | } 13 | 14 | #method-list-box ul li.c-method:hover { 15 | background-color: transparent; 16 | } 17 | 18 | #method-list-box ul li.c-method:hover:nth-child(odd) { 19 | background-color: #f5f5f5; 20 | } 21 | 22 | #method-list-box ul li:hover { 23 | background-color: #ead3ff; 24 | } 25 | -------------------------------------------------------------------------------- /client/app/modules/app.method/controllers/method_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $rootScope, $timeout, MethodGroup, 5 | methodService, groupBar, _) { 6 | // --- Private variables --------------------------------------------------- 7 | 8 | var methodGroup = null; 9 | 10 | // --- Public variables ---------------------------------------------------- 11 | 12 | $scope.methods = []; 13 | 14 | // --- Private methods ----------------------------------------------------- 15 | 16 | var sortMethods = function(methods) { 17 | return _.sortBy(methods, 'name'); 18 | }; 19 | 20 | var selectMethod = function(method) { 21 | method.selected = true; 22 | }; 23 | 24 | var deselectMethod = function(method) { 25 | method.selected = false; 26 | }; 27 | 28 | var collectAllMethods = function() { 29 | var methods; 30 | 31 | if (groupBar.classSide) { 32 | methods = methodGroup.classMethods(); 33 | } else { 34 | methods = methodGroup.instanceMethods(); 35 | } 36 | 37 | $scope.methods = sortMethods(methods); 38 | }; 39 | 40 | var collectGroupMethods = function(group) { 41 | var methods; 42 | 43 | if (groupBar.classSide) { 44 | methods = methodGroup.classMethodsInGroup(group.name); 45 | } else { 46 | methods = methodGroup.instanceMethodsInGroup(group.name); 47 | } 48 | 49 | $scope.methods = sortMethods(methods); 50 | }; 51 | 52 | var getMethodSource = function(method) { 53 | $rootScope.$broadcast('get:source', methodGroup.owner, method); 54 | }; 55 | 56 | // --- Events -------------------------------------------------------------- 57 | 58 | $scope.$on('get:method:all', function(_event, behaviour) { 59 | methodService.getAllFrom(behaviour.name).then(function(methods) { 60 | $rootScope.$broadcast('reset-source'); 61 | 62 | methodGroup = new MethodGroup(methods, behaviour.name); 63 | $rootScope.$broadcast('add:method-group', methodGroup); 64 | 65 | var count = { 66 | instanceMethods: methodGroup.instanceMethods().length, 67 | classMethods: methodGroup.classMethods().length 68 | }; 69 | 70 | $rootScope.$broadcast('method-count:method', count); 71 | 72 | $timeout(function() { 73 | $scope.$apply(collectAllMethods); 74 | }); 75 | }); 76 | }); 77 | 78 | $scope.$on('reset-methods', function() { 79 | $scope.methods = []; 80 | }); 81 | 82 | $scope.$on('update-method-side', function() { 83 | if (methodGroup === null) { 84 | return; 85 | } 86 | 87 | $rootScope.$broadcast('add:method-group', methodGroup); 88 | collectAllMethods(); 89 | }); 90 | 91 | $scope.$on('filter:method', function(_event, group) { 92 | collectGroupMethods(group); 93 | }); 94 | 95 | $scope.$on('list-box:method:selected', function() { 96 | $scope.methods.forEach(deselectMethod); 97 | }); 98 | 99 | // --- Public methods ------------------------------------------------------ 100 | 101 | $scope.selectMethod = function(method) { 102 | if (!method.c_method) { 103 | getMethodSource(method); 104 | $rootScope.$broadcast('list-box:method:selected'); 105 | selectMethod(method); 106 | } 107 | }; 108 | }; 109 | 110 | global.app.method.controller('MethodController', [ 111 | '$scope', 112 | '$rootScope', 113 | '$timeout', 114 | 'MethodGroup', 115 | 'methodService', 116 | 'groupBar', 117 | '_', 118 | controller]); 119 | })(window.global); 120 | -------------------------------------------------------------------------------- /client/app/modules/app.method/factories/method_group.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var MethodGroup = function(methods, owner) { 5 | this.owner = owner; 6 | 7 | this.publicInstance = methods.public.instance; 8 | this.publicClass = methods.public.singleton; 9 | 10 | this.privateInstance = methods.private.instance; 11 | this.privateClass = methods.private.singleton; 12 | 13 | this.protectedInstance = methods.protected.instance; 14 | this.protectedClass = methods.protected.singleton; 15 | }; 16 | 17 | MethodGroup.prototype.constructor = MethodGroup; 18 | 19 | MethodGroup.prototype.methodToClassMethod = function(method) { 20 | return '.' + method.name; 21 | }; 22 | 23 | MethodGroup.prototype.methodToInstanceMethod = function(method) { 24 | return '#' + method.name; 25 | }; 26 | 27 | MethodGroup.prototype.instanceMethods = function() { 28 | var methods = []; 29 | 30 | methods = methods. 31 | concat(this.publicInstance). 32 | concat(this.privateInstance). 33 | concat(this.protectedInstance); 34 | 35 | return this.prepForDisplay(methods, this.methodToInstanceMethod); 36 | }; 37 | 38 | MethodGroup.prototype.classMethods = function() { 39 | var methods = []; 40 | 41 | methods = methods. 42 | concat(this.publicClass). 43 | concat(this.privateClass). 44 | concat(this.protectedClass); 45 | 46 | return this.prepForDisplay(methods, this.methodToClassMethod); 47 | }; 48 | 49 | MethodGroup.prototype.execInCtx = function(ctx, collection1, collection2) { 50 | if (ctx == 'instance') { 51 | return collection1; 52 | } else if (ctx == 'singleton') { 53 | return collection2; 54 | } else { 55 | throw new Error('unknown context: ' + ctx); 56 | } 57 | }; 58 | 59 | MethodGroup.prototype.onlyCMethods = function(collection) { 60 | return collection.filter(function(method) { return method.c_method; }); 61 | }; 62 | 63 | MethodGroup.prototype.onlyRbMethods = function(collection) { 64 | return collection.filter(function(method) { return !method.c_method; }); 65 | }; 66 | 67 | MethodGroup.prototype.anyPublicMethods = function(ctx) { 68 | var collection = this.execInCtx(ctx, this.publicInstance, this.publicClass); 69 | 70 | return collection.length > 0; 71 | }; 72 | 73 | MethodGroup.prototype.anyPrivateMethods = function(ctx) { 74 | var collection = this.execInCtx(ctx, this.privateInstance, this.privateClass); 75 | 76 | return collection.length > 0; 77 | }; 78 | 79 | MethodGroup.prototype.anyProtectedMethods = function(ctx) { 80 | var collection = this.execInCtx(ctx, this.protectedInstance, this.protectedClass); 81 | 82 | return collection.length > 0; 83 | }; 84 | 85 | MethodGroup.prototype.cInstanceMethods = function() { 86 | return this.onlyCMethods(this.instanceMethods()); 87 | }; 88 | 89 | MethodGroup.prototype.cClassMethods = function() { 90 | return this.onlyCMethods(this.classMethods()); 91 | }; 92 | 93 | MethodGroup.prototype.rbInstanceMethods = function() { 94 | return this.onlyRbMethods(this.instanceMethods()); 95 | }; 96 | 97 | 98 | MethodGroup.prototype.rbClassMethods = function() { 99 | return this.onlyRbMethods(this.classMethods()); 100 | }; 101 | 102 | MethodGroup.prototype.anyRubyMethods = function(ctx) { 103 | var collection = this.execInCtx( 104 | ctx, 105 | this.rbInstanceMethods(), 106 | this.rbClassMethods() 107 | ); 108 | 109 | return collection.length > 0; 110 | }; 111 | 112 | MethodGroup.prototype.anyCMethods = function(ctx) { 113 | var collection = this.execInCtx( 114 | ctx, 115 | this.cInstanceMethods(), 116 | this.cClassMethods() 117 | ); 118 | 119 | return collection.length > 0; 120 | }; 121 | 122 | MethodGroup.prototype.prepForDisplay = function(methods, prepFunc) { 123 | return methods.map(function(method) { 124 | method.displayName = prepFunc(method); 125 | return method; 126 | }, this); 127 | }; 128 | 129 | MethodGroup.prototype.methodsInGroup = function(group, ctx) { 130 | var allFunc, pub, priv, prot, cMethFunc, rbMethFunc, prepFunc; 131 | 132 | var methods = []; 133 | var labels = this.group.labels; 134 | 135 | allFunc = this[ctx.toLowerCase() + 'Methods']; 136 | pub = this[labels.public + ctx]; 137 | priv = this[labels.private + ctx]; 138 | prot = this[labels.protected + ctx]; 139 | cMethFunc = this['c' + ctx + 'Methods']; 140 | rbMethFunc = this['rb' + ctx + 'Methods']; 141 | prepFunc = this['methodTo' + ctx + 'Method']; 142 | 143 | switch(group) { 144 | case labels.all: 145 | methods = allFunc.bind(this)(); 146 | break; 147 | case labels.public: 148 | methods = pub; 149 | break; 150 | case labels.private: 151 | methods = priv; 152 | break; 153 | case labels.protected: 154 | methods = prot; 155 | break; 156 | case labels.c: 157 | methods = cMethFunc.bind(this)(); 158 | break; 159 | case labels.rb: 160 | methods = rbMethFunc.bind(this)(); 161 | break; 162 | } 163 | 164 | return this.prepForDisplay(methods, prepFunc); 165 | }; 166 | 167 | MethodGroup.prototype.classMethodsInGroup = function(group) { 168 | return this.methodsInGroup.bind(this)(group, 'Class'); 169 | }; 170 | 171 | MethodGroup.prototype.instanceMethodsInGroup = function(group) { 172 | return this.methodsInGroup.bind(this)(group, 'Instance'); 173 | }; 174 | 175 | var factory = function(group, _) { 176 | MethodGroup.prototype.group = group; 177 | MethodGroup.prototype._ = _; 178 | 179 | return MethodGroup; 180 | }; 181 | 182 | global.app.method.factory('MethodGroup', [ 183 | 'group', 184 | '_', 185 | factory 186 | ]); 187 | })(window.global); 188 | -------------------------------------------------------------------------------- /client/app/modules/app.method/partials/_method_item.html: -------------------------------------------------------------------------------- 1 |
  • 4 | 5 | {{ method.displayName }} 6 | 7 |
  • 8 | -------------------------------------------------------------------------------- /client/app/modules/app.method/partials/_method_list_box.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
      4 | 5 |
    6 |
    7 |
    8 | -------------------------------------------------------------------------------- /client/app/modules/app.method/services/method_service.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var methodService = function(socket, Request) { 5 | this.socket = socket; 6 | this.Request = Request; 7 | }; 8 | 9 | methodService.prototype.constructor = methodService; 10 | 11 | methodService.prototype.getAllFrom = function(behaviour) { 12 | var req = new this.Request('get', 'method', behaviour); 13 | return this.socket.send(req); 14 | }; 15 | 16 | global.app.method.service('methodService', [ 17 | 'socket', 18 | 'Request', 19 | methodService 20 | ]); 21 | })(window.global); 22 | -------------------------------------------------------------------------------- /client/app/modules/app.source/app.source.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.app.source = angular.module('app.source', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/modules/app.source/assets/css/source.css: -------------------------------------------------------------------------------- 1 | #source { 2 | height: calc(100% - 21px); 3 | overflow: auto; 4 | } 5 | 6 | #source div { 7 | height: 100%; 8 | } 9 | 10 | #source h1 { 11 | margin: 0; 12 | font-family: PT Sans Narrow; 13 | letter-spacing: 1px; 14 | } 15 | 16 | #source .CodeRay .code { 17 | overflow: auto; 18 | } 19 | 20 | #source .dependency { 21 | float: left; 22 | clear: both; 23 | color: blue; 24 | border-bottom: 1px solid rgba(0, 0, 255, 0.3); 25 | margin-bottom: 5px; 26 | } 27 | 28 | #source .dependency:hover { 29 | cursor: pointer; 30 | color: red; 31 | border-bottom: 1px solid rgba(255, 0, 0, 0.3); 32 | } 33 | 34 | #source .deps, #source .deps div { 35 | height: auto !important; 36 | } 37 | 38 | #source .deps { 39 | float: left; 40 | clear: both; 41 | } 42 | 43 | #source .chart { 44 | float: left; 45 | width: 75%; 46 | height: auto; 47 | z-index: -1; 48 | position: relative; 49 | } 50 | 51 | #source gem-dependency { 52 | float: left; 53 | clear: both; 54 | } 55 | 56 | #source-layout { 57 | height: 100%; 58 | } 59 | 60 | #source .source-content { 61 | z-index: 1; 62 | position: relative; 63 | } 64 | -------------------------------------------------------------------------------- /client/app/modules/app.source/controllers/source_controller.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var controller = function($scope, $element, $sce, $compile, $timeout, 5 | sourceService, gemService, marked) { 6 | $scope.runtime_deps = []; 7 | $scope.development_deps = []; 8 | 9 | $scope.$on('get:source', function(_event, owner, method) { 10 | sourceService.extract(owner, method).then(function(source) { 11 | $timeout(function() { 12 | $scope.$apply(function() { 13 | $scope.source = $sce.trustAsHtml(source); 14 | }); 15 | }); 16 | }); 17 | }); 18 | 19 | $scope.$on('reset-source', function() { 20 | $scope.source = null; 21 | }); 22 | 23 | $scope.$on('show-gem-deps', function(_event, gemspec) { 24 | $scope.runtime_deps = []; 25 | $scope.development_deps = []; 26 | 27 | var desc = gemspec.description; 28 | 29 | if (gemspec.runtime_deps) { 30 | $scope.runtime_deps = gemspec.runtime_deps.map(function(dep) { 31 | return {full_name: dep, name: dep.split(' ')[0]}; 32 | }); 33 | } 34 | 35 | if (gemspec.development_deps) { 36 | $scope.development_deps = gemspec.development_deps.map(function(dep) { 37 | return {full_name: dep, name: dep.split(' ')[0]}; 38 | }); 39 | } 40 | 41 | var depsElem = $compile('')($scope); 42 | 43 | if (gemspec.behaviours) { 44 | $scope.chart = { 45 | type: 'BarChart', 46 | data: { 47 | cols: [ 48 | {label: "Behaviours", type: "string"}, 49 | {label: "Quantity", type: "number"}, 50 | {label: "Color", type: "string", role: 'style'}, 51 | ], 52 | rows: [ 53 | {c: [{v: "Classes"}, {v: gemspec.behaviours.classes}, {v: '#63a25f'}]}, 54 | {c: [{v: "Modules"}, {v: gemspec.behaviours.modules}, {v: '#b6b567'}]}, 55 | {c: [{v: "Exceptions"}, {v: gemspec.behaviours.exceptions}, {v: '#b6678b'}]} 56 | ] 57 | }, 58 | options: { 59 | title: 'Behaviours', 60 | height: 150, 61 | legend: 'none' 62 | } 63 | }; 64 | } 65 | 66 | $scope.source = $sce.trustAsHtml(marked(desc)); 67 | $scope.$apply(); 68 | 69 | if (gemspec.behaviours) { 70 | var chartElem = $compile('
    ')($scope); 71 | $element[0].querySelector('.ng-binding').appendChild(chartElem[0]); 72 | } 73 | $element[0].querySelector('.ng-binding').appendChild(depsElem[0]); 74 | }); 75 | 76 | $scope.selectGem = function(dep) { 77 | $scope.runtime_deps = []; 78 | $scope.development_deps = []; 79 | 80 | gemService.selectGem(dep); 81 | }; 82 | }; 83 | 84 | global.app.source.controller('SourceController', [ 85 | '$scope', 86 | '$element', 87 | '$sce', 88 | '$compile', 89 | '$timeout', 90 | 'sourceService', 91 | 'gemService', 92 | 'marked', 93 | controller]); 94 | })(window.global); 95 | -------------------------------------------------------------------------------- /client/app/modules/app.source/directives/gem_dependency.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var directive = function() { 5 | return { 6 | restrict: 'E', 7 | 8 | templateUrl: 'modules/app.source/partials/_gem_dependency_directive.html' 9 | }; 10 | }; 11 | 12 | global.app.source.directive('gemDependency', [directive]); 13 | })(window.global); 14 | -------------------------------------------------------------------------------- /client/app/modules/app.source/partials/_gem_dependency_directive.html: -------------------------------------------------------------------------------- 1 | 2 |

    Runtime Dependencies

    3 | 4 |
    5 |
      6 |
    • 8 | {{ dep.full_name }} 9 |
    • 10 |
    11 |
    12 |
    13 | 14 | 15 |

    Development Dependencies

    16 | 17 |
    18 |
      19 |
    • 21 | {{ dep.full_name }} 22 |
    • 23 |
    24 |
    25 |
    26 | -------------------------------------------------------------------------------- /client/app/modules/app.source/partials/_source.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |
    5 |
    6 | -------------------------------------------------------------------------------- /client/app/modules/app.source/services/source_service.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var sourceService = function(Request, socket) { 5 | this.Request = Request; 6 | this.socket = socket; 7 | }; 8 | 9 | sourceService.prototype.constructor = sourceService; 10 | 11 | sourceService.prototype.extract = function(methodOwner, methodName) { 12 | var params = { 13 | owner: methodOwner, 14 | method: methodName 15 | }; 16 | 17 | var req = new this.Request('get', 'source', 'with-comment', params); 18 | return this.socket.send(req); 19 | }; 20 | 21 | global.app.source.service('sourceService', [ 22 | 'Request', 23 | 'socket', 24 | sourceService 25 | ]); 26 | })(window.global); 27 | -------------------------------------------------------------------------------- /client/app/modules/vendor/marked/factories/marked.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var marked = require('marked'); 5 | 6 | var factory = function() { 7 | marked.setOptions({ 8 | renderer: new marked.Renderer(), 9 | gfm: true, 10 | tables: true 11 | }); 12 | 13 | return marked; 14 | }; 15 | 16 | global.marked.factory('marked', [factory]); 17 | })(window.global); 18 | -------------------------------------------------------------------------------- /client/app/modules/vendor/marked/marked.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.marked = angular.module('marked', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/modules/vendor/underscore/factories/_.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var _ = require('underscore'); 5 | 6 | var factory = function() { 7 | return _; 8 | }; 9 | 10 | global.underscore.factory('_', [factory]); 11 | })(window.global); 12 | -------------------------------------------------------------------------------- /client/app/modules/vendor/underscore/underscore.module.js: -------------------------------------------------------------------------------- 1 | (function(global, angular) { 2 | 'use strict'; 3 | 4 | global.underscore = angular.module('underscore', []); 5 | })(window.global, window.angular); 6 | -------------------------------------------------------------------------------- /client/app/services/callback_store_service.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var callbackStoreService = function() { 5 | this.nextId = 0; 6 | this.MAX_ID = 1000; 7 | this.callbacks = {}; 8 | }; 9 | 10 | callbackStoreService.prototype.constructor = callbackStoreService; 11 | 12 | callbackStoreService.prototype.set = function(defer) { 13 | var callbackId = this.generateId(); 14 | 15 | this.callbacks[callbackId] = { 16 | time: new Date(), 17 | callback: defer 18 | }; 19 | 20 | return callbackId; 21 | }; 22 | 23 | callbackStoreService.prototype.get = function(callbackId) { 24 | if (this.callbacks.hasOwnProperty(callbackId)) { 25 | var cb = this.callbacks[callbackId]; 26 | delete this.callbacks[callbackId]; 27 | 28 | return cb; 29 | } else { 30 | return null; 31 | } 32 | }; 33 | 34 | callbackStoreService.prototype.generateId = function() { 35 | this.nextId += 1; 36 | 37 | if (this.nextId > this.MAX_ID) { 38 | this.nextId = 0; 39 | } 40 | 41 | return this.nextId; 42 | }; 43 | 44 | global.app.gem.service('callbackStoreService', [ 45 | callbackStoreService 46 | ]); 47 | })(window.global); 48 | -------------------------------------------------------------------------------- /client/app/services/event_loop.js: -------------------------------------------------------------------------------- 1 | (function(global, process) { 2 | 'use strict'; 3 | 4 | var EventLoop = function($rootScope, socket, messageDispatcher, 5 | callbackStoreService) { 6 | this.$rootScope = $rootScope; 7 | this.socket = socket; 8 | this.dispatcher = messageDispatcher; 9 | this.callbackStoreService = callbackStoreService; 10 | }; 11 | 12 | EventLoop.prototype.constructor = EventLoop; 13 | 14 | EventLoop.prototype.init = function() { 15 | var that = this; 16 | 17 | return function(data) { 18 | var messages = that.dispatcher.dispatch(data); 19 | 20 | messages.forEach(function(message) { 21 | var data = message.data, 22 | cb = that.callbackStoreService.get(message.callbackId); 23 | 24 | if (cb === null) { 25 | that.$rootScope.$broadcast(data.action, data.data); 26 | return cb; 27 | } else { 28 | return that.$rootScope.$apply(cb.callback.resolve(data.data)); 29 | } 30 | }); 31 | }; 32 | }; 33 | 34 | global.app.service('eventLoop', [ 35 | '$rootScope', 36 | 'socket', 37 | 'messageDispatcher', 38 | 'callbackStoreService', 39 | EventLoop]); 40 | })(window.global, window.process); 41 | -------------------------------------------------------------------------------- /client/app/services/init_service.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var initService = function(Request, socket) { 5 | this.Request = Request; 6 | this.socket = socket; 7 | }; 8 | 9 | initService.prototype.constructor = initService; 10 | 11 | initService.prototype.sendPid = function(pid) { 12 | var req = new this.Request('set-window-pid', pid); 13 | this.socket.send(req); 14 | }; 15 | 16 | global.app.service('initService', [ 17 | 'Request', 18 | 'socket', 19 | initService 20 | ]); 21 | })(window.global); 22 | -------------------------------------------------------------------------------- /client/app/services/message_dispatcher.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var MessageDispatcher = function() { 5 | }; 6 | 7 | MessageDispatcher.prototype.constructor = MessageDispatcher; 8 | 9 | MessageDispatcher.prototype.dispatch = function(data) { 10 | var dataArray, messages; 11 | 12 | dataArray = data.toString().split('\n'); 13 | 14 | messages = dataArray.filter(function(elem) { 15 | return elem === 0 || elem; 16 | }); 17 | 18 | return messages.map(function(message) { 19 | var msg = JSON.parse(message); 20 | 21 | return { 22 | callbackId: msg.callback_id, 23 | data: msg.system_browser_client 24 | }; 25 | }); 26 | }; 27 | 28 | global.app.service('messageDispatcher', [MessageDispatcher]); 29 | })(window.global); 30 | -------------------------------------------------------------------------------- /client/app/services/source.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var Source = function(Request, socket) { 5 | this.Request = Request; 6 | this.socket = socket; 7 | }; 8 | 9 | Source.prototype.constructor = Source; 10 | 11 | Source.prototype.extract = function(methodOwner, methodName) { 12 | var params = { 13 | owner: methodOwner, 14 | method: methodName 15 | }; 16 | 17 | var req = new this.Request('get', 'source', 'with-comment', params); 18 | this.socket.send(req); 19 | }; 20 | 21 | global.app.service('source', ['Request', 'socket', Source]); 22 | })(window.global); 23 | -------------------------------------------------------------------------------- /client/app/services/toolbar.js: -------------------------------------------------------------------------------- 1 | (function(global) { 2 | 'use strict'; 3 | 4 | var Toolbar = function() { 5 | }; 6 | 7 | Toolbar.prototype.constructor = Toolbar; 8 | 9 | global.app.service('toolbar', [Toolbar]); 10 | })(window.global); 11 | -------------------------------------------------------------------------------- /client/app/views/main.html: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 4 | 5 | 6 | 7 |
    8 | 9 |
    10 |
    11 |
    12 |
    13 | 14 | 15 |
    16 |
    17 |
    18 | -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System Browser 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
    13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "system_browser", 3 | "version": "0.1.0", 4 | "main": "dist/index.html", 5 | "window": { 6 | "toolbar": false, 7 | "width": 800, 8 | "height": 600 9 | }, 10 | "scripts": { 11 | "test": "karma start test/config/karma.conf.js" 12 | }, 13 | "dependencies": { 14 | "angular": "~1.4.1", 15 | "angular-route": "^1.4.1", 16 | "angular-scroll": "^0.7.1", 17 | "angular-socket-io": "^0.7.0", 18 | "marked": "^0.3.3", 19 | "normalize.css": "^3.0.3", 20 | "underscore": "^1.8.3" 21 | }, 22 | "devDependencies": { 23 | "angular-mocks": "^1.4.1", 24 | "grunt": "^0.4.5", 25 | "grunt-contrib-concat": "^0.5.1", 26 | "grunt-contrib-copy": "^0.8.0", 27 | "grunt-contrib-cssmin": "^0.12.3", 28 | "grunt-contrib-watch": "^0.6.1", 29 | "grunt-nw-builder": "^2.0.0", 30 | "jasmine-core": "^2.3.4", 31 | "karma": "^0.12.37", 32 | "karma-jasmine": "^0.3.6", 33 | "karma-nodewebkit-launcher": "0.0.12" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /screenshots/readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kyrylo/system_browser_client/e54ed63ba4d01a16ed98fef56af3f28b673b6839/screenshots/readme.png -------------------------------------------------------------------------------- /test/config/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Mon Jul 06 2015 21:57:31 GMT+0300 (EEST) 3 | 4 | module.exports = function(config) { 5 | 'use strict'; 6 | 7 | config.set({ 8 | 9 | // base path that will be used to resolve all patterns (eg. files, exclude) 10 | basePath: '', 11 | 12 | 13 | // frameworks to use 14 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 15 | frameworks: ['jasmine'], 16 | 17 | 18 | // list of files / patterns to load in the browser 19 | files: [ 20 | '../../node_modules/angular/angular.js', 21 | '../../node_modules/angular-route/angular-route.js', 22 | '../../node_modules/angular-mocks/angular-mocks.js', 23 | 24 | '../../client/app/app.module.js', 25 | '../../client/app/app.routes.js', 26 | '../../client/app/app.config.js', 27 | '../../client/app/**/*.js', 28 | 29 | '../../test/unit/**/*.js' 30 | ], 31 | 32 | 33 | // list of files to exclude 34 | exclude: [ 35 | ], 36 | 37 | 38 | // preprocess matching files before serving them to the browser 39 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 40 | preprocessors: { 41 | }, 42 | 43 | 44 | // test results reporter to use 45 | // possible values: 'dots', 'progress' 46 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 47 | reporters: ['progress'], 48 | 49 | 50 | // web server port 51 | port: 9876, 52 | 53 | 54 | // enable / disable colors in the output (reporters and logs) 55 | colors: true, 56 | 57 | 58 | // level of logging 59 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 60 | logLevel: config.LOG_INFO, 61 | 62 | 63 | // enable / disable watching file and executing tests whenever any file changes 64 | autoWatch: true, 65 | 66 | 67 | // start these browsers 68 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 69 | browsers: ['NodeWebkit'], 70 | 71 | 72 | // Continuous Integration mode 73 | // if true, Karma captures browsers, runs the tests and exits 74 | singleRun: false 75 | }); 76 | }; 77 | -------------------------------------------------------------------------------- /test/unit/gem_controller_spec.js: -------------------------------------------------------------------------------- 1 | /* 2 | * -------------- 3 | * /!\ NO-OP /!\ 4 | * -------------- 5 | * 6 | * It's cheaper not write tests for sockets rather than to write them. 7 | */ 8 | 9 | (function (angular, describe, it, expect, beforeEach, inject, module, spyOn) { 10 | 'use strict'; 11 | 12 | angular.module('app', ['ngMock']); 13 | 14 | describe('GemController', function() { 15 | beforeEach(function() { 16 | module('underscore'); 17 | module('app.gem'); 18 | }); 19 | 20 | var $controller, $scope, $rootScope, 21 | socketMock, RequestMock, GemServiceMock; 22 | 23 | beforeEach(module(function($provide) { 24 | socketMock = {}; 25 | RequestMock = {}; 26 | 27 | GemServiceMock = { 28 | getAll: function() { 29 | $rootScope.broadcast('add:gem:all'); 30 | return ['gem1', 'gem2', 'gem3']; 31 | } 32 | }; 33 | 34 | $provide.value('socket', socketMock); 35 | $provide.value('Request', RequestMock); 36 | $provide.value('GemService', GemServiceMock); 37 | })); 38 | 39 | beforeEach(inject(function(_$controller_, _$rootScope_) { 40 | $controller = _$controller_; 41 | $rootScope = _$rootScope_; 42 | $scope = $rootScope.$new(); 43 | 44 | $controller('GemController', { 45 | $scope: $scope, 46 | _: window.global.underscore 47 | }); 48 | })); 49 | 50 | describe("events", function() { 51 | 52 | describe("$scope.$on get:gem:all", function() { 53 | 54 | it("retrieves all gems", function() { 55 | return; 56 | expect($scope.gems.length).toEqual(0); 57 | 58 | $rootScope.$broadcast('get:gem:all'); 59 | 60 | expect($scope.gems.length).toEqual(3); 61 | }); 62 | 63 | }); 64 | 65 | }); 66 | 67 | }); 68 | }(window.angular, window.describe, window.it, window.expect, window.beforeEach, 69 | window.inject, window.module, window.spyOn)); 70 | --------------------------------------------------------------------------------