├── .gitignore ├── .jshintrc ├── LICENSE ├── docs ├── design.md ├── developing-gcli.md ├── index.md ├── running-tests.md ├── writing-commands.md ├── writing-tests.md └── writing-types.md └── lib └── gcli ├── cli.js ├── commands ├── clear.js ├── commands.js ├── context.js ├── help.js ├── mocks.js ├── moz.build ├── pref.js ├── preflist.js └── test.js ├── connectors ├── connectors.js └── moz.build ├── converters ├── basic.js ├── converters.js ├── html.js ├── moz.build └── terminal.js ├── fields ├── delegate.js ├── fields.js ├── moz.build └── selection.js ├── index.js ├── l10n.js ├── languages ├── command.html ├── command.js ├── javascript.js ├── languages.js └── moz.build ├── moz.build ├── mozui ├── completer.js ├── inputter.js ├── moz.build └── tooltip.js ├── settings.js ├── system.js ├── types ├── array.js ├── boolean.js ├── command.js ├── date.js ├── delegate.js ├── file.js ├── fileparser.js ├── javascript.js ├── moz.build ├── node.js ├── number.js ├── resource.js ├── selection.js ├── setting.js ├── string.js ├── types.js ├── union.js └── url.js ├── ui ├── focus.js ├── history.js ├── intro.js ├── menu.css ├── menu.html ├── menu.js ├── moz.build └── view.js └── util ├── domtemplate.js ├── fileparser.js ├── filesystem.js ├── host.js ├── l10n.js ├── legacy.js ├── moz.build ├── prism.js ├── spell.js └── util.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Junk that could exist anywhere: 3 | .DS_Store 4 | *.swp 5 | *.tmp 6 | .*.gz 7 | 8 | # Editor junk 9 | /.project 10 | /.pydevproject 11 | /.settings/ 12 | /.settings.xml 13 | /.settings.xml.old 14 | /.idea/ 15 | /.externalToolBuilders/ 16 | /gcli.iml 17 | 18 | # A handy place to put stuff that git should ignore: 19 | /ignore 20 | 21 | # Build artifacts 22 | /built 23 | 24 | # Manually installed node packages should be ignored 25 | /node_modules/ 26 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "undef": true, 3 | "camelcase": false, 4 | "curly": true, 5 | "noarg": true, 6 | "quotmark": "single", 7 | "strict": true, 8 | "eqnull": true, 9 | "evil": true, 10 | "eqnull": true, 11 | "browser": true, 12 | "devel": true, 13 | "node": true, 14 | "phantom": true, 15 | "laxbreak": true, 16 | "iterator": true, 17 | "unused":"vars", 18 | "esnext": true, 19 | "globals": { 20 | "define": false, 21 | "Components": false, 22 | "Event": false, 23 | "CSSRule": false, 24 | "Proxy": false, 25 | "info": false, 26 | "io": false, 27 | "ok": false 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/design.md: -------------------------------------------------------------------------------- 1 | 2 | # The Design of GCLI 3 | 4 | ## Design Goals 5 | 6 | GCLI should be: 7 | 8 | - primarily for technical users. 9 | - as fast as a traditional CLI. It should be possible to put your head down, 10 | and look at the keyboard and use GCLI 'blind' at full speed without making 11 | mistakes. 12 | - principled about the way it encourages people to build commands. There is 13 | benefit from unifying the underlying concepts. 14 | - automatically helpful. 15 | 16 | GCLI should not attempt to: 17 | 18 | - convert existing GUI users to a CLI. 19 | - use natural language input. The closest we should get to natural language is 20 | thinking of commands as ```verb noun --adjective```. 21 | - gain a touch based interface. Whilst it's possible (even probable) that touch 22 | can provide further benefits to command line users, that can wait while we 23 | catch up with 1985. 24 | - slavishly follow the syntax of existing commands, predictability is more 25 | important. 26 | - be a programming language. Shell scripts are mini programming languages but 27 | we have JavaScript sat just next door. It's better to integrate than compete. 28 | 29 | 30 | ## Design Challenges 31 | 32 | What has changed since 1970 that might cause us to make changes to the design 33 | of the command line? 34 | 35 | 36 | ### Connection limitations 37 | 38 | Unix pre-dates the Internet and treats almost everything as a file. Since the 39 | Internet it could be more useful to use URIs as ways to identify sources of data. 40 | 41 | 42 | ### Memory limitations 43 | 44 | Modern computers have something like 6 orders of magnitude more memory than the 45 | PDP-7 on which Unix was developed. Innovations like stdin/stdout and pipes are 46 | ways to connect systems without long-term storage of the results. The ability 47 | to store results for some time (potentially in more than one format) 48 | significantly reduces the need for these concepts. We should make the results 49 | of past commands addressable for re-use at a later time. 50 | 51 | There are a number of possible policies for eviction of items from the history. 52 | We should investigate options other than a simple stack. 53 | 54 | 55 | ### Multi-tasking limitations 56 | 57 | Multi-tasking was a problem in 1970; the problem was getting a computer to do 58 | many jobs on 1 core. Today the problem is getting a computer to do one job on 59 | many cores. However we're stuck with this legacy in 2 ways. Firstly that the 60 | default is to force everything to wait until the previous job is finished, but 61 | more importantly that output from parallel jobs frequently collides 62 | 63 | $ find / -ctime 5d -print & 64 | $ find / -uid 0 -print & 65 | // good luck working out what came from where 66 | 67 | $ tail -f logfile.txt & 68 | $ vi main.c 69 | // have a nice time editing that file 70 | 71 | GCLI should allow commands to be asynchronous and will provide UI elements to 72 | inform the user of job completion. It will also keep asynchronous command 73 | output contained within it's own display area. 74 | 75 | 76 | ### Output limitations 77 | 78 | The PDP-7 had a teletype. There is something like 4 orders of magnitude more 79 | information that can be displayed on a modern display than a 80x24 character 80 | based console. We can use this flexibility to provide better help to the user 81 | in entering their command. 82 | 83 | The additional display richness can also allow interaction with result output. 84 | Command output can include links to follow-up commands, and even ask for 85 | additional input. (e.g. "your search returned zero results do you want to try 86 | again with a different search string") 87 | 88 | There is no reason why output must be static. For example, it could be 89 | informative to see the results of an "ls" command alter given changes made by 90 | subsequent commands. (It should be noted that there are times when historical 91 | information is important too) 92 | 93 | 94 | ### Integration limitations 95 | 96 | In 1970, command execution meant retrieving a program from storage, and running 97 | it. This required minimal interaction between the command line processor and 98 | the program being run, and was good for resource constrained systems. 99 | This lack of interaction resulted in the processing of command line arguments 100 | being done everywhere, when the task was better suited to command line. 101 | We should provide metadata about the commands being run, to allow the command 102 | line to process, interpret and provide help on the input. 103 | -------------------------------------------------------------------------------- /docs/developing-gcli.md: -------------------------------------------------------------------------------- 1 | 2 | # Developing GCLI 3 | 4 | ## About the code 5 | 6 | The majority of the GCLI source is stored in the ``lib`` directory. 7 | 8 | The ``docs`` directory contains documentation. 9 | The ``scripts`` directory contains RequireJS that GCLI uses. 10 | The ``build`` directory contains files used when creating builds. 11 | The ``mozilla`` directory contains the mercurial patch queue of patches to apply 12 | to mozilla-central. 13 | The ``selenium-tests`` directory contains selenium web-page integration tests. 14 | 15 | The source in the ``lib`` directory is split into 4 sections: 16 | 17 | - ``lib/demo`` contains commands used in the demo page. It is not needed except 18 | for demo purposes. 19 | - ``lib/test`` contains a small test harness for testing GCLI. 20 | - ``lib/gclitest`` contains tests that run in the test harness 21 | - ``lib/gcli`` contains the actual meat 22 | 23 | GCLI is split into a UI portion and a Model/Controller portion. 24 | 25 | 26 | ## The GCLI Model 27 | 28 | The heart of GCLI is a ``Requisition``, which is an AST for the input. A 29 | ``Requisition`` is a command that we'd like to execute, and we're filling out 30 | all the inputs required to execute the command. 31 | 32 | A ``Requisition`` has a ``Command`` that is to be executed. Each Command has a 33 | number of ``Parameter``s, each of which has a name and a type as detailed 34 | above. 35 | 36 | As you type, your input is split into ``Argument``s, which are then assigned to 37 | ``Parameter``s using ``Assignment``s. Each ``Assignment`` has a ``Conversion`` 38 | which stores the input argument along with the value that is was converted into 39 | according to the type of the parameter. 40 | 41 | There are special assignments called ``CommandAssignment`` which the 42 | ``Requisition`` uses to link to the command to execute, and 43 | ``UnassignedAssignment``used to store arguments that do not have a parameter 44 | to be assigned to. 45 | 46 | 47 | ## The GCLI UI 48 | 49 | There are several components of the GCLI UI. Each can have a script portion, 50 | some template HTML and a CSS file. The template HTML is processed by 51 | ``domtemplate`` before use. 52 | 53 | DomTemplate is fully documented in [it's own repository] 54 | (https://github.com/joewalker/domtemplate). 55 | 56 | The components are: 57 | 58 | - ``Inputter`` controls the input field, processing special keyboard events and 59 | making sure that it stays in sync with the Requisition. 60 | - ``Completer`` updates a div that is located behind the input field and used 61 | to display completion advice and hint highlights. It is stored in 62 | completer.js. 63 | - ``Display`` is responsible for containing the popup hints that are displayed 64 | above the command line. Typically Display contains a Hinter and a RequestsView 65 | although these are not both required. Display itself is optional, and isn't 66 | planned for use in the first release of GCLI in Firefox. 67 | - ``Hinter`` Is used to display input hints. It shows either a Menu or an 68 | ArgFetch component depending on the state of the Requisition 69 | - ``Menu`` is used initially to select the command to be executed. It can act 70 | somewhat like the Start menu on windows. 71 | - ``ArgFetch`` Once the command to be executed has been selected, ArgFetch 72 | shows a 'dialog' allowing the user to enter the parameters to the selected 73 | command. 74 | - ``RequestsView`` Contains a set of ``RequestView`` components, each of which 75 | displays a command that has been invoked. RequestsView is a poor name, and 76 | should better be called ReportView 77 | 78 | ArgFetch displays a number of Fields. There are fields for most of the Types 79 | discussed earlier. See 'Writing Fields' above for more information. 80 | 81 | 82 | ## Testing 83 | 84 | GCLI contains 2 test suites: 85 | 86 | - JS level testing is run with the ``test`` command. The tests are located in 87 | ``lib/gclitest`` and they use the test runner in ``lib/test``. This is fairly 88 | comprehensive, however it does not do UI level testing. 89 | If writing a new test it needs to be registered in ``lib/gclitest/index``. 90 | For an example of how to write tests, see ``lib/gclitest/testSplit.js``. 91 | The test functions are implemented in ``lib/test/assert``. 92 | - Browser integration tests are included in ``browser_webconsole_gcli_*.js``, 93 | in ``toolkit/components/console/hudservice/tests/browser``. These are 94 | run with the rest of the Mozilla test suite. 95 | 96 | 97 | ## Coding Conventions 98 | 99 | The coding conventions for the GCLI project come from the Bespin/Skywriter and 100 | Ace projects. They are roughly [Crockford] 101 | (http://javascript.crockford.com/code.html) with a few exceptions and 102 | additions: 103 | 104 | * ``var`` does not need to be at the top of each function, we'd like to move 105 | to ``let`` when it's generally available, and ``let`` doesn't have the same 106 | semantic twists as ``var``. 107 | 108 | * Strings are generally enclosed in single quotes. 109 | 110 | * ``eval`` is to be avoided, but we don't declare it evil. 111 | 112 | The [Google JavaScript conventions] 113 | (https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) are 114 | more detailed, we tend to deviate in: 115 | 116 | * Custom exceptions: We generally just use ``throw new Error('message');`` 117 | 118 | * Multi-level prototype hierarchies: Allowed; we don't have ``goog.inherits()`` 119 | 120 | * ``else`` begins on a line by itself: 121 | 122 | if (thing) { 123 | doThis(); 124 | } 125 | else { 126 | doThat(); 127 | } 128 | 129 | 130 | ## Startup 131 | 132 | Internally GCLI modules have ``startup()``/``shutdown()`` functions which are 133 | called on module init from the top level ``index.js`` of that 'package'. 134 | 135 | In order to initialize a package all that is needed is to require the package 136 | index (e.g. ``require('package/index')``). 137 | 138 | The ``shutdown()`` function was useful when GCLI was used in Bespin as part of 139 | dynamic registration/de-registration. It is not known if this feature will be 140 | useful in the future. So it has not been entirely removed, it may be at some 141 | future date. 142 | 143 | 144 | ## Running the Unit Tests 145 | 146 | Start the GCLI static server: 147 | 148 | cd path/to/gcli 149 | node gcli.js 150 | 151 | Now point your browser to http://localhost:9999/localtest.html. When the page 152 | loads the tests will be automatically run outputting to the console, or you can 153 | enter the ``test`` command to run the unit tests. 154 | 155 | 156 | ## Contributing Code 157 | 158 | Please could you do the following to help minimize the amount of rework that we 159 | do: 160 | 161 | 1. Check the unit tests run correctly (see **Running the Unit Tests** above) 162 | 2. Check the code follows the style guide. At a minimum it should look like the 163 | code around it. For more detailed notes, see **Coding Conventions** above 164 | 3. Help me review your work by using good commit comments. Which means 2 things 165 | * Well formatted messages, i.e. 50 char summary including bug tag, followed 166 | by a blank line followed by a more in-depth message wrapped to 72 chars 167 | per line. This is basically the format used by the Linux Kernel. See the 168 | [commit log](https://github.com/joewalker/gcli/commits/master) for 169 | examples. The be extra helpful, please use the "shortdesc-BUGNUM: " if 170 | possible which also helps in reviews. 171 | * Commit your changes as a story. Make it easy for me to understand the 172 | changes that you've made. 173 | 4. Sign your work. To improve tracking of who did what, we follow the sign-off 174 | procedure used in the Linux Kernel. 175 | The sign-off is a simple line at the end of the explanation for the 176 | patch, which certifies that you wrote it or otherwise have the right to 177 | pass it on as an open-source patch. The rules are pretty simple: if you 178 | can certify the below: 179 | 180 | Developer's Certificate of Origin 1.1 181 | 182 | By making a contribution to this project, I certify that: 183 | 184 | (a) The contribution was created in whole or in part by me and I 185 | have the right to submit it under the open source license 186 | indicated in the file; or 187 | 188 | (b) The contribution is based upon previous work that, to the best 189 | of my knowledge, is covered under an appropriate open source 190 | license and I have the right under that license to submit that 191 | work with modifications, whether created in whole or in part 192 | by me, under the same open source license (unless I am 193 | permitted to submit under a different license), as indicated 194 | in the file; or 195 | 196 | (c) The contribution was provided directly to me by some other 197 | person who certified (a), (b) or (c) and I have not modified 198 | it. 199 | 200 | (d) I understand and agree that this project and the contribution 201 | are public and that a record of the contribution (including all 202 | personal information I submit with it, including my sign-off) is 203 | maintained indefinitely and may be redistributed consistent with 204 | this project or the open source license(s) involved. 205 | 206 | then you just add a line saying 207 | 208 | Signed-off-by: Random J Developer 209 | 210 | using your real name (sorry, no pseudonyms or anonymous contributions.) 211 | 212 | Thanks for wanting to contribute code. 213 | 214 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | 2 | # About GCLI 3 | 4 | ## GCLI is a Graphical Command Line Interpreter. 5 | 6 | GCLI is a command line for modern computers. When command lines were invented, 7 | computers were resource-limited, disconnected systems with slow multi-tasking 8 | and poor displays. The design of the Unix CLI made sense in 1970, but over 40 9 | years on, considering the pace of change, there are many improvements we can 10 | make. 11 | 12 | CLIs generally suffer from poor discoverability; It's hard when faced with a 13 | blank command line to work out what to do. As a result the majority of programs 14 | today use purely graphical user interfaces, however in doing so, they lose some 15 | of the benefits of CLIs. CLIs are still used because generally, in the hands of 16 | a skilled user they are faster, and have a wider range of available options. 17 | 18 | GCLI attempts to get the best of the GUI world and the CLI world to produce 19 | something that is both easy to use and learn as well as fast and powerful. 20 | 21 | GCLI has a type system to help ensure that users are inputting valid commands 22 | and to enable us to provide sensible context sensitive help. GCLI provides 23 | integration with JavaScript rather than being an alternative (like CoffeeScript). 24 | 25 | 26 | ## History 27 | 28 | GCLI was born as part of the 29 | [Bespin](http://ajaxian.com/archives/canvas-for-a-text-editor) project and was 30 | [discussed at the time](http://j.mp/bespin-cli). The command line component 31 | survived the rename of Bepsin to Skywriter and the merger with Ace, got a name 32 | of it's own (Cockpit) which didn't last long before the project was named GCLI. 33 | It is now being used in the Firefox's web console where it doesn't have a 34 | separate identity but it's still called GCLI outside of Firefox. It is also 35 | used in [Eclipse Orion](http://www.eclipse.org/orion/). 36 | 37 | 38 | ## Environments 39 | 40 | GCLI is designed to work in a number of environments: 41 | 42 | 1. As a component of Firefox developer tools. 43 | 2. As an adjunct to Orion/Ace and other online editors. 44 | 3. As a plugin to any web-page wishing to provide its own set of commands. 45 | 4. As part of a standalone web browser extension with it's own set of commands. 46 | 47 | 48 | ## Related Pages 49 | 50 | Other sources of GCLI documentation: 51 | 52 | - [Writing Commands](writing-commands.md) 53 | - [Writing Types](writing-types.md) 54 | - [Developing GCLI](developing-gcli.md) 55 | - [Writing Tests](writing-tests.md) / [Running Tests](running-tests.md) 56 | - [The Design of GCLI](design.md) 57 | - Source 58 | - The most up-to-date source is in [this Github repository](https://github.com/joewalker/gcli/). 59 | - When a feature is 'done' it's merged into the [Mozilla clone](https://github.com/mozilla/gcli/). 60 | - From which it flows into [Mozilla Central](https://hg.mozilla.org/mozilla-central/file/tip/devtools/client/commandline). 61 | - [Demo of GCLI](http://mozilla.github.com/gcli/) with an arbitrary set of demo 62 | commands 63 | - Other Documentation 64 | - [Embedding docs](https://github.com/mozilla/gcli/blob/master/docs/index.md) 65 | - [Status page](http://mozilla.github.com/devtools/2011/status.html#gcli) 66 | 67 | 68 | ## Accessibility 69 | 70 | GCLI uses ARIA roles to guide a screen-reader as to the important sections to 71 | voice. We welcome [feedback on how these roles are implemented](https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox&component=Developer+Tools:+Graphic+Commandline+and+Toolbar&rep_platform=All&op_sys=All&short_desc=GCLI). 72 | 73 | The command line uses TAB as a method of completing current input, this 74 | prevents use of TAB for keyboard navigation. Instead of using TAB to move to 75 | the next field you can use F6. In addition to F6, ALT+TAB, CTRL+TAB, META+TAB 76 | make an attempt to move the focus on. How well this works depends on your 77 | OS/browser combination. 78 | 79 | 80 | ## Embedding GCLI 81 | 82 | There are 3 basic steps in using GCLI in your system. 83 | 84 | 1. Import a GCLI JavaScript file. 85 | For serious use of GCLI you are likely to be creating a custom build (see 86 | below) however if you just want to have a quick play, you can use 87 | ``gcli-uncompressed.js`` from [the gh-pages branch of GCLI] 88 | (https://github.com/mozilla/gcli/tree/gh-pages) 89 | Just place the following wherever you place your script files. 90 | 91 | 92 | 93 | 2. Having imported GCLI, we need to tell it where to display. The simplest 94 | method is to include an elements with the id of ``gcli-input`` and 95 | ``gcli-display``. 96 | 97 | 98 |
99 | 100 | 3. Tell GCLI what commands to make available. See the sections on Writing 101 | Commands, Writing Types and Writing Fields for more information. 102 | 103 | GCLI uses the CommonJS AMD format for it's files, so a 'require' statement 104 | is needed to get started. 105 | 106 | require([ 'gcli/index' ], function(gcli) { 107 | gcli.add(...); // Register custom commands/types/etc 108 | gcli.createTerminal(); // Create a user interface 109 | }); 110 | 111 | The createTerminal() function takes an ``options`` objects which allows 112 | customization. At the current time the documentation of these object is left 113 | to the source. 114 | 115 | 116 | ## Backwards Compatibility 117 | 118 | The goals of the GCLI project are: 119 | 120 | - Aim for very good backwards compatibility with code required from an 121 | 'index' module. This means we will not break code without a cycle of 122 | deprecation warnings. 123 | 124 | There are currently 3 'index' modules: 125 | - gcli/index (all you need to get started with GCLI) 126 | - demo/index (a number of demo commands) 127 | - gclitest/index (GCLI test suite) 128 | 129 | Code from these modules uses the module pattern to prevent access to internal 130 | functions, so in essence, if you can get to it from an index module, you 131 | should be ok. 132 | 133 | - We try to avoid needless change to other modules, however we don't make any 134 | promises, and don't provide a deprecation cycle. 135 | 136 | Code from other modules uses classes rather than modules, so member variables 137 | are exposed. Many classes mark private members using the `_underscorePrefix` 138 | pattern. Particular care should be taken if access is needed to a private 139 | member. 140 | 141 | 142 | ## Creating Custom Builds 143 | 144 | GCLI uses [DryIce](https://github.com/mozilla/dryice) to create custom builds. 145 | If dryice is installed (``npm install .``) then you can create a built 146 | version of GCLI simply using ``node gcli.js standard``. DryIce supplies a custom 147 | module loader to replace RequireJS for built applications. 148 | 149 | The build will be output to the ``built`` directory. The directory will be 150 | created if it doesn't exist. 151 | -------------------------------------------------------------------------------- /docs/running-tests.md: -------------------------------------------------------------------------------- 1 | 2 | # Running Tests 3 | 4 | GCLI has a test suite that can be run in a number of different environments. 5 | Some of the tests don't work in all environments. These should be automatically 6 | skipped when not applicable. 7 | 8 | 9 | ## Web 10 | 11 | Running a limited set of test from the web is the easiest. Simply load 12 | 'localtest.html' and the unit tests should be run automatically, with results 13 | displayed on the console. Tests can be re-run using the 'test' command. 14 | 15 | It also creates a function 'testCommands()' to be run at a JS prompt, which 16 | enables the test commands for debugging purposes. 17 | 18 | 19 | ## Firefox 20 | 21 | GCLI's test suite integrates with Mochitest and runs automatically on each test 22 | run. Dryice packages the tests to format them for the Firefox build system. 23 | 24 | For more information about running Mochitest on Firefox (including GCLI) see 25 | [the MDN, Mochitest docs](https://developer.mozilla.org/en/Mochitest) 26 | 27 | 28 | # Node 29 | 30 | Running the test suite under node can be done as follows: 31 | 32 | $ node gcli.js test 33 | 34 | Or, using the `test` command: 35 | 36 | $ node gcli.js 37 | Serving GCLI to http://localhost:9999/ 38 | This is also a limited GCLI prompt. 39 | Type 'help' for a list of commands, CTRL+C twice to exit: 40 | : test 41 | 42 | testCli: Pass (funcs=9, checks=208) 43 | testCompletion: Pass (funcs=1, checks=139) 44 | testExec: Pass (funcs=1, checks=133) 45 | testHistory: Pass (funcs=3, checks=13) 46 | .... 47 | 48 | Summary: Pass (951 checks) 49 | 50 | 51 | # Travis CI 52 | 53 | GCLI check-ins are automatically tested by [Travis CI](https://travis-ci.org/joewalker/gcli). 54 | 55 | 56 | # Test Case Generation 57 | 58 | GCLI can generate test cases automagically. Load ```localtest.html```, type a 59 | command to be tested into GCLI, and the press F2. GCLI will output to the 60 | console a template test case for the entered command. 61 | -------------------------------------------------------------------------------- /docs/writing-tests.md: -------------------------------------------------------------------------------- 1 | 2 | # Writing Tests 3 | 4 | There are several sources of GCLI tests and several environments in which they 5 | are run. 6 | 7 | The majority of GCLI tests are stored in 8 | [this repository](https://github.com/joewalker/gcli/) in files named like 9 | ```./lib/gclitest/test*.js```. These tests run in Firefox, Chrome, Opera, 10 | and NodeJS/JsDom 11 | 12 | See [Running Tests](running-tests.md) for further details. 13 | 14 | GCLI comes with a generic unit test harness (in ```./lib/test/```) and a 15 | set of helpers for creating GCLI tests (in ```./lib/gclitest/helpers.js```). 16 | 17 | # GCLI tests in Firefox 18 | 19 | The build process converts the GCLI tests to run under Mochitest inside the 20 | Firefox unit tests. It also adds some 21 | -------------------------------------------------------------------------------- /docs/writing-types.md: -------------------------------------------------------------------------------- 1 | 2 | # Writing Types 3 | 4 | Commands are a fundamental building block because they are what the users 5 | directly interacts with, however they are built on ``Type``s. There are a 6 | number of built in types: 7 | 8 | * string. This is a JavaScript string 9 | * number. A JavaScript number 10 | * boolean. A JavaScript boolean 11 | * selection. This is an selection from a number of alternatives 12 | * delegate. This type could change depending on other factors, but is well 13 | defined when one of the conversion routines is called. 14 | 15 | There are a number of additional types defined by Pilot and GCLI as 16 | extensions to the ``selection`` and ``delegate`` types 17 | 18 | * setting. One of the defined settings 19 | * settingValue. A value that can be applied to an associated setting. 20 | * command. One of the defined commands 21 | 22 | Most of our types are 'static' e.g. there is only one type of 'string', however 23 | some types like 'selection' and 'delegate' are customizable. 24 | 25 | All types must inherit from Type and have the following methods: 26 | 27 | /** 28 | * Convert the given value to a string representation. 29 | * Where possible, there should be round-tripping between values and their 30 | * string representations. 31 | */ 32 | stringify: function(value) { return 'string version of value'; }, 33 | 34 | /** 35 | * Convert the given str to an instance of this type. 36 | * Where possible, there should be round-tripping between values and their 37 | * string representations. 38 | * @return Conversion 39 | */ 40 | parse: function(str) { return new Conversion(...); }, 41 | 42 | /** 43 | * The plug-in system, and other things need to know what this type is 44 | * called. The name alone is not enough to fully specify a type. Types like 45 | * 'selection' and 'delegate' need extra data, however this function returns 46 | * only the name, not the extra data. 47 | *

In old bespin, equality was based on the name. This may turn out to be 48 | * important in Ace too. 49 | */ 50 | name: 'example', 51 | 52 | In addition, defining the following function can be helpful, although Type 53 | contains default implementations: 54 | 55 | * nudge(value, by) 56 | 57 | Type, Conversion and Status are all declared by commands.js. 58 | 59 | The values produced by the parse function can be of any type, but if you are 60 | producing your own, you are strongly encouraged to include properties called 61 | ``name`` and ``description`` where it makes sense. There are a number of 62 | places in GCLI where the UI will be able to provide better help to users if 63 | your values include these properties. 64 | 65 | 66 | # Writing Fields 67 | 68 | Fields are visual representations of types. For simple types like string it is 69 | enough to use ````, however more complex types we may wish to 70 | provide a custom widget to allow the user to enter values of the given type. 71 | 72 | This is an example of a very simple new password field type: 73 | 74 | function PasswordField(doc) { 75 | this.doc = doc; 76 | } 77 | 78 | PasswordField.prototype = Object.create(Field.prototype); 79 | 80 | PasswordField.prototype.createElement = function(assignment) { 81 | this.assignment = assignment; 82 | this.input = dom.createElement(this.doc, 'input'); 83 | this.input.type = 'password'; 84 | this.input.value = assignment.arg ? assignment.arg.text : ''; 85 | 86 | this.onKeyup = function() { 87 | this.assignment.setValue(this.input.value); 88 | }.bind(this); 89 | this.input.addEventListener('keyup', this.onKeyup, false); 90 | 91 | this.onChange = function() { 92 | this.input.value = this.assignment.arg.text; 93 | }; 94 | this.assignment.onAssignmentChange.add(this.onChange, this); 95 | 96 | return this.input; 97 | }; 98 | 99 | PasswordField.prototype.destroy = function() { 100 | this.input.removeEventListener('keyup', this.onKeyup, false); 101 | this.assignment.onAssignmentChange.remove(this.onChange, this); 102 | }; 103 | 104 | PasswordField.claim = function(type) { 105 | return type.name === 'password' ? Field.claim.MATCH : Field.claim.NO_MATCH; 106 | }; 107 | -------------------------------------------------------------------------------- /lib/gcli/commands/clear.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var l10n = require('../util/l10n'); 20 | 21 | exports.items = [ 22 | { 23 | // A command to clear the output area 24 | item: 'command', 25 | runAt: 'client', 26 | name: 'clear', 27 | description: l10n.lookup('clearDesc'), 28 | returnType: 'clearoutput', 29 | exec: function(args, context) { } 30 | }, 31 | { 32 | item: 'converter', 33 | from: 'clearoutput', 34 | to: 'view', 35 | exec: function(ignore, conversionContext) { 36 | return { 37 | html: '', 38 | data: { 39 | onload: function(ev) { 40 | // element starts off being the span above, and we walk up the 41 | // tree looking for the terminal 42 | var element = ev.target; 43 | while (element != null && element.terminal == null) { 44 | element = element.parentElement; 45 | } 46 | 47 | if (element == null) { 48 | // This is only an event handler on a completed command 49 | // So we're relying on this showing up in the console 50 | throw new Error('Failed to find clear'); 51 | } 52 | 53 | element.terminal.clear(); 54 | } 55 | } 56 | }; 57 | } 58 | } 59 | ]; 60 | -------------------------------------------------------------------------------- /lib/gcli/commands/context.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var l10n = require('../util/l10n'); 20 | var cli = require('../cli'); 21 | 22 | /** 23 | * 'context' command 24 | */ 25 | var context = { 26 | item: 'command', 27 | name: 'context', 28 | description: l10n.lookup('contextDesc'), 29 | manual: l10n.lookup('contextManual'), 30 | params: [ 31 | { 32 | name: 'prefix', 33 | type: 'command', 34 | description: l10n.lookup('contextPrefixDesc'), 35 | defaultValue: null 36 | } 37 | ], 38 | returnType: 'string', 39 | // The context command is client only because it's essentially sugar for 40 | // typing commands. When there is a command prefix in action, it is the job 41 | // of the remoter to add the prefix to the typed strings that are sent for 42 | // remote execution 43 | noRemote: true, 44 | exec: function echo(args, context) { 45 | var requisition = cli.getMapping(context).requisition; 46 | 47 | if (args.prefix == null) { 48 | requisition.prefix = null; 49 | return l10n.lookup('contextEmptyReply'); 50 | } 51 | 52 | if (args.prefix.exec != null) { 53 | throw new Error(l10n.lookupFormat('contextNotParentError', 54 | [ args.prefix.name ])); 55 | } 56 | 57 | requisition.prefix = args.prefix.name; 58 | return l10n.lookupFormat('contextReply', [ args.prefix.name ]); 59 | } 60 | }; 61 | 62 | exports.items = [ context ]; 63 | -------------------------------------------------------------------------------- /lib/gcli/commands/mocks.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var cli = require('../cli'); 20 | var mockCommands = require('../test/mockCommands'); 21 | var mockFileCommands = require('../test/mockFileCommands'); 22 | var mockSettings = require('../test/mockSettings'); 23 | 24 | var isNode = (typeof(process) !== 'undefined' && 25 | process.title.indexOf('node') != -1); 26 | 27 | exports.items = [ 28 | { 29 | item: 'command', 30 | name: 'mocks', 31 | description: 'Add/remove mock commands', 32 | params: [ 33 | { 34 | name: 'included', 35 | type: { 36 | name: 'selection', 37 | data: [ 'on', 'off' ] 38 | }, 39 | description: 'Turn mock commands on or off', 40 | } 41 | ], 42 | returnType: 'string', 43 | 44 | exec: function(args, context) { 45 | var requisition = cli.getMapping(context).requisition; 46 | this[args.included](requisition); 47 | return 'Mock commands are now ' + args.included; 48 | }, 49 | 50 | on: function(requisition) { 51 | mockCommands.setup(requisition); 52 | mockSettings.setup(requisition.system); 53 | 54 | if (isNode) { 55 | mockFileCommands.setup(requisition); 56 | } 57 | }, 58 | 59 | off: function(requisition) { 60 | mockCommands.shutdown(requisition); 61 | mockSettings.shutdown(requisition.system); 62 | 63 | if (isNode) { 64 | mockFileCommands.shutdown(requisition); 65 | } 66 | } 67 | } 68 | ]; 69 | -------------------------------------------------------------------------------- /lib/gcli/commands/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | DevToolsModules( 8 | 'clear.js', 9 | 'commands.js', 10 | 'context.js', 11 | 'help.js', 12 | 'mocks.js', 13 | 'pref.js', 14 | 'preflist.js', 15 | 'test.js', 16 | ) 17 | -------------------------------------------------------------------------------- /lib/gcli/commands/pref.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var l10n = require('../util/l10n'); 20 | 21 | exports.items = [ 22 | { 23 | // 'pref' command 24 | item: 'command', 25 | name: 'pref', 26 | description: l10n.lookup('prefDesc'), 27 | manual: l10n.lookup('prefManual') 28 | }, 29 | { 30 | // 'pref show' command 31 | item: 'command', 32 | name: 'pref show', 33 | runAt: 'client', 34 | description: l10n.lookup('prefShowDesc'), 35 | manual: l10n.lookup('prefShowManual'), 36 | params: [ 37 | { 38 | name: 'setting', 39 | type: 'setting', 40 | description: l10n.lookup('prefShowSettingDesc'), 41 | manual: l10n.lookup('prefShowSettingManual') 42 | } 43 | ], 44 | exec: function(args, context) { 45 | return l10n.lookupFormat('prefShowSettingValue', 46 | [ args.setting.name, args.setting.value ]); 47 | } 48 | }, 49 | { 50 | // 'pref set' command 51 | item: 'command', 52 | name: 'pref set', 53 | runAt: 'client', 54 | description: l10n.lookup('prefSetDesc'), 55 | manual: l10n.lookup('prefSetManual'), 56 | params: [ 57 | { 58 | name: 'setting', 59 | type: 'setting', 60 | description: l10n.lookup('prefSetSettingDesc'), 61 | manual: l10n.lookup('prefSetSettingManual') 62 | }, 63 | { 64 | name: 'value', 65 | type: 'settingValue', 66 | description: l10n.lookup('prefSetValueDesc'), 67 | manual: l10n.lookup('prefSetValueManual') 68 | } 69 | ], 70 | exec: function(args, context) { 71 | args.setting.value = args.value; 72 | } 73 | }, 74 | { 75 | // 'pref reset' command 76 | item: 'command', 77 | name: 'pref reset', 78 | runAt: 'client', 79 | description: l10n.lookup('prefResetDesc'), 80 | manual: l10n.lookup('prefResetManual'), 81 | params: [ 82 | { 83 | name: 'setting', 84 | type: 'setting', 85 | description: l10n.lookup('prefResetSettingDesc'), 86 | manual: l10n.lookup('prefResetSettingManual') 87 | } 88 | ], 89 | exec: function(args, context) { 90 | args.setting.setDefault(); 91 | } 92 | } 93 | ]; 94 | -------------------------------------------------------------------------------- /lib/gcli/commands/preflist.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var l10n = require('../util/l10n'); 20 | 21 | /** 22 | * Format a list of settings for display 23 | */ 24 | var prefsViewConverter = { 25 | item: 'converter', 26 | from: 'prefsData', 27 | to: 'view', 28 | exec: function(prefsData, conversionContext) { 29 | var prefList = new PrefList(prefsData, conversionContext); 30 | return { 31 | html: 32 | '

\n' + 33 | ' \n' + 39 | ' \n' + 40 | ' \n' + 41 | ' \n' + 42 | ' \n' + 43 | ' \n' + 44 | ' \n' + 45 | ' \n' + 46 | ' \n' + 47 | ' \n' + 48 | '
${l10n.prefOutputName}${l10n.prefOutputValue}
\n' + 49 | '
\n' + 50 | ' \n' + 51 | '
\n' + 52 | '
\n' + 53 | '
\n', 54 | data: prefList, 55 | options: { 56 | blankNullUndefined: true, 57 | allowEval: true, 58 | stack: 'prefsData->view' 59 | }, 60 | css: 61 | '.gcli-pref-list-scroller {\n' + 62 | ' max-height: 200px;\n' + 63 | ' overflow-y: auto;\n' + 64 | ' overflow-x: hidden;\n' + 65 | ' display: inline-block;\n' + 66 | '}\n' + 67 | '\n' + 68 | '.gcli-pref-list-table {\n' + 69 | ' width: 500px;\n' + 70 | ' table-layout: fixed;\n' + 71 | '}\n' + 72 | '\n' + 73 | '.gcli-pref-list-table tr > th {\n' + 74 | ' text-align: left;\n' + 75 | '}\n' + 76 | '\n' + 77 | '.gcli-pref-list-table tr > td {\n' + 78 | ' text-overflow: elipsis;\n' + 79 | ' word-wrap: break-word;\n' + 80 | '}\n' + 81 | '\n' + 82 | '.gcli-pref-list-name {\n' + 83 | ' width: 70%;\n' + 84 | '}\n' + 85 | '\n' + 86 | '.gcli-pref-list-command {\n' + 87 | ' display: none;\n' + 88 | '}\n' + 89 | '\n' + 90 | '.gcli-pref-list-row:hover .gcli-pref-list-command {\n' + 91 | ' /* \'pref list\' is a bit broken and unimportant. Band-aid follows */\n' + 92 | ' /* display: inline-block; */\n' + 93 | '}\n', 94 | cssId: 'gcli-pref-list' 95 | }; 96 | } 97 | }; 98 | 99 | /** 100 | * Format a list of settings for display 101 | */ 102 | var prefsStringConverter = { 103 | item: 'converter', 104 | from: 'prefsData', 105 | to: 'string', 106 | exec: function(prefsData, conversionContext) { 107 | var reply = ''; 108 | prefsData.settings.forEach(function(setting) { 109 | reply += setting.name + ' -> ' + setting.value + '\n'; 110 | }); 111 | return reply; 112 | } 113 | }; 114 | 115 | /** 116 | * 'pref list' command 117 | */ 118 | var prefList = { 119 | item: 'command', 120 | name: 'pref list', 121 | description: l10n.lookup('prefListDesc'), 122 | manual: l10n.lookup('prefListManual'), 123 | params: [ 124 | { 125 | name: 'search', 126 | type: 'string', 127 | defaultValue: null, 128 | description: l10n.lookup('prefListSearchDesc'), 129 | manual: l10n.lookup('prefListSearchManual') 130 | } 131 | ], 132 | returnType: 'prefsData', 133 | exec: function(args, context) { 134 | return new Promise(function(resolve, reject) { 135 | // This can be slow, get out of the way of the main thread 136 | setTimeout(function() { 137 | var prefsData = { 138 | settings: context.system.settings.getAll(args.search), 139 | search: args.search 140 | }; 141 | resolve(prefsData); 142 | }.bind(this), 10); 143 | }); 144 | } 145 | }; 146 | 147 | /** 148 | * A manager for our version of about:config 149 | */ 150 | function PrefList(prefsData, conversionContext) { 151 | this.search = prefsData.search; 152 | this.settings = prefsData.settings; 153 | this.conversionContext = conversionContext; 154 | 155 | this.onLoad = this.onLoad.bind(this); 156 | } 157 | 158 | /** 159 | * A load event handler registered by the template engine so we can load the 160 | * inner document 161 | */ 162 | PrefList.prototype.onLoad = function(element) { 163 | var table = element.querySelector('.gcli-pref-list-table'); 164 | this.updateTable(table); 165 | return ''; 166 | }; 167 | 168 | /** 169 | * Forward localization lookups 170 | */ 171 | PrefList.prototype.l10n = l10n.propertyLookup; 172 | 173 | /** 174 | * Called from the template onkeyup for the filter element 175 | */ 176 | PrefList.prototype.updateTable = function(table) { 177 | var view = this.conversionContext.createView({ 178 | html: 179 | '\n' + 180 | ' \n' + 181 | ' \n' + 182 | ' \n' + 183 | ' \n' + 184 | ' \n' + 185 | ' \n' + 186 | ' \n' + 190 | ' \n' + 191 | '
${setting.name}\n' + 187 | ' ${setting.value}\n' + 188 | ' [Edit]\n' + 189 | '
\n', 192 | options: { blankNullUndefined: true, stack: 'prefsData#inner' }, 193 | data: this 194 | }); 195 | 196 | view.appendTo(table, true); 197 | }; 198 | 199 | PrefList.prototype.onFilterChange = function(ev) { 200 | if (ev.target.value !== this.search) { 201 | this.search = ev.target.value; 202 | 203 | var root = ev.target.parentNode.parentNode; 204 | var table = root.querySelector('.gcli-pref-list-table'); 205 | this.updateTable(table); 206 | } 207 | }; 208 | 209 | PrefList.prototype.onSetClick = function(ev) { 210 | var typed = ev.currentTarget.getAttribute('data-command'); 211 | this.conversionContext.update(typed); 212 | }; 213 | 214 | exports.items = [ prefsViewConverter, prefsStringConverter, prefList ]; 215 | -------------------------------------------------------------------------------- /lib/gcli/commands/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var examiner = require('../testharness/examiner'); 20 | var stati = require('../testharness/status').stati; 21 | var helpers = require('../test/helpers'); 22 | var suite = require('../test/suite'); 23 | var cli = require('../cli'); 24 | var Requisition = require('../cli').Requisition; 25 | var createRequisitionAutomator = require('../test/automators/requisition').createRequisitionAutomator; 26 | 27 | var isNode = (typeof(process) !== 'undefined' && 28 | process.title.indexOf('node') != -1); 29 | 30 | suite.init(isNode); 31 | 32 | exports.optionsContainer = []; 33 | 34 | exports.items = [ 35 | { 36 | item: 'type', 37 | name: 'suite', 38 | parent: 'selection', 39 | cacheable: true, 40 | lookup: function() { 41 | return Object.keys(examiner.suites).map(function(name) { 42 | return { name: name, value: examiner.suites[name] }; 43 | }); 44 | } 45 | }, 46 | { 47 | item: 'command', 48 | name: 'test', 49 | description: 'Run GCLI unit tests', 50 | params: [ 51 | { 52 | name: 'suite', 53 | type: 'suite', 54 | description: 'Test suite to run.', 55 | defaultValue: examiner 56 | }, 57 | { 58 | name: 'usehost', 59 | type: 'boolean', 60 | description: 'Run the unit tests in the host window', 61 | option: true 62 | } 63 | ], 64 | returnType: 'examiner-output', 65 | noRemote: true, 66 | exec: function(args, context) { 67 | if (args.usehost && exports.optionsContainer.length === 0) { 68 | throw new Error('Can\'t use --usehost without injected options'); 69 | } 70 | 71 | var options; 72 | if (args.usehost) { 73 | options = exports.optionsContainer[0]; 74 | } 75 | else { 76 | var env = { 77 | document: document, 78 | window: window 79 | }; 80 | options = { 81 | isNode: isNode, 82 | isFirefox: false, 83 | isPhantomjs: false, 84 | requisition: new Requisition(context.system, { environment: env }) 85 | }; 86 | options.automator = createRequisitionAutomator(options.requisition); 87 | } 88 | 89 | var requisition = options.requisition; 90 | requisition.system.commands.get('mocks').on(requisition); 91 | helpers.resetResponseTimes(); 92 | examiner.reset(); 93 | 94 | return args.suite.run(options).then(function() { 95 | requisition.system.commands.get('mocks').off(requisition); 96 | var output = context.typedData('examiner-output', examiner.toRemote()); 97 | 98 | if (output.data.summary.status === stati.pass) { 99 | return output; 100 | } 101 | else { 102 | cli.logErrors = false; 103 | throw output; 104 | } 105 | }); 106 | } 107 | }, 108 | { 109 | item: 'converter', 110 | from: 'examiner-output', 111 | to: 'string', 112 | exec: function(output, conversionContext) { 113 | return '\n' + examiner.detailedResultLog('NodeJS/NoDom') + 114 | '\n' + helpers.timingSummary; 115 | } 116 | }, 117 | { 118 | item: 'converter', 119 | from: 'examiner-output', 120 | to: 'view', 121 | exec: function(output, conversionContext) { 122 | return { 123 | html: 124 | '
\n' + 125 | ' \n' + 126 | ' \n' + 127 | ' \n' + 128 | ' \n' + 129 | ' \n' + 130 | ' \n' + 131 | ' \n' + 132 | ' \n' + 133 | ' \n' + 134 | ' \n' + 135 | ' \n' + 136 | ' \n' + 137 | ' \n' + 138 | ' \n' + 139 | ' \n' + 140 | ' \n' + 141 | ' \n' + 150 | ' \n' + 151 | ' \n' + 152 | ' \n' + 153 | ' \n' + 154 | ' \n' + 155 | ' \n' + 156 | ' \n' + 157 | ' \n' + 158 | ' \n' + 159 | ' \n' + 160 | ' \n' + 161 | '
SuiteTestResultsChecksNotes
${suite.name}${test.title}${test.status.name}${test.checks}\n' + 142 | '
\n' + 143 | ' ${failure.message}\n' + 144 | '
    \n' + 145 | '
  • P1: ${failure.p1}
  • \n' + 146 | '
  • P2: ${failure.p2}
  • \n' + 147 | '
\n' + 148 | '
\n' + 149 | '
Total${summary.status.name}${summary.checks}
\n' + 162 | '
', 163 | css: 164 | '.gcliTestSkipped {\n' + 165 | ' background-color: #EEE;\n' + 166 | ' color: #000;\n' + 167 | '}\n' + 168 | '\n' + 169 | '.gcliTestExecuting {\n' + 170 | ' background-color: #888;\n' + 171 | ' color: #FFF;\n' + 172 | '}\n' + 173 | '\n' + 174 | '.gcliTestWaiting {\n' + 175 | ' background-color: #FFA;\n' + 176 | ' color: #000;\n' + 177 | '}\n' + 178 | '\n' + 179 | '.gcliTestPass {\n' + 180 | ' background-color: #8F8;\n' + 181 | ' color: #000;\n' + 182 | '}\n' + 183 | '\n' + 184 | '.gcliTestFail {\n' + 185 | ' background-color: #F00;\n' + 186 | ' color: #FFF;\n' + 187 | '}\n' + 188 | '\n' + 189 | 'td.gcliTestSuite {\n' + 190 | ' font-family: monospace;\n' + 191 | ' font-size: 90%;\n' + 192 | ' text-align: right;\n' + 193 | '}\n' + 194 | '\n' + 195 | '.gcliTestResults th.gcliTestSuite,\n' + 196 | '.gcliTestResults .gcliTestChecks {\n' + 197 | ' text-align: right;\n' + 198 | '}\n' + 199 | '\n' + 200 | '.gcliTestResults th {\n' + 201 | ' text-align: left;\n' + 202 | '}\n' + 203 | '\n' + 204 | '.gcliTestMessages ul {\n' + 205 | ' margin: 0 0 10px;\n' + 206 | ' padding-left: 20px;\n' + 207 | ' list-style-type: square;\n' + 208 | '}\n', 209 | cssId: 'gcli-test', 210 | data: output, 211 | options: { allowEval: true, stack: 'test.html' } 212 | }; 213 | } 214 | } 215 | ]; 216 | -------------------------------------------------------------------------------- /lib/gcli/connectors/connectors.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | /** 20 | * This is how to implement a connector 21 | * var baseConnector = { 22 | * item: 'connector', 23 | * name: 'foo', 24 | * 25 | * connect: function(url) { 26 | * return Promise.resolve(new FooConnection(url)); 27 | * } 28 | * }; 29 | */ 30 | 31 | /** 32 | * A prototype base for Connectors 33 | */ 34 | function Connection() { 35 | } 36 | 37 | /** 38 | * Add an event listener 39 | */ 40 | Connection.prototype.on = function(event, action) { 41 | if (!this._listeners) { 42 | this._listeners = {}; 43 | } 44 | if (!this._listeners[event]) { 45 | this._listeners[event] = []; 46 | } 47 | this._listeners[event].push(action); 48 | }; 49 | 50 | /** 51 | * Remove an event listener 52 | */ 53 | Connection.prototype.off = function(event, action) { 54 | if (!this._listeners) { 55 | return; 56 | } 57 | var actions = this._listeners[event]; 58 | if (actions) { 59 | this._listeners[event] = actions.filter(function(li) { 60 | return li !== action; 61 | }.bind(this)); 62 | } 63 | }; 64 | 65 | /** 66 | * Emit an event. For internal use only 67 | */ 68 | Connection.prototype._emit = function(event, data) { 69 | if (this._listeners == null || this._listeners[event] == null) { 70 | return; 71 | } 72 | 73 | var listeners = this._listeners[event]; 74 | listeners.forEach(function(listener) { 75 | // Fail fast if we mutate the list of listeners while emitting 76 | if (listeners !== this._listeners[event]) { 77 | throw new Error('Listener list changed while emitting'); 78 | } 79 | 80 | try { 81 | listener.call(null, data); 82 | } 83 | catch (ex) { 84 | console.log('Error calling listeners to ' + event); 85 | console.error(ex); 86 | } 87 | }.bind(this)); 88 | }; 89 | 90 | /** 91 | * Send a message to the other side of the connection 92 | */ 93 | Connection.prototype.call = function(feature, data) { 94 | throw new Error('Not implemented'); 95 | }; 96 | 97 | /** 98 | * Disconnecting a Connection destroys the resources it holds. There is no 99 | * common route back to being connected once this has been called 100 | */ 101 | Connection.prototype.disconnect = function() { 102 | return Promise.resolve(); 103 | }; 104 | 105 | exports.Connection = Connection; 106 | 107 | /** 108 | * A manager for the registered Connectors 109 | */ 110 | function Connectors() { 111 | // This is where we cache the connectors that we know about 112 | this._registered = {}; 113 | } 114 | 115 | /** 116 | * Add a new connector to the cache 117 | */ 118 | Connectors.prototype.add = function(connector) { 119 | this._registered[connector.name] = connector; 120 | }; 121 | 122 | /** 123 | * Remove an existing connector from the cache 124 | */ 125 | Connectors.prototype.remove = function(connector) { 126 | var name = typeof connector === 'string' ? connector : connector.name; 127 | delete this._registered[name]; 128 | }; 129 | 130 | /** 131 | * Get access to the list of known connectors 132 | */ 133 | Connectors.prototype.getAll = function() { 134 | return Object.keys(this._registered).map(function(name) { 135 | return this._registered[name]; 136 | }.bind(this)); 137 | }; 138 | 139 | var defaultConnectorName; 140 | 141 | /** 142 | * Get access to a connector by name. If name is undefined then first try to 143 | * use the same connector that we used last time, and if there was no last 144 | * time, then just use the first registered connector as a default. 145 | */ 146 | Connectors.prototype.get = function(name) { 147 | if (name == null) { 148 | name = (defaultConnectorName == null) ? 149 | Object.keys(this._registered)[0] : 150 | defaultConnectorName; 151 | } 152 | 153 | defaultConnectorName = name; 154 | return this._registered[name]; 155 | }; 156 | 157 | exports.Connectors = Connectors; 158 | -------------------------------------------------------------------------------- /lib/gcli/connectors/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | DevToolsModules( 8 | 'connectors.js', 9 | ) 10 | -------------------------------------------------------------------------------- /lib/gcli/converters/basic.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | 21 | /** 22 | * Several converters are just data.toString inside a 'p' element 23 | */ 24 | function nodeFromDataToString(data, conversionContext) { 25 | var node = util.createElement(conversionContext.document, 'p'); 26 | node.textContent = data.toString(); 27 | return node; 28 | } 29 | 30 | exports.items = [ 31 | { 32 | item: 'converter', 33 | from: 'string', 34 | to: 'dom', 35 | exec: nodeFromDataToString 36 | }, 37 | { 38 | item: 'converter', 39 | from: 'number', 40 | to: 'dom', 41 | exec: nodeFromDataToString 42 | }, 43 | { 44 | item: 'converter', 45 | from: 'boolean', 46 | to: 'dom', 47 | exec: nodeFromDataToString 48 | }, 49 | { 50 | item: 'converter', 51 | from: 'undefined', 52 | to: 'dom', 53 | exec: function(data, conversionContext) { 54 | return util.createElement(conversionContext.document, 'span'); 55 | } 56 | }, 57 | { 58 | item: 'converter', 59 | from: 'json', 60 | to: 'view', 61 | exec: function(json, context) { 62 | var html = JSON.stringify(json, null, ' ').replace(/\n/g, '
'); 63 | return { 64 | html: '
' + html + '
' 65 | }; 66 | } 67 | }, 68 | { 69 | item: 'converter', 70 | from: 'number', 71 | to: 'string', 72 | exec: function(data) { return '' + data; } 73 | }, 74 | { 75 | item: 'converter', 76 | from: 'boolean', 77 | to: 'string', 78 | exec: function(data) { return '' + data; } 79 | }, 80 | { 81 | item: 'converter', 82 | from: 'undefined', 83 | to: 'string', 84 | exec: function(data) { return ''; } 85 | }, 86 | { 87 | item: 'converter', 88 | from: 'json', 89 | to: 'string', 90 | exec: function(json, conversionContext) { 91 | return JSON.stringify(json, null, ' '); 92 | } 93 | } 94 | ]; 95 | -------------------------------------------------------------------------------- /lib/gcli/converters/converters.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | var host = require('../util/host'); 21 | 22 | // It's probably easiest to read this bottom to top 23 | 24 | /** 25 | * Best guess at creating a DOM element from random data 26 | */ 27 | var fallbackDomConverter = { 28 | from: '*', 29 | to: 'dom', 30 | exec: function(data, conversionContext) { 31 | return conversionContext.document.createTextNode(data || ''); 32 | } 33 | }; 34 | 35 | /** 36 | * Best guess at creating a string from random data 37 | */ 38 | var fallbackStringConverter = { 39 | from: '*', 40 | to: 'string', 41 | exec: function(data, conversionContext) { 42 | return data == null ? '' : data.toString(); 43 | } 44 | }; 45 | 46 | /** 47 | * Convert a view object to a DOM element 48 | */ 49 | var viewDomConverter = { 50 | item: 'converter', 51 | from: 'view', 52 | to: 'dom', 53 | exec: function(view, conversionContext) { 54 | if (!view.isView) { 55 | view = conversionContext.createView(view); 56 | } 57 | return view.toDom(conversionContext.document); 58 | } 59 | }; 60 | 61 | /** 62 | * Convert a view object to a string 63 | */ 64 | var viewStringConverter = { 65 | item: 'converter', 66 | from: 'view', 67 | to: 'string', 68 | exec: function(view, conversionContext) { 69 | if (!view.isView) { 70 | view = conversionContext.createView(view); 71 | } 72 | return view.toDom(conversionContext.document).textContent; 73 | } 74 | }; 75 | 76 | /** 77 | * Convert a view object to a string 78 | */ 79 | var stringViewStringConverter = { 80 | item: 'converter', 81 | from: 'stringView', 82 | to: 'string', 83 | exec: function(view, conversionContext) { 84 | if (!view.isView) { 85 | view = conversionContext.createView(view); 86 | } 87 | return view.toDom(conversionContext.document).textContent; 88 | } 89 | }; 90 | 91 | /** 92 | * Convert an exception to a DOM element 93 | */ 94 | var errorDomConverter = { 95 | item: 'converter', 96 | from: 'error', 97 | to: 'dom', 98 | exec: function(ex, conversionContext) { 99 | var node = util.createElement(conversionContext.document, 'p'); 100 | node.className = 'gcli-error'; 101 | node.textContent = errorStringConverter.exec(ex, conversionContext); 102 | return node; 103 | } 104 | }; 105 | 106 | /** 107 | * Convert an exception to a string 108 | */ 109 | var errorStringConverter = { 110 | item: 'converter', 111 | from: 'error', 112 | to: 'string', 113 | exec: function(ex, conversionContext) { 114 | if (typeof ex === 'string') { 115 | return ex; 116 | } 117 | if (ex instanceof Error) { 118 | return '' + ex; 119 | } 120 | if (typeof ex.message === 'string') { 121 | return ex.message; 122 | } 123 | return '' + ex; 124 | } 125 | }; 126 | 127 | /** 128 | * Create a new converter by using 2 converters, one after the other 129 | */ 130 | function getChainConverter(first, second) { 131 | if (first.to !== second.from) { 132 | throw new Error('Chain convert impossible: ' + first.to + '!=' + second.from); 133 | } 134 | return { 135 | from: first.from, 136 | to: second.to, 137 | exec: function(data, conversionContext) { 138 | var intermediate = first.exec(data, conversionContext); 139 | return second.exec(intermediate, conversionContext); 140 | } 141 | }; 142 | } 143 | 144 | /** 145 | * A manager for the registered Converters 146 | */ 147 | function Converters() { 148 | // This is where we cache the converters that we know about 149 | this._registered = { 150 | from: {} 151 | }; 152 | } 153 | 154 | /** 155 | * Add a new converter to the cache 156 | */ 157 | Converters.prototype.add = function(converter) { 158 | var fromMatch = this._registered.from[converter.from]; 159 | if (fromMatch == null) { 160 | fromMatch = {}; 161 | this._registered.from[converter.from] = fromMatch; 162 | } 163 | 164 | fromMatch[converter.to] = converter; 165 | }; 166 | 167 | /** 168 | * Remove an existing converter from the cache 169 | */ 170 | Converters.prototype.remove = function(converter) { 171 | var fromMatch = this._registered.from[converter.from]; 172 | if (fromMatch == null) { 173 | return; 174 | } 175 | 176 | if (fromMatch[converter.to] === converter) { 177 | fromMatch[converter.to] = null; 178 | } 179 | }; 180 | 181 | /** 182 | * Work out the best converter that we've got, for a given conversion. 183 | */ 184 | Converters.prototype.get = function(from, to) { 185 | var fromMatch = this._registered.from[from]; 186 | if (fromMatch == null) { 187 | return this._getFallbackConverter(from, to); 188 | } 189 | 190 | var converter = fromMatch[to]; 191 | if (converter == null) { 192 | // Someone is going to love writing a graph search algorithm to work out 193 | // the smallest number of conversions, or perhaps the least 'lossy' 194 | // conversion but for now the only 2 step conversions which we are going to 195 | // special case are foo->view->dom and foo->stringView->string. 196 | if (to === 'dom') { 197 | converter = fromMatch.view; 198 | if (converter != null) { 199 | return getChainConverter(converter, viewDomConverter); 200 | } 201 | } 202 | 203 | if (to === 'string') { 204 | converter = fromMatch.stringView; 205 | if (converter != null) { 206 | return getChainConverter(converter, stringViewStringConverter); 207 | } 208 | converter = fromMatch.view; 209 | if (converter != null) { 210 | return getChainConverter(converter, viewStringConverter); 211 | } 212 | } 213 | 214 | return this._getFallbackConverter(from, to); 215 | } 216 | return converter; 217 | }; 218 | 219 | /** 220 | * Get all the registered converters. Most for debugging 221 | */ 222 | Converters.prototype.getAll = function() { 223 | return Object.keys(this._registered.from).map(function(name) { 224 | return this._registered.from[name]; 225 | }.bind(this)); 226 | }; 227 | 228 | /** 229 | * Helper for get to pick the best fallback converter 230 | */ 231 | Converters.prototype._getFallbackConverter = function(from, to) { 232 | console.error('No converter from ' + from + ' to ' + to + '. Using fallback'); 233 | 234 | if (to === 'dom') { 235 | return fallbackDomConverter; 236 | } 237 | 238 | if (to === 'string') { 239 | return fallbackStringConverter; 240 | } 241 | 242 | throw new Error('No conversion possible from ' + from + ' to ' + to + '.'); 243 | }; 244 | 245 | /** 246 | * Convert some data from one type to another 247 | * @param data The object to convert 248 | * @param from The type of the data right now 249 | * @param to The type that we would like the data in 250 | * @param conversionContext An execution context (i.e. simplified requisition) 251 | * which is often required for access to a document, or createView function 252 | */ 253 | Converters.prototype.convert = function(data, from, to, conversionContext) { 254 | try { 255 | if (from === to) { 256 | return Promise.resolve(data); 257 | } 258 | 259 | var converter = this.get(from, to); 260 | return host.exec(function() { 261 | return converter.exec(data, conversionContext); 262 | }.bind(this)); 263 | } 264 | catch (ex) { 265 | var converter = this.get('error', to); 266 | return host.exec(function() { 267 | return converter.exec(ex, conversionContext); 268 | }.bind(this)); 269 | } 270 | }; 271 | 272 | exports.Converters = Converters; 273 | 274 | /** 275 | * Items for export 276 | */ 277 | exports.items = [ 278 | viewDomConverter, viewStringConverter, stringViewStringConverter, 279 | errorDomConverter, errorStringConverter 280 | ]; 281 | -------------------------------------------------------------------------------- /lib/gcli/converters/html.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | 21 | /** 22 | * 'html' means a string containing HTML markup. We use innerHTML to inject 23 | * this into a DOM which has security implications, so this module will not 24 | * be used in all implementations. 25 | */ 26 | exports.items = [ 27 | { 28 | item: 'converter', 29 | from: 'html', 30 | to: 'dom', 31 | exec: function(html, conversionContext) { 32 | var div = util.createElement(conversionContext.document, 'div'); 33 | div.innerHTML = html; 34 | return div; 35 | } 36 | }, 37 | { 38 | item: 'converter', 39 | from: 'html', 40 | to: 'string', 41 | exec: function(html, conversionContext) { 42 | var div = util.createElement(conversionContext.document, 'div'); 43 | div.innerHTML = html; 44 | return div.textContent; 45 | } 46 | } 47 | ]; 48 | -------------------------------------------------------------------------------- /lib/gcli/converters/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | DevToolsModules( 8 | 'basic.js', 9 | 'converters.js', 10 | 'html.js', 11 | 'terminal.js', 12 | ) 13 | -------------------------------------------------------------------------------- /lib/gcli/converters/terminal.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | 21 | /** 22 | * A 'terminal' object is a string or an array of strings, which are typically 23 | * the output from a shell command 24 | */ 25 | exports.items = [ 26 | { 27 | item: 'converter', 28 | from: 'terminal', 29 | to: 'dom', 30 | createTextArea: function(text, conversionContext) { 31 | var node = util.createElement(conversionContext.document, 'textarea'); 32 | node.classList.add('gcli-row-subterminal'); 33 | node.readOnly = true; 34 | node.textContent = text; 35 | return node; 36 | }, 37 | exec: function(data, conversionContext) { 38 | if (Array.isArray(data)) { 39 | var node = util.createElement(conversionContext.document, 'div'); 40 | data.forEach(function(member) { 41 | node.appendChild(this.createTextArea(member, conversionContext)); 42 | }); 43 | return node; 44 | } 45 | return this.createTextArea(data); 46 | } 47 | }, 48 | { 49 | item: 'converter', 50 | from: 'terminal', 51 | to: 'string', 52 | exec: function(data, conversionContext) { 53 | return Array.isArray(data) ? data.join('') : '' + data; 54 | } 55 | } 56 | ]; 57 | -------------------------------------------------------------------------------- /lib/gcli/fields/delegate.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | var Field = require('./fields').Field; 21 | 22 | /** 23 | * A field that works with delegate types by delaying resolution until that 24 | * last possible time 25 | */ 26 | function DelegateField(type, options) { 27 | Field.call(this, type, options); 28 | this.options = options; 29 | 30 | this.element = util.createElement(this.document, 'div'); 31 | this.update(); 32 | 33 | this.onFieldChange = util.createEvent('DelegateField.onFieldChange'); 34 | } 35 | 36 | DelegateField.prototype = Object.create(Field.prototype); 37 | 38 | DelegateField.prototype.update = function() { 39 | var subtype = this.type.getType(this.options.requisition.executionContext); 40 | if (typeof subtype.parse !== 'function') { 41 | subtype = this.options.requisition.system.types.createType(subtype); 42 | } 43 | 44 | // It's not clear that we can compare subtypes in this way. 45 | // Perhaps we need a type.equals(...) function 46 | if (subtype === this.subtype) { 47 | return; 48 | } 49 | 50 | if (this.field) { 51 | this.field.destroy(); 52 | } 53 | 54 | this.subtype = subtype; 55 | var fields = this.options.requisition.system.fields; 56 | this.field = fields.get(subtype, this.options); 57 | 58 | util.clearElement(this.element); 59 | this.element.appendChild(this.field.element); 60 | }; 61 | 62 | DelegateField.claim = function(type, context) { 63 | return type.isDelegate ? Field.MATCH : Field.NO_MATCH; 64 | }; 65 | 66 | DelegateField.prototype.destroy = function() { 67 | this.element = undefined; 68 | this.options = undefined; 69 | if (this.field) { 70 | this.field.destroy(); 71 | } 72 | this.subtype = undefined; 73 | Field.prototype.destroy.call(this); 74 | }; 75 | 76 | DelegateField.prototype.setConversion = function(conversion) { 77 | this.field.setConversion(conversion); 78 | }; 79 | 80 | DelegateField.prototype.getConversion = function() { 81 | return this.field.getConversion(); 82 | }; 83 | 84 | Object.defineProperty(DelegateField.prototype, 'isImportant', { 85 | get: function() { 86 | return this.field.isImportant; 87 | }, 88 | enumerable: true 89 | }); 90 | 91 | /** 92 | * Exported items 93 | */ 94 | exports.items = [ 95 | DelegateField 96 | ]; 97 | -------------------------------------------------------------------------------- /lib/gcli/fields/fields.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | 21 | /** 22 | * A Field is a way to get input for a single parameter. 23 | * This class is designed to be inherited from. It's important that all 24 | * subclasses have a similar constructor signature because they are created 25 | * via Fields.get(...) 26 | * @param type The type to use in conversions 27 | * @param options A set of properties to help fields configure themselves: 28 | * - document: The document we use in calling createElement 29 | * - requisition: The requisition that we're attached to 30 | */ 31 | function Field(type, options) { 32 | this.type = type; 33 | this.document = options.document; 34 | this.requisition = options.requisition; 35 | } 36 | 37 | /** 38 | * Enable registration of fields using addItems 39 | */ 40 | Field.prototype.item = 'field'; 41 | 42 | /** 43 | * Subclasses should assign their element with the DOM node that gets added 44 | * to the 'form'. It doesn't have to be an input node, just something that 45 | * contains it. 46 | */ 47 | Field.prototype.element = undefined; 48 | 49 | /** 50 | * Called from the outside to indicate that the command line has changed and 51 | * the field should update itself 52 | */ 53 | Field.prototype.update = function() { 54 | }; 55 | 56 | /** 57 | * Indicates that this field should drop any resources that it has created 58 | */ 59 | Field.prototype.destroy = function() { 60 | this.messageElement = undefined; 61 | this.document = undefined; 62 | this.requisition = undefined; 63 | }; 64 | 65 | // Note: We could/should probably change Fields from working with Conversions 66 | // to working with Arguments (Tokens), which makes for less calls to parse() 67 | 68 | /** 69 | * Update this field display with the value from this conversion. 70 | * Subclasses should provide an implementation of this function. 71 | */ 72 | Field.prototype.setConversion = function(conversion) { 73 | throw new Error('Field should not be used directly'); 74 | }; 75 | 76 | /** 77 | * Extract a conversion from the values in this field. 78 | * Subclasses should provide an implementation of this function. 79 | */ 80 | Field.prototype.getConversion = function() { 81 | throw new Error('Field should not be used directly'); 82 | }; 83 | 84 | /** 85 | * Set the element where messages and validation errors will be displayed 86 | * @see setMessage() 87 | */ 88 | Field.prototype.setMessageElement = function(element) { 89 | this.messageElement = element; 90 | }; 91 | 92 | /** 93 | * Display a validation message in the UI 94 | */ 95 | Field.prototype.setMessage = function(message) { 96 | if (this.messageElement) { 97 | util.setTextContent(this.messageElement, message || ''); 98 | } 99 | }; 100 | 101 | /** 102 | * Some fields contain information that is more important to the user, for 103 | * example error messages and completion menus. 104 | */ 105 | Field.prototype.isImportant = false; 106 | 107 | /** 108 | * 'static/abstract' method to allow implementations of Field to lay a claim 109 | * to a type. This allows claims of various strength to be weighted up. 110 | * See the Field.*MATCH values. 111 | */ 112 | Field.claim = function(type, context) { 113 | throw new Error('Field should not be used directly'); 114 | }; 115 | 116 | /** 117 | * How good a match is a field for a given type 118 | */ 119 | Field.MATCH = 3; // Good match 120 | Field.DEFAULT = 2; // A default match 121 | Field.BASIC = 1; // OK in an emergency. i.e. assume Strings 122 | Field.NO_MATCH = 0; // This field can't help with the given type 123 | 124 | exports.Field = Field; 125 | 126 | 127 | /** 128 | * A manager for the registered Fields 129 | */ 130 | function Fields() { 131 | // Internal array of known fields 132 | this._fieldCtors = []; 133 | } 134 | 135 | /** 136 | * Add a field definition by field constructor 137 | * @param fieldCtor Constructor function of new Field 138 | */ 139 | Fields.prototype.add = function(fieldCtor) { 140 | if (typeof fieldCtor !== 'function') { 141 | console.error('fields.add erroring on ', fieldCtor); 142 | throw new Error('fields.add requires a Field constructor'); 143 | } 144 | this._fieldCtors.push(fieldCtor); 145 | }; 146 | 147 | /** 148 | * Remove a Field definition 149 | * @param field A previously registered field, specified either with a field 150 | * name or from the field name 151 | */ 152 | Fields.prototype.remove = function(field) { 153 | if (typeof field !== 'string') { 154 | this._fieldCtors = this._fieldCtors.filter(function(test) { 155 | return test !== field; 156 | }); 157 | } 158 | else if (field instanceof Field) { 159 | this.remove(field.name); 160 | } 161 | else { 162 | console.error('fields.remove erroring on ', field); 163 | throw new Error('fields.remove requires an instance of Field'); 164 | } 165 | }; 166 | 167 | /** 168 | * Find the best possible matching field from the specification of the type 169 | * of field required. 170 | * @param type An instance of Type that we will represent 171 | * @param options A set of properties that we should attempt to match, and use 172 | * in the construction of the new field object: 173 | * - document: The document to use in creating new elements 174 | * - requisition: The requisition we're monitoring, 175 | * @return A newly constructed field that best matches the input options 176 | */ 177 | Fields.prototype.get = function(type, options) { 178 | var FieldConstructor; 179 | var highestClaim = -1; 180 | this._fieldCtors.forEach(function(fieldCtor) { 181 | var context = (options.requisition == null) ? 182 | null : options.requisition.executionContext; 183 | var claim = fieldCtor.claim(type, context); 184 | if (claim > highestClaim) { 185 | highestClaim = claim; 186 | FieldConstructor = fieldCtor; 187 | } 188 | }); 189 | 190 | if (!FieldConstructor) { 191 | console.error('Unknown field type ', type, ' in ', this._fieldCtors); 192 | throw new Error('Can\'t find field for ' + type); 193 | } 194 | 195 | if (highestClaim < Field.DEFAULT) { 196 | return new BlankField(type, options); 197 | } 198 | 199 | return new FieldConstructor(type, options); 200 | }; 201 | 202 | /** 203 | * Get all the registered fields. Most for debugging 204 | */ 205 | Fields.prototype.getAll = function() { 206 | return this._fieldCtors.slice(); 207 | }; 208 | 209 | exports.Fields = Fields; 210 | 211 | /** 212 | * For use with delegate types that do not yet have anything to resolve to. 213 | * BlankFields are not for general use. 214 | */ 215 | function BlankField(type, options) { 216 | Field.call(this, type, options); 217 | 218 | this.element = util.createElement(this.document, 'div'); 219 | 220 | this.onFieldChange = util.createEvent('BlankField.onFieldChange'); 221 | } 222 | 223 | BlankField.prototype = Object.create(Field.prototype); 224 | 225 | BlankField.claim = function(type, context) { 226 | return type.name === 'blank' ? Field.MATCH : Field.NO_MATCH; 227 | }; 228 | 229 | BlankField.prototype.destroy = function() { 230 | this.element = undefined; 231 | Field.prototype.destroy.call(this); 232 | }; 233 | 234 | BlankField.prototype.setConversion = function(conversion) { 235 | this.setMessage(conversion.message); 236 | }; 237 | 238 | BlankField.prototype.getConversion = function() { 239 | return this.type.parseString('', this.requisition.executionContext); 240 | }; 241 | 242 | /** 243 | * Items for export 244 | */ 245 | exports.items = [ BlankField ]; 246 | -------------------------------------------------------------------------------- /lib/gcli/fields/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | DevToolsModules( 8 | 'delegate.js', 9 | 'fields.js', 10 | 'selection.js', 11 | ) 12 | -------------------------------------------------------------------------------- /lib/gcli/fields/selection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | var Menu = require('../ui/menu').Menu; 21 | 22 | var Argument = require('../types/types').Argument; 23 | var Conversion = require('../types/types').Conversion; 24 | var Field = require('./fields').Field; 25 | 26 | /** 27 | * A field that allows selection of one of a number of options 28 | */ 29 | function SelectionField(type, options) { 30 | Field.call(this, type, options); 31 | 32 | this.arg = new Argument(); 33 | 34 | this.menu = new Menu({ 35 | document: this.document, 36 | maxPredictions: Conversion.maxPredictions 37 | }); 38 | this.element = this.menu.element; 39 | 40 | this.onFieldChange = util.createEvent('SelectionField.onFieldChange'); 41 | 42 | // i.e. Register this.onItemClick as the default action for a menu click 43 | this.menu.onItemClick.add(this.itemClicked, this); 44 | } 45 | 46 | SelectionField.prototype = Object.create(Field.prototype); 47 | 48 | SelectionField.claim = function(type, context) { 49 | if (context == null) { 50 | return Field.NO_MATCH; 51 | } 52 | return type.getType(context).hasPredictions ? Field.DEFAULT : Field.NO_MATCH; 53 | }; 54 | 55 | SelectionField.prototype.destroy = function() { 56 | this.menu.onItemClick.remove(this.itemClicked, this); 57 | this.menu.destroy(); 58 | this.menu = undefined; 59 | this.element = undefined; 60 | Field.prototype.destroy.call(this); 61 | }; 62 | 63 | SelectionField.prototype.setConversion = function(conversion) { 64 | this.arg = conversion.arg; 65 | this.setMessage(conversion.message); 66 | 67 | var context = this.requisition.executionContext; 68 | conversion.getPredictions(context).then(function(predictions) { 69 | var items = predictions.map(function(prediction) { 70 | // If the prediction value is an 'item' (that is an object with a name and 71 | // description) then use that, otherwise use the prediction itself, because 72 | // at least that has a name. 73 | return prediction.value && prediction.value.description ? 74 | prediction.value : 75 | prediction; 76 | }, this); 77 | if (this.menu != null) { 78 | this.menu.show(items, conversion.arg.text); 79 | } 80 | }.bind(this)).catch(util.errorHandler); 81 | }; 82 | 83 | SelectionField.prototype.itemClicked = function(ev) { 84 | var arg = new Argument(ev.name, '', ' '); 85 | var context = this.requisition.executionContext; 86 | 87 | this.type.parse(arg, context).then(function(conversion) { 88 | this.onFieldChange({ conversion: conversion }); 89 | this.setMessage(conversion.message); 90 | }.bind(this)).catch(util.errorHandler); 91 | }; 92 | 93 | SelectionField.prototype.getConversion = function() { 94 | // This tweaks the prefix/suffix of the argument to fit 95 | this.arg = this.arg.beget({ text: this.input.value }); 96 | return this.type.parse(this.arg, this.requisition.executionContext); 97 | }; 98 | 99 | /** 100 | * Allow the terminal to use RETURN to chose the current menu item when 101 | * it can't execute the command line 102 | * @return true if an item was 'clicked', false otherwise 103 | */ 104 | SelectionField.prototype.selectChoice = function() { 105 | var selected = this.menu.selected; 106 | if (selected == null) { 107 | return false; 108 | } 109 | 110 | this.itemClicked({ name: selected }); 111 | return true; 112 | }; 113 | 114 | Object.defineProperty(SelectionField.prototype, 'isImportant', { 115 | get: function() { 116 | return this.type.name !== 'command'; 117 | }, 118 | enumerable: true 119 | }); 120 | 121 | /** 122 | * Allow registration and de-registration. 123 | */ 124 | exports.items = [ SelectionField ]; 125 | -------------------------------------------------------------------------------- /lib/gcli/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var Cc = require('chrome').Cc; 20 | var Ci = require('chrome').Ci; 21 | 22 | 23 | var prefSvc = Cc['@mozilla.org/preferences-service;1'] 24 | .getService(Ci.nsIPrefService); 25 | var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch2); 26 | 27 | exports.hiddenByChromePref = function() { 28 | return !prefBranch.prefHasUserValue('devtools.chrome.enabled'); 29 | }; 30 | -------------------------------------------------------------------------------- /lib/gcli/l10n.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var Cc = require('chrome').Cc; 20 | var Ci = require('chrome').Ci; 21 | var Cu = require('chrome').Cu; 22 | 23 | var prefSvc = Cc['@mozilla.org/preferences-service;1'] 24 | .getService(Ci.nsIPrefService); 25 | var prefBranch = prefSvc.getBranch(null).QueryInterface(Ci.nsIPrefBranch); 26 | 27 | var Services = require("Services"); 28 | var stringBundle = Services.strings.createBundle( 29 | 'chrome://devtools-shared/locale/gclicommands.properties'); 30 | 31 | /** 32 | * Lookup a string in the GCLI string bundle 33 | */ 34 | exports.lookup = function(name) { 35 | try { 36 | return stringBundle.GetStringFromName(name); 37 | } 38 | catch (ex) { 39 | throw new Error('Failure in lookup(\'' + name + '\')'); 40 | } 41 | }; 42 | 43 | /** 44 | * An alternative to lookup(). 45 | * l10n.lookup('BLAH') === l10n.propertyLookup.BLAH 46 | * This is particularly nice for templates because you can pass 47 | * l10n:l10n.propertyLookup in the template data and use it 48 | * like ${l10n.BLAH} 49 | */ 50 | exports.propertyLookup = new Proxy({}, { 51 | get: function(rcvr, name) { 52 | return exports.lookup(name); 53 | } 54 | }); 55 | 56 | /** 57 | * Lookup a string in the GCLI string bundle 58 | */ 59 | exports.lookupFormat = function(name, swaps) { 60 | try { 61 | return stringBundle.formatStringFromName(name, swaps, swaps.length); 62 | } 63 | catch (ex) { 64 | throw new Error('Failure in lookupFormat(\'' + name + '\')'); 65 | } 66 | }; 67 | 68 | /** 69 | * Allow GCLI users to be hidden by the 'devtools.chrome.enabled' pref. 70 | * Use it in commands like this: 71 | *
72 |  *   name: "somecommand",
73 |  *   hidden: l10n.hiddenByChromePref(),
74 |  *   exec: function(args, context) { ... }
75 |  * 
76 | */ 77 | exports.hiddenByChromePref = function() { 78 | return !prefBranch.getBoolPref('devtools.chrome.enabled'); 79 | }; 80 | -------------------------------------------------------------------------------- /lib/gcli/languages/command.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
6 | :${output.typed} 10 |
11 |
12 |
13 |
14 |
15 | -------------------------------------------------------------------------------- /lib/gcli/languages/javascript.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var host = require('../util/host'); 20 | var prism = require('../util/prism').Prism; 21 | 22 | function isMultiline(text) { 23 | return typeof text === 'string' && text.indexOf('\n') > -1; 24 | } 25 | 26 | exports.items = [ 27 | { 28 | // Language implementation for Javascript 29 | item: 'language', 30 | name: 'javascript', 31 | prompt: '>', 32 | 33 | constructor: function(terminal) { 34 | this.document = terminal.document; 35 | this.focusManager = terminal.focusManager; 36 | 37 | this.updateHints(); 38 | }, 39 | 40 | destroy: function() { 41 | this.document = undefined; 42 | }, 43 | 44 | exec: function(input) { 45 | return this.evaluate(input).then(function(response) { 46 | var output = (response.exception != null) ? 47 | response.exception.class : 48 | response.output; 49 | 50 | var isSameString = typeof output === 'string' && 51 | input.substr(1, input.length - 2) === output; 52 | var isSameOther = typeof output !== 'string' && 53 | input === '' + output; 54 | 55 | // Place strings in quotes 56 | if (typeof output === 'string' && response.exception == null) { 57 | if (output.indexOf('\'') === -1) { 58 | output = '\'' + output + '\''; 59 | } 60 | else { 61 | output = output.replace(/\\/, '\\').replace(/"/, '"').replace(/'/, '\''); 62 | output = '"' + output + '"'; 63 | } 64 | } 65 | 66 | var line; 67 | if (isSameString || isSameOther || output === undefined) { 68 | line = input; 69 | } 70 | else if (isMultiline(output)) { 71 | line = input + '\n/*\n' + output + '\n*/'; 72 | } 73 | else { 74 | line = input + ' // ' + output; 75 | } 76 | 77 | var grammar = prism.languages[this.name]; 78 | return prism.highlight(line, grammar, this.name); 79 | }.bind(this)); 80 | }, 81 | 82 | evaluate: function(input) { 83 | return host.script.evaluate(input); 84 | } 85 | } 86 | ]; 87 | -------------------------------------------------------------------------------- /lib/gcli/languages/languages.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | 21 | var RESOLVED = Promise.resolve(true); 22 | 23 | /** 24 | * This is the base implementation for all languages 25 | */ 26 | var baseLanguage = { 27 | item: 'language', 28 | name: undefined, 29 | 30 | constructor: function(terminal) { 31 | }, 32 | 33 | destroy: function() { 34 | }, 35 | 36 | updateHints: function() { 37 | util.clearElement(this.terminal.tooltipElement); 38 | }, 39 | 40 | description: '', 41 | message: '', 42 | caretMoved: function() {}, 43 | 44 | handleUpArrow: function() { 45 | return Promise.resolve(false); 46 | }, 47 | 48 | handleDownArrow: function() { 49 | return Promise.resolve(false); 50 | }, 51 | 52 | handleTab: function() { 53 | return this.terminal.unsetChoice().then(function() { 54 | return RESOLVED; 55 | }, util.errorHandler); 56 | }, 57 | 58 | handleInput: function(input) { 59 | if (input === ':') { 60 | return this.terminal.setInput('').then(function() { 61 | return this.terminal.pushLanguage('commands'); 62 | }.bind(this)); 63 | } 64 | 65 | return this.terminal.unsetChoice().then(function() { 66 | return RESOLVED; 67 | }, util.errorHandler); 68 | }, 69 | 70 | handleReturn: function(input) { 71 | var rowoutEle = this.document.createElement('pre'); 72 | rowoutEle.classList.add('gcli-row-out'); 73 | rowoutEle.classList.add('gcli-row-script'); 74 | rowoutEle.setAttribute('aria-live', 'assertive'); 75 | 76 | return this.exec(input).then(function(line) { 77 | rowoutEle.innerHTML = line; 78 | 79 | this.terminal.addElement(rowoutEle); 80 | this.terminal.scrollToBottom(); 81 | 82 | this.focusManager.outputted(); 83 | 84 | this.terminal.unsetChoice().catch(util.errorHandler); 85 | this.terminal.inputElement.value = ''; 86 | }.bind(this)); 87 | }, 88 | 89 | setCursor: function(cursor) { 90 | this.terminal.inputElement.selectionStart = cursor.start; 91 | this.terminal.inputElement.selectionEnd = cursor.end; 92 | }, 93 | 94 | getCompleterTemplateData: function() { 95 | return Promise.resolve({ 96 | statusMarkup: [ 97 | { 98 | string: this.terminal.inputElement.value.replace(/ /g, '\u00a0'), // i.e.   99 | className: 'gcli-in-valid' 100 | } 101 | ], 102 | unclosedJs: false, 103 | directTabText: '', 104 | arrowTabText: '', 105 | emptyParameters: '' 106 | }); 107 | }, 108 | 109 | showIntro: function() { 110 | }, 111 | 112 | exec: function(input) { 113 | throw new Error('Missing implementation of handleReturn() or exec() ' + this.name); 114 | } 115 | }; 116 | 117 | /** 118 | * A manager for the registered Languages 119 | */ 120 | function Languages() { 121 | // This is where we cache the languages that we know about 122 | this._registered = {}; 123 | } 124 | 125 | /** 126 | * Add a new language to the cache 127 | */ 128 | Languages.prototype.add = function(language) { 129 | this._registered[language.name] = language; 130 | }; 131 | 132 | /** 133 | * Remove an existing language from the cache 134 | */ 135 | Languages.prototype.remove = function(language) { 136 | var name = typeof language === 'string' ? language : language.name; 137 | delete this._registered[name]; 138 | }; 139 | 140 | /** 141 | * Get access to the list of known languages 142 | */ 143 | Languages.prototype.getAll = function() { 144 | return Object.keys(this._registered).map(function(name) { 145 | return this._registered[name]; 146 | }.bind(this)); 147 | }; 148 | 149 | /** 150 | * Find a previously registered language 151 | */ 152 | Languages.prototype.createLanguage = function(name, terminal) { 153 | if (name == null) { 154 | name = Object.keys(this._registered)[0]; 155 | } 156 | 157 | var language = (typeof name === 'string') ? this._registered[name] : name; 158 | if (!language) { 159 | console.error('Known languages: ' + Object.keys(this._registered).join(', ')); 160 | throw new Error('Unknown language: \'' + name + '\''); 161 | } 162 | 163 | // clone 'type' 164 | var newInstance = {}; 165 | util.copyProperties(baseLanguage, newInstance); 166 | util.copyProperties(language, newInstance); 167 | 168 | if (typeof newInstance.constructor === 'function') { 169 | var reply = newInstance.constructor(terminal); 170 | return Promise.resolve(reply).then(function() { 171 | return newInstance; 172 | }); 173 | } 174 | else { 175 | return Promise.resolve(newInstance); 176 | } 177 | }; 178 | 179 | exports.Languages = Languages; 180 | -------------------------------------------------------------------------------- /lib/gcli/languages/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | DevToolsModules( 8 | 'command.html', 9 | 'command.js', 10 | 'javascript.js', 11 | 'languages.js', 12 | ) 13 | -------------------------------------------------------------------------------- /lib/gcli/moz.build: -------------------------------------------------------------------------------- 1 | # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- 2 | # vim: set filetype=python: 3 | # This Source Code Form is subject to the terms of the Mozilla Public 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 | 7 | DevToolsModules( 8 | 'cli.js', 9 | 'index.js', 10 | 'l10n.js', 11 | 'settings.js', 12 | 'system.js', 13 | ) 14 | -------------------------------------------------------------------------------- /lib/gcli/mozui/completer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012, Mozilla Foundation and contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 'use strict'; 18 | 19 | var util = require('../util/util'); 20 | var host = require('../util/host'); 21 | var domtemplate = require('../util/domtemplate'); 22 | 23 | var completerHtml = 24 | '\n' + 26 | ' \n' + 27 | ' \n' + 28 | ' \n' + 29 | '