├── .gitignore
├── R
└── startup.R
├── README.md
├── bower.json
├── build
├── icon-inverse.svg
├── icon.icns
├── icon.ico
├── icon.svg
└── install-spinner.gif
├── components
├── detail.html
├── grid.html
├── history.html
├── panel.html
├── shell-preferences.html
├── split-panel.html
└── virtual-list.html
├── core
├── data-cache.js
├── extendR.js
├── file-cache.js
├── menus.js
├── messages.js
├── package-manager.js
├── renderer.js
├── search-history.js
├── settings.js
└── utils.js
├── data
├── codemirror-minimal.css
├── menus.json
└── messages.json
├── ext
├── cogs
│ ├── README.md
│ ├── cogs.css
│ ├── cogs.eot
│ ├── cogs.svg
│ ├── cogs.ttf
│ └── cogs.woff
└── material
│ ├── MaterialIcons-Regular.ijmap
│ ├── MaterialIcons-Regular.woff2
│ ├── README.md
│ ├── codepoints
│ └── material-icons.css
├── gulpfile.js
├── html
└── index.html
├── main
└── main.js
├── package.json
├── packages
├── README.md
├── browser
│ ├── browser.html
│ ├── browser.js
│ └── package.json
├── cran
│ ├── cran.html
│ ├── cran.js
│ └── package.json
├── download
│ ├── download.R
│ ├── download.js
│ └── package.json
├── file-watcher
│ ├── file-watcher.js
│ └── package.json
├── graphics
│ ├── graphics-device.js
│ ├── graphics-panel.html
│ └── package.json
├── histogram-view
│ ├── histogram-view.js
│ ├── histogram.html
│ └── package.json
├── html-dialog
│ ├── dialog.html
│ ├── html-dialog.js
│ └── package.json
├── pager
│ ├── package.json
│ └── pager.js
├── progress
│ ├── package.json
│ ├── progress.R
│ └── progress.js
├── side-panel
│ ├── package.json
│ └── side-panel.js
├── table
│ ├── package.json
│ └── table.js
└── theme
│ ├── help
│ ├── bluesans.css
│ └── sans.css
│ ├── package.json
│ ├── shell
│ ├── dark.css
│ ├── elegant.css
│ ├── night.css
│ ├── old-school.css
│ └── zenburn.css
│ ├── theme.js
│ └── ui
│ └── dark.css
├── postcss
├── button.css
├── checkbox2.css
├── chooser.css
├── dialog.css
├── grid.css
├── history.css
├── locals.css
├── main.css
├── panel.css
├── progress.css
├── scrollbars.css
└── textbox.css
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | bower_components
3 | dist
4 | app
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ConstructR
2 | ==========
3 |
4 | ![ConstructR logo][logo]
5 |
6 | Electron-based R shell.
7 |
8 | Install
9 | =======
10 |
11 | To install and run without building from scratch, grab an installer
12 | for your platform from the [releases][5] page.
13 |
14 |
15 | Build
16 | =====
17 |
18 | ### Prerequisites ###
19 |
20 | * git https://git-scm.com
21 |
22 | * node (and npm) https://nodejs.org
23 |
24 | * R https://www.r-project.org/
25 |
26 | Either your distribution's R package or R built from source.
27 | If you build from source, make sure to use the configure option
28 | `--enable-R-shlib` to build shared libraries. On Windows, the
29 | binary install of R is fine.
30 |
31 | R must be visible on your PATH for the build to succeed.
32 |
33 | * Build tools: the commands below will build a native node add-on
34 | ([controlr][1]) and a binary R package ([jsclientlib][2]). You
35 | need standard build tools for C/C++. On Windows, the R package
36 | will be installed as a prebuilt binary, so you don't need the
37 | [Rtools][3] package.
38 |
39 | ### Download, Build and Run ###
40 |
41 | Note: on Windows, these instructions will work if you are using a
42 | unix-like shell (like [Git bash][4]). If you are
43 | using the Windows shell ("Command Prompt"), you have to turn around
44 | the slashes. See below.
45 |
46 | ```bash
47 | # download
48 | git clone https://github.com/sdllc/constructr.git
49 |
50 | # install packages
51 | cd constructr
52 | npm install
53 |
54 | # install modules and build.
55 | node_modules/.bin/gulp
56 |
57 | # build and install the `jsclientlib` R library
58 | node_modules/.bin/gulp jsclientlib
59 |
60 | # now run
61 | node_modules/.bin/electron app
62 |
63 | # alternatively, run with livereload:
64 | # node_modules/.bin/gulp watch
65 | ```
66 |
67 | Windows shell version:
68 | ```bat
69 | REM * download
70 | git clone https://github.com/sdllc/constructr.git
71 |
72 | REM * install packages
73 | cd constructr
74 | npm install
75 |
76 | REM * install modules and build.
77 | node_modules\.bin\gulp
78 |
79 | REM * build and install the `jsclientlib` R library
80 | node_modules\.bin\gulp jsclientlib
81 |
82 | REM * now run
83 | node_modules\.bin\electron app
84 |
85 | REM * alternatively, run with livereload:
86 | REM * node_modules\.bin\gulp watch
87 | ```
88 |
89 | [logo]: https://cdn.rawgit.com/sdllc/constructr/master/build/icon-inverse.svg
90 |
91 | [1]: https://github.com/sdllc/controlr
92 | [2]: https://github.com/sdllc/jsclientlib
93 | [3]: https://cran.r-project.org/bin/windows/Rtools/
94 | [4]: https://git-scm.com
95 | [5]: https://github.com/sdllc/constructr/releases
96 |
97 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "editr",
3 | "description": "editr",
4 | "main": "app/main.js",
5 | "moduleType": [],
6 | "homepage": "",
7 | "ignore": [
8 | "**/.*",
9 | "node_modules",
10 | "bower_components",
11 | "test",
12 | "tests"
13 | ],
14 | "dependencies": {
15 | "polymer": "Polymer/polymer#^1.3.1",
16 | "codemirror": "^5.15.2"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/build/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/build/icon.icns
--------------------------------------------------------------------------------
/build/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/build/icon.ico
--------------------------------------------------------------------------------
/build/install-spinner.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/build/install-spinner.gif
--------------------------------------------------------------------------------
/components/detail.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
51 |
52 |
53 |
54 |
55 |
56 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/components/history.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
83 |
84 |
85 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/components/panel.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
30 |
31 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/components/shell-preferences.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
37 |
38 |
39 | Size (pixels):
40 |
41 | x
42 |
43 |
44 |
45 |
46 |
47 |
48 |
75 |
76 |
77 |
78 |
81 |
82 |
83 |
84 |
85 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | {{label}}
109 |
110 |
111 |
112 |
167 |
168 |
169 |
170 |
171 |
172 |
186 |
187 | {{label}}
188 |
193 |
194 |
195 |
196 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
241 |
242 |
243 |
244 |
245 | ...
246 |
247 |
248 |
249 |
250 |
251 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
339 |
340 |
341 |
342 |
343 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
380 |
381 |
382 |
383 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
398 |
399 |
400 |
401 |
402 |
412 |
413 |
414 |
415 |
--------------------------------------------------------------------------------
/components/virtual-list.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
54 |
55 |
56 |
57 |
58 |
59 |
66 |
67 |
68 |
69 |
70 |
71 |
230 |
231 |
232 |
--------------------------------------------------------------------------------
/core/data-cache.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | /**
26 | * utility: temporary cache for package list, mirrors, and similar.
27 | * currently using localStorage, but we should think about swapping
28 | * in file storage (as an option).
29 | *
30 | * actually we might alternatively use in-memory storage: the only
31 | * difference would be on startup.
32 | */
33 | function DataCache(R){
34 |
35 | /**
36 | * test if data is available
37 | */
38 | this.have_cached_data = function( key, max_cache_time ){
39 |
40 | if( typeof max_cache_time === "undefined" ) max_cache_time = DEFAULT_CACHE_TIME;
41 | var cached = localStorage.getItem( key );
42 | if( cached ){
43 | cached = JSON.parse(cached);
44 | if( Date.now() - cached.retrieved > max_cache_time ){
45 | return false;
46 | }
47 | return true;
48 | }
49 | return false;
50 |
51 | };
52 |
53 | /**
54 | * generic cache/expire/fetch routine
55 | */
56 | this.get_cached_data = function( key, max_cache_time, r_cmd, scrub ){
57 |
58 | var cached = localStorage.getItem( key );
59 | if( cached ){
60 | cached = JSON.parse(cached);
61 | if( Date.now() - cached.retrieved > max_cache_time ){
62 | // console.info( `${key}: flushing cache` );
63 | localStorage.removeItem( key );
64 | }
65 | else {
66 | // console.info( "using cache" );
67 | return new Promise( function( resolve, reject ){ resolve(cached.data); })
68 | }
69 | }
70 |
71 | return new Promise( function( resolve, reject ){
72 | R.queued_internal( r_cmd ).then( function( obj ){
73 | if( obj.response ){
74 | var data = obj.response;
75 | if( scrub ) data = scrub.call( this, data );
76 | localStorage.setItem( key, JSON.stringify({
77 | retrieved: Date.now(),
78 | data: data
79 | }));
80 | resolve(data);
81 | }
82 | else reject();
83 | }).catch( function(e){
84 | reject(e);
85 | });
86 | });
87 |
88 | };
89 |
90 | /**
91 | * transpose a frame, where the data is named.
92 | */
93 | this.transpose_frame = function(frame){
94 |
95 | var keys = Object.keys(frame);
96 | var len = frame[keys[0]].length;
97 | var rslt = new Array( len );
98 | for( var i = 0; i< len; i++ ){
99 | var o = {};
100 | keys.map( function( key ){
101 | o[key] = frame[key][i];
102 | });
103 | rslt[i] = o;
104 | }
105 | return rslt;
106 | };
107 |
108 | /**
109 | * matrices are just vectors with row counts (and optionally dimnames)
110 | * FIXME: this should go into some data management class
111 | */
112 | this.build_matrix = function(data, nrow, ncol, rowdominant){
113 |
114 | var cols = new Array(ncol);
115 | for( var c = 0; c< ncol; c++ ){
116 | var rows = data.splice( 0, nrow );
117 | cols[c] = rows ;
118 | }
119 | if( rowdominant ) return this.transpose_array( cols );
120 | return cols;
121 | };
122 |
123 | /**
124 | * utility method
125 | */
126 | this.apply_names = function( mat, order, names ){
127 | if( order === 1 ){
128 | var rslt = new Array( mat.length );
129 | for( var i = 0; i< mat.length; i++ ){
130 | var o = {};
131 | for( var j = 0; j< names.length; j++ ){
132 | o[names[j]] = mat[i][j];
133 | }
134 | rslt[i] = o;
135 | }
136 | return rslt;
137 | }
138 | }
139 |
140 | /**
141 | * transpose an array [[]] that we get from R. matrices and data
142 | * frames are column-dominant, for our purposes we (sometimes)
143 | * want row-dominant arrays.
144 | */
145 | this.transpose_array = function(arr){
146 |
147 | // NOTE: we expect this to be regular (i.e. no short columns)
148 |
149 | var len1 = arr.length;
150 | var len2 = arr[0].length;
151 |
152 | var rslt = new Array( len2 );
153 | for( var i = 0; i< len2; i++ ){
154 | var row = new Array( len1 );
155 | for( var j = 0; j< len1; j++ ){
156 | row[j] = arr[j][i];
157 | }
158 | rslt[i] = row;
159 | }
160 |
161 | return rslt;
162 | }
163 |
164 | };
165 |
166 | // FIXME: configurable
167 | DataCache.prototype.DEFAULT_CACHE_TIME = ( 1000 * 60 * 120 );
168 |
169 | module.exports = DataCache;
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/core/extendR.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const path = require( "path" );
26 | //const ControlR = require( "controlr" );
27 | const ControlR = require( "../../controlr/js/controlr.js" );
28 |
29 | /**
30 | * controlR is the basic R wrapper. here we're adding some convenience
31 | * methods and functions that are useful for our UI, but aren't core
32 | * functionality (and hence aren't included in controlR).
33 | */
34 | var ExtendR = function(){
35 |
36 | // fixme: utility lib
37 |
38 | /**
39 | * enquote with double quotes; escape other quotes.
40 | *
41 | * FIXME: is this correct? what if the string already has escaped quotes
42 | * in it (called twice, for example)? should it then double-escape?
43 | */
44 | function enquote(s){
45 | return `"${s.replace( /"/g, '\\\"')}"`; // chrome doesn't support 'g' with string match?
46 | }
47 |
48 | // === public methods =====================================================
49 |
50 | /** get all options */
51 | this.options = function(){
52 | return this.internal( `options()` );
53 | };
54 |
55 | /** get option */
56 | this.get_option = function( key ){
57 | return this.internal( `options('${key}')` );
58 | };
59 |
60 | /** set option. strings will be quoted. */
61 | this.set_option = function( key, value ){
62 | if( typeof value === "string" ) value = enquote( value );
63 | return this.internal( `options(${key}=${value})` );
64 | };
65 |
66 | /** set multiple options */
67 | this.set_options = function( kvpairs ){
68 |
69 | var s = [];
70 | for( var key in kvpairs ){
71 | var val = kvpairs[key];
72 | if( typeof val === "string" ) val = enquote( val );
73 | s.push( `${key}=${val}` );
74 | }
75 | var cmd = `options(${s.join( "," )})`;
76 | console.info(cmd);
77 | return this.internal(cmd);
78 |
79 | };
80 |
81 | /**
82 | * set cran mirror, which has to be in a list. this is in an
83 | * anonymous function to prevent leaving any detritus in the environment
84 | */
85 | this.set_cran_mirror = function( mirror, message ){
86 |
87 | mirror = mirror ? enquote( mirror ) : "NULL" ;
88 |
89 | var cmd = [
90 | `(function(){ repos <- getOption('repos')`,
91 | `repos['CRAN'] <- ${mirror}`,
92 | `options(repos = repos)`
93 | ];
94 |
95 | if( message ) cmd.push(
96 | `cat('${message}\n')`
97 | );
98 |
99 | cmd.push( `})()` );
100 | return this.internal( cmd );
101 |
102 | };
103 |
104 | /** get cran mirror (shortcut) */
105 | this.get_cran_mirror = function(){
106 | var instance = this;
107 | return new Promise( function( resolve, reject ){
108 | instance.internal( "getOption('repos')").then( function( rslt ){
109 | if( rslt && rslt.response ){
110 | if( typeof rslt.response === "string" ){
111 | resolve( rslt.response.repos );
112 | }
113 | else if( rslt.response.CRAN ){
114 | resolve( rslt.response.CRAN );
115 | }
116 | else reject("NULL or not set");
117 | }
118 | else reject("NULL or not set");
119 | }).catch( function(e){
120 | reject(e);
121 | });
122 | });
123 | };
124 |
125 | /**
126 | * set console width (in characters)
127 | */
128 | this.set_console_width = function(chars){
129 | this.internal( `options(width=${chars})`, "set.console.width" );
130 | };
131 |
132 | // === constructor ========================================================
133 |
134 | // superclass init
135 | ControlR.apply( this, arguments );
136 |
137 | };
138 |
139 | // inherit prototype methods
140 | for (var x in ControlR.prototype){
141 | ExtendR.prototype[x] = ControlR.prototype[x];
142 | }
143 |
144 | module.exports = ExtendR;
--------------------------------------------------------------------------------
/core/file-cache.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | var fs = require( "fs" );
26 |
27 | /**
28 | * utility: cache file contents and refresh on mtime.
29 | */
30 | var FileCache = function(){
31 |
32 | var store = {};
33 |
34 | var cache = function( path ){
35 | return new Promise( function( resolve, reject ){
36 | fs.readFile( path, { encoding: "utf8" }, function( err, contents ){
37 | if( err ) reject( err );
38 | if( !contents ) contents = "";
39 | store[path] = {
40 | contents: contents,
41 | cache_time: new Date().getTime()
42 | };
43 | resolve( contents );
44 | });
45 | });
46 | };
47 |
48 | this.ensure = function( path ){
49 | return new Promise( function( resolve, reject ){
50 | var cached = store[path];
51 | if( typeof cached !== "undefined" ){
52 | fs.stat( path, function(err, stats){
53 | if( err ) reject( err );
54 | if( stats.mtime.getTime() > cached.cache_time ){
55 | cache( path ).then( function( contents ){
56 | resolve(contents);
57 | }).catch( function( err ){ reject(err); });
58 | }
59 | else {
60 | resolve( cached.contents );
61 | }
62 | });
63 | }
64 | else {
65 | cache( path ).then( function( contents ){
66 | resolve(contents);
67 | }).catch( function( err ){ reject(err); });
68 | }
69 | });
70 | };
71 |
72 | };
73 |
74 | module.exports = FileCache;
75 |
76 |
--------------------------------------------------------------------------------
/core/menus.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | /**
24 | * this is a layer on top of electron's existing menu template
25 | * scheme that adds a few features we specifically want.
26 | */
27 |
28 | "use strict";
29 |
30 | const PubSub = require( "pubsub-js" );
31 | const fs = require( "fs" );
32 | const path = require( "path" );
33 | const Menu = require('electron').remote.Menu;
34 |
35 | /**
36 | * (1) remove any elements that have a "platform" attribute which
37 | * does not match the current platform.
38 | *
39 | * UPDATE: we now also support !platform (gyp style)
40 | *
41 | * (2) translate elements with a platform suffix if the platform
42 | * matches.
43 | *
44 | * (3) if an element has a "message" attribute, construct a click
45 | * function.
46 | */
47 | function build_menu_template( src, name, click ){
48 |
49 | if( !click ) click = function(message){
50 | PubSub.publish( "menu-click", {
51 | menu: name,
52 | message: message,
53 | item: arguments[1],
54 | focusedWindow: arguments[2]
55 | });
56 | };
57 |
58 | var platform_key = "-" + process.platform;
59 | return src.filter( function( entry ){
60 |
61 | var keys = Object.keys( entry );
62 | keys.forEach( function( key ){
63 | if( key.endsWith( platform_key )){
64 | entry[key.substr( 0, key.length - platform_key.length )] = entry[key];
65 | }
66 | });
67 |
68 | for( var key in entry ){
69 | if(( key === "platform" && entry[key] !== process.platform )
70 | || ( key === "!platform" && entry[key] === process.platform )){
71 | return undefined;
72 | }
73 | else {
74 | switch( key ){
75 | case "submenu":
76 | entry[key] = build_menu_template( entry[key], name, click );
77 | break;
78 | case "message":
79 | entry.click = click.bind( this, entry.message );
80 | break;
81 | }
82 | }
83 | }
84 | return entry;
85 | });
86 |
87 | }
88 |
89 | var MenuTemplate = function(path){
90 |
91 | try {
92 | this._template = fs.readFileSync(path, { encoding: "utf8" });
93 | if( this._template ){
94 | this._template = JSON.parse( this._template );
95 | var keys = Object.keys( this._template );
96 | keys.forEach( function( key ){
97 | this[key] = Menu.buildFromTemplate( build_menu_template(
98 | this._template[key], key
99 | ));
100 | }, this);
101 | }
102 | }
103 | catch( e ){
104 | console.error(e);
105 | }
106 |
107 | };
108 |
109 | module.exports = MenuTemplate;
110 |
--------------------------------------------------------------------------------
/core/messages.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | /**
26 | * load messages from json file. placeholder for i18n.
27 | */
28 |
29 | const fs = require( "fs" );
30 |
31 | /** array -> multiline string */
32 | var concatenate_strings = function( obj ){
33 | var keys = Object.keys(obj);
34 | keys.forEach( function( key ){
35 | var val = obj[key];
36 | if( Array.isArray( val )) obj[key] = val.join( "\n" );
37 | else if( typeof val === "object" ) obj[key] = concatenate_strings(val);
38 | });
39 | return obj;
40 | }
41 |
42 | /** singleton */
43 | var Messages = function(){
44 | this.load = function(path){
45 | var contents = fs.readFileSync( path, { encoding: "utf8" });
46 | if( !contents ) return {};
47 | try {
48 | contents = JSON.parse( contents );
49 | return concatenate_strings( contents );
50 | }
51 | catch( e ){
52 | console.err( e );
53 | }
54 | return {};
55 | };
56 | };
57 |
58 | module.exports = new Messages();
59 |
60 |
--------------------------------------------------------------------------------
/core/package-manager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const path = require( 'path' );
26 | const fs = require( 'fs' );
27 |
28 | const X = eval( 'require' ); // !webpack
29 |
30 | window.fs = fs;
31 |
32 | var PackageManager = function(){
33 |
34 | /** loaded packages, by name */
35 | this.packages = {};
36 |
37 | /** pending packages (missing deps) */
38 | this.pending = [];
39 |
40 | /** hold on to spec */
41 | this.spec = {};
42 |
43 | /**
44 | * list packages, concatenate name
45 | */
46 | this.list_packages = function( package_dir ){
47 | return new Promise( function( resolve, reject ){
48 | fs.readdir( package_dir, function(err, files){
49 | resolve(files.map( function( file ){
50 | return path.join( package_dir, file );
51 | }));
52 | });
53 | });
54 | };
55 |
56 | /**
57 | * consolidate list/load
58 | */
59 | this.load_packages = function( dir, core, opts ){
60 | let instance = this;
61 | return new Promise( function( resolve, reject ){
62 | instance.list_packages( dir ).then( function( list ){
63 | return instance.load_packages_list( list, core, opts );
64 | }).then( function(){
65 | resolve();
66 | }).catch( function(e){
67 | reject(e);
68 | });
69 | });
70 | };
71 |
72 | /**
73 | * load packages, recursively, from list.
74 | * added dependencies. todo: versioned deps
75 | *
76 | * FIXME: this is turning into spaghetti. refactor.
77 | */
78 | this.load_packages_list = function( list, core, opts ){
79 |
80 | let self = this;
81 |
82 | opts = opts || {};
83 | if( !Array.isArray( list )) list = [list];
84 |
85 | let platformkey = "default." + process.platform;
86 | let default_settings = function( prefs ){
87 | for( let key in prefs ){
88 | if( typeof core.Settings[key] === "undefined" )
89 | {
90 | if( typeof prefs[key][platformkey] !== "undefined" )
91 | core.Settings[key] = prefs[key][platformkey];
92 | else if( typeof prefs[key].default !== "undefined" )
93 | core.Settings[key] = prefs[key].default;
94 | }
95 | }
96 | };
97 |
98 | let init_package_js = function( pkg, elt ){
99 |
100 | console.info( "Installing package", pkg.name );
101 |
102 | // if this package has options, with defaults,
103 | // enforce defaults.
104 | if( pkg.preferences ) default_settings( pkg.preferences );
105 | if( pkg.preferenceGroups ){
106 | for( let group in pkg.preferenceGroups ){
107 | default_settings( pkg.preferenceGroups[group] );
108 | }
109 | }
110 |
111 | // load html files (polymer components)
112 | if( pkg.htmlComponents ){
113 |
114 | // don't blindly do that; in this case, we _want_ files from the archive
115 |
116 | //let source_dir = core.Utils.escape_backslashes(core.Utils.patch_asar_path(elt), 2);
117 | let source_dir = core.Utils.escape_backslashes(elt, 2);
118 | pkg.htmlComponents.forEach( function(component){
119 | core.Utils.install_html_component(
120 | path.join( source_dir, component ));
121 | })
122 | }
123 |
124 | // ok, load and call init
125 | self.packages[ pkg.name ] = pkg.module;
126 | self.spec[ pkg.name ] = pkg;
127 |
128 | // can use a different method name (?)
129 | var func = "init";
130 | if( pkg.init ) func = pkg.init;
131 |
132 | if( pkg.module[func] ){
133 | let rslt = pkg.module[func].call( this, core);
134 | return rslt ? rslt : Promise.resolve();
135 | }
136 | else return Promise.resolve();
137 |
138 | };
139 |
140 | let init_package = function( pkg, elt ){
141 | return new Promise( function( resolve, reject ){
142 | init_package_js( pkg, elt ).then( function(){
143 | if( pkg.R ){
144 | let source_dir = core.Utils.escape_backslashes(
145 | core.Utils.patch_asar_path(elt), 2);
146 | let source_file = core.Utils.escape_backslashes(
147 | core.Utils.patch_asar_path(path.join( elt, pkg.R )), 2 );
148 | let cmd = `.dirname <- "${source_dir}"; source("${source_file}"); rm(.dirname);`;
149 | return core.R.exec( cmd );
150 | }
151 | else return Promise.resolve();
152 | }).then( function(){
153 | resolve();
154 | });
155 | });
156 | };
157 |
158 | return new Promise( function( resolve, reject ){
159 | if( list.length ){
160 | let elt = list.shift();
161 | self.load( elt ).then( function( pkg, err ){
162 | if( pkg ){
163 |
164 | // don't reload if we have already loaded
165 | // (UPDATE: argument)
166 | if( !opts.allow_override && self.packages[ pkg.name ]) return Promise.resolve();
167 |
168 | // check deps
169 | if( pkg.packageDependencies ){
170 | let deps = Array.isArray( pkg.packageDependencies ) ?
171 | pkg.packageDependencies : Object.keys( pkg.packageDependencies );
172 | if( deps.some( function( dep ){
173 | return !self.packages[ dep ];
174 | })){
175 | pkg.__dirname = elt;
176 | self.pending.push( pkg );
177 | return Promise.resolve();
178 | }
179 | }
180 | return init_package( pkg, elt );
181 |
182 |
183 | }
184 | return Promise.resolve();
185 | }).then( function(){
186 | return self.load_packages_list( list, core, opts );
187 | }).then( function(){
188 | resolve();
189 | });
190 | }
191 | else {
192 |
193 | if( self.pending.length ){
194 | for( let i = 0; i< self.pending.length; i++ ){
195 | let pkg = self.pending[i];
196 | let deps = Array.isArray( pkg.packageDependencies ) ?
197 | pkg.packageDependencies : Object.keys( pkg.packageDependencies );
198 | if( deps.every( function( dep ){
199 | return !!self.packages[ dep ];
200 | })){
201 | self.pending.splice( i, 1 );
202 | init_package( pkg, pkg.__dirname ).then( function(){
203 | return self.load_packages_list( list, core, opts );
204 | }).then( function(){
205 | resolve();
206 | });
207 | return;
208 | }
209 | }
210 | if( self.pending.length ){
211 | let unresolvable = self.pending.map( function( pkg ){
212 | return pkg.name;
213 | });
214 | console.info( "Some packages have unresolvable dependencies:" );
215 | self.pending.map( function( pkg ){
216 | console.info( pkg.name + ":", pkg.packageDependencies );
217 | });
218 | }
219 | }
220 | resolve();
221 |
222 | }
223 | });
224 | };
225 |
226 | /**
227 | * load a pkg
228 | */
229 | this.load = function(dir){
230 | return new Promise( function( resolve, reject ){
231 |
232 | // check for a pkg file
233 | var package_file = path.join( dir, "package.json" );
234 | fs.readFile( package_file, { encoding: "utf8" }, (err, contents) => {
235 | if (err){
236 | resolve( false, err );
237 | return;
238 | }
239 | try {
240 | var spec = JSON.parse( contents );
241 | if( !spec.main ) throw( "package main not found" );
242 | if( !spec.name ) throw( "package name not found" );
243 | console.info( "Loading package", spec.name);
244 | spec.module = X(path.join( dir, spec.main ));
245 | resolve(spec);
246 | }
247 | catch( err ){
248 | console.error( err );
249 | resolve( false, err );
250 | }
251 | });
252 |
253 | });
254 | };
255 |
256 | };
257 |
258 | module.exports = new PackageManager();
259 |
--------------------------------------------------------------------------------
/core/settings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | /**
24 | * dictionary object with a backing store (currently localStorage,
25 | * but should be pluggable) that broadcasts changes.
26 | */
27 |
28 | "use strict";
29 |
30 | const PubSub = require( "pubsub-js" );
31 |
32 | const LocalStorageBase = function(key){
33 |
34 | if( !key ) throw( "provide a key for localStorage" );
35 | Object.defineProperty( this, "__storage_key__", {
36 | enumerable: false,
37 | configurable: false,
38 | value: key
39 | });
40 |
41 | let json = localStorage.getItem( key );
42 | let js = json ? JSON.parse( json ) : {};
43 | Object.assign( this, js );
44 |
45 | };
46 |
47 | LocalStorageBase.prototype.save = function(){
48 | localStorage.setItem( this.__storage_key__, JSON.stringify( this ));
49 | }
50 |
51 | const Settings = new Proxy( new LocalStorageBase( "settings" ), {
52 |
53 | set: function(target, property, value, receiver) {
54 |
55 | if( typeof value === "undefined" || null === value ) delete target[property];
56 | else target[property] = value;
57 |
58 | // save to backing store
59 | target.save();
60 |
61 | // broadcast
62 | PubSub.publish( "settings-change", { key: property, val: value });
63 |
64 | return true;
65 | },
66 |
67 | /**
68 | * NOTE: we're deep-copying here. why? to prevent accidental update
69 | * of object values. There's a case in which you get an object
70 | *
71 | * let x = Settings['x']
72 | *
73 | * and then modify that object,
74 | *
75 | * x.y = 1
76 | *
77 | * which has the effect of calling set() on the Settings object because
78 | * it's a reference. while that might be useful behavior, it's unintuitive
79 | * and different from non-object behavior. therefore we use copies.
80 | */
81 | get: function(target, property, receiver) {
82 | let rslt = target[property];
83 | if( typeof rslt === "object"){
84 | // cheaper way?
85 | rslt = JSON.parse( JSON.stringify( rslt ));
86 | }
87 | return rslt;
88 | }
89 |
90 | });
91 |
92 | module.exports = Settings;
93 |
94 |
--------------------------------------------------------------------------------
/core/utils.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | const DataCache = require( "./data-cache.js" );
24 | const FileCache = require( "./file-cache.js" );
25 |
26 | module.exports = {
27 |
28 | escape_backslashes: function(s, count){
29 | count = count || 1;
30 | var repl = "";
31 | for( var i = 0; i< count; i++ ) repl += '\\';
32 | return s.replace( /\\/g, repl );
33 | },
34 |
35 | /**
36 | * add an import to document head. will remove/re-add.
37 | */
38 | install_html_component: function( href ){
39 |
40 | var nodes = document.querySelectorAll( "link[rel='import']");
41 | for( var i = 0; i< nodes.length; i++ ){
42 | if( nodes[i].href === href ) nodes[i].parentElement.removeChild(nodes[i]);
43 | }
44 | var node = document.createElement( "link" );
45 | node.setAttribute( "rel", "import" );
46 | node.setAttribute( "href", href );
47 | document.querySelector( "head" ).appendChild( node );
48 |
49 | },
50 |
51 | /**
52 | * is needle === haystack, or is needle _in_ haystack,
53 | * or if need is an array, is one element of needle === or in haystack
54 | */
55 | array_cross_match: function( haystack, needle ){
56 | if( !Array.isArray( needle )) needle = [needle];
57 | return needle.some( function( test ){
58 | return ( Array.isArray( haystack ) && haystack.includes( test )) || haystack === test;
59 | });
60 | },
61 |
62 | /**
63 | * patch for file paths when running in a hybrid asar packed/unpacked
64 | * environment. we generally use __dirname to map paths, but that will
65 | * fail for our unpacked binaries.
66 | *
67 | * broken out to normalize.
68 | */
69 | patch_asar_path: function( original_path ){
70 |
71 | // (1) should almost certainly not be /g.
72 | // (2) is it guaranteed to be "app.asar"?
73 |
74 | return original_path.replace( /app\.asar/g, "app.asar.unpacked" );
75 |
76 | },
77 |
78 | init: function(R){
79 | this.data_cache = new DataCache(R);
80 | this.file_cache = new FileCache();
81 | return this;
82 | }
83 |
84 | };
85 |
86 |
87 |
--------------------------------------------------------------------------------
/data/codemirror-minimal.css:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * this is a cut-down version of the codemirror stylesheet. it also
4 | * includes some core styles from the constructr stylesheets.
5 | *
6 | * FIXME: automate construction
7 | */
8 |
9 | .CodeMirror {
10 | font-family: monospace;
11 | color: black;
12 | }
13 |
14 | /* PADDING */
15 |
16 | .CodeMirror-lines {
17 | padding: 4px 0; /* Vertical padding around content */
18 | }
19 | .CodeMirror pre {
20 | padding: 0 4px; /* Horizontal padding of content */
21 | }
22 |
23 | .cm-tab { display: inline-block; text-decoration: inherit; }
24 |
25 | /* DEFAULT THEME */
26 |
27 | .cm-s-default .cm-header {color: blue;}
28 | .cm-s-default .cm-quote {color: #090;}
29 | .cm-negative {color: #d44;}
30 | .cm-positive {color: #292;}
31 | .cm-header, .cm-strong {font-weight: bold;}
32 | .cm-em {font-style: italic;}
33 | .cm-link {text-decoration: underline;}
34 | .cm-strikethrough {text-decoration: line-through;}
35 |
36 | .cm-s-default .cm-keyword {color: #708;}
37 | .cm-s-default .cm-atom {color: #219;}
38 | .cm-s-default .cm-number {color: #164;}
39 | .cm-s-default .cm-def {color: #00f;}
40 | .cm-s-default .cm-variable,
41 | .cm-s-default .cm-punctuation,
42 | .cm-s-default .cm-property,
43 | .cm-s-default .cm-operator {}
44 | .cm-s-default .cm-variable-2 {color: #05a;}
45 | .cm-s-default .cm-variable-3 {color: #085;}
46 | .cm-s-default .cm-comment {color: #a50;}
47 | .cm-s-default .cm-string {color: #a11;}
48 | .cm-s-default .cm-string-2 {color: #f50;}
49 | .cm-s-default .cm-meta {color: #555;}
50 | .cm-s-default .cm-qualifier {color: #555;}
51 | .cm-s-default .cm-builtin {color: #30a;}
52 | .cm-s-default .cm-bracket {color: #997;}
53 | .cm-s-default .cm-tag {color: #170;}
54 | .cm-s-default .cm-attribute {color: #00c;}
55 | .cm-s-default .cm-hr {color: #999;}
56 | .cm-s-default .cm-link {color: #00c;}
57 |
58 | .cm-s-default .cm-error {color: #f00;}
59 | .cm-invalidchar {color: #f00;}
60 |
61 | .CodeMirror-composing { border-bottom: 2px solid; }
62 |
63 | /* Default styles for common addons */
64 |
65 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
66 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
67 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
68 | .CodeMirror-activeline-background {background: #e8f2ff;}
69 |
70 | /* STOP */
71 |
72 | /* The rest of this file contains styles related to the mechanics of
73 | the editor. You probably shouldn't touch them. */
74 |
75 | .CodeMirror {
76 | background: white;
77 | }
78 |
79 | .CodeMirror-lines {
80 | cursor: text;
81 | min-height: 1px; /* prevents collapsing before first draw */
82 | }
83 | .CodeMirror pre {
84 | /* Reset some styles that the rest of the page might have set */
85 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
86 | border-width: 0;
87 | background: transparent;
88 | font-family: inherit;
89 | font-size: inherit;
90 | margin: 0;
91 | white-space: pre;
92 | word-wrap: normal;
93 | line-height: inherit;
94 | color: inherit;
95 | z-index: 2;
96 | position: relative;
97 | overflow: visible;
98 | -webkit-tap-highlight-color: transparent;
99 | -webkit-font-variant-ligatures: none;
100 | font-variant-ligatures: none;
101 | }
102 | .CodeMirror-wrap pre {
103 | word-wrap: break-word;
104 | white-space: pre-wrap;
105 | word-break: normal;
106 | }
107 |
108 | .CodeMirror-linebackground {
109 | position: absolute;
110 | left: 0; right: 0; top: 0; bottom: 0;
111 | z-index: 0;
112 | }
113 |
114 | .CodeMirror-linewidget {
115 | position: relative;
116 | z-index: 2;
117 | overflow: auto;
118 | }
119 |
120 | .CodeMirror-widget {}
121 |
122 | .CodeMirror-code {
123 | outline: none;
124 | }
125 |
126 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
127 | .CodeMirror span { *vertical-align: text-bottom; }
128 |
129 | /* Used to force a border model for a node */
130 | .cm-force-border { padding-right: .1px; }
131 |
132 | /* See issue #2901 */
133 | .cm-tab-wrap-hack:after { content: ''; }
134 |
135 | /* --- constructr core ----------------------- */
136 |
137 | .CodeMirror, .fixed-width {
138 | font-family: consolas, monospace;
139 | font-size: 14.25px;
140 | height: 100%;
141 | }
142 |
143 | .linux .CodeMirror, .linux .fixed-width {
144 | font-family: "Liberation Mono";
145 | }
146 |
147 | .ubuntu .CodeMirror, .ubuntu .fixed-width {
148 | font-family: "Ubuntu Mono";
149 | font-size: 13pt;
150 | }
151 |
152 | .osx .CodeMirror, .osx .fixed-width {
153 | font-family: "Menlo";
154 | font-size: 13px;
155 | }
156 |
157 | .cm-s-default span.shell-parse-error { color: #ff4f3f !important; }
158 | .cm-s-default span.pager { color: #000 !important; }
159 | .cm-s-default span.shell-piped-stream { color: #000 !important; }
160 | .cm-s-default span.shell-prompt-debug { color: red; }
161 |
162 | .CodeMirror .graphics-device {
163 | margin: 1em 1em 1em 1em;
164 | border: 1px solid #ccc;
165 | width: 576px;
166 | height: 360px;
167 | }
168 |
169 |
--------------------------------------------------------------------------------
/data/menus.json:
--------------------------------------------------------------------------------
1 | {
2 | "shell-context-menu": [
3 | {
4 | "label": "Cut",
5 | "role": "cut"
6 | },
7 | {
8 | "label": "Copy",
9 | "role": "copy"
10 | },
11 | {
12 | "label": "Paste",
13 | "role": "paste"
14 | },
15 | {
16 | "label": "Select All",
17 | "message": "context-select-all"
18 | },
19 | {
20 | "type": "separator"
21 | },
22 | {
23 | "label": "Clear Shell",
24 | "message": "clear-shell"
25 | },
26 | {
27 | "label": "Save Shell Contents...",
28 | "message": "save-shell"
29 | },
30 | {
31 | "label": "Shell Preferences",
32 | "message": "preferences"
33 | },
34 | {
35 | "type": "separator"
36 | },
37 | {
38 | "label": "Cancel"
39 | }
40 | ],
41 | "application": [
42 | {
43 | "label": "ConstructR-Shell",
44 | "platform": "darwin",
45 | "submenu": [
46 | {
47 | "label": "About ConstructR-Shell",
48 | "role": "about"
49 | },
50 | {
51 | "type": "separator"
52 | },
53 | {
54 | "label": "Hide ConstructR-Shell",
55 | "accelerator": "Command+H",
56 | "role": "hide"
57 | },
58 | {
59 | "label": "Hide Others",
60 | "accelerator": "Command+Alt+H",
61 | "role": "hideothers"
62 | },
63 | {
64 | "label": "Show All",
65 | "role": "unhide"
66 | },
67 | {
68 | "type": "separator"
69 | },
70 | {
71 | "label": "Quit",
72 | "accelerator": "Command+Q",
73 | "message": "close"
74 | }
75 | ]
76 | },
77 | {
78 | "label": "File",
79 | "submenu": [
80 | {
81 | "label": "Save History...",
82 | "message": "save-history"
83 | },
84 | {
85 | "label": "Save Shell Contents as HTML...",
86 | "message": "save-shell"
87 | },
88 | {
89 | "label": "Save Environment",
90 | "message": "save-environment"
91 | },
92 | { "type": "separator" },
93 | {
94 | "label": "Quit",
95 | "accelerator": "CmdOrCtrl+Q",
96 | "message": "close"
97 | }
98 | ]
99 | },
100 | {
101 | "label": "Edit",
102 | "submenu": [
103 | {
104 | "label": "Undo",
105 | "accelerator": "CmdOrCtrl+Z",
106 | "role": "undo"
107 | },
108 | {
109 | "label": "Redo",
110 | "accelerator": "Shift+CmdOrCtrl+Z",
111 | "role": "redo"
112 | },
113 | {
114 | "type": "separator"
115 | },
116 | {
117 | "label": "Cut",
118 | "accelerator": "CmdOrCtrl+X",
119 | "role": "cut"
120 | },
121 | {
122 | "label": "Copy",
123 | "accelerator": "CmdOrCtrl+C",
124 | "role": "copy"
125 | },
126 | {
127 | "label": "Paste",
128 | "accelerator": "CmdOrCtrl+V",
129 | "role": "paste"
130 | },
131 | {
132 | "label": "Select All",
133 | "accelerator": "CmdOrCtrl+A",
134 | "role": "selectall"
135 | }
136 | ]
137 | },
138 | {
139 | "label": "View",
140 | "submenu": [
141 | {
142 | "label": "Reload",
143 | "accelerator": "CmdOrCtrl+R",
144 | "message": "reload"
145 | },
146 | {
147 | "label": "Toggle Full Screen",
148 | "accelerator": "F11",
149 | "accelerator-darwin": "Ctrl+Command+F",
150 | "message": "full-screen"
151 | },
152 | {
153 | "label": "Toggle Developer Tools",
154 | "accelerator": "Ctrl+Shift+I",
155 | "accelerator-darwin": "Alt+Command+I",
156 | "message": "developer-tools"
157 | },
158 | {
159 | "label": "Graphics Panel",
160 | "message": "graphics-panel"
161 | },
162 | {
163 | "type": "separator"
164 | },
165 | {
166 | "label": "Locals",
167 | "accelerator": "F9",
168 | "message": "locals"
169 | },
170 | {
171 | "label": "Watch",
172 | "accelerator": "F8",
173 | "message": "watch"
174 | },
175 | {
176 | "label": "History",
177 | "accelerator": "Ctrl+H",
178 | "message": "history"
179 | },
180 | {
181 | "type": "separator"
182 | },
183 | {
184 | "label": "Clear Shell",
185 | "accelerator": "Ctrl+Shift+X",
186 | "accelerator-darwin": "Alt+Command+X",
187 | "message": "clear-shell"
188 | },
189 | {
190 | "label": "Toggle Shell Preferences",
191 | "accelerator": "Ctrl+Shift+P",
192 | "accelerator-darwin": "Alt+Command+P",
193 | "message": "preferences"
194 | },
195 | {
196 | "type": "separator"
197 | },
198 | {
199 | "label": "Choose CRAN Mirror",
200 | "accelerator": "Ctrl+Shift+M",
201 | "accelerator-darwin": "Alt+Command+M",
202 | "message": "choose-mirror"
203 | },
204 | {
205 | "label": "Install Packages",
206 | "accelerator": "Ctrl+Shift+K",
207 | "accelerator-darwin": "Alt+Command+K",
208 | "message": "install-packages"
209 | }
210 | ]
211 | },
212 | {
213 | "label": "Window",
214 | "role": "window",
215 | "submenu": [
216 | {
217 | "label": "Minimize",
218 | "accelerator": "CmdOrCtrl+M",
219 | "role": "minimize"
220 | },
221 | {
222 | "label": "Close",
223 | "accelerator": "CmdOrCtrl+W",
224 | "message": "close"
225 | },
226 | {
227 | "label": "Bring All to Front",
228 | "role": "front",
229 | "platform": "darwin"
230 | }
231 | ]
232 | },
233 | {
234 | "label": "&Help",
235 | "role": "help",
236 | "submenu": [
237 | {
238 | "label": "Learn More",
239 | "message": "learn-more"
240 | },
241 | {
242 | "label": "&About",
243 | "message": "about",
244 | "!platform": "darwin"
245 | }
246 | ]
247 | }
248 | ]
249 | }
250 |
--------------------------------------------------------------------------------
/data/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "MISSING_JSCLIENTLIB": [
3 | "WARNING: The jsClientLib library is not available. Many functions",
4 | "in the shell depend on this library, and will not work as expected.",
5 | "You can download the library from the git repository:",
6 | "",
7 | "https://github.com/sdllc/jsclientlib",
8 | "",
9 | ""
10 | ],
11 |
12 | "R_HOME_NOT_FOUND": [
13 | "",
14 | "We can't initialize R because R_HOME was not found.",
15 | "",
16 | "You can do one of the following:",
17 | "",
18 | " * use the command line argument --r-home=/path/to/R",
19 | " * set an environment variable R_HOME",
20 | " * use a version of ControlR that bundles R",
21 | " * update your PATH to point to the R executable",
22 | ""
23 | ],
24 |
25 | "LOADING_MIRROR_LIST": "Loading mirror list, please wait...",
26 | "SET_CRAN_MIRROR": "Set CRAN mirror",
27 |
28 | "LOADING_STARTUP_FILE": "Loading startup file",
29 |
30 | "ERROR_OCCURRED": "An error occurred. Please try again later.",
31 |
32 | "INSTALLED_FLAG": "(installed)",
33 | "INSTALLING_PACKAGES": "Installing packages...\n",
34 |
35 | "LOADING_PACKAGE_LIST": "Loading package list, please wait...",
36 | "PLEASE_SELECT_MIRROR": "Please select a CRAN mirror before installing packages\n",
37 |
38 | "TRYING_URL": "Trying URL",
39 | "DOWNLOADING": "Downloading",
40 | "DOWNLOAD_COMPLETE": "Download complete",
41 | "DOWNLOAD_FAILED": "Download failed",
42 |
43 | "SAVED_OK": "Saved OK",
44 |
45 | "FIELD": "Field",
46 | "CLASS": "Class",
47 | "SIZE": "Size",
48 | "VALUE": "Value",
49 | "DETAILS": "Details",
50 | "ADD_WATCH": "Add Watch",
51 | "WATCHES": "Watches",
52 | "LOCALS": "Locals",
53 | "WATCH": "Watch",
54 | "REMOVE_WATCH": "Remove watch"
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/ext/cogs/README.md:
--------------------------------------------------------------------------------
1 |
2 | Cog icons by [Jiri Silha][1]. I found them via [Pasquale Vitiello][2].
3 |
4 | [1] https://dribbble.com/shots/1631956-Settings-Icons-PSD
5 | [2] https://github.com/pasqualevitiello/Tumblr-Style-Cog-Spinners
6 |
7 |
--------------------------------------------------------------------------------
/ext/cogs/cogs.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: 'cogs';
4 | src: url('./cogs.eot');
5 | src: url('./cogs.eot?#iefix') format('embedded-opentype'),
6 | url('./cogs.woff') format('woff'),
7 | url('./cogs.ttf') format('truetype'),
8 | url('./cogs.svg#cogs') format('svg');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 | [class*='cog-']:before{
13 | display: inline-block;
14 | font-family: 'cogs';
15 | font-style: normal;
16 | font-weight: normal;
17 | line-height: 1;
18 | -webkit-font-smoothing: antialiased;
19 | -moz-osx-font-smoothing: grayscale
20 | }
21 |
22 | .cog-01:before{content:'\0054';}
23 | .cog-02:before{content:'\0055';}
24 | .cog-03:before{content:'\0056';}
25 | .cog-04:before{content:'\0057';}
26 | .cog-05:before{content:'\0058';}
27 | .cog-06:before{content:'\0047';}
28 | .cog-07:before{content:'\0041';}
29 | .cog-08:before{content:'\0042';}
30 | .cog-09:before{content:'\0043';}
31 | .cog-10:before{content:'\0044';}
32 | .cog-11:before{content:'\0045';}
33 | .cog-12:before{content:'\0046';}
34 | .cog-13:before{content:'\0048';}
35 | .cog-14:before{content:'\0049';}
36 | .cog-15:before{content:'\004a';}
37 | .cog-16:before{content:'\004b';}
38 | .cog-17:before{content:'\004c';}
39 | .cog-18:before{content:'\004d';}
40 | .cog-19:before{content:'\004e';}
41 | .cog-20:before{content:'\004f';}
42 | .cog-21:before{content:'\0050';}
43 | .cog-22:before{content:'\0051';}
44 | .cog-23:before{content:'\0052';}
45 | .cog-24:before{content:'\0053';}
46 |
--------------------------------------------------------------------------------
/ext/cogs/cogs.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/ext/cogs/cogs.eot
--------------------------------------------------------------------------------
/ext/cogs/cogs.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/ext/cogs/cogs.ttf
--------------------------------------------------------------------------------
/ext/cogs/cogs.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/ext/cogs/cogs.woff
--------------------------------------------------------------------------------
/ext/material/MaterialIcons-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sdllc/constructr/4cbc17a9760006bc4b7f72e8e6e1e588d62d7cf5/ext/material/MaterialIcons-Regular.woff2
--------------------------------------------------------------------------------
/ext/material/README.md:
--------------------------------------------------------------------------------
1 | The recommended way to use the Material Icons font is by linking to the web font hosted on Google Fonts:
2 |
3 | ```html
4 |
6 | ```
7 |
8 | Read more in our full usage guide:
9 | http://google.github.io/material-design-icons/#icon-font-for-the-web
10 |
--------------------------------------------------------------------------------
/ext/material/material-icons.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Material Icons';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: local('Material Icons'),
6 | local('MaterialIcons-Regular'),
7 | url(MaterialIcons-Regular.woff2) format('woff2');
8 | }
9 |
10 | .material-icons {
11 | font-family: 'Material Icons';
12 | font-weight: normal;
13 | font-style: normal;
14 | font-size: 24px; /* Preferred icon size */
15 | display: inline-block;
16 | width: 1em;
17 | height: 1em;
18 | line-height: 1;
19 | text-transform: none;
20 | letter-spacing: normal;
21 | word-wrap: normal;
22 | white-space: nowrap;
23 | direction: ltr;
24 |
25 | /* Support for all WebKit browsers. */
26 | -webkit-font-smoothing: antialiased;
27 | /* Support for Safari and Chrome. */
28 | text-rendering: optimizeLegibility;
29 |
30 | /* Support for Firefox. */
31 | -moz-osx-font-smoothing: grayscale;
32 |
33 | /* Support for IE. */
34 | font-feature-settings: 'liga';
35 | }
36 |
--------------------------------------------------------------------------------
/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
25 |
26 |
27 |
28 |
29 | ConstructR Shell
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/main/main.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | 'use strict';
24 |
25 | if (require('electron-squirrel-startup')) return; // installer
26 |
27 | const path = require( "path" );
28 | const electron = require('electron');
29 | const electronwindowstate = require('electron-window-state');
30 |
31 | const {ipcMain} = electron;
32 | const {app} = electron;
33 | const {BrowserWindow} = electron;
34 |
35 | let win;
36 |
37 | app.on('ready', function(){
38 |
39 | let windowstate = electronwindowstate({
40 | defaultWidth: 1000,
41 | defaultHeight: 800
42 | });
43 |
44 | win = new BrowserWindow({
45 | 'x': windowstate.x,
46 | 'y': windowstate.y,
47 | 'width': windowstate.width,
48 | 'height': windowstate.height,
49 | 'webPreferences': {
50 | 'experimentalCanvasFeatures': true
51 | }
52 | });
53 |
54 | win.main_process_args = process.argv.slice(0);
55 |
56 | windowstate.manage(win);
57 | win.loadURL('file://' + __dirname + '/index.html');
58 | win.on('closed', function() { win = null; });
59 |
60 | });
61 |
62 | app.on('window-all-closed', function () {
63 | if (process.platform !== 'darwin') {
64 | app.quit();
65 | }
66 | });
67 |
68 | app.on('activate', function () {
69 | if (win === null) {
70 | createWindow();
71 | }
72 | });
73 |
74 | ipcMain.on('system', function(event, arg){
75 | if( arg === "quit" ){
76 | app.quit();
77 | }
78 | });
79 |
80 | ipcMain.on('download', function(event, opts = {}){
81 |
82 | let url = opts.url;
83 | let dest = opts.destfile;
84 | let listener = function(event, item, webContents){
85 |
86 | let totalBytes = item.getTotalBytes();
87 | let filePath = dest || path.join(app.getPath('downloads'), item.getFilename());
88 |
89 | // NOTE: this fails with unix-y paths. R is building these paths incorrectly
90 |
91 | if( process.platform === "win32" ){
92 | filePath = filePath.replace( /\//g, "\\" );
93 | }
94 | item.setSavePath(filePath);
95 |
96 | item.on('updated', () => {
97 | win.setProgressBar(item.getReceivedBytes() / totalBytes);
98 | webContents.send( 'download-progress', { received: item.getReceivedBytes(), total: totalBytes });
99 | });
100 |
101 | item.on('done', (e, state) => {
102 |
103 | if (!win.isDestroyed()) {
104 | win.setProgressBar(-1);
105 | }
106 |
107 | if (state === 'interrupted') {
108 | // electron.dialog.showErrorBox('Download error', `The download of ${item.getFilename()} was interrupted`);
109 | }
110 |
111 | webContents.send( 'download-complete', { path: filePath, name: item.getFilename(), size: totalBytes, state: state });
112 | webContents.session.removeListener('will-download', listener);
113 |
114 | });
115 |
116 | };
117 |
118 | win.webContents.session.on( 'will-download', listener );
119 | win.webContents.downloadURL(url);
120 |
121 | });
122 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "constructr",
3 | "productName": "ConstructR",
4 | "version": "0.8.3",
5 | "description": "Electron-based R shell",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/sdllc/constructr.git"
9 | },
10 | "author": {
11 | "name": "Structured Data LLC",
12 | "email": "info@riskamp.com"
13 | },
14 | "homepage": "https://constructr-project.com/shell",
15 | "license": "MIT",
16 | "main": "app/main.js",
17 | "scripts": {
18 | "start": "electron app/main.js",
19 | "dist": "build"
20 | },
21 | "build": {
22 | "asar": true,
23 | "asar-unpack": "**/*.R",
24 | "asar-unpack-dir": "**/{R-3.*,node_modules/controlr,library,R}/**/*",
25 | "win": {
26 | "iconUrl": "https://raw.githubusercontent.com/sdllc/constructr/master/build/icon.ico"
27 | },
28 | "linux": {
29 | "depends": [
30 | "r-base-core",
31 | "r-base",
32 | "r-recommended",
33 | "libappindicator1",
34 | "libnotify-bin"
35 | ],
36 | "fpm": [
37 | "--deb-priority",
38 | "optional",
39 | "--category",
40 | "devel"
41 | ]
42 | }
43 | },
44 | "dependencies": {
45 | "chokidar": "^1.4.3",
46 | "cmjs-shell": "^0.3.0",
47 | "controlr": "^2.0.1",
48 | "electron-squirrel-startup": "^1.0.0",
49 | "electron-window-state": "^3.0.3",
50 | "pubsub-js": "^1.5.3",
51 | "untildify": "^3.0.2",
52 | "xml": "^1.0.1"
53 | },
54 | "devDependencies": {
55 | "bower": "^1.7.9",
56 | "del": "^2.2.0",
57 | "electron-builder": "^3.25.0",
58 | "electron-prebuilt": "^1.2.3",
59 | "extract-zip": "^1.5.0",
60 | "gulp": "^3.9.1",
61 | "gulp-add-src": "^0.2.0",
62 | "gulp-concat": "^2.6.0",
63 | "gulp-cssnano": "^2.1.2",
64 | "gulp-htmlmin": "^2.0.0",
65 | "gulp-if": "^2.0.0",
66 | "gulp-livereload": "^3.8.1",
67 | "gulp-postcss": "^6.1.0",
68 | "gulp-rename": "^1.2.2",
69 | "gulp-run": "^1.6.12",
70 | "gulp-sourcemaps": "^1.6.0",
71 | "gulp-uglify": "^1.5.3",
72 | "gulp-util": "^3.0.7",
73 | "lodash.assign": "^4.0.7",
74 | "postcss-import": "^8.1.0",
75 | "request": "^2.72.0",
76 | "vinyl-buffer": "^1.0.0",
77 | "vinyl-source-stream": "^1.1.0",
78 | "webpack": "^1.12.14",
79 | "webpack-stream": "^3.1.0"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/packages/README.md:
--------------------------------------------------------------------------------
1 | Packages
2 | ========
3 |
4 | Package API in development. Packages are intended to split
5 | core and non-core functionality, but they also replace the
6 | plugin system so there's a single unified interface for
7 | default and optional packages.
8 |
9 | Packages are like libraries but with some application-
10 | and runtime-specific functionality
11 |
12 | Packages *may* (but don't have to) export an `init(core)`
13 | method, returning a promise. This will be called with a parameter
14 | containing useful objects -- R, Settings, Settings, Hooks,
15 | Constants, Messages, Utilities, (...)
16 |
17 | Dependencies
18 | ------------
19 |
20 | Dependendies are supported using a `packageDepdendencies` array
21 | (or object, for versioning). The package won't be initialized
22 | until all depdendencies are available, but it will be loaded;
23 | so don't expect dependencies to be available before the `init()`
24 | method is called.
25 |
26 |
27 |
--------------------------------------------------------------------------------
/packages/browser/browser.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
28 |
29 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
88 |
89 |
90 |
91 |
191 |
192 |
193 |
194 |
--------------------------------------------------------------------------------
/packages/browser/browser.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | var path = require("path");
26 | var PubSub = require( "pubsub-js" );
27 |
28 | /** inject stylesheet into help pages */
29 | var help_stylesheet = null;
30 |
31 | module.exports = {
32 |
33 | init: function(core){
34 |
35 | var html = path.join( "packages", "browser", "browser.html" );
36 | core.Utils.install_html_component( html );
37 |
38 | core.Hooks.install( "browser", function(hook, url){
39 |
40 | if( core.Settings["browser.panel"])
41 | {
42 | var browser = document.getElementById( "browser-pane" );
43 | if( !browser ){
44 |
45 | browser = document.createElement( "browser-component" );
46 | browser.id = "browser-pane";
47 | browser.cache = core.Utils.file_cache;
48 |
49 | // don't destroy if the panel closes
50 | browser.setAttribute( "data-preserve", true );
51 |
52 | // close box
53 | browser.addEventListener( "close", function(e){
54 | //sidePanel.pop();
55 | PubSub.publish( "side-panel-pop", browser );
56 | });
57 |
58 | // on load, possibly inject stylesheet
59 | browser.addEventListener( 'load-commit', function(e){
60 | var url = e.url || e.detail.url;
61 | if( url && url.match( /^http\:\/\/127\.0\.0\.1\:\d+\/(?:library|doc)\// )){
62 | if( help_stylesheet && help_stylesheet !== "default" ){
63 | var ss = path.join( help_styles.dir, help_stylesheet + ".css" );
64 | browser.inject_stylesheet( ss );
65 | }
66 | }
67 | });
68 |
69 | }
70 |
71 | /**
72 | * this must be done _after_ the parent has been changed
73 | * or it will crash hard (this is a bug in electron or polymer?)
74 | */
75 | browser._onShow = function(){
76 | browser.open( url );
77 | };
78 |
79 | PubSub.publish( "side-panel-attach", { node: browser });
80 | return true;
81 |
82 | }
83 | return false;
84 |
85 | });
86 |
87 | return Promise.resolve();
88 | }
89 |
90 | };
--------------------------------------------------------------------------------
/packages/browser/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "browser",
3 | "version": "0.1.0",
4 | "main": "browser.js",
5 | "packageDependencies": [
6 | "side-panel"
7 | ],
8 | "preferences": {
9 | "browser.panel": {
10 | "type": "boolean",
11 | "default": false,
12 | "label": "Use browser panel"
13 | }
14 | }
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/packages/cran/cran.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const path = require( "path" );
26 | const PubSub = require( "pubsub-js" );
27 | const electron = require('electron');
28 |
29 | /**
30 | * open the CRAN mirror selector in the panel
31 | *
32 | * TODO: make this modal?
33 | */
34 | var open_mirror_chooser = function(core, cb){
35 |
36 | console.info( "OMC CB", cb, "t?", typeof cb );
37 |
38 | var panel = document.createElement( "mirror-chooser" );
39 | panel.id = "mirror-chooser-panel";
40 | panel.className = "panel";
41 | panel.addEventListener( 'close', function(){
42 | PubSub.publish( core.Constants.SIDE_PANEL_POP, panel );
43 | });
44 | panel.$.cancel.addEventListener( 'click', function(){
45 | PubSub.publish( core.Constants.SIDE_PANEL_POP, panel );
46 | });
47 |
48 | panel.message = core.Messages.LOADING_MIRROR_LIST;
49 |
50 | PubSub.publish( core.Constants.SIDE_PANEL_PUSH, { node: panel });
51 |
52 | var mirror = undefined;
53 | var selected_index = 0;
54 |
55 | core.R.get_cran_mirror().then( function( m ){
56 | if( m && m !== "@CRAN@" ){
57 | mirror = m;
58 | panel.http = ( !mirror.startsWith( "https" ));
59 | }
60 | else panel.http = false;
61 | return core.Utils.data_cache.get_cached_data( "cran.mirrors", core.Utils.data_cache.DEFAULT_CACHE_TIME, "getCRANmirrors()" );
62 | }).then( function( obj ){
63 |
64 | if( obj && obj.$data ){
65 | var data = core.Utils.data_cache.transpose_frame( obj.$data );
66 |
67 | // check the current mirror, independent of filter
68 |
69 | // it seems like chooseCRANmirror sets the mirror WITHOUT
70 | // a trailing slash, even though the list entries have a
71 | // trailing slash. So.
72 |
73 | for( var i = 0; i< data.length; i++ ){
74 | data[i].checked = false;
75 | data[i].index = i;
76 | if( mirror && ( mirror === data[i].URL || mirror + "/" === data[i].URL )){
77 | data[i].checked = true;
78 | data[i].initial_selection = true;
79 | }
80 | }
81 |
82 | var filter_http = function(http){
83 | var filtered = data.filter(function(elt){
84 | return ( http || elt.URL.startsWith( "https" ));
85 | });
86 | selected_index = 0;
87 | for( var i = 0; i< filtered.length; i++ ){
88 | if( filtered[i].checked ){
89 | selected_index = i;
90 | break;
91 | }
92 | }
93 | return filtered;
94 | }
95 |
96 | panel.addEventListener( 'filter-change', function(){
97 | panel.data = filter_http(panel.http);
98 | panel.scrollTo(selected_index);
99 | });
100 |
101 | panel.addEventListener( 'selection-change', function(e){
102 | var index = e.detail.index;
103 | for( var i = 0; i< data.length; i++ ){
104 | if(i === index){
105 | data[i].checked = true;
106 | mirror = data[i].URL;
107 | }
108 | else data[i].checked = false;
109 | }
110 | panel.data = filter_http(panel.http);
111 | });
112 |
113 | panel.$.accept.addEventListener( 'click', function(e){
114 |
115 | e.stopPropagation();
116 | e.preventDefault();
117 |
118 | if( typeof mirror === "undefined" ) return;
119 |
120 | if( panel.setDefault ){
121 | console.info( "setting default mirror" );
122 | core.Settings["default.mirror"] = mirror ;
123 | }
124 |
125 | var msg = `${core.Messages.SET_CRAN_MIRROR}: ${mirror}`;
126 |
127 | core.R.set_cran_mirror( mirror, msg ).then( function(){
128 | if( cb ) cb.call(this);
129 | else PubSub.publish( core.Constants.SIDE_PANEL_POP, panel);
130 | PubSub.publish( core.Constants.SHELL_FOCUS );
131 | //shell.focus();
132 | }).catch( function(){
133 | if( cb ) cb.call(this);
134 | else PubSub.publish( core.Constants.SIDE_PANEL_POP, panel);
135 | PubSub.publish( core.Constants.SHELL_FOCUS );
136 | //shell.focus();
137 | });
138 |
139 | });
140 |
141 | panel.data = filter_http(panel.http);
142 | panel.scrollTo(selected_index);
143 | }
144 | else {
145 | panel.message = core.Messages.ERROR_OCCURRED;
146 | }
147 |
148 | });
149 |
150 | };
151 |
152 |
153 | /**
154 | * if a mirror is set, open the package chooser. otherwise open the
155 | * mirror chooser, and then (on accept) open the package chooser.
156 | */
157 | var open_package_chooser_if = function(core){
158 |
159 | core.R.get_cran_mirror().then( function( m ){
160 | if( m && !m.startsWith('@')){
161 | open_package_chooser(core);
162 | }
163 | else {
164 | PubSub.publish(core.Constants.SHELL_MESSAGE, [ core.Messages.PLEASE_SELECT_MIRROR, "shell-system-information"]);
165 | open_mirror_chooser( core, open_package_chooser.bind(this, core));
166 | }
167 | }).catch( function(e){
168 | console.info(e);
169 | });
170 |
171 | };
172 |
173 | var open_package_chooser = function(core){
174 |
175 | var panel = document.createElement( "package-chooser" );
176 | panel.id = "package-chooser-panel";
177 | panel.className = "panel";
178 | panel.addEventListener( 'close', function(){ PubSub.publish( core.Constants.SIDE_PANEL_POP, panel ); });
179 | panel.$.cancel.addEventListener( 'click', function(){ PubSub.publish( core.Constants.SIDE_PANEL_POP, panel ); });
180 |
181 | let mirror;
182 | let click_cran_link = function(e){
183 | electron.shell.openExternal(e.detail.href);
184 | }
185 | panel.addEventListener( "click-link", click_cran_link );
186 |
187 | panel.message = core.Messages.LOADING_PACKAGE_LIST;
188 |
189 | PubSub.publish( core.Constants.SIDE_PANEL_PUSH, { node: panel });
190 |
191 | var packages = undefined;
192 | var installed = undefined;
193 |
194 | core.R.get_cran_mirror().then( function( rslt ){
195 | mirror = rslt ;
196 | if( !mirror.endsWith( "/" )) mirror = mirror + "/";
197 | return core.Utils.data_cache.get_cached_data( "available.packages", core.Utils.data_cache.DEFAULT_CACHE_TIME, "available.packages()" );
198 |
199 | }).then( function( obj ){
200 |
201 | packages = core.Utils.data_cache.build_matrix(obj.$data, obj.$nrows, obj.$ncols, true );
202 |
203 | // this is nice, but unecessary and expensive
204 | packages = core.Utils.data_cache.apply_names( packages, 1, obj.$dimnames.$data[1] );
205 | return core.R.queued_internal( "installed.packages()" );
206 |
207 | }).then( function( obj ){
208 |
209 | // leave this one as column-dominant; we only need the first column
210 | installed = core.Utils.data_cache.build_matrix( obj.response.$data, obj.response.$nrows, obj.response.$ncols );
211 | var installed_packages = installed[0];
212 |
213 | // why not walk both arrays at once? there's a possibility
214 | // that something is installed but not from CRAN; we might
215 | // get stuck on that (FIXME: just use lexical comparison)
216 |
217 | for( var i = 0; i< packages.length; i++ ){
218 |
219 | // using property bindings we need these to be !undef
220 | packages[i].installed = false;
221 | packages[i].checked = false;
222 |
223 | // for ref
224 | packages[i].index = i;
225 | packages[i].link = mirror + "web/packages/" + packages[i].Package;
226 |
227 | for( var j = 0; j< installed_packages.length; j++ ){
228 | if( packages[i].Package === installed_packages[j] ){
229 | packages[i].installed = true;
230 | packages[i].Package += ( " " + core.Messages.INSTALLED_FLAG );
231 | break;
232 | }
233 | }
234 | }
235 |
236 | // console.info( packages, installed );
237 | panel.data = packages;
238 | panel.$.accept.addEventListener( 'click', function(){
239 |
240 | var list = [];
241 | for( var i = 0; i< packages.length; i++ ){
242 | if( packages[i].checked ){
243 | console.info( packages[i] );
244 | list.push( `"${packages[i].Package}"`);
245 | }
246 | }
247 |
248 | PubSub.publish( core.Constants.SIDE_PANEL_POP, panel );
249 | setImmediate(function(){
250 | if( list.length ){
251 | //shell.block();
252 | PubSub.publish(core.Constants.SHELL_BLOCK);
253 | PubSub.publish(core.Constants.SHELL_MESSAGE, [core.Messages.INSTALLING_PACKAGES, "shell-system-information" ]);
254 | var cmd = `install.packages(c(${list.join(',')}))`;
255 | core.R.queued_exec( cmd ).then( function( packet ){
256 | //shell.unblock.apply( this, arguments );
257 | PubSub.publish( core.Constants.SHELL_UNBLOCK, arguments );
258 | PubSub.publish( core.Constants.SHELL_FOCUS );
259 | })
260 | }
261 | });
262 | });
263 |
264 | }).catch(function(e){
265 | panel.message = core.Messages.ERROR_OCCURRED;
266 | console.info(e);
267 | });
268 |
269 | }
270 |
271 |
272 | module.exports = {
273 |
274 | init: function(core){
275 |
276 | var html = path.join( "packages", "cran", "cran.html" );
277 | core.Utils.install_html_component( html );
278 |
279 | // menu
280 | PubSub.subscribe( "menu-click", function(){
281 | var data = arguments[1];
282 | if( data.message === "choose-mirror" ){
283 | open_mirror_chooser( core );
284 | }
285 | if( data.message === "install-packages" ){
286 | open_package_chooser_if( core );
287 | }
288 | });
289 |
290 | // direct
291 | PubSub.subscribe( "open-mirror-chooser", function(cb){
292 | open_mirror_chooser( core, cb );
293 | });
294 |
295 | // system (shoudl be hook)
296 | core.R.on('system', function(msg){
297 | var cmd = msg.$data ? msg.$data.cmd : undefined;
298 | if( cmd === "chooseCRANmirror" ){
299 | open_mirror_chooser( core );
300 | }
301 | if( cmd === "install.packages" ){
302 | open_package_chooser_if( core );
303 | }
304 | });
305 |
306 | }
307 | };
308 |
--------------------------------------------------------------------------------
/packages/cran/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cran",
3 | "version": "0.1.0",
4 | "main": "cran.js",
5 | "packageDependencies": [
6 | "side-panel"
7 | ]
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/packages/download/download.R:
--------------------------------------------------------------------------------
1 |
2 | #------------------------------------------------------------------------------
3 | # download: add handler for download method "js", which uses built-in
4 | # support in electron
5 | #------------------------------------------------------------------------------
6 |
7 | (function(){
8 |
9 | override.binding <- function( name, func, ns, assign.in.namespace=T ){
10 | if( exists( name ) ){
11 | package <- paste0( "package:", ns );
12 | unlockBinding( name, as.environment(package));
13 | assign( name, func, as.environment(package));
14 | if( assign.in.namespace ){
15 | ns <- asNamespace( ns );
16 | if (bindingIsLocked(name, ns)) {
17 | unlockBinding(name, ns)
18 | assign(name, func, envir = ns, inherits = FALSE)
19 | w <- options("warn")
20 | on.exit(options(w))
21 | options(warn = -1)
22 | lockBinding(name, ns)
23 | }
24 | else assign(name, func, envir = ns, inherits = FALSE);
25 | }
26 | lockBinding( name, as.environment(package));
27 | }
28 | }
29 |
30 | download.file.original <- get( "download.file", envir=as.environment( "package:utils" ));
31 | override.binding( "download.file",
32 | function (url, destfile, method, quiet = FALSE, mode = "w", cacheOK = TRUE,
33 | extra = getOption("download.file.extra"))
34 | {
35 | # arglist <- as.list( match.call())[-1];
36 | # cat( paste( "DL", url, destfile, "\n" ));
37 |
38 | method <- if (missing(method))
39 | getOption("download.file.method", default = "auto")
40 | else match.arg(method, c("auto", "internal", "wininet", "libcurl",
41 | "wget", "curl", "lynx", "js"))
42 |
43 | if( method == "js" ){
44 | jsClientLib:::.js.client.callback.sync( list( arguments=as.list(environment()), command="download" ));
45 | }
46 | else {
47 | # do.call( download.file.original, arglist, envir=parent.env(environment()));
48 | do.call( download.file.original, as.list(environment()), envir=parent.env(environment()));
49 | }
50 | }, "utils", T );
51 |
52 | options( download.file.method="js" );
53 |
54 | })();
55 |
56 |
--------------------------------------------------------------------------------
/packages/download/download.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const PubSub = require( "pubsub-js" );
26 | const ipcRenderer = require('electron').ipcRenderer;
27 |
28 | /**
29 | * download a file, using electron utilities and
30 | * (optionally) adding a progress bar.
31 | */
32 | const download_file = function(core, opts){
33 |
34 | return new Promise( function( resolve, reject ){
35 |
36 | let progressbar = null;
37 |
38 | if( !opts.quiet ){
39 | PubSub.publish( core.Constants.SHELL_MESSAGE, [ `\n${core.Messages.TRYING_URL}: ${opts.url}\n` ]);
40 | progressbar = core.Packages.progress.createProgressBar({
41 | label: function(p){
42 | return ( p >= 100 ) ? core.Messages.DOWNLOAD_COMPLETE : `${core.Messages.DOWNLOADING}: ${p}%`;
43 | },
44 | min: 0, max: 1, value: 0, width: 30
45 | });
46 | PubSub.publish( core.Constants.SHELL_INSERT_NODE, [progressbar.node, true]);
47 | }
48 |
49 | ipcRenderer.on( 'download-progress', function( event, args ){
50 | if( progressbar ){
51 | if( progressbar.max() === 1 ) progressbar.max( args.total );
52 | progressbar.value( args.received );
53 | }
54 | });
55 |
56 | ipcRenderer.on( 'download-complete', function( event, args ){
57 |
58 | if( args.state !== "completed" ){
59 | PubSub.publish(Constants.SHELL_MESSAGE, [
60 | `\n${core.Messages.DOWNLOAD_FAILED}: ${args.state}\n`
61 | ]);
62 | }
63 |
64 | ipcRenderer.removeAllListeners( "download-complete" );
65 | ipcRenderer.removeAllListeners( "download-progress" );
66 |
67 | resolve( args.state === "completed" ? 0 : -1 );
68 |
69 | });
70 |
71 | ipcRenderer.send( "download", opts );
72 |
73 | });
74 |
75 | };
76 |
77 | module.exports = {
78 | init: function(core){
79 | core.Hooks.install( "sync-request-p", function(hook, req){
80 | let cmd = req.command ? req.command : req.$data ? req.$data.command : null;
81 | if( cmd === "download" ){
82 | return download_file( core, req.$data.arguments.$data );
83 | }
84 | return null;
85 | });
86 | }
87 | };
88 |
89 |
--------------------------------------------------------------------------------
/packages/download/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "download",
3 | "version": "0.1.0",
4 | "main": "download.js",
5 | "R": "download.R",
6 | "packageDependencies": [
7 | "progress"
8 | ]
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/packages/file-watcher/file-watcher.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const fs = require("fs");
26 | const PubSub = require( "pubsub-js" );
27 | const untildify = require( "untildify" );
28 | const Chokidar = require( "chokidar" );
29 |
30 | const DEBOUNCE_TIMEOUT = 250;
31 |
32 | const debounce_id = {};
33 |
34 | let listener = function( change_callback, original_path, filename ){
35 |
36 | // here's what happens on windows: there are two events fired on
37 | // file change. generally the file will be locked on the first
38 | // event, and unlocked on the second event. using a debounce
39 | // timer should ensure we get the second event and the file will
40 | // be unlocked.
41 |
42 | // I don't know what the expected or average wait is between events
43 | // 1 and 2. an alternative would be to check for locks (how? just
44 | // try to open for reading). also we can probably skip all this on
45 | // linux/osx.
46 |
47 | if( debounce_id[original_path] ) clearTimeout( debounce_id[original_path] );
48 | debounce_id[original_path] = setTimeout( function(){
49 | change_callback.call( null, filename, original_path );
50 | debounce_id[original_path] = 0;
51 | }, DEBOUNCE_TIMEOUT );
52 |
53 | };
54 |
55 | var internal_watches = {};
56 |
57 | var FileWatcher = function(){
58 |
59 | var watches = {};
60 | var changes = {};
61 | var instance = this;
62 |
63 | this.init = function(opts){
64 |
65 | if( opts.source ){
66 |
67 | opts.source.on( 'file.watch', function(data){
68 | //console.info( data );
69 | if( data.$data.command === "watch" ){
70 | //console.info( "watch", data.$data.path );
71 | data.$data.path = untildify( data.$data.path );
72 |
73 | if( watches[data.$data.path] ) watches[data.$data.path].close();
74 | watches[data.$data.path] = Chokidar.watch( data.$data.path );
75 | if( opts.change_callback )
76 | watches[data.$data.path].on( 'change', listener.bind( this, opts.change_callback, data.$data.path ));
77 | }
78 | else if( data.$data.command === "unwatch" ){
79 | if( watches[data.$data.path] ){
80 | watches[data.$data.path].close();
81 | delete( watches[data.$data.path] );
82 | }
83 | }
84 | else if( data.$data.command === "clear" ){
85 | Object.keys( watches ).map( function( key ){
86 | watches[key].close();
87 | })
88 | watches = {};
89 | }
90 | else if( data.$data.command === "reloading" ){
91 | if( opts.notify_callback ){
92 | var msg;
93 | if( data.$data.filename === data.$data.original_path )
94 | msg = `FileWatcher reloading ${data.$data.filename}\n` ;
95 | else msg = `FileWatcher reloading ${data.$data.filename} (${data.$data.original_path})\n` ;
96 | if( opts.notify_callback ) opts.notify_callback.call( this, msg );
97 | }
98 | }
99 | });
100 | }
101 |
102 | };
103 |
104 | }
105 |
106 | module.exports = {
107 |
108 | init: function( core ){
109 | new FileWatcher().init({
110 | source: core.R,
111 | change_callback: function( path, original_path ){
112 | //console.info( `filewatcher cb: ${path} (${original_path})`);
113 | var cmd = `.js.client.file.changed('${core.Utils.escape_backslashes(path, 2)}', '${core.Utils.escape_backslashes(original_path, 2)}')`;
114 | //console.info( cmd );
115 | core.R.queued_internal( cmd ); // FIXME: should use exec?
116 | },
117 | notify_callback: function(msg){
118 | PubSub.publish( core.Constants.SHELL_MESSAGE, [ msg, "shell-system-information" ]);
119 | }
120 | });
121 | return Promise.resolve();
122 | },
123 |
124 | /**
125 | * we have a separate file watcher for internal operations
126 | */
127 | watch_internal: function(opts){
128 | opts.path = untildify( opts.path );
129 | internal_watches[ opts.path ] = Chokidar.watch( opts.path );
130 | internal_watches[ opts.path ].on( 'change', listener.bind( null, opts.change, opts.path ));
131 | },
132 |
133 | unwatch_internal: function(opts){
134 | opts.path = untildify( opts.path );
135 | internal_watches[ opts.path ].close();
136 | delete internal_watches[ opts.path ];
137 | }
138 |
139 | };
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/packages/file-watcher/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "file-watcher",
3 | "version": "0.1.0",
4 | "main": "file-watcher.js"
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/packages/graphics/graphics-panel.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
28 |
50 |
51 |
52 |
57 |
58 |
59 |
60 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/packages/graphics/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "graphics",
3 | "version": "0.1.1",
4 | "main": "graphics-device.js",
5 | "htmlComponents": [
6 | "graphics-panel.html"
7 | ],
8 | "preferences": {
9 | "save.graphics.data": {
10 | "type": "boolean",
11 | "default": true,
12 | "label": "Save graphics data"
13 | }
14 | },
15 | "preferenceGroups": {
16 | "Graphics Target": {
17 | "graphics.target": {
18 | "type": "choice",
19 | "options": "get_graphics_targets",
20 | "label": "",
21 | "default": "inline"
22 | }
23 | },
24 | "Inline Graphics Size": {
25 | "inline.graphics.size": {
26 | "type": "size",
27 | "default": { "width": 600, "height": 400 },
28 | "min": { "width": 50, "height": 50 }
29 | }
30 | },
31 | "Graphics Font Mapping": {
32 | "font.map.sans": {
33 | "type": "input",
34 | "placeholder": "sans-serif",
35 | "default.win32": "Segoe UI",
36 | "default.darwin": "Helvetica Neue"
37 | },
38 | "font.map.serif": {
39 | "type": "input",
40 | "placeholder": "serif",
41 | "default.win32": "Palatino",
42 | "default.darwin": "Georgia"
43 | },
44 | "font.map.mono": {
45 | "type": "input",
46 | "placeholder": "monospace",
47 | "default.win32": "Consolas",
48 | "default.darwin": "Menlo"
49 | }
50 | }
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/packages/histogram-view/histogram-view.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const PubSub = require( "pubsub-js" );
26 |
27 | const electron = require('electron');
28 | const remote = electron.remote;
29 | const Menu = remote.Menu;
30 | const MenuItem = remote.MenuItem;
31 |
32 | var path = require( "path" );
33 |
34 | const createInstance = function(core, opts, src){
35 |
36 | let node = document.createElement("histogram-panel");
37 | node.data = src === "locals" ? opts.data.$data.histogram : opts.histogram;
38 |
39 | let token = 0;
40 | node.field = opts.name;
41 |
42 | let onclose = function(){
43 | PubSub.publish( core.Constants.SIDE_PANEL_REMOVE, node );
44 | };
45 |
46 | node.addEventListener( "close", onclose );
47 | node.onShow = function(){
48 |
49 | if( src === "locals" ){
50 | let name = opts.name;
51 | token = PubSub.subscribe( "locals", function(ch, locals){
52 | if( !locals.$data.fields.$data[name]
53 | || !locals.$data.fields.$data[name].$data
54 | || !locals.$data.fields.$data[name].$data.histogram ){
55 | node.data = null;
56 | return;
57 | }
58 | node.data = locals.$data.fields.$data[name].$data.histogram;
59 | });
60 | }
61 | else if( src === "watches" ){
62 | token = PubSub.subscribe( "watch", function(ch, watches){
63 | let key = opts.key;
64 | for( let i = 0; i< watches.length; i++ ){
65 | if( key === watches[i].key ){
66 | node.data = watches[i].histogram;
67 | return;
68 | }
69 | }
70 | node.data = null;
71 | });
72 | }
73 | };
74 |
75 | node.onHide = function(){
76 | if( token ){
77 | PubSub.unsubscribe( token );
78 | token = 0;
79 | }
80 | };
81 |
82 | node.onUnload = function(){
83 | if( token ){
84 | PubSub.unsubscribe( token );
85 | token = 0;
86 | }
87 | node.removeEventListener( "close", onclose );
88 | };
89 |
90 | return node;
91 |
92 | };
93 |
94 | module.exports = {
95 |
96 | init: function(core){
97 |
98 | core.Hooks.install( "locals_click", function( hook, opts ){
99 | // ...
100 | return false;
101 | });
102 |
103 | const show = function(node){
104 | let pos = core.Settings["histogram.panel.position"];
105 | if( !pos ) pos = core.Settings["details.panel.position"];
106 | if( !pos ) pos = { row: 3, column: 0 };
107 | PubSub.publish( core.Constants.SIDE_PANEL_ATTACH, { position: pos, node: node });
108 | };
109 |
110 | let menuitem = new MenuItem({
111 | label: "View histogram",
112 | click: function( menuitem ){
113 | show(createInstance( core, menuitem.menu_target, menuitem.menu_source ));
114 | }
115 | });
116 |
117 | core.Hooks.install( "locals_context_menu", function( hook, menu ){
118 | menuitem.menu_target = menu.target;
119 | menuitem.menu_source = "locals";
120 | menu.insert( 3, menuitem );
121 | menuitem.visible = !!menu.target.data.$data.histogram;
122 | });
123 |
124 | core.Hooks.install( "watches_context_menu", function( hook, menu ){
125 | menuitem.menu_target = menu.target;
126 | menuitem.menu_source = "watches";
127 | menu.insert( 3, menuitem );
128 | menuitem.visible = !!menu.target.histogram;
129 | });
130 |
131 | // (optionally) override default click on locals
132 | core.Hooks.install( "locals_click", function( hook, opts ){
133 |
134 | if( !core.Utils.array_cross_match( core.Settings["locals.default.view"], "histogram" )
135 | || !opts.data.$data.histogram ) return false;
136 |
137 | // we have to be well-behaved
138 | if( opts.handled ) return false;
139 | opts.handled = true;
140 |
141 | show( createInstance( core, opts, "locals" ));
142 |
143 | return true;
144 | });
145 |
146 | core.Hooks.install( "watches_click", function( hook, opts ){
147 |
148 | if( !core.Utils.array_cross_match( core.Settings["watches.default.view"], "histogram" )
149 | || !opts.histogram ) return false;
150 |
151 | // we have to be well-behaved
152 | if( opts.handled ) return false;
153 | opts.handled = true;
154 |
155 | show( createInstance( core, opts, "watches" ));
156 |
157 | return true;
158 | });
159 |
160 | }
161 |
162 | };
--------------------------------------------------------------------------------
/packages/histogram-view/histogram.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
63 |
66 |
67 |
76 |
77 |
78 |
79 |
80 |
103 |
104 |
105 |
166 |
167 |
168 |
169 |
170 |
173 |
174 |
178 |
179 |
180 |
205 |
206 |
207 |
--------------------------------------------------------------------------------
/packages/histogram-view/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "histogram-view",
3 | "version": "0.1.0",
4 | "main": "histogram-view.js",
5 | "packageDependencies": [
6 | "side-panel"
7 | ],
8 | "htmlComponents": [
9 | "histogram.html"
10 | ]
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/packages/html-dialog/dialog.html:
--------------------------------------------------------------------------------
1 |
24 |
25 |
26 |
27 |
100 |
101 |
102 |
103 |
111 |
112 |
113 |
114 |
122 |
123 |
124 |
125 |
192 |
--------------------------------------------------------------------------------
/packages/html-dialog/html-dialog.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const path = require( "path" );
26 | const PubSub = require( "pubsub-js" );
27 |
28 | /**
29 | * show a dialog and return result/cancel via promise
30 | *
31 | * fields title, cancel-button, cancel-text, accept-button,
32 | * accept-text are passed directly to dialog (there are defaults).
33 | *
34 | * validate() is a function that is called on accept events,
35 | * if validate returns false it prevents closing the dialog.
36 | *
37 | * content is added to the dialog, this should be an html node.
38 | * content will get removed the next time this function is called,
39 | * so manage that node if you need to.
40 | */
41 | function show_dialog(core, opts){
42 |
43 | opts = opts || {};
44 |
45 | var on_cancel = function(){
46 | dialog.removeEventListener( "cancel", on_cancel );
47 | dialog.removeEventListener( "accept", on_accept );
48 | dialog.show(false);
49 | setImmediate( function(){
50 | if( opts.complete ) opts.complete(false);
51 | });
52 | };
53 |
54 | var on_accept = function(){
55 | if( opts.validate && !opts.validate.call(dialog)) return;
56 | dialog.removeEventListener( "cancel", on_cancel );
57 | dialog.removeEventListener( "accept", on_accept );
58 | dialog.show(false);
59 | setImmediate( function(){
60 | if( opts.complete ) opts.complete(true);
61 | });
62 | };
63 |
64 | var dialog = document.getElementById( "dialog" );
65 | if( !dialog ){
66 | dialog = document.createElement( "html-dialog" );
67 | dialog.id = "dialog";
68 | dialog.addEventListener( "cancel", function(e){
69 | dialog.show(false);
70 | });
71 | dialog.show(false);
72 | document.body.appendChild( dialog );
73 | }
74 |
75 | dialog.addEventListener( "cancel", on_cancel );
76 | dialog.addEventListener( "accept", on_accept );
77 |
78 | // content
79 |
80 | dialog.clear();
81 | if( opts.content ) dialog.appendChild( opts.content );
82 |
83 | // set fields or defaults
84 |
85 | ['cancel-button', 'accept-button'].map( function( a ){
86 | if( typeof opts[a] !== "undefined" ) dialog[a] = opts[a];
87 | else dialog[a] = true;
88 | })
89 |
90 | dialog['cancel-text'] = opts['cancel-text'] || "Cancel";
91 | dialog['accept-text'] = opts['accept-text'] || "Accept";
92 |
93 | dialog.title = opts.title || "Dialog";
94 |
95 | // ok, show
96 |
97 | dialog.show();
98 |
99 | }
100 |
101 | module.exports = {
102 |
103 | init: function(core){
104 |
105 | let html = path.join( "packages", "html-dialog", "dialog.html" );
106 | core.Utils.install_html_component( html );
107 |
108 | Object.assign( core.Constants, {
109 | DIALOG_SHOW: "dialog-show"
110 | });
111 |
112 | PubSub.subscribe( core.Constants.DIALOG_SHOW, function( channel, opts ){
113 | show_dialog( core, opts );
114 | });
115 |
116 | }
117 |
118 | };
--------------------------------------------------------------------------------
/packages/html-dialog/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "html-dialog",
3 | "version": "0.1.0",
4 | "main": "html-dialog.js"
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/packages/pager/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "pager",
3 | "version": "0.1.0",
4 | "main": "pager.js"
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/packages/pager/pager.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const fs = require( "fs" );
26 | const PubSub = require( "pubsub-js" );
27 |
28 | var Pager = function(){
29 |
30 | this.init = function(opts){
31 |
32 | opts.source.on( 'pager', function(obj){
33 |
34 | var data = obj.$data;
35 |
36 | // can be one or many? use the loop
37 |
38 | if( typeof( data.files ) === "string" ){
39 | data.files = [data.files];
40 | data.title = [data.title];
41 | data['delete.file'] = [data['delete.file']];
42 | }
43 |
44 | if( opts.debug ) console.info( data );
45 |
46 | if( typeof( data.files ) === "object" ){
47 | for( var i = 0; i< data.files.length; i++ ){
48 |
49 | var title = null;
50 | if( data.title[i] && data.title[i].trim().length ){
51 | title = "\n" + data.title[i] + "\n" + Array( data.title[i].length + 1 ).join('=') + "\n";
52 | }
53 |
54 | if( data.files[i] ){
55 | fs.readFile( data.files[i], { encoding: "utf8" }, function(err, contents){
56 |
57 | if( title ) opts.text( title, "pager pager-title", true );
58 |
59 | if( contents && contents.length ){
60 | opts.text( "\n" + contents, "pager pager-text", true );
61 | }
62 |
63 | opts.text( "\n", undefined, true );
64 |
65 | if( data['delete.file'][i] ){
66 | console.info( "unlink", data.files[i] );
67 | fs.unlink( data.files[i] );
68 | }
69 | });
70 | }
71 |
72 | }
73 | }
74 |
75 | });
76 | };
77 |
78 | };
79 |
80 | module.exports = {
81 |
82 | init: function( core ){
83 | new Pager().init({
84 | source: core.R,
85 | text: function(){
86 | var args = [];
87 | for( var i = 0; i< arguments.length; i++ ) args[i] = arguments[i];
88 | PubSub.publish( core.Constants.SHELL_MESSAGE, args );
89 | }
90 | });
91 | return Promise.resolve();
92 | }
93 |
94 | };
95 |
--------------------------------------------------------------------------------
/packages/progress/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "progress",
3 | "version": "0.1.0",
4 | "main": "progress.js",
5 | "R": "progress.R"
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/packages/progress/progress.R:
--------------------------------------------------------------------------------
1 |
2 | (function(){
3 |
4 | #-----------------------------------------------------------------------------
5 | # replace win progress bar with an inline progress bar. there's a slight
6 | # difference in that these functions might not exist on linux.
7 | #-----------------------------------------------------------------------------
8 |
9 | override.binding <- function( name, func, ns, assign.in.namespace=T ){
10 | if( exists( name ) ){
11 | package <- paste0( "package:", ns );
12 | unlockBinding( name, as.environment(package));
13 | assign( name, func, as.environment(package));
14 | if( assign.in.namespace ){
15 | ns <- asNamespace( ns );
16 | if (bindingIsLocked(name, ns)) {
17 | unlockBinding(name, ns)
18 | assign(name, func, envir = ns, inherits = FALSE)
19 | w <- options("warn")
20 | on.exit(options(w))
21 | options(warn = -1)
22 | lockBinding(name, ns)
23 | }
24 | else assign(name, func, envir = ns, inherits = FALSE);
25 | }
26 | lockBinding( name, as.environment(package));
27 | }
28 | }
29 |
30 | override.binding( "winProgressBar", js.client.progress.bar, "utils");
31 | override.binding( "setWinProgressBar", js.client.set.progress.bar, "utils");
32 | override.binding( "getWinProgressBar", js.client.get.progress.bar, "utils");
33 |
34 | # optionally this one as well
35 |
36 | #override.binding( "txtProgressBar", js.client.progress.bar, "utils");
37 | #override.binding( "setTxtProgressBar", js.client.set.progress.bar, "utils");
38 | #override.binding( "getTxtProgressBar", js.client.get.progress.bar, "utils");
39 |
40 | })();
41 |
--------------------------------------------------------------------------------
/packages/progress/progress.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | const PubSub = require( "pubsub-js" );
26 |
27 | /**
28 | * progress bar(s)
29 | *
30 | * reimplemented as a singleton, which handles its own events.
31 | * we need to bind an event source potentially after construction,
32 | * so there's an init() method.
33 | */
34 | var ProgressBar = function(opts){
35 |
36 | opts = opts || { min: 0, max: 1, initial: 0, label: undefined };
37 | if( typeof opts.value === "undefined" ) opts.value = opts.initial || 0;
38 | opts.range = opts.max - opts.min;
39 |
40 | this.render = function(){
41 | var p = Math.round( 100 * ( opts.value - opts.min ) / opts.range );
42 | progress.setAttribute( 'style', `width: ${p}%` );
43 | if( opts.label ){
44 | if( typeof opts.label === "function" ){
45 | label.textContent = opts.label.call( this, p );
46 | }
47 | else {
48 | label.textContent = `${opts.label}: ${p}%`;
49 | }
50 | }
51 | else label.textContent = `${p}%`;
52 |
53 | }
54 |
55 | this.value = function (){
56 | if( arguments.length ){
57 | opts.value = arguments[0];
58 | this.render();
59 | }
60 | return opts.value;
61 | }
62 |
63 | this.max = function (){
64 | if( arguments.length ){
65 | opts.max = arguments[0];
66 | opts.range = opts.max - opts.min;
67 | this.render();
68 | }
69 | return opts.max;
70 | }
71 |
72 | this.min = function (){
73 | if( arguments.length ){
74 | opts.min = arguments[0];
75 | opts.range = opts.max - opts.min;
76 | this.render();
77 | }
78 | return opts.min;
79 | }
80 |
81 | this.label = function(){
82 | if( arguments.length ){
83 | opts.label = arguments[0];
84 | this.render();
85 | }
86 | return opts.label;
87 | }
88 |
89 | this.node = document.createElement( "div" );
90 | this.node.className = "progress-bar";
91 |
92 | var progress = document.createElement( "div" );
93 | progress.className = "progress-bar-progress";
94 | this.node.appendChild( progress );
95 |
96 | var label = document.createElement( "div" );
97 | label.className = "progress-bar-label";
98 | this.node.appendChild( label );
99 |
100 | if( opts.style ) this.node.setAttribute( 'style', data.$data.style );
101 | else if( opts.width ){
102 | var width = opts.width;
103 | if( typeof opts.width === "number" ) width = opts.width + "em";
104 | this.node.setAttribute( 'style', `width: ${width};` );
105 | }
106 |
107 | this.render();
108 |
109 | };
110 |
111 | var progress_bars = {};
112 |
113 | var handler = function(func, data){
114 |
115 | console.info( data );
116 | if( typeof data.$data.key === "undefined" ) return; // should be an error?
117 |
118 | var key = data.$data.key.toString();
119 | var ref = progress_bars[key];
120 |
121 | if( !ref && !data.$data.closed ){
122 | progress_bars[key] = new ProgressBar( data.$data );
123 | ref = progress_bars[key];
124 | if( func ) func.call( this, ref.node );
125 | }
126 | else if( ref ){
127 | ref.value(data.$data.value);
128 | if( data.$data.closed ){
129 | delete(progress_bars[key]);
130 | }
131 | }
132 |
133 | }
134 |
135 | module.exports = {
136 |
137 | init: function(core){
138 | core.R.on( 'progress.bar', handler.bind( this, function(node){
139 | //core.shell.insert_node( node, true );
140 | PubSub.publish( core.Constants.SHELL_INSERT_NODE, [node, true]);
141 | }));
142 | return Promise.resolve()
143 | },
144 |
145 | createProgressBar: function(opts){
146 | return new ProgressBar( opts );
147 | }
148 |
149 | };
--------------------------------------------------------------------------------
/packages/side-panel/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "side-panel",
3 | "version": "0.1.0",
4 | "main": "side-panel.js"
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/packages/table/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "table",
3 | "version": "0.4.4",
4 | "main": "table.js",
5 | "packageDependencies": [
6 | "side-panel"
7 | ]
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/packages/theme/help/bluesans.css:
--------------------------------------------------------------------------------
1 |
2 | /** this is a stylesheet injected into R help pages */
3 |
4 | html, body {
5 | background: #002040 !important;
6 | color: white !important;
7 | font-family: segoe ui;
8 | font-size: 15px !important;
9 | }
10 |
11 | body { margin: 1em; }
12 |
13 |
14 | table {
15 | background: inherit;
16 | }
17 |
18 | pre, code {
19 | background: #002280;
20 | font-family: courier new !important;
21 | font-size: 15px;
22 | padding-left: .25em;
23 | padding-right: .25em;
24 | }
25 |
26 | a {
27 | background: inherit !important;
28 | color: white !important;
29 | }
30 |
31 | h1, h2, h3, h4 {
32 | background: #002040 !important;
33 | font-family: inherit !important;
34 | color: white !important;
35 | font-weight: 600 !important;
36 | }
37 |
--------------------------------------------------------------------------------
/packages/theme/help/sans.css:
--------------------------------------------------------------------------------
1 |
2 | /** this is a stylesheet injected into R help pages */
3 |
4 | html, body {
5 | font-family: segoe ui;
6 | font-size: 15px !important;
7 | }
8 |
9 | body {
10 | margin: 2em;
11 | }
12 |
13 | h1, h2, h3, h4 {
14 | font-family: inherit !important;
15 | font-weight: 400 !important;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/theme/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "theme",
3 | "version": "0.1.0",
4 | "main": "theme.js",
5 | "preferenceGroups": {
6 | "Shell Theme": {
7 | "theme": {
8 | "type": "choice",
9 | "default": "default",
10 | "options": "get_shell_themes"
11 | }
12 | },
13 | "UI Theme": {
14 | "uitheme": {
15 | "type": "choice",
16 | "default": "default",
17 | "options": "get_ui_themes"
18 | }
19 | }
20 | }
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/packages/theme/shell/dark.css:
--------------------------------------------------------------------------------
1 | /****************************************************************/
2 | /* */
3 | /* Based on CodeMirror's mbo theme. From mbo.css: */
4 | /* */
5 | /* Based on mbonaci's Brackets mbo theme */
6 | /* https://github.com/mbonaci/global/blob/master/Mbo.tmTheme */
7 | /* Create your own: http://tmtheme-editor.herokuapp.com */
8 | /****************************************************************/
9 |
10 | .cm-s-dark.CodeMirror { background: #2c2c2c; color: #ffffec; }
11 | .cm-s-dark div.CodeMirror-selected { background: #716C62; }
12 | .cm-s-dark .CodeMirror-line::selection, .cm-s-dark .CodeMirror-line > span::selection, .cm-s-dark .CodeMirror-line > span > span::selection { background: rgba(113, 108, 98, .99); }
13 | .cm-s-dark .CodeMirror-line::-moz-selection, .cm-s-dark .CodeMirror-line > span::-moz-selection, .cm-s-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(113, 108, 98, .99); }
14 | .cm-s-dark .CodeMirror-gutters { background: #4e4e4e; border-right: 0px; }
15 | .cm-s-dark .CodeMirror-guttermarker { color: white; }
16 | .cm-s-dark .CodeMirror-guttermarker-subtle { color: grey; }
17 | .cm-s-dark .CodeMirror-linenumber { color: #dadada; }
18 | .cm-s-dark .CodeMirror-cursor { border-left: 1px solid #ffffec; }
19 |
20 | .cm-s-dark span.cm-comment { color: #95958a; }
21 | .cm-s-dark span.cm-atom { color: #00a8c6; }
22 | .cm-s-dark span.cm-number { color: #00a8c6; }
23 |
24 | .cm-s-dark span.cm-property, .cm-s-dark span.cm-attribute { color: #9ddfe9; }
25 | .cm-s-dark span.cm-keyword { color: #ffb928; }
26 | .cm-s-dark span.cm-string { color: #ffcf6c; }
27 | .cm-s-dark span.cm-string.cm-property { color: #ffffec; }
28 |
29 | .cm-s-dark span.cm-variable { color: #ffffec; }
30 | .cm-s-dark span.cm-variable-2 { color: #00a8c6; }
31 | .cm-s-dark span.cm-def { color: #ffffec; }
32 | .cm-s-dark span.cm-bracket { color: #fffffc; font-weight: bold; }
33 | .cm-s-dark span.cm-tag { color: #9ddfe9; }
34 | .cm-s-dark span.cm-link { color: #f54b07; }
35 | .cm-s-dark span.cm-error { border-bottom: #636363; color: #ffffec; }
36 | .cm-s-dark span.cm-qualifier { color: #ffffec; }
37 |
38 | .cm-s-dark .CodeMirror-activeline-background { background: #494b41; }
39 | .cm-s-dark .CodeMirror-matchingbracket { color: #ffb928 !important; }
40 | .cm-s-dark .CodeMirror-matchingtag { background: rgba(255, 255, 255, .37); }
41 |
42 | /* light spinner */
43 |
44 | .status-overlay { color: #fff; }
45 |
46 | /* error has a red background */
47 |
48 | .cm-s-dark .shell-error {
49 | background: rgba(192, 20, 0, .5 );
50 | color: white;
51 | }
52 |
53 | /* light scrollbars for dark content */
54 |
55 | #shell-container ::-webkit-scrollbar-track { background: rgba(255, 255, 255, 0.125); }
56 | #shell-container ::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.5); }
57 | .CodeMirror-scrollbar-filler { background: rgba(255, 255, 255, 0.125); }
58 |
59 | /* function tip styling */
60 |
61 | .cmjs-shell-function-tip {
62 | border: 1px solid #ccc;
63 | background: rgba( 255, 255, 240, 1 );
64 | box-shadow: 2px 3px 5px rgba(0,0,0,.2);
65 | color: #000;
66 | }
67 |
68 | /* progress bars */
69 |
70 | .progress-bar {
71 | background: #2c2c2c;
72 | border: 1px solid #636363;
73 | }
74 |
75 | .progress-bar-progress {
76 | background: #4e4e4e;
77 | }
78 |
79 | .progress-bar-label {
80 | color: #ffffec;
81 | }
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/packages/theme/shell/elegant.css:
--------------------------------------------------------------------------------
1 | .cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom { color: #762; }
2 | .cm-s-elegant span.cm-comment { color: #262; font-style: italic; line-height: 1em; }
3 | .cm-s-elegant span.cm-meta { color: #555; font-style: italic; line-height: 1em; }
4 | .cm-s-elegant span.cm-variable { color: black; }
5 | .cm-s-elegant span.cm-variable-2 { color: #b11; }
6 | .cm-s-elegant span.cm-qualifier { color: #555; }
7 | .cm-s-elegant span.cm-keyword { color: #730; }
8 | .cm-s-elegant span.cm-builtin { color: #30a; }
9 | .cm-s-elegant span.cm-link { color: #762; }
10 | .cm-s-elegant span.cm-error { background-color: #fdd; }
11 |
12 | .cm-s-elegant .CodeMirror-activeline-background { background: #e8f2ff; }
13 | .cm-s-elegant .CodeMirror-matchingbracket { outline:1px solid #ccc; background: #eee; color:black !important; }
14 |
--------------------------------------------------------------------------------
/packages/theme/shell/night.css:
--------------------------------------------------------------------------------
1 | /* Loosely based on the Midnight Textmate theme */
2 |
3 | .cm-s-night.CodeMirror { background: #0a001f; color: #f8f8f8; }
4 | .cm-s-night div.CodeMirror-selected { background: #447; }
5 | .cm-s-night .CodeMirror-line::selection, .cm-s-night .CodeMirror-line > span::selection, .cm-s-night .CodeMirror-line > span > span::selection { background: rgba(68, 68, 119, .99); }
6 | .cm-s-night .CodeMirror-line::-moz-selection, .cm-s-night .CodeMirror-line > span::-moz-selection, .cm-s-night .CodeMirror-line > span > span::-moz-selection { background: rgba(68, 68, 119, .99); }
7 | .cm-s-night .CodeMirror-gutters { background: #0a001f; border-right: 1px solid #aaa; }
8 | .cm-s-night .CodeMirror-guttermarker { color: white; }
9 | .cm-s-night .CodeMirror-guttermarker-subtle { color: #bbb; }
10 | .cm-s-night .CodeMirror-linenumber { color: #f8f8f8; }
11 | .cm-s-night .CodeMirror-cursor { border-left: 1px solid white; }
12 |
13 | .cm-s-night span.cm-comment { color: #6900a1; }
14 | .cm-s-night span.cm-atom { color: #845dc4; }
15 | .cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }
16 | .cm-s-night span.cm-keyword { color: #599eff; }
17 | .cm-s-night span.cm-string { color: #37f14a; }
18 | .cm-s-night span.cm-meta { color: #7678e2; }
19 | .cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }
20 | .cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; }
21 | .cm-s-night span.cm-bracket { color: #8da6ce; }
22 | .cm-s-night span.cm-comment { color: #6900a1; }
23 | .cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }
24 | .cm-s-night span.cm-link { color: #845dc4; }
25 | .cm-s-night span.cm-error { color: #9d1e15; }
26 |
27 | .cm-s-night .CodeMirror-activeline-background { background: #1C005A; }
28 | .cm-s-night .CodeMirror-matchingbracket { outline:1px solid grey; color:white !important; }
29 |
30 | #shell-container ::-webkit-scrollbar-track
31 | {
32 | background: rgba(255, 255, 255, 0.125);
33 | }
34 |
35 | #shell-container ::-webkit-scrollbar-thumb
36 | {
37 | background: rgba(255, 255, 255, 0.5);
38 | }
39 |
40 | .CodeMirror-scrollbar-filler {
41 | background: rgba(255, 255, 255, 0.125);
42 | }
43 |
--------------------------------------------------------------------------------
/packages/theme/shell/old-school.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | /**
4 | * no highlighting, just one color for user and one color for R.
5 | *
6 | * NOTE: using a google web font, so this requires network.
7 | * Cousine, by the great Steve Matteson, is metrically compatible
8 | * with Courier New. If that's useful to you.
9 | */
10 |
11 | @import "https://fonts.googleapis.com/css?family=Cousine:400";
12 |
13 | .cm-s-old-school.CodeMirror {
14 | font-family: "Cousine";
15 | font-weight: 400;
16 | font-size: 10.5pt;
17 | background: white;
18 | color:red;
19 | }
20 |
21 | .cm-s-old-school .shell-text,
22 | .cm-s-old-school .shell-system-information,
23 | .cm-s-old-school .shell-error {
24 | color: darkblue;
25 | }
26 |
27 | .cm-s-old-school .pager {
28 | color: black;
29 | }
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/packages/theme/shell/zenburn.css:
--------------------------------------------------------------------------------
1 | /**
2 | * "
3 | * Using Zenburn color palette from the Emacs Zenburn Theme
4 | * https://github.com/bbatsov/zenburn-emacs/blob/master/zenburn-theme.el
5 | *
6 | * Also using parts of https://github.com/xavi/coderay-lighttable-theme
7 | * "
8 | * From: https://github.com/wisenomad/zenburn-lighttable-theme/blob/master/zenburn.css
9 | */
10 |
11 | .cm-s-zenburn .CodeMirror-gutters { background: #3f3f3f !important; }
12 | .cm-s-zenburn .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { color: #999; }
13 | .cm-s-zenburn .CodeMirror-cursor { border-left: 1px solid white; }
14 | .cm-s-zenburn { background-color: #3f3f3f; color: #dcdccc; }
15 | .cm-s-zenburn span.cm-builtin { color: #dcdccc; font-weight: bold; }
16 | .cm-s-zenburn span.cm-comment { color: #7f9f7f; }
17 | .cm-s-zenburn span.cm-keyword { color: #f0dfaf; font-weight: bold; }
18 | .cm-s-zenburn span.cm-atom { color: #bfebbf; }
19 | .cm-s-zenburn span.cm-def { color: #dcdccc; }
20 | .cm-s-zenburn span.cm-variable { color: #dfaf8f; }
21 | .cm-s-zenburn span.cm-variable-2 { color: #dcdccc; }
22 | .cm-s-zenburn span.cm-string { color: #cc9393; }
23 | .cm-s-zenburn span.cm-string-2 { color: #cc9393; }
24 | .cm-s-zenburn span.cm-number { color: #dcdccc; }
25 | .cm-s-zenburn span.cm-tag { color: #93e0e3; }
26 | .cm-s-zenburn span.cm-property { color: #dfaf8f; }
27 | .cm-s-zenburn span.cm-attribute { color: #dfaf8f; }
28 | .cm-s-zenburn span.cm-qualifier { color: #7cb8bb; }
29 | .cm-s-zenburn span.cm-meta { color: #f0dfaf; }
30 | .cm-s-zenburn span.cm-header { color: #f0efd0; }
31 | .cm-s-zenburn span.cm-operator { color: #f0efd0; }
32 | .cm-s-zenburn span.CodeMirror-matchingbracket { box-sizing: border-box; background: transparent; border-bottom: 1px solid; }
33 | .cm-s-zenburn span.CodeMirror-nonmatchingbracket { border-bottom: 1px solid; background: none; }
34 | .cm-s-zenburn .CodeMirror-activeline { background: #000000; }
35 | .cm-s-zenburn .CodeMirror-activeline-background { background: #000000; }
36 | .cm-s-zenburn div.CodeMirror-selected { background: #545454; }
37 | .cm-s-zenburn .CodeMirror-focused div.CodeMirror-selected { background: #4f4f4f; }
38 |
39 | /** r shell overloads */
40 |
41 | .cm-s-zenburn span.shell-parse-error { color: #ff4f3f !important; }
42 | .cm-s-zenburn span.pager {
43 | color: white !important;
44 | font-weight: 400 ;
45 | }
46 |
47 | .cm-s-zenburn span.shell-piped-stream {
48 | color: white;
49 | font-weight: 400 ;
50 | }
51 |
52 | .cm-s-zenburn span.shell-prompt { color: #dcdccc; }
53 | .cm-s-zenburn span.shell-prompt-debug { color: #cc9393; }
54 |
55 |
56 | /** UI (FIXME: separate UI themes) */
57 |
58 | .progress-bar {
59 | background: #f0dfaf;
60 | }
61 |
62 | .progress-bar-progress {
63 | background: #dfaf8f;
64 | }
65 |
66 | .progress-bar-label {
67 | color: #3f3f3f;
68 | }
69 |
70 | .status-overlay { color: #fff; }
71 |
72 | #shell-container ::-webkit-scrollbar-track
73 | {
74 | background: rgba(255, 255, 255, 0.125);
75 | }
76 |
77 | #shell-container ::-webkit-scrollbar-thumb
78 | {
79 | background: rgba(255, 255, 255, 0.5);
80 | }
81 |
82 | .CodeMirror-scrollbar-filler {
83 | background: rgba(255, 255, 255, 0.125);
84 | }
85 |
86 |
--------------------------------------------------------------------------------
/packages/theme/theme.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | "use strict";
24 |
25 | // FIXME: we shouldn't be using filenames as identifiers. use
26 | // some sort of parameter in the file (minimally, make it optional)
27 | // OTOH, there's no real CSS support for anything like that...
28 |
29 | const fs = require( "fs" );
30 | const path = require( "path" );
31 | const PubSub = require( "pubsub-js" );
32 |
33 | var Theme = function(opts, core){
34 |
35 | opts = opts || {};
36 |
37 | this.dir = opts.dir || "./themes";
38 | this.default = opts.default || "default";
39 | this.themes = [ this.default ];
40 | this.key = opts.key || "theme";
41 | this.active = this.default;
42 |
43 | this.list_themes = function(){
44 | var instance = this;
45 | fs.readdir( this.dir, function(err, files){
46 | if( err ) console.info( "E", err );
47 | files.map( function( f ){
48 | instance.themes.push( f.replace( /\.css$/, "" ));
49 | });
50 | })
51 | };
52 |
53 | /** load from settings */
54 | this.load = function(){
55 |
56 | var theme = core.Settings[ this.key ] || this.default;
57 | // console.info( this.key, theme );
58 | this.load_theme( theme, false );
59 |
60 | };
61 |
62 | /** load explicitly (by name) */
63 | this.load_theme = function( theme, save ){
64 |
65 | // console.info( "load theme", theme );
66 | if( !theme ) theme = this.default;
67 |
68 | // save to settings
69 | if( save ) core.Settings[ this.key ] = theme ;
70 |
71 | // set field for lookups
72 | this.active = theme;
73 |
74 | // remove existing theme, if any
75 | var elts = document.querySelectorAll( `link[data-target=${this.key}]` );
76 | for( var i = 0; i< elts.length; i++ ){
77 | elts[i].parentElement.removeChild( elts[i] );
78 | }
79 |
80 | // default? don't add
81 | if( theme && theme === this.default ){
82 | // special
83 | if( this.key === "theme" ){
84 | //opts.shell.setOption( "theme", this.default );
85 | //opts.shell.refresh();
86 | PubSub.publish( core.Constants.SHELL_UPDATE_THEME, this.default);
87 | }
88 | this.active = "default";
89 | return;
90 | }
91 |
92 | if( !theme.match( /\.css$/ )) theme = theme + ".css";
93 |
94 | var elt = document.querySelector( "link[data-position=last]" );
95 | var n = document.createElement( "link" );
96 | n.setAttribute( "rel", "stylesheet" );
97 | n.setAttribute( "data-target", this.key );
98 | n.setAttribute( "href", path.join( this.dir, theme ) );
99 |
100 | if( elt ) elt.parentElement.insertBefore( n, elt );
101 | else document.head.appendChild( n );
102 |
103 | // special
104 | if( this.key === "theme" ){
105 | //opts.shell.setOption( "theme", theme.replace( /\.css$/, "" ));
106 | //opts.shell.refresh();
107 | PubSub.publish( core.Constants.SHELL_UPDATE_THEME, theme.replace( /\.css$/, "" ));
108 | }
109 |
110 | };
111 |
112 | };
113 |
114 | var shell_theme, ui_theme;
115 |
116 | module.exports = {
117 |
118 | init: function( core ){
119 |
120 | shell_theme = new Theme({
121 | default: "default",
122 | dir: path.join( __dirname, "shell" )
123 | }, core);
124 |
125 | ui_theme = new Theme({
126 | default: "default",
127 | key: "uitheme",
128 | dir: path.join( __dirname, "ui" ),
129 | }, core);
130 |
131 | PubSub.subscribe( core.Constants.SETTINGS_CHANGE, function(channel, obj){
132 | switch( obj.key ){
133 | case "theme":
134 | shell_theme.load_theme( obj.val, false );
135 | break;
136 | case "uitheme":
137 | ui_theme.load_theme( obj.val, false );
138 | break;
139 | };
140 | });
141 |
142 | shell_theme.list_themes();
143 | shell_theme.load();
144 |
145 | ui_theme.list_themes();
146 | ui_theme.load();
147 |
148 | return Promise.resolve();
149 | },
150 |
151 | get_shell_themes: function(){
152 | return shell_theme.themes;
153 | },
154 |
155 | get_ui_themes: function(){
156 | return ui_theme.themes;
157 | },
158 |
159 | };
--------------------------------------------------------------------------------
/packages/theme/ui/dark.css:
--------------------------------------------------------------------------------
1 |
2 | /* --- panel and lists ------------------------- */
3 |
4 | .panel {
5 | background: #222;
6 | color: #fff;
7 | }
8 |
9 | .panel input[type=text] {
10 | background: #eee;
11 | }
12 |
13 | .panel .list-entry {
14 | background: #444;
15 | }
16 |
17 | .panel .list-entry:nth-child(even) {
18 | background: #333;
19 | }
20 |
21 | .panel .list-header, panel-header a {
22 | color: rgb(255, 207, 108);
23 | }
24 |
25 | .panel .sub-header {
26 | color: #bbb;
27 | }
28 |
29 | .watch-content {
30 | background: #333;
31 | }
32 |
33 | .panel-content-header-temp {
34 | color: #fff;
35 | }
36 |
37 | /* --- scrollbars ------------------------------ */
38 |
39 | .panel ::-webkit-scrollbar-track
40 | {
41 | background: rgba(255, 255, 255, 0.125);
42 | }
43 |
44 | .panel ::-webkit-scrollbar-thumb
45 | {
46 | background: rgba(255, 255, 255, 0.5);
47 | }
48 |
49 | .panel ::-webkit-scrollbar-corner, {
50 | background: rgba(255, 255, 255, 0.125);
51 | }
52 |
53 |
54 | /* --- buttons --------------------------------- */
55 |
56 | panel-header button {
57 | border: 1px solid #222;
58 | color: #888;
59 | }
60 |
61 | panel-header button:hover {
62 | color: #aaa;
63 | background: #444;
64 | }
65 |
66 |
67 | /*
68 | button.close-panel.panel-close-box {
69 | border: 1px solid #222;
70 | color: #888;
71 | }
72 |
73 | button.close-panel.panel-close-box:hover {
74 | color: #aaa;
75 | background: #444;
76 | }
77 | button.close-panel.panel-close-box:active {
78 | }
79 | */
80 |
81 | .panel-footer {
82 | border-top: 1px solid #666;
83 | }
84 |
85 | .panel-footer button {
86 | background: #333;
87 | color: #fff;
88 | border-color: #555;
89 | }
90 |
91 | .panel-footer button:hover {
92 | background: #222;
93 | }
94 |
95 | .panel-footer button:active {
96 | background: #444;
97 | }
98 |
99 | /* --- checkbox and radio ---------------------- */
100 |
101 | input[type=checkbox], input[type=radio] {
102 | color: rgb(0, 168, 198);
103 | }
104 |
105 | input[type=checkbox]:disabled, input[type=radio]:disabled {
106 | color: rgb(0, 168, 198);
107 | }
108 |
109 | /* --- inputs ---------------------------------- */
110 |
111 | input[type=text]{
112 | background: rgba( 255,255,255,.125 ) !important;
113 | border-color: #333;
114 | color: #fff;
115 | }
116 |
117 | /* --- dialog ---------------------------------- */
118 |
119 | html-dialog .dialog-chrome {
120 | background: #333;
121 | border: 1px solid #666;
122 | color: #fff;
123 | }
124 |
125 | html-dialog input, html-dialog select {
126 | background: #eee;
127 | }
128 |
129 | html-dialog .dialog-header {
130 | color: rgb(255, 207, 108);
131 | border-bottom: 1px solid #666;
132 | }
133 |
134 | html-dialog .dialog-header button {
135 | border: 1px solid #222;
136 | color: #888;
137 | }
138 |
139 | html-dialog .dialog-header button:hover {
140 | color: #aaa;
141 | background: #444;
142 | }
143 |
144 | html-dialog .dialog-footer {
145 | border-top: 1px solid #666;
146 | }
147 |
148 | html-dialog .dialog-footer button {
149 | background: #333;
150 | color: #fff;
151 | border-color: #555;
152 | }
153 |
154 | html-dialog .dialog-footer button:hover {
155 | background: #222;
156 | }
157 |
158 | html-dialog .dialog-footer button:active {
159 | background: #444;
160 | }
161 |
162 | /* --- grid (needs work) ---*/
163 |
164 | display-grid {
165 | background: #222;
166 | }
167 |
168 | display-grid grid-row {
169 | background: #666;
170 | }
171 |
172 | display-grid .grid-cell {
173 | background: #444;
174 | }
175 |
176 | display-grid .grid-header {
177 | background: #333;
178 | }
179 |
180 | display-grid .grid-cell.selected {
181 | border-color: rgba(0,0,0,0);
182 | background: #888;
183 | }
184 |
185 | display-grid .grid-header.selected {
186 | border-color: rgba( 0,0,0,0 );
187 | background: #eee;
188 | color: #000;
189 | }
190 |
191 | /* alternate rows, except for selection */
192 | display-grid grid-row:nth-child(odd) .table-cell:not(.selected) {
193 | background: #333;
194 | }
195 |
--------------------------------------------------------------------------------
/postcss/button.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | button {
24 |
25 | font-family: inherit;
26 | font-size: inherit;
27 | padding: 3px;
28 |
29 | display: inline-block;
30 | padding: 6px 12px;
31 | margin-bottom: 0;
32 |
33 | font-weight: 400;
34 | line-height: 1.42857143; /* FIXME: this is calculated, but from what? check bs */
35 | text-align: center;
36 | white-space: nowrap;
37 | vertical-align: middle;
38 |
39 | cursor: pointer;
40 | -webkit-user-select: none;
41 | user-select: none;
42 | background: none;
43 | border-radius: 3px;
44 | background: #fff;
45 |
46 | }
47 |
48 | /** these are perhaps theme-specific? */
49 |
50 | button {
51 | border: 1px solid #ccc;
52 | }
53 |
54 | button:hover {
55 | background: #eee;
56 | }
57 |
58 | button:active {
59 | border: 1px solid #999;
60 | background: #ccc;
61 | }
62 |
--------------------------------------------------------------------------------
/postcss/checkbox2.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | input[type=radio], input[type=checkbox] {
24 |
25 | color: #444;
26 | font-family: 'Material Icons';
27 | font-weight: normal;
28 | font-style: normal;
29 | text-transform: none;
30 | letter-spacing: normal;
31 | font-size: 22px;
32 | word-wrap: normal;
33 | white-space: nowrap;
34 | direction: ltr;
35 | text-rendering: optimizeLegibility; /* ligatures */
36 | -webkit-font-smoothing: antialiased;
37 | -webkit-appearance: none;
38 |
39 | /* put in container or style */
40 | padding: 0;
41 | margin: 0;
42 |
43 | }
44 |
45 | input[type=radio]:disabled, input[type=checkbox]:disabled {
46 | color: #888;
47 | }
48 |
49 | input[type=radio]:disabled::before, input[type=checkbox]:disabled::before {
50 | opacity: .5;
51 | }
52 |
53 | input[type=checkbox]::before {
54 | content: 'check_box_outline_blank';
55 | }
56 |
57 | input[type=checkbox]:checked::before {
58 | content: 'check_box';
59 | }
60 |
61 | input[type=radio] {
62 | font-size: 18px;
63 | }
64 |
65 | input[type=radio]::before {
66 | content: 'radio_button_unchecked';
67 | }
68 |
69 | input[type=radio]:checked::before {
70 | content: 'radio_button_checked';
71 | }
72 |
73 | input[type=checkbox]:focus::before, input[type=radio]:focus::before,
74 | input[type=checkbox]:focus, input[type=radio]:focus {
75 | outline: none;
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/postcss/chooser.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | .chooser-list-footer {
24 | padding: 1em;
25 | flex-grow: 0;
26 | position: relative;
27 | }
28 |
29 | .chooser-list-footer {
30 | border-top: 1px solid #666;
31 | }
32 |
33 |
34 | /* ------------------------- */
35 |
36 | .chooser-list2 {
37 | height: 100%;
38 | width: 100%;
39 | overflow: auto;
40 | }
41 |
42 | .watch-text {
43 | height: 100%;
44 | position: absolute;
45 | padding: 0 1em 0 1em;
46 | cursor: default;
47 | }
48 |
49 | .watch-content {
50 | background: #fff;
51 | padding: 1em;
52 | line-height: 1.4;
53 | margin: 0;
54 | overflow-y: auto;
55 | overflow-x: hidden;
56 | text-overflow: ellipsis;
57 | }
58 |
59 | /* --- layout specific to mirror chooser ----------------- */
60 |
61 | mirror-list-entry > div {
62 | padding: 10px 6px 10px 12px;
63 | }
64 |
65 | mirror-list-entry > div:nth-child(2) {
66 | padding-left: 0;
67 | }
68 |
69 | .mirror-list-host {
70 | font-size: .9em;
71 | text-overflow: ellipsis;
72 | }
73 |
74 | /* --- layout specific to package chooser ----------------- */
75 |
76 | package-list-entry {
77 | padding: 0;
78 | white-space: nowrap;
79 | padding-left: .5em;
80 | position: relative;
81 | display: flex;
82 | align-items: center;
83 | }
84 |
85 | package-list-entry[installed] div {
86 | opacity: .5;
87 | }
88 |
89 | package-list-entry > div {
90 | position: relative;
91 | cursor: default;
92 | line-height: 1;
93 | padding: 12px 4px 12px 8px;
94 | }
95 |
96 | .cran_link, .cran_link:visited, .cran_link:active {
97 | text-decoration: none;
98 | color: inherit;
99 | }
100 |
101 | /* --- layout specific to preferences ----------------- */
102 |
103 | preferences-option, preferences-size, preferences-input, preferences-select {
104 | padding: 0;
105 | white-space: nowrap;
106 | padding-left: .5em;
107 | position: relative;
108 | display: flex;
109 | align-items: center;
110 | }
111 |
112 | preferences-option > div, preferences-size > div, preferences-input > div, preferences-select > div {
113 | position: relative;
114 | cursor: default;
115 | line-height: 1;
116 | padding: 12px 4px 12px 8px;
117 | }
118 |
119 | a.browse {
120 | cursor: pointer;
121 | line-height: 1;
122 | padding: 12px 4px 12px 8px;
123 | }
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/postcss/dialog.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | html-dialog .dialog-chrome {
24 | background: #fff;
25 | border: 1px solid #666;
26 | }
27 |
28 | html-dialog .dialog-header {
29 | border-bottom: 1px solid #ddd;
30 | }
31 |
32 | html-dialog .dialog-header button {
33 | border: 1px solid rgb(248,248,248);
34 | color: #aaa;
35 | }
36 |
37 | html-dialog .dialog-header button:hover {
38 | color: #fff;
39 | background: #ddd ;
40 | }
41 |
42 | html-dialog .dialog-header button:active {
43 | border: 1px solid #666;
44 | }
45 |
46 | html-dialog .dialog-footer {
47 | text-align: center;
48 | border-top: 1px solid #ddd;
49 | }
50 |
--------------------------------------------------------------------------------
/postcss/grid.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | display-grid {
24 | background: #ccc;
25 | }
26 |
27 | #grid-column-header {
28 | background: #ccc;
29 | }
30 |
31 | #grid-row-header-background {
32 | background: #ccc;
33 | }
34 |
35 | .grid-header {
36 | background: #eee;
37 | text-align: center;
38 | cursor: text;
39 | }
40 |
41 | .table-cell {
42 | background: #fff;
43 | cursor: cell;
44 | text-align: right;
45 | }
46 |
47 | .table-cell-date, .table-cell-factor, .table-cell-string {
48 | text-align: center;
49 | }
50 |
51 | .table-cell-left {
52 | text-align: left;
53 | }
54 |
55 | .table-cell.fixed-width {
56 | font-size: 1em;
57 | }
58 |
59 | /* alternate rows, except for selection */
60 | grid-row:nth-child(odd) .table-cell:not(.selected) {
61 | /* background: #ecf9fe; */
62 | background: rgb(235, 240, 250);
63 | }
64 |
65 | /* === selections =============================== */
66 |
67 | .table-cell.selected {
68 | background: lightyellow;
69 | }
70 |
71 | .grid-header.selected {
72 | background: yellow;
73 | }
74 |
75 | .selection-border {
76 | background: yellow;
77 | min-width: 3px;
78 | min-height: 3px;
79 | }
80 |
81 | /* === sort arrows ============================== */
82 |
83 | .header-column-sort-ascending::after {
84 | content: "▲";
85 | position: absolute;
86 | opacity: .4;
87 | font-size: .6em;
88 | padding-left: .6em;
89 | padding-top: .4em;
90 | }
91 |
92 | .header-column-sort-descending::after {
93 | content: "▼";
94 | position: absolute;
95 | opacity: .4;
96 | font-size: .6em;
97 | padding-left: .6em;
98 | padding-top: .4em;
99 | }
--------------------------------------------------------------------------------
/postcss/history.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | .history-gutter {
24 | width: 1em;
25 | }
26 |
27 | .history-gutter-marker {
28 |
29 | }
30 |
31 | .history-gutter-marker:after {
32 | content: "x";
33 | margin-left: .5em;
34 | }
35 |
36 | .history-scrollbar-annotation {
37 | background: rgba( 160, 200, 255, .75 );
38 | }
39 |
40 | .history-marker-background {
41 | background: rgba( 160, 200, 255, .25 );
42 | }
43 |
44 |
--------------------------------------------------------------------------------
/postcss/locals.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | #locals-panel .grid-cell, #watches-panel .grid-cell {
24 | padding: .6em;
25 | cursor: default;
26 | }
27 |
28 | #locals-panel .grid-cell.selected, #watches-panel .grid-cell.selected {
29 | background: #3399ff;
30 | color: #fff;
31 | }
32 |
33 | #locals-panel .selection-border, #watches-panel .selection-border {
34 | background-color: rgba( 0, 0, 0, 0 );
35 | }
36 |
37 |
38 | /*
39 | #locals-panel grid-row:nth-child(odd) .table-cell:not(.selected) {
40 | background: #fff;
41 | }
42 | */
43 |
--------------------------------------------------------------------------------
/postcss/main.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | @import "checkbox2.css";
24 | @import "chooser.css";
25 | @import "panel.css";
26 |
27 | @import "textbox.css";
28 | @import "progress.css";
29 | @import "button.css";
30 | @import "dialog.css";
31 |
32 | @import "scrollbars.css";
33 | @import "grid.css";
34 | @import "locals.css";
35 | @import "history.css";
36 |
37 | html, body {
38 | font-family: "Segoe UI", "Open Sans";
39 | font-size: 10pt;
40 | padding: 0px;
41 | margin: 0px;
42 | overflow: hidden;
43 | height: 100%;
44 | width: 100%;
45 | }
46 |
47 | body {
48 | top: 0px;
49 | bottom: 0px;
50 | position: absolute;
51 | }
52 |
53 | body.osx {
54 | font-family: "Helvetica Neue";
55 | }
56 |
57 | body.linux {
58 | font-family: "Liberation Sans";
59 | font-size: 11pt;
60 | }
61 |
62 | body.ubuntu {
63 | font-family: "Ubuntu";
64 | font-size: 11pt;
65 | }
66 |
67 | #shell-layout {
68 | margin: 0;
69 | top: 0px;
70 | bottom: 0px;
71 | left: 0px;
72 | right: 0px;
73 | position: absolute;
74 | opacity: 0;
75 | }
76 |
77 | .CodeMirror, .fixed-width {
78 | font-family: consolas, monospace;
79 | font-size: 14.25px;
80 | height: 100%;
81 | }
82 |
83 | .linux .CodeMirror, .linux .fixed-width {
84 | font-family: "Liberation Mono";
85 | }
86 |
87 | .ubuntu .CodeMirror, .ubuntu .fixed-width {
88 | font-family: "Ubuntu Mono";
89 | font-size: 13pt;
90 | }
91 |
92 | .osx .CodeMirror, .osx .fixed-width {
93 | font-family: "Menlo";
94 | font-size: 13px;
95 | }
96 |
97 | .cm-s-default span.shell-parse-error { color: #ff4f3f !important; }
98 | .cm-s-default span.pager { color: #000 !important; }
99 | .cm-s-default span.shell-piped-stream { color: #000 !important; }
100 | .cm-s-default span.shell-prompt-debug { color: red; }
101 |
102 | /** I just don't like the default */
103 | .cm-s-default .CodeMirror-matchingbracket { outline:1px solid #ccc; background: #ddd; color:black !important; }
104 |
105 | #shell-container {
106 | position: absolute;
107 | width: 100%;
108 | height: 100%;
109 | }
110 |
111 | /*
112 | .shell-parse-error {
113 | color: red !important;
114 | }
115 | */
116 |
117 | .CodeMirror-hints {
118 |
119 | letter-spacing: .25px;
120 | /* font-weight: bold; */
121 |
122 | font-family: consolas, "helvetica neue", monospace;
123 | font-size: 11px;
124 |
125 | }
126 |
127 | /* --- status icon --------------------------------------------------- */
128 |
129 | .status-icon {
130 | width: 1em;
131 | height: 1em;
132 | opacity: 0;
133 | transition: opacity .2s ease-in-out;
134 | border: 0;
135 | margin: 0;
136 | padding: 0;
137 | line-height: 0;
138 | font-size: 40px;
139 | }
140 |
141 | .status-overlay {
142 | color: black;
143 | }
144 |
145 | .status-icon.busy {
146 | opacity: .45;
147 | animation: spin 1.8s linear infinite;
148 | }
149 |
150 | @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
151 |
152 | .overlay-bottom-right {
153 | position: absolute;
154 | bottom: 4px;
155 | right: 4px;
156 | z-index: 9999;
157 | }
158 |
159 | .overlay-bottom-right.scrollbar-offset-x {
160 | right: 13px; /* fixme: depends on scrollbar, so use a variable */
161 | }
162 |
163 | .overlay-bottom-right.scrollbar-offset-y {
164 | bottom: 13px; /* fixme: depends on scrollbar, so use a variable */
165 | }
166 |
167 | /* --- toolbar ------------------------------------------------------ */
168 |
169 | .toolbar {
170 | border-bottom: 1px solid #ccc;
171 | overflow-x: hidden;
172 | display: flex;
173 | flex-direction: row;
174 | padding: 2px;
175 | }
176 |
177 | .toolbar-btn:focus, .toolbar-btn:active {
178 | outline: 0;
179 | }
180 |
181 | .toolbar-btn:active {
182 | background-color: #bbb;
183 | outline: 0;
184 | border: 0;
185 | }
186 |
187 | .toolbar-btn:hover {
188 | background-color: #eee;
189 | cursor: pointer;
190 | }
191 |
192 | .toolbar-btn {
193 | background: none;
194 | border: 0;
195 | background-color: #fff;
196 | color: #666;
197 | padding: 0;
198 | margin: 2px;
199 | padding: 0;
200 | outline: 0;
201 | border-radius: 3px;
202 | }
203 |
204 | .toolbar-btn i {
205 | margin: 2px;
206 | padding: 0;
207 | outline: 0;
208 | display: block;
209 | }
210 |
211 | .toolbar-btn.pull-right {
212 | margin-left: auto; /* hooray! */
213 | }
214 |
215 | .btn:disabled {
216 | color: #bbb;
217 | }
218 |
219 | .btn:disabled:hover {
220 | background:none;
221 | cursor: default;
222 | }
223 |
224 | .toolbar #find-in-browser {
225 | position: relative;
226 | margin: 2px;
227 | margin-top: 4px;
228 | background: inherit;
229 | margin-left: 1em;
230 | }
231 |
232 | /* --- for inline --- */
233 |
234 | .CodeMirror .graphics-device {
235 | margin: 1em 1em 1em 1em;
236 | border: 1px solid #ccc;
237 | width: 576px;
238 | height: 360px;
239 | }
240 |
241 |
242 | /* --- for panel --- */
243 | graphics-panel .graphics-device {
244 | padding: 0;
245 | margin: 0;
246 | }
247 |
248 | .panel-graphics-container {
249 | top: 0px;
250 | left: 0px;
251 | right: 0px;
252 | bottom: 0px;
253 | position: absolute;
254 | }
255 |
256 | /* --- orphans is a block for stuff that's laid out but not displayed --- */
257 |
258 | #orphans {
259 |
260 | display: none;
261 | position: absolute;
262 | top: 0px;
263 | left: 0px;
264 |
265 | }
266 |
267 | /* --- function tips ---------------------------------------------------- */
268 |
269 | .cmjs-shell-function-tip-container {
270 | position: absolute;
271 | z-index: 15;
272 | overflow: visible;
273 | opacity: 0;
274 | transition: opacity .1s;
275 | }
276 |
277 | .cmjs-shell-function-tip {
278 | position: relative;
279 | transform: translateY(-100%);
280 | bottom: .5em;
281 | left: -1em;
282 | border: 1px solid #ccc;
283 | background: rgba( 255, 255, 200, .9 );
284 | padding: 2px;
285 | border-radius: 2px;
286 | box-shadow: 2px 3px 5px rgba(0,0,0,.2);
287 | }
288 |
289 | .cmjs-shell-function-tip-container.visible {
290 | opacity: 1;
291 | }
292 |
--------------------------------------------------------------------------------
/postcss/panel.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | .panel {
24 |
25 | display: flex;
26 | flex-direction: column;
27 | height: 100%;
28 | width: 100%;
29 | position: absolute;
30 | background: rgb(248,248,248);
31 | color: #222;
32 |
33 | }
34 |
35 | .panel-header {
36 | color: rgb(255, 207, 108);
37 | }
38 |
39 | .panel .sub-header {
40 | color: #999;
41 | }
42 |
43 | panel-header {
44 | border-bottom: 1px dotted #ccc;
45 | order: 0;
46 | }
47 |
48 | panel-header button {
49 | height: 2em;
50 | width: 2em;
51 | margin: 10px 10px 10px 0px;
52 | padding: 0 0;
53 | background: inherit;
54 | text-align: right;
55 | overflow: hidden;
56 | border: 1px solid rgb(248,248,248);
57 | color: #aaa;
58 | }
59 |
60 | panel-header a {
61 | color: #aaa;
62 | }
63 |
64 | panel-header button:hover {
65 | color: #fff;
66 | background: #ddd ;
67 | }
68 | panel-header button:active {
69 | border: 1px solid #666;
70 | }
71 |
72 | .panel-body {
73 | flex-grow: 1;
74 | order: 1;
75 | position: relative;
76 | display: flex;
77 | flex-direction: column;
78 | }
79 |
80 | .panel-footer {
81 | border-top: 1px solid #ccc;
82 | order: 2;
83 | }
84 |
85 | .panel-footer button {
86 | margin: .75em .3em .75em .3em;
87 | background: #fff;
88 | color: #333;
89 | border-color: #aaa;
90 | }
91 |
92 | .panel-footer button:hover {
93 | background: #eee;
94 | }
95 |
96 | .panel-footer button:active {
97 | background: #ddd;
98 | }
99 |
100 | .panel-footer.centered {
101 | text-align: center;
102 | }
103 |
104 | /** flex patch */
105 | .panel-container {
106 | position: absolute;
107 | height: 100%;
108 | width: 100%;
109 | overflow: hidden;
110 | }
111 |
112 | .panel-content-header {
113 | padding: .5em;
114 | }
115 |
116 | .panel-content-header-temp {
117 | padding: .5em;
118 | text-align: center;
119 | color: #444;
120 | }
121 |
122 | .panel-container2 {
123 | flex-grow: 1;
124 | position: relative;
125 | }
126 |
127 | .panel input[type=text] {
128 | background: #fff;
129 | }
130 |
131 | input[type=text].value-invalid {
132 | background: #f58c56;
133 | }
134 |
135 | .panel .list-entry {
136 | background: rgb( 235, 240, 250 );
137 | }
138 |
139 | .panel .list-entry:nth-child(even) {
140 | background: #fff;
141 | }
142 |
143 | .panel .list-header {
144 | color: #666;
145 | display: flex;
146 | padding: 1em;
147 | flex-grow: 0;
148 | position: relative;
149 | }
150 |
--------------------------------------------------------------------------------
/postcss/progress.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | .progress-bar {
24 | position: relative;
25 | box-sizing: border-box;
26 | width: 80%;
27 | height: 1.4em;
28 | background: #fff;
29 | margin: 1em;
30 | border-radius: 2px;
31 | overflow: hidden;
32 | border: 1px solid #ccc;
33 | }
34 |
35 | .progress-bar-progress {
36 | position: absolute;
37 | box-sizing: border-box;
38 | top: 0px;
39 | left: 0px;
40 | height: 100%;
41 | z-index: 1;
42 | background: #eef;
43 | padding: 0;
44 | margin: 0;
45 | }
46 |
47 | .progress-bar-label {
48 | position: absolute;
49 | box-sizing: border-box;
50 | top: 50%;
51 | left: 0px;
52 | width: 100%;
53 | text-align: center;
54 | z-index: 2;
55 | color: #333;
56 | padding: 0;
57 | margin: 0;
58 | -webkit-transform: translateY(-50%);
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/postcss/scrollbars.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | /* default scrollbars */
24 |
25 | ::-webkit-scrollbar
26 | {
27 | width: 12px;
28 | height: 12px;
29 | }
30 |
31 | ::-webkit-scrollbar-track
32 | {
33 | background: rgba(0, 0, 0, 0.125);
34 | }
35 |
36 | ::-webkit-scrollbar-thumb
37 | {
38 | background: rgba(0, 0, 0, 0.125);
39 |
40 | /*
41 | * does not work.
42 | * see https://bugs.webkit.org/show_bug.cgi?id=104412
43 | */
44 | transition: background-color 1s;
45 | }
46 |
47 | ::-webkit-scrollbar-thumb:hover
48 | {
49 | background: rgba(0, 0, 0, 0.25);
50 | }
51 |
52 | ::-webkit-scrollbar-corner,
53 | .CodeMirror-scrollbar-filler {
54 | background: rgba(0, 0, 0, 0.125);
55 | }
56 |
--------------------------------------------------------------------------------
/postcss/textbox.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016 Structured Data, LLC
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to
6 | * deal in the Software without restriction, including without limitation the
7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 | * sell copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 | * IN THE SOFTWARE.
21 | */
22 |
23 | input[type=text] {
24 | font-size: inherit;
25 | border-radius: 3px;
26 | border: 1px solid #ccc;
27 | height: 1.5em;
28 | padding-left: .35em;
29 | }
30 |
31 | select {
32 | font-size: inherit;
33 | border-radius: 3px;
34 | border: 1px solid #ccc;
35 | height: 1.8em;
36 | padding-left: .35em;
37 | }
38 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var webpack = require("webpack");
2 | var fs = require( "fs" );
3 | var ext = fs.readdirSync('node_modules').filter( function( name ){
4 | return( !name.match( /^\./ ));
5 | });
6 |
7 | module.exports = {
8 | entry: "./core/renderer.js",
9 | output: {
10 | filename: "./app/core.js"
11 | },
12 | externals: [
13 | function( context, request, callback ){
14 | if(/^[a-z\-0-9]+$/.test(request) || /plugin/.test(request) || /packages/.test(request)){
15 | return callback( null, "require('" + request + "');" );
16 | }
17 | callback();
18 | }
19 | ],
20 | target: 'node',
21 | devtool: 'inline-source-map',
22 | plugins: [
23 | // new webpack.optimize.UglifyJsPlugin({})
24 | ],
25 | node: {
26 | __dirname: false
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------