├── .babelrc ├── .codeclimate.yml ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── content └── index.jpg ├── gulpfile.js ├── config.js ├── index.js ├── tasks │ ├── build.js │ ├── css.js │ ├── deploy.js │ ├── dev.js │ ├── html.js │ ├── production.js │ ├── scripts.js │ ├── server.js │ └── watch.js └── webpack.config.js ├── package.json ├── src ├── css │ └── terminal.css ├── index.html └── js │ ├── terminal │ ├── commandDecorator.js │ ├── commandRepository.js │ ├── commandRepositoryFactory.js │ ├── commands │ │ ├── index.js │ │ ├── nullCommand.js │ │ ├── openWindowCommand.js │ │ ├── replyToCreatorCommand.js │ │ ├── replyToDateCommand.js │ │ ├── replyToGitCommand.js │ │ ├── replyToGitPushOriginMasterCommand.js │ │ ├── replyToGitStatusCommand.js │ │ ├── replyToHelloCommand.js │ │ ├── replyToHelpCommand.js │ │ ├── replyToLoveYouCommand.js │ │ ├── replyToTimeCommand.js │ │ ├── searchOnCommandFactory.js │ │ ├── searchOnGoogleCommand.js │ │ ├── searchOnIiissaCommand.js │ │ ├── searchOnWikiCommand.js │ │ ├── searchOnYahooCommand.js │ │ └── searchOnYoutubeCommand.js │ └── terminal.js │ ├── underscore │ ├── array │ │ ├── flatten.js │ │ └── index.js │ ├── core │ │ ├── index.js │ │ ├── isArray.js │ │ ├── isFunction.js │ │ ├── noop.js │ │ ├── padLeft.js │ │ └── toStringTag.js │ ├── functions │ │ ├── flow.js │ │ ├── identity.js │ │ ├── index.js │ │ └── once.js │ ├── index.js │ ├── object │ │ ├── copyProperty.js │ │ ├── decorateMethod.js │ │ ├── decorateMethods.js │ │ ├── extend.js │ │ ├── index.js │ │ └── invoke.js │ └── tags │ │ ├── decoratorExpression.js │ │ ├── escape.js │ │ └── index.js │ └── view │ ├── index.js │ └── terminalFactory.js └── test └── unit └── underscore ├── array └── flatten.spec.js └── core ├── isArray.spec.js ├── isFunction.spec.js ├── noop.spec.js ├── padLeft.spec.js └── toStringTag.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"] 3 | } 4 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | duplication: 3 | enabled: true 4 | config: 5 | languages: 6 | - javascript 7 | eslint: 8 | enabled: true 9 | ratings: 10 | paths: 11 | - src/js/** 12 | exclude_paths: 13 | - gulpfile.js/**/* 14 | - test/**/* 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb", 4 | "rules": { 5 | "arrow-parens": ["error", "as-needed"], 6 | "arrow-body-style": "off", 7 | "comma-dangle": ["error", "always-multiline"], 8 | "func-names": "off", 9 | "global-require": "off", 10 | "import/no-dynamic-require": "off", 11 | "no-param-reassign": "off", 12 | "no-prototype-builtins": "off", 13 | "no-mixed-operators": ["error", {"allowSamePrecedence": true}], 14 | "no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "WithStatement"] 15 | }, 16 | "env": { 17 | "browser": true, 18 | "mocha": true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | dist/ 4 | .nyc_output/ 5 | .publish/ 6 | .idea/ 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | - "7" 5 | - "6" 6 | - "5" 7 | - "4" 8 | after_script: 9 | - npm install -g codeclimate-test-reporter 10 | - codeclimate-test-reporter < coverage/lcov.info 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JS Terminal 2 | 3 | [![Build Status](https://travis-ci.org/gtkatakura/js-terminal.svg?branch=master)](https://travis-ci.org/gtkatakura/js-terminal) 4 | [![Test Coverage](https://codeclimate.com/github/gtkatakura/js-terminal/badges/coverage.svg)](https://codeclimate.com/github/gtkatakura/js-terminal/coverage) 5 | [![Code Climate](https://codeclimate.com/github/pedrolaxe/js-terminal/badges/gpa.svg)](https://codeclimate.com/github/pedrolaxe/js-terminal) 6 | 7 | Terminal developed in pure JS 8 | 9 | ![drag] 10 | 11 | ### Version 12 | 1.2.3 13 | 14 | ### Authors 15 | Pedro Laxe, Gabriel Takashi Katakura 16 | 17 | ### Contributors 18 | William Reis Fernandes (@globsecure) 19 | 20 | ### Getting Started 21 | ```bash 22 | git clone https://github.com/pedrolaxe/js-terminal.git js-terminal 23 | cd js-terminal 24 | npm install 25 | ``` 26 | 27 | ### Dev 28 | ```bash 29 | gulp dev 30 | ``` 31 | 32 | [drag]: https://raw.githubusercontent.com/pedrolaxe/js-terminal/master/content/index.jpg 33 | -------------------------------------------------------------------------------- /content/index.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pedrolaxe/js-terminal/2d7617b3e2815cc3da1fc499fd82778816ab2eb5/content/index.jpg -------------------------------------------------------------------------------- /gulpfile.js/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: { 3 | src: './src', 4 | dest: './dist' 5 | }, 6 | html: { 7 | src: 'src/*.html', 8 | dest: 'dist', 9 | htmlmin: { 10 | collapseWhitespace: true 11 | } 12 | }, 13 | css: { 14 | src: 'src/css/*.css', 15 | dest: 'dist/css' 16 | }, 17 | scripts: { 18 | src: [ 19 | 'babel-polyfill', 20 | 'src/js/underscore/**/*.js', 21 | 'src/js/terminal/terminal.js', 22 | 'src/js/terminal/**/*.js', 23 | 'src/js/view/index.js' 24 | ], 25 | dest: 'dist/js' 26 | }, 27 | browserSync: { 28 | server: { 29 | baseDir: 'dist' 30 | } 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /gulpfile.js/index.js: -------------------------------------------------------------------------------- 1 | var requireDir = require('require-dir'); 2 | 3 | requireDir('./tasks', { recurse: true }); 4 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | 3 | gulp.task('build', ['html', 'css', 'scripts']); 4 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/css.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | gulp = require('gulp'), 3 | gulpif = require('gulp-if'), 4 | cssmin = require('gulp-cssmin'), 5 | concat = require('gulp-concat'), 6 | browserSync = require('browser-sync'); 7 | 8 | gulp.task('css', function() { 9 | return gulp.src(config.css.src) 10 | .pipe(gulpif(config.env === 'production', cssmin())) 11 | .pipe(concat('terminal.css')) 12 | .pipe(gulp.dest(config.css.dest)) 13 | .pipe(browserSync.stream()); 14 | }); 15 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/deploy.js: -------------------------------------------------------------------------------- 1 | var packageConfig = require('../../package.json'), 2 | config = require('../config'), 3 | gulp = require('gulp'), 4 | path = require('path'), 5 | ghPages = require('gulp-gh-pages'), 6 | open = require('open'); 7 | 8 | var settings = { 9 | url: packageConfig.homepage, 10 | src: path.join(config.root.dest, '/**/*') 11 | }; 12 | 13 | gulp.task('deploy', ['production'], function() { 14 | return gulp.src(settings.src) 15 | .pipe(ghPages()) 16 | .on('end', function() { 17 | open(settings.url); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/dev.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | gulp = require('gulp'); 3 | 4 | gulp.task('set-dev-node-env', function() { 5 | return process.env.NODE_ENV = config.env = 'development'; 6 | }); 7 | 8 | gulp.task('dev', ['set-dev-node-env', 'server']); 9 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/html.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | gulp = require('gulp'), 3 | gulpif = require('gulp-if'), 4 | htmlmin = require('gulp-htmlmin'), 5 | browserSync = require('browser-sync'); 6 | 7 | gulp.task('html', function() { 8 | return gulp.src(config.html.src) 9 | .pipe(gulpif(config.env === 'production', htmlmin(config.html.htmlmin))) 10 | .pipe(gulp.dest(config.html.dest)) 11 | .pipe(browserSync.stream()); 12 | }); 13 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/production.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | gulp = require('gulp'); 3 | 4 | gulp.task('set-prod-node-env', function() { 5 | return process.env.NODE_ENV = config.env = 'production'; 6 | }); 7 | 8 | gulp.task('production', ['set-prod-node-env', 'build']); 9 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/scripts.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | webpackConfig = require('../webpack.config.js'), 3 | gulp = require('gulp'), 4 | gulpif = require('gulp-if'), 5 | eslint = require('gulp-eslint'), 6 | webpack = require('webpack-stream'), 7 | uglify = require('gulp-uglify'), 8 | loadPlugins = require('gulp-load-plugins')(), 9 | browserSync = require('browser-sync'); 10 | 11 | gulp.task('scripts', function() { 12 | return gulp.src(config.scripts.src) 13 | .pipe(eslint()) 14 | .pipe(eslint.format()) 15 | .pipe(webpack(webpackConfig)) 16 | .pipe(gulpif(config.env === 'production', uglify())) 17 | .pipe(gulp.dest(config.scripts.dest)) 18 | .pipe(loadPlugins.size()) 19 | .pipe(browserSync.stream()); 20 | }); 21 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/server.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | gulp = require('gulp'), 3 | browserSync = require('browser-sync'); 4 | 5 | gulp.task('server', ['build', 'watch'], function() { 6 | browserSync(config.browserSync); 7 | }); 8 | -------------------------------------------------------------------------------- /gulpfile.js/tasks/watch.js: -------------------------------------------------------------------------------- 1 | var config = require('../config'), 2 | gulp = require('gulp'); 3 | 4 | gulp.task('watch', function() { 5 | gulp.watch(config.html.src, ['html']); 6 | gulp.watch(config.css.src, ['css']); 7 | gulp.watch(config.scripts.src, ['scripts']); 8 | }); 9 | -------------------------------------------------------------------------------- /gulpfile.js/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | module: { 3 | loaders: [ 4 | { 5 | test: /\.js$/, 6 | loader: 'babel-loader', 7 | exclude: /node_modules/, 8 | query: { 9 | presets: ['es2015', 'stage-0'], 10 | plugins: ['transform-runtime'] 11 | } 12 | } 13 | ] 14 | }, 15 | output: { 16 | filename: 'app.js' 17 | } 18 | };; 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-terminal", 3 | "version": "1.2.3", 4 | "description": "Terminal developed in pure JS", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm run coverage", 8 | "test:unit": "mocha --compilers js:babel-core/register test/unit/**/*.spec.js", 9 | "coverage": "nyc -r=lcov -r=text -i babel-core/register mocha test/**/*.spec.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/pedrolaxe/js-terminal.git" 14 | }, 15 | "author": { 16 | "name": "pedrolaxe", 17 | "email": "pedro@phpsec.com.br", 18 | "url": "http://phpsec.com.br" 19 | }, 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/pedrolaxe/js-terminal/issues" 23 | }, 24 | "homepage": "https://github.com/pedrolaxe/js-terminal/", 25 | "keywords": [ 26 | "js-terminal", 27 | "terminal" 28 | ], 29 | "dependencies": {}, 30 | "devDependencies": { 31 | "babel-core": "^6.8.0", 32 | "babel-eslint": "^7.2.1", 33 | "babel-loader": "^6.2.4", 34 | "babel-plugin-transform-runtime": "^6.9.0", 35 | "babel-polyfill": "^6.9.0", 36 | "babel-preset-es2015": "^6.24.0", 37 | "babel-preset-stage-0": "^6.22.0", 38 | "babel-runtime": "^6.9.0", 39 | "browser-sync": "^2.11.1", 40 | "chai": "^4.1.0", 41 | "eslint": "^2.13.1", 42 | "eslint-config-airbnb": "^9.0.1", 43 | "eslint-plugin-import": "^1.8.0", 44 | "eslint-plugin-jsx-a11y": "^1.2.2", 45 | "eslint-plugin-react": "^5.1.1", 46 | "gulp": "^3.9.1", 47 | "gulp-babel": "^6.1.2", 48 | "gulp-concat": "^2.6.0", 49 | "gulp-cssmin": "^0.1.7", 50 | "gulp-eslint": "^2.0.0", 51 | "gulp-gh-pages": "^0.5.4", 52 | "gulp-htmlmin": "^1.3.0", 53 | "gulp-if": "^2.0.0", 54 | "gulp-load-plugins": "^1.2.0", 55 | "gulp-size": "^2.0.0", 56 | "gulp-uglify": "^1.5.2", 57 | "jshint": "^2.9.1", 58 | "jshint-stylish": "^2.1.0", 59 | "mocha": "^3.4.2", 60 | "nyc": "^11.0.3", 61 | "open": "^6.0.0", 62 | "path": "^0.12.7", 63 | "require-dir": "^0.3.0", 64 | "webpack": "^1.13.0", 65 | "webpack-stream": "^3.2.0" 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/css/terminal.css: -------------------------------------------------------------------------------- 1 | /** 2 | * JS Terminal 3 | * Version 1.2.3 4 | * Author: Pedro Laxe 5 | **/ 6 | 7 | @import url(https://fonts.googleapis.com/css?family=Roboto); 8 | html { 9 | box-sizing: border-box; 10 | } 11 | *, *:before, *:after { 12 | box-sizing: inherit; 13 | } 14 | 15 | body{ 16 | font-family: 'Roboto','Helvetica Neue', 'Lucida Grande', sans-serif; 17 | } 18 | 19 | body, h1,h2,h3, p{ 20 | margin:0; 21 | padding:0; 22 | } 23 | 24 | .gradientCont{ 25 | background: linear-gradient(to bottom, #F62E24 0%, #FF3679 100%) repeat scroll 0% 0%; 26 | width:100%; 27 | height: 300px; 28 | } 29 | 30 | .mainCont{ 31 | width:1000px; 32 | margin: 0 auto; 33 | } 34 | 35 | .mainCont h2{ 36 | display: inline-block; 37 | width: 100%; 38 | text-align: center; 39 | font-weight: 300; 40 | font-size: 48px; 41 | margin: 60px 0 0 0; 42 | color: white; 43 | opacity: 0; 44 | 45 | animation: fadeInAnimation 1s ease-in-out 0s forwards; 46 | } 47 | 48 | .mainCont h3{ 49 | text-align: center; 50 | font-weight: 400; 51 | font-size: 18px; 52 | color: rgba(255,255,255,0.75); 53 | margin: 0 0 60px 0; 54 | opacity: 0; 55 | 56 | animation: fadeInAnimation 1s ease-in-out 0.5s forwards; 57 | } 58 | 59 | .terminalCont{ 60 | background: black; 61 | width: 100%; 62 | border-radius: 4px; 63 | padding:12px 0 0 0; 64 | margin-top: -20px; 65 | font-family: 'Roboto Mono', monospace; 66 | opacity: 0; 67 | 68 | animation: 69 | slideDownAnimation 1s ease-in-out 1s forwards, 70 | fadeInAnimation 0.8s ease-in-out 1s forwards; 71 | } 72 | 73 | .userEnteredText{ 74 | color: rgba(255,255,255,0.5); 75 | margin: 0; 76 | padding: 0; 77 | display: inline-block; 78 | } 79 | 80 | #terminalReslutsCont{ 81 | width:100%; 82 | height: 400px; 83 | padding: 12px; 84 | overflow-y: auto; 85 | resize: none; 86 | border: none; 87 | font-size: 14px; 88 | line-height: 28px; 89 | display: block; 90 | color: rgba(255,255,255,0.9); 91 | } 92 | 93 | 94 | #terminalReslutsCont a{ 95 | color: rgba(255,255,255,0.9); 96 | text-decoration: none; 97 | } 98 | 99 | #terminalReslutsCont a:hover{ 100 | text-decoration: underline; 101 | } 102 | 103 | #terminalTextInput{ 104 | background: black; 105 | display: block; 106 | border: none; 107 | border-top: 1px solid rgba(255,255,255,0.2); 108 | border-radius: 0 0 4px 4px; 109 | width: 100%; 110 | color: white; 111 | padding: 18px; 112 | font-size: 14px; 113 | outline: none; 114 | font-family: 'Roboto Mono', monospace; 115 | } 116 | 117 | .footerCont{ 118 | width: 100%; 119 | text-align: center; 120 | margin-top: 50px; 121 | color:#333; 122 | font-size: 12px; 123 | opacity: 0; 124 | animation: fadeInAnimation 1s ease-in-out 2s forwards; 125 | } 126 | 127 | .footerCont a{ 128 | color: #037AFF; 129 | text-decoration: none; 130 | } 131 | @-webkit-keyframes fadeInAnimation { 132 | 0% { opacity: 0; } 133 | 100% { opacity: 1; } 134 | } 135 | @-moz-keyframes fadeInAnimation { 136 | 0% { opacity: 0; } 137 | 100% { opacity: 1; } 138 | } 139 | @-o-keyframes fadeInAnimation { 140 | 0% { opacity: 0; } 141 | 100% { opacity: 1; } 142 | } 143 | @keyframes fadeInAnimation { 144 | 0% { opacity: 0; } 145 | 100% { opacity: 1; } 146 | } 147 | 148 | @-webkit-keyframes slideDownAnimation { 149 | 0% { margin-top: -20px;} 150 | 100% { margin-top: 0;} 151 | } 152 | @-moz-keyframes slideDownAnimation { 153 | 0% { margin-top: -20px;} 154 | 100% { margin-top: 0;} 155 | } 156 | @-o-keyframes slideDownAnimation { 157 | 0% { margin-top: -20px;} 158 | 100% { margin-top: 0;} 159 | } 160 | @keyframes slideDownAnimation { 161 | 0% { margin-top: -20px;} 162 | 100% { margin-top: 0;} 163 | } 164 | 165 | /* Make me responsive */ 166 | @media screen and (max-width: 1000px){ 167 | .gradientCont{ 168 | height: 220px; 169 | } 170 | .mainCont{ 171 | width: 100%; 172 | padding: 0 12px; 173 | } 174 | .mainCont h2{ 175 | font-size: 32px; 176 | margin: 40px 0 0 0; 177 | } 178 | .mainCont h3{ 179 | font-size: 16px; 180 | margin: 0 0 40px 0; 181 | } 182 | #terminalReslutsCont{ 183 | height: 100px; 184 | } 185 | 186 | } 187 | .coracao{ 188 | color:#e2264d; 189 | } 190 | /* Scrollbar in Webkit*/ 191 | #terminalReslutsCont::-webkit-scrollbar-track{ 192 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3); 193 | border-radius: 10px; 194 | background-color: #F5F5F5; 195 | } 196 | #terminalReslutsCont::-webkit-scrollbar{ 197 | width: 5px; 198 | background-color: #F5F5F5; 199 | } 200 | #terminalReslutsCont::-webkit-scrollbar-thumb{ 201 | border-radius: 10px; 202 | -webkit-box-shadow: inset 0 0 6px rgba(0,0,0,.3); 203 | background-color: #555; 204 | } 205 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JS Terminal 7 | 8 | 9 | 10 | 11 | Fork me on GitHub
12 |
13 |
14 |

JS Terminal

15 |

Say hi or type help for show commands

16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/js/terminal/commandDecorator.js: -------------------------------------------------------------------------------- 1 | import _ from '../underscore'; 2 | 3 | const commandBase = { 4 | name: '', 5 | aliases: [], 6 | parameter: null, 7 | response: null, 8 | execute() { 9 | const response = _.invoke(this, 'response'); 10 | return _.flatten([response]).join('
'); 11 | }, 12 | get aliasesAndName() { 13 | return this.aliases.concat([this.name]); 14 | }, 15 | }; 16 | 17 | const decorateExecute = execute => { 18 | return function (param) { 19 | if (param === '' && this.parameter) { 20 | return this.parameter.messageIfMissing; 21 | } 22 | 23 | return this::execute(param); 24 | }; 25 | }; 26 | 27 | const commandDecorator = command => { 28 | const decorated = _.extend({}, commandBase, command); 29 | 30 | return _.decorateMethods(decorated, { 31 | [_]: _.once, 32 | execute: decorateExecute, 33 | }); 34 | }; 35 | 36 | export default commandDecorator; 37 | -------------------------------------------------------------------------------- /src/js/terminal/commandRepository.js: -------------------------------------------------------------------------------- 1 | import _ from '../underscore'; 2 | import commandDecorator from './commandDecorator'; 3 | 4 | const decorateCommands = commands => Array.from(commands).map(commandDecorator); 5 | 6 | const create = _.flow(decorateCommands, commands => { 7 | const findByCommandText = commandText => { 8 | const finder = command => { 9 | const { aliasesAndName, parameter } = command; 10 | 11 | if (parameter) { 12 | return aliasesAndName.find(::commandText.startsWith); 13 | } 14 | 15 | return aliasesAndName.includes(commandText); 16 | }; 17 | 18 | return commands.find(finder); 19 | }; 20 | 21 | return { findByCommandText }; 22 | }); 23 | 24 | export default { create }; 25 | -------------------------------------------------------------------------------- /src/js/terminal/commandRepositoryFactory.js: -------------------------------------------------------------------------------- 1 | import CommandRepository from './commandRepository'; 2 | import commands from './commands'; 3 | 4 | const commandRepositoryFactory = () => CommandRepository.create(commands); 5 | 6 | export default commandRepositoryFactory; 7 | -------------------------------------------------------------------------------- /src/js/terminal/commands/index.js: -------------------------------------------------------------------------------- 1 | const names = [ 2 | 'nullCommand', 3 | 'openWindowCommand', 4 | 'replyToCreatorCommand', 5 | 'replyToDateCommand', 6 | 'replyToGitPushOriginMasterCommand', 7 | 'replyToGitStatusCommand', 8 | 'replyToHelloCommand', 9 | 'replyToHelpCommand', 10 | 'replyToLoveYouCommand', 11 | 'replyToTimeCommand', 12 | 'searchOnGoogleCommand', 13 | 'searchOnIiissaCommand', 14 | 'searchOnWikiCommand', 15 | 'searchOnYahooCommand', 16 | 'searchOnYoutubeCommand', 17 | ]; 18 | 19 | const commands = names.reduce((hash, name) => { 20 | hash[name] = require(`./${name}`).default; 21 | return hash; 22 | }, {}); 23 | 24 | commands[Symbol.iterator] = function*() { 25 | for (const name of names) { 26 | yield commands[name]; 27 | } 28 | }; 29 | 30 | export default commands; 31 | -------------------------------------------------------------------------------- /src/js/terminal/commands/nullCommand.js: -------------------------------------------------------------------------------- 1 | import _ from '../../underscore'; 2 | 3 | export default { 4 | execute(text) { 5 | return _.escape`'${text}' was not found. Type 'help'.`; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /src/js/terminal/commands/openWindowCommand.js: -------------------------------------------------------------------------------- 1 | import _ from '../../underscore'; 2 | 3 | export default { 4 | name: 'open', 5 | parameter: { 6 | name: 'link', 7 | messageIfMissing: 'Type open + something to navigate.', 8 | }, 9 | execute(link) { 10 | window.open(`http://${link.toLowerCase()}`, '_blank'); 11 | return _.escape`The URL ${link} should be opened now.`; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToCreatorCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'creators', 3 | response: 'My creator is Pedro Laxe with cooperation of Gabriel Takashi Katakura.', 4 | }; 5 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToDateCommand.js: -------------------------------------------------------------------------------- 1 | import _ from '../../underscore'; 2 | 3 | export default { 4 | name: 'date', 5 | response() { 6 | const date = new Date(); 7 | const day = _.padLeft(date.getDate(), 2); 8 | const month = _.padLeft(date.getMonth() + 1, 2); 9 | const fullYear = date.getFullYear(); 10 | 11 | return `${day}/${month}/${fullYear}`; 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToGitCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'git', 3 | response: [ 4 | 'git push origin master', 5 | 'you can check this project\'s repo on GitHub: https://github.com/pedrolaxe/js-terminal', 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToGitPushOriginMasterCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'git push origin master', 3 | response: 'Push me baby!', // \o/ 4 | }; 5 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToGitStatusCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'git status', 3 | response: 'nothing to commit, working directory clean.', 4 | }; 5 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToHelloCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'hello', 3 | aliases: ['hi', 'hola', 'oi'], 4 | response: 'Hello, it\'s me... I was wondering if after all these years you\'d like to meet...', 5 | }; 6 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToHelpCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'help', 3 | aliases: ['?'], 4 | response: [ 5 | '- Open + website URL to open it in the browser (ex. open phpsec.com.br)', 6 | '- Google + keyword to search directly in Google (ex. google phpsec)', 7 | '- Yahoo + keyword to search directly in Yahoo (ex. yahoo dogs)', 8 | '- iiissa + keyword to search directly in IIISSA (ex. iiissa pedro)', 9 | '- YouTube + keyword to search directly in YouTube (ex. youtube fora pt)', 10 | '- Wiki + keyword to search directly in Wikipedia (ex. wiki javascript)', 11 | "- 'time' will display the current time.", 12 | "- 'date' will display the current date.", 13 | "- 'creators' show the creators names.", 14 | '* There are more keywords that you have to discover by yourself.', 15 | ], 16 | }; 17 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToLoveYouCommand.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'love you', 3 | aliases: ['i love you', 'love'], 4 | response: 'Aww! That\'s so sweet. Here\'s some love for you too ❤ ❤ ❤ !', 5 | }; 6 | -------------------------------------------------------------------------------- /src/js/terminal/commands/replyToTimeCommand.js: -------------------------------------------------------------------------------- 1 | import _ from '../../underscore'; 2 | 3 | export default { 4 | name: 'time', 5 | response() { 6 | const date = new Date(); 7 | const hours = _.padLeft(date.getHours(), 2); 8 | const minutes = _.padLeft(date.getMinutes(), 2); 9 | 10 | return `${hours}:${minutes}`; 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /src/js/terminal/commands/searchOnCommandFactory.js: -------------------------------------------------------------------------------- 1 | import _ from '../../underscore'; 2 | 3 | const searchOnCommandFactory = ({ engine, uri, name = engine.toLowerCase(), keyName = 'q' }) => { 4 | return { 5 | name, 6 | parameter: { 7 | name: 'search', 8 | messageIfMissing: `Type ${name} + something to search for.`, 9 | }, 10 | execute(search) { 11 | window.open(`${uri}?${keyName}=${search}`, '_blank'); 12 | return _.escape`I\'ve searched on ${name} for ${search} see results.`; 13 | }, 14 | }; 15 | }; 16 | 17 | export default searchOnCommandFactory; 18 | -------------------------------------------------------------------------------- /src/js/terminal/commands/searchOnGoogleCommand.js: -------------------------------------------------------------------------------- 1 | import searchOnCommandFactory from './searchOnCommandFactory'; 2 | 3 | export default searchOnCommandFactory({ 4 | engine: 'Google', 5 | uri: 'https://www.google.com.br/search', 6 | }); 7 | -------------------------------------------------------------------------------- /src/js/terminal/commands/searchOnIiissaCommand.js: -------------------------------------------------------------------------------- 1 | import searchOnCommandFactory from './searchOnCommandFactory'; 2 | 3 | export default searchOnCommandFactory({ 4 | engine: 'IIISSA', 5 | uri: 'https://iiissa.com.br/buscar', 6 | }); 7 | -------------------------------------------------------------------------------- /src/js/terminal/commands/searchOnWikiCommand.js: -------------------------------------------------------------------------------- 1 | import searchOnCommandFactory from './searchOnCommandFactory'; 2 | 3 | export default searchOnCommandFactory({ 4 | engine: 'Wikipedia', 5 | name: 'wiki', 6 | uri: 'https://pt.wikipedia.org/w/index.php', 7 | keyName: 'search', 8 | }); 9 | -------------------------------------------------------------------------------- /src/js/terminal/commands/searchOnYahooCommand.js: -------------------------------------------------------------------------------- 1 | import searchOnCommandFactory from './searchOnCommandFactory'; 2 | 3 | export default searchOnCommandFactory({ 4 | engine: 'Yahoo', 5 | uri: 'https://br.search.yahoo.com/search', 6 | keyName: 'p', 7 | }); 8 | -------------------------------------------------------------------------------- /src/js/terminal/commands/searchOnYoutubeCommand.js: -------------------------------------------------------------------------------- 1 | import searchOnCommandFactory from './searchOnCommandFactory'; 2 | 3 | export default searchOnCommandFactory({ 4 | engine: 'YouTube', 5 | uri: 'https://www.youtube.com/results', 6 | keyName: 'search_query', 7 | }); 8 | -------------------------------------------------------------------------------- /src/js/terminal/terminal.js: -------------------------------------------------------------------------------- 1 | import _ from '../underscore'; 2 | import commands from './commands'; 3 | 4 | const create = ({ 5 | commandRepository: { findByCommandText }, 6 | elements: { results, input }, 7 | }) => { 8 | const addTextToResults = texts => { 9 | const fullText = _.flatten([texts]).map(text => `

${text}

`).join(''); 10 | results.innerHTML += fullText; 11 | }; 12 | 13 | const clearInput = () => { 14 | input.value = ''; 15 | }; 16 | 17 | const scrollToBottomOfResults = () => { 18 | results.scrollTop = results.scrollHeight; 19 | }; 20 | 21 | const focus = () => { 22 | input.focus(); 23 | scrollToBottomOfResults(); 24 | }; 25 | 26 | const findCommand = commandText => { 27 | return findByCommandText(commandText.toLowerCase()) || commands.nullCommand; 28 | }; 29 | 30 | const enter = () => { 31 | const commandText = input.value.trim(); 32 | const command = findCommand(commandText); 33 | const parameter = commandText.replace(command.name, '').trim(); 34 | const response = command.execute(parameter); 35 | 36 | addTextToResults(_.escape`

> ${commandText}

`); 37 | addTextToResults(response); 38 | clearInput(); 39 | scrollToBottomOfResults(); 40 | }; 41 | 42 | return { focus, enter }; 43 | }; 44 | 45 | export default { create }; 46 | -------------------------------------------------------------------------------- /src/js/underscore/array/flatten.js: -------------------------------------------------------------------------------- 1 | const flatten = array => [].concat(...array); 2 | 3 | export default flatten; 4 | -------------------------------------------------------------------------------- /src/js/underscore/array/index.js: -------------------------------------------------------------------------------- 1 | import flatten from './flatten'; 2 | 3 | export default { 4 | flatten, 5 | }; 6 | -------------------------------------------------------------------------------- /src/js/underscore/core/index.js: -------------------------------------------------------------------------------- 1 | import isArray from './isArray'; 2 | import isFunction from './isFunction'; 3 | import noop from './noop'; 4 | import padLeft from './padLeft'; 5 | import toStringTag from './toStringTag'; 6 | 7 | export default { 8 | isArray, 9 | isFunction, 10 | noop, 11 | padLeft, 12 | toStringTag, 13 | }; 14 | -------------------------------------------------------------------------------- /src/js/underscore/core/isArray.js: -------------------------------------------------------------------------------- 1 | import toStringTag from './toStringTag'; 2 | 3 | const isArray = value => toStringTag(value) === '[object Array]'; 4 | 5 | export default isArray; 6 | -------------------------------------------------------------------------------- /src/js/underscore/core/isFunction.js: -------------------------------------------------------------------------------- 1 | import toStringTag from './toStringTag'; 2 | 3 | const isFunction = value => toStringTag(value) === '[object Function]'; 4 | 5 | export default isFunction; 6 | -------------------------------------------------------------------------------- /src/js/underscore/core/noop.js: -------------------------------------------------------------------------------- 1 | const noop = () => {}; 2 | 3 | export default noop; 4 | -------------------------------------------------------------------------------- /src/js/underscore/core/padLeft.js: -------------------------------------------------------------------------------- 1 | const padLeft = (value, totalWidth, paddingChar = '0') => { 2 | const length = totalWidth - value.toString().length + 1; 3 | return Array(length).join(paddingChar) + value; 4 | }; 5 | 6 | export default padLeft; 7 | -------------------------------------------------------------------------------- /src/js/underscore/core/toStringTag.js: -------------------------------------------------------------------------------- 1 | const toStringTag = Function.call.bind(Object.prototype.toString); 2 | 3 | export default toStringTag; 4 | -------------------------------------------------------------------------------- /src/js/underscore/functions/flow.js: -------------------------------------------------------------------------------- 1 | const flow = (...funcs) => { 2 | return value => { 3 | return funcs.reduce((currentValue, func) => { 4 | return func(currentValue); 5 | }, value); 6 | }; 7 | }; 8 | 9 | export default flow; 10 | -------------------------------------------------------------------------------- /src/js/underscore/functions/identity.js: -------------------------------------------------------------------------------- 1 | const identity = value => value; 2 | 3 | export default identity; 4 | -------------------------------------------------------------------------------- /src/js/underscore/functions/index.js: -------------------------------------------------------------------------------- 1 | import flow from './flow'; 2 | import identity from './identity'; 3 | import once from './once'; 4 | 5 | export default { 6 | flow, 7 | identity, 8 | once, 9 | }; 10 | -------------------------------------------------------------------------------- /src/js/underscore/functions/once.js: -------------------------------------------------------------------------------- 1 | const once = func => { 2 | let called = false; 3 | let result = null; 4 | 5 | return function (...args) { 6 | if (called) { 7 | return result; 8 | } 9 | 10 | result = this::func(...args); 11 | called = true; 12 | return result; 13 | }; 14 | }; 15 | 16 | export default once; 17 | -------------------------------------------------------------------------------- /src/js/underscore/index.js: -------------------------------------------------------------------------------- 1 | import array from './array'; 2 | import core from './core'; 3 | import functions from './functions'; 4 | import object from './object'; 5 | import tags from './tags'; 6 | 7 | export default Object.assign( 8 | {}, 9 | array, 10 | core, 11 | functions, 12 | object, 13 | tags, 14 | ); 15 | -------------------------------------------------------------------------------- /src/js/underscore/object/copyProperty.js: -------------------------------------------------------------------------------- 1 | const copyProperty = (target, source, key) => { 2 | const descriptor = Object.getOwnPropertyDescriptor(source, key); 3 | Object.defineProperty(target, key, descriptor); 4 | return target; 5 | }; 6 | 7 | export default copyProperty; 8 | -------------------------------------------------------------------------------- /src/js/underscore/object/decorateMethod.js: -------------------------------------------------------------------------------- 1 | import isFunction from '../core/isFunction'; 2 | 3 | const decorateMethod = (source, methodName, decorator) => { 4 | const descriptor = Object.getOwnPropertyDescriptor(source, methodName); 5 | const accessorName = descriptor.hasOwnProperty('get') ? 'get' : 'value'; 6 | const method = descriptor[accessorName]; 7 | const decorated = isFunction(method) ? decorator(method) : method; 8 | 9 | const newDescriptor = Object.assign({}, descriptor, { 10 | [accessorName]: decorated, 11 | }); 12 | 13 | Object.defineProperty(source, methodName, newDescriptor); 14 | return source; 15 | }; 16 | 17 | export default decorateMethod; 18 | -------------------------------------------------------------------------------- /src/js/underscore/object/decorateMethods.js: -------------------------------------------------------------------------------- 1 | import _ from '../index'; 2 | import decorateMethod from './decorateMethod'; 3 | 4 | const decorateMethods = (source, mappings) => { 5 | return Object.keys(source).reduce((decorated, key) => { 6 | const decorator = mappings[key] || mappings[_] || _.identity; 7 | return decorateMethod(decorated, key, decorator); 8 | }, source); 9 | }; 10 | 11 | export default decorateMethods; 12 | -------------------------------------------------------------------------------- /src/js/underscore/object/extend.js: -------------------------------------------------------------------------------- 1 | import copyProperty from './copyProperty'; 2 | 3 | const extend = (target, ...sources) => { 4 | sources.forEach(source => { 5 | Object.keys(source).forEach(key => { 6 | copyProperty(target, source, key); 7 | }); 8 | }); 9 | 10 | return target; 11 | }; 12 | 13 | export default extend; 14 | -------------------------------------------------------------------------------- /src/js/underscore/object/index.js: -------------------------------------------------------------------------------- 1 | import copyProperty from './copyProperty'; 2 | import decorateMethod from './decorateMethod'; 3 | import decorateMethods from './decorateMethods'; 4 | import extend from './extend'; 5 | import invoke from './invoke'; 6 | 7 | export default { 8 | copyProperty, 9 | decorateMethod, 10 | decorateMethods, 11 | extend, 12 | invoke, 13 | }; 14 | -------------------------------------------------------------------------------- /src/js/underscore/object/invoke.js: -------------------------------------------------------------------------------- 1 | import isFunction from '../core/isFunction'; 2 | 3 | const invoke = (source, methodName, args = []) => { 4 | const method = source[methodName]; 5 | return isFunction(method) ? source::method(...args) : method; 6 | }; 7 | 8 | export default invoke; 9 | -------------------------------------------------------------------------------- /src/js/underscore/tags/decoratorExpression.js: -------------------------------------------------------------------------------- 1 | const decoratorExpression = transformation => { 2 | return (template, ...expressions) => { 3 | return template.reduce((acc, part, index) => { 4 | return acc + transformation(expressions[index - 1]) + part; 5 | }); 6 | }; 7 | }; 8 | 9 | export default decoratorExpression; 10 | -------------------------------------------------------------------------------- /src/js/underscore/tags/escape.js: -------------------------------------------------------------------------------- 1 | import decoratorExpression from './decoratorExpression'; 2 | 3 | const reHasUnescapedHtml = /[&<>"'`]/g; 4 | 5 | const htmlEscapes = { 6 | '&': '&', 7 | '<': '<', 8 | '>': '>', 9 | '"': '"', 10 | "'": ''', 11 | '`': '`', 12 | }; 13 | 14 | const escapeHtmlChar = char => htmlEscapes[char]; 15 | 16 | const escape = decoratorExpression(value => { 17 | return String(value).replace(reHasUnescapedHtml, escapeHtmlChar); 18 | }); 19 | 20 | export default escape; 21 | -------------------------------------------------------------------------------- /src/js/underscore/tags/index.js: -------------------------------------------------------------------------------- 1 | import escape from './escape'; 2 | 3 | export default { 4 | escape, 5 | }; 6 | -------------------------------------------------------------------------------- /src/js/view/index.js: -------------------------------------------------------------------------------- 1 | import terminalFactory from './terminalFactory'; 2 | 3 | document.addEventListener('DOMContentLoaded', () => { 4 | const terminal = terminalFactory(); 5 | terminal.focus(); 6 | 7 | document.getElementsByTagName('form')[0].onsubmit = event => { 8 | event.preventDefault(); 9 | terminal.enter(); 10 | window.scrollTo(0, 150); 11 | }; 12 | }); 13 | -------------------------------------------------------------------------------- /src/js/view/terminalFactory.js: -------------------------------------------------------------------------------- 1 | import Terminal from '../terminal/terminal'; 2 | import commandRepositoryFactory from '../terminal/commandRepositoryFactory'; 3 | 4 | const terminalFactory = () => { 5 | return Terminal.create({ 6 | commandRepository: commandRepositoryFactory(), 7 | elements: { 8 | results: document.getElementById('terminalReslutsCont'), 9 | input: document.getElementById('terminalTextInput'), 10 | }, 11 | }); 12 | }; 13 | 14 | export default terminalFactory; 15 | -------------------------------------------------------------------------------- /test/unit/underscore/array/flatten.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import flatten from '../../../../src/js/underscore/array/flatten'; 3 | 4 | describe('flatten', () => { 5 | describe('when is a empty array', () => { 6 | it('should return a empty array', () => { 7 | expect(flatten([])).to.eql([]); 8 | }); 9 | }); 10 | 11 | describe('when is a linear array', () => { 12 | it('should return a linear array', () => { 13 | const result = flatten([1, 2, 3]); 14 | expect(result).to.eql([1, 2, 3]); 15 | }); 16 | }); 17 | 18 | describe('when is a array with one level', () => { 19 | it('should return a linear array', () => { 20 | const result = flatten([[1, 2, 3]]); 21 | expect(result).to.eql([1, 2, 3]); 22 | }); 23 | }); 24 | 25 | describe('when is a array with two levels', () => { 26 | it('should return a array with one level', () => { 27 | const result = flatten([[[1, 2, 3]]]); 28 | expect(result).to.eql([[1, 2, 3]]); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /test/unit/underscore/core/isArray.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import isArray from '../../../../src/js/underscore/core/isArray'; 3 | 4 | describe('isArray', () => { 5 | describe('when is a array', () => { 6 | it('should return true', () => { 7 | expect(isArray([])).to.eql(true); 8 | }); 9 | }); 10 | 11 | describe('when is a object', () => { 12 | it('should return false', () => { 13 | expect(isArray({})).to.eql(false); 14 | }); 15 | }); 16 | 17 | describe('when is a string', () => { 18 | it('should return false', () => { 19 | expect(isArray('')).to.eql(false); 20 | }); 21 | }); 22 | 23 | describe('when is a array-like object', () => { 24 | it('should return false', () => { 25 | const input = { 0: 'zero', 1: 'one', length: 2 }; 26 | expect(isArray(input)).to.eql(false); 27 | }); 28 | }); 29 | 30 | describe('when is a object with [[Prototype]] == Array', () => { 31 | it('should return false', () => { 32 | const input = Object.setPrototypeOf({}, Array); 33 | expect(isArray(input)).to.eql(false); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/unit/underscore/core/isFunction.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import isFunction from '../../../../src/js/underscore/core/isFunction'; 3 | 4 | describe('isFunction', () => { 5 | describe('when is a function expression', () => { 6 | it('should return true', () => { 7 | const input = function () { }; 8 | expect(isFunction(input)).to.eql(true); 9 | }); 10 | }); 11 | 12 | describe('when is a named function expression', () => { 13 | it('should return true', () => { 14 | const input = function input() { }; 15 | expect(isFunction(input)).to.eql(true); 16 | }); 17 | }); 18 | 19 | describe('when is a declaration function', () => { 20 | it('should return true', () => { 21 | function input() { } 22 | expect(isFunction(input)).to.eql(true); 23 | }); 24 | }); 25 | 26 | describe('when is a arrow function', () => { 27 | it('should return true', () => { 28 | const input = () => {}; 29 | expect(isFunction(input)).to.eql(true); 30 | }); 31 | }); 32 | 33 | describe('when is a object', () => { 34 | it('should return false', () => { 35 | expect(isFunction({})).to.eql(false); 36 | }); 37 | }); 38 | 39 | describe('when is a object with [[Prototype]] == Function', () => { 40 | it('should return false', () => { 41 | const input = Object.setPrototypeOf({}, Function); 42 | expect(isFunction(input)).to.eql(false); 43 | }); 44 | }); 45 | 46 | describe('when is a regexp', () => { 47 | it('should return false', () => { 48 | expect(isFunction(/test/)).to.eql(false); 49 | }); 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /test/unit/underscore/core/noop.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import noop from '../../../../src/js/underscore/core/noop'; 3 | 4 | describe('noop', () => { 5 | describe('[[Type]]', () => { 6 | it('should be a function', () => { 7 | expect(noop).to.be.a('function'); 8 | }); 9 | }); 10 | 11 | describe('when is called', () => { 12 | it('should return undefined', () => { 13 | expect(noop()).to.eql(undefined); 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /test/unit/underscore/core/padLeft.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import padLeft from '../../../../src/js/underscore/core/padLeft'; 3 | 4 | describe('padLeft', () => { 5 | describe('when is a value with less chars then totalWidth', () => { 6 | it('should return a value padded', () => { 7 | expect(padLeft('x', 2, 'X')).to.eql('Xx'); 8 | }); 9 | }); 10 | 11 | describe('when is a value with same chars then totalWidth', () => { 12 | it('should return a value', () => { 13 | expect(padLeft('xx', 2, 'X')).to.eql('xx'); 14 | }); 15 | }); 16 | 17 | describe('when is a value with more chars then totalWidth', () => { 18 | it('should return a value', () => { 19 | expect(padLeft('xxx', 2, 'X')).to.eql('xxx'); 20 | }); 21 | }); 22 | 23 | describe('when paddingChar is ommited', () => { 24 | it('should return a value padded with zeros', () => { 25 | expect(padLeft('x', 2)).to.eql('0x'); 26 | }); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /test/unit/underscore/core/toStringTag.spec.js: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai'; 2 | import toStringTag from '../../../../src/js/underscore/core/toStringTag'; 3 | 4 | describe('toStringTag', () => { 5 | describe('when is a object', () => { 6 | it('should return [object Object]', () => { 7 | const input = {}; 8 | expect(toStringTag(input)).to.eql('[object Object]'); 9 | }); 10 | }); 11 | 12 | describe('when is a array', () => { 13 | it('should return [object Array]', () => { 14 | const input = []; 15 | expect(toStringTag(input)).to.eql('[object Array]'); 16 | }); 17 | }); 18 | 19 | describe('when is a function', () => { 20 | it('should return [object Function]', () => { 21 | const input = function () { }; 22 | expect(toStringTag(input)).to.eql('[object Function]'); 23 | }); 24 | }); 25 | 26 | describe('when is a regexp', () => { 27 | it('should return [object RegExp]', () => { 28 | const input = /test/; 29 | expect(toStringTag(input)).to.eql('[object RegExp]'); 30 | }); 31 | }); 32 | 33 | describe('when is a number', () => { 34 | it('should return [object Number]', () => { 35 | const input = 1; 36 | expect(toStringTag(input)).to.eql('[object Number]'); 37 | }); 38 | }); 39 | 40 | describe('when is a string', () => { 41 | it('should return [object String]', () => { 42 | const input = 'test'; 43 | expect(toStringTag(input)).to.eql('[object String]'); 44 | }); 45 | }); 46 | 47 | describe('when is a null', () => { 48 | it('should return [object Null]', () => { 49 | const input = null; 50 | expect(toStringTag(input)).to.eql('[object Null]'); 51 | }); 52 | }); 53 | 54 | describe('when is a undefined', () => { 55 | it('should return [object Undefined]', () => { 56 | const input = undefined; 57 | expect(toStringTag(input)).to.eql('[object Undefined]'); 58 | }); 59 | }); 60 | }); 61 | --------------------------------------------------------------------------------