├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── bin
├── README.md
├── ace_src_files.txt
├── ice-appcache.sh
├── ice-compile.sh
└── ice-de-symlink-packages.sh
├── ice_code_editor.png
├── lib
├── css
│ └── ice.css
├── editor.dart
├── fonts
│ └── inconsolata.woff
├── full.dart
├── full
│ ├── copy_dialog.dart
│ ├── default_project.dart
│ ├── dialog.dart
│ ├── download_dialog.dart
│ ├── export_dialog.dart
│ ├── help_action.dart
│ ├── image_list_dialog.dart
│ ├── image_upload_dialog.dart
│ ├── import_dialog.dart
│ ├── menu_action.dart
│ ├── menu_item.dart
│ ├── new_project_dialog.dart
│ ├── notify.dart
│ ├── open_dialog.dart
│ ├── remove_dialog.dart
│ ├── rename_dialog.dart
│ ├── save_action.dart
│ ├── share_dialog.dart
│ ├── snapshotter.dart
│ ├── templates.dart
│ ├── validate.dart
│ └── whats_new_action.dart
├── gzip.dart
├── html
│ ├── code-OFFLINE.html
│ └── code.html
├── ice.dart
├── images
│ └── clipboard.png
├── js
│ ├── ace
│ │ └── worker-javascript.js
│ └── deflate
│ │ ├── rawdeflate.js
│ │ └── rawinflate.js
├── polymer
│ ├── ice_code_editor.html
│ └── ice_code_editor_element.dart
├── settings.dart
└── store.dart
├── pubspec.yaml
├── test
├── editor_test.dart
├── embed.html
├── full
│ ├── copy_dialog_test.dart
│ ├── download_test.dart
│ ├── export_test.dart
│ ├── hide_button_test.dart
│ ├── image_upload_test.dart
│ ├── import_test.dart
│ ├── keyboard_shortcuts_test.dart
│ ├── new_project_dialog_test.dart
│ ├── notification_test.dart
│ ├── open_dialog_test.dart
│ ├── remove_dialog_test.dart
│ ├── rename_dialog_test.dart
│ ├── save_dialog_test.dart
│ ├── share_dialog_test.dart
│ ├── show_button_test.dart
│ ├── snapshotter_test.dart
│ ├── update_button_test.dart
│ └── whats_new_test.dart
├── full_test.dart
├── gzip_test.dart
├── helpers.dart
├── html5.html
├── html5_test.dart
├── ice_test.dart
├── polymer
│ ├── embed_bar.html
│ ├── embed_baz.html
│ ├── embed_foo.html
│ ├── index.html
│ └── test.dart
├── settings_test.dart
└── store_test.dart
├── tool
└── dartdoc
└── web
├── embed_a.html
├── embed_b.html
├── embed_c.html
├── full.dart
├── full.html
├── index.html
├── main.dart
└── polymer.html
/.gitignore:
--------------------------------------------------------------------------------
1 | packages
2 | pubspec.lock
3 | out
4 | *.js.deps
5 | *.js.map
6 | *.dart.js
7 | docs
8 | .DS_Store
9 | build
10 | .pub
11 | .packages
12 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: dart
2 | dart_task:
3 | - dartanalyzer: --fatal-warnings lib/ice.dart
4 | - test: --platform dartium test/ice_test.dart
5 | install_dartium: true
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This software is licensed under the MIT License.
2 |
3 | Copyright Chris Strom, 2013.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ICE Code Editor
2 |
3 | [](https://travis-ci.org/eee-c/ice-code-editor)
4 |
5 | __*** [Try it now!](https://code3dgames.com/3de/) ***__
6 |
7 | The Code Editor + Visualization Preview used in the book “[3D Game Programming for Kids](https://code3dgames.com).” Written in **[Dart](http://dartlang.org)**.
8 |
9 | 
10 |
11 | The old [JavaScript version](https://github.com/eee-c/code-editor) proved unmaintainable, hence the switch to **Dart**. This version leverages many of the benefits of **Dart**: cross-browser support, testing, documentation.
12 |
13 | ## Running the Example App
14 |
15 | You'll need **[Dart](http://dartlang.org)** installed. To run the examples:
16 |
17 | 1. Install dependencies with `pub install`
18 | 2. Start the [pub](http://pub.dartlang.org) web server with `pub serve`
19 | 3. Open the full-screen version of ICE at http://localhost:8080/full.html with Dartium
20 |
21 | Examples are contained in the `web` directory.
22 |
23 | ## Features
24 |
25 | * Update button
26 | * Hide Code button
27 | * Main Menu button
28 | * Open
29 | * New
30 | * Make a Copy
31 | * Save
32 | * Rename
33 | * Share
34 | * Download
35 | * Remove
36 | * Help
37 |
38 | ## Build
39 |
40 | Because ICE relies on [js-interop](http://dart-lang.github.io/js-interop/docs/js.html), not just `dart:js`, the build process requires that it is always built for development release (even in production):
41 |
42 | ````sh
43 | $ pub build --mode=development
44 | ````
45 |
46 | ## Core Collaborators
47 |
48 | * [Srdjan Pejic](http://batasrki.github.io/)
49 | * [James Hurford](https://github.com/terrasea)
50 | * [Kashyap Kondamudi](https://github.com/kgrz)
51 |
52 | ## Emeritus Collaborators
53 |
54 | * [Santiago Arias](https://github.com/santiaago)
55 | * [Timothy King](https://github.com/lordzork)
56 |
57 | ## Contributors
58 |
59 | * [Sangeet Agarwal](https://github.com/SangeetAgarwal)
60 | * [Robert Åkerblom-Andersson](https://github.com/scorpiion)
61 | * [Luke Barbuto](https://github.com/lexun)
62 | * [Kate Bladow](https://github.com/kbladow)
63 | * [Stephen Cagle](https://github.com/samedhi)
64 | * [Alex Chacon](https://github.com/alexgchacon)
65 | * [Joe Curtis](http://github.com/toklok)
66 | * [Jon Davison](https://github.com/jcdavison)
67 | * [Damon Douglas](https://github.com/damondouglas)
68 | * [Ashok Dudhade](https://github.com/ashokdudhade)
69 | * [William Estoque](https://github.com/westoque)
70 | * [Daniel Gempesaw](https://github.com/gempesaw)
71 | * [Abtin Ghods](https://github.com/abetss)
72 | * [Simone Giacomelli](https://github.com/simonegiacomelli)
73 | * [Richard Gould](https://github.com/rgould)
74 | * [Nik Graf](https://github.com/nikgraf)
75 | * [Marty Hines](https://github.com/martyhines)
76 | * [Erik Isaksen](https://github.com/nevraeka)
77 | * [Jonathan Kaye](https://github.com/jonkaye)
78 | * [Colin Kennedy](https://github.com/cmkcmk)
79 | * [Jon Kirkman](https://github.com/jonkirkman)
80 | * [Anita Kuno](https://github.com/anteaya)
81 | * [Lindsey Miller](https://github.com/tech-bluenette)
82 | * [Morgan Nelson](https://github.com/korishev)
83 | * [Michael Reynolds](https://github.com/mr170)
84 | * [Michael Risse](https://github.com/rissem)
85 | * [Chris Sciolla](https://github.com/chrisski)
86 | * [Christian Smith](https://github.com/christiansmith)
87 | * [Paul Spain](https://github.com/pvspain)
88 | * [Alper Sunar](https://github.com/asunar)
89 | * [Dmitriy Vasilyev](https://github.com/kelegorm)
90 | * Stefan Dausend-Werner
91 |
92 | ## Want to Help?
93 |
94 | [](https://calendar.google.com/calendar/selfsched?sstoken=UUNwdmNwR09IRm4wfGRlZmF1bHR8NmVjZjU2MGY0MzU4MTBlMjFkZTE0ZDgzYjdkMGU4ZjM)
95 |
96 | Chris ([twitter](https://twitter.com/eee_c) / [blog](http://japhr.blogspot.com/)) runs nightly (1030pm EDT / 0230 UTC) pairing sessions. [Sign up for free](https://www.google.com/calendar/selfsched?sstoken=UUNwdmNwR09IRm4wfGRlZmF1bHR8NmVjZjU2MGY0MzU4MTBlMjFkZTE0ZDgzYjdkMGU4ZjM) to help out and learn some [Dart](http://dartlang.org)! _Absolutely no experience required. Really :)_
97 |
--------------------------------------------------------------------------------
/bin/README.md:
--------------------------------------------------------------------------------
1 | # Build Scripts
2 |
3 | Scripts are run in the following order:
4 |
5 | ```
6 | ~/repos/ice-code-editor/bin/ice-de-symlink-packages.sh
7 | ~/repos/ice-code-editor/bin/ice-appcache.sh
8 | ~/repos/ice-code-editor/bin/ice-compile.sh main.dart
9 | ```
10 |
11 |
12 | To use, the scripts expect the following directory structure (used on code3dgames.com):
13 |
14 | ```
15 | appcache.js
16 | index.html
17 | main.dart
18 | pubspec.yaml
19 | ```
20 |
21 | Where appcache.js is the standard Application Cache JavaScript file.
22 |
23 | Index.html is:
24 |
25 | ```html
26 |
27 |
28 |
29 | code editor
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ```
38 |
39 | main.dart is:
40 |
41 | ```dart
42 | library main;
43 |
44 | import 'package:ice_code_editor/ice.dart' as ICE;
45 |
46 | main()=> new ICE.Full();
47 | ```
48 |
49 | And pubspec.yaml is:
50 |
51 | ```yaml
52 | name: full_screen_ice
53 | version: 0.0.1
54 | description: Full screen ICE
55 | dependencies:
56 | ice_code_editor: any
57 | ```
58 |
--------------------------------------------------------------------------------
/bin/ace_src_files.txt:
--------------------------------------------------------------------------------
1 | ace/src/js/mode-css.js$
2 | ace/src/js/ace.js$
3 | ace/src/js/theme-textmate.js$
4 | ace/src/js/theme-chrome.js$
5 | ace/src/js/ext-searchbox.js$
6 | ace/src/js/mode-html.js$
7 | ace/src/js/mode-javascript.js$
8 | ace/src/js/keybinding-emacs.js$
9 |
--------------------------------------------------------------------------------
/bin/ice-appcache.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Generates an application cache manifest file suitable for
4 | # deployment. The EXTERNAL_LIBS shown are specific to the code3dgames.com
5 | # site, but can be tweaked for deployments to other sites.
6 |
7 | # TODO: investigate excluding main.dart.precompiled.js from manifest
8 |
9 | # Name of the manifest file:
10 | APPCACHE=editor.appcache
11 |
12 | # Change this list to suit different deployment needs:
13 | read -d '' EXTERNAL_LIBS < $APPCACHE
38 | date +'# %Y-%m-%d %H:%M:%S' >> $APPCACHE
39 | echo >> $APPCACHE
40 |
41 | ##
42 | # Cache
43 |
44 | echo 'CACHE:' >> $APPCACHE
45 |
46 | # Fixed files used within the editor for creating Three.js worlds:
47 | echo "$LIST_FIRST" >> $APPCACHE
48 | echo >> $APPCACHE
49 | echo "$EXTERNAL_LIBS" >> $APPCACHE
50 | echo >> $APPCACHE
51 |
52 | # Dart and JS code used to make the editor:
53 | find | \
54 | grep -e \\.js$ -e \\.map$ -e \\.html$ -e \\.css$ | \
55 | grep -v -e unittest -e /lib/ -e polymer -e web_components -e js/deflate | \
56 | grep -v packages/ace/src | \
57 | sed 's/^\.\///' \
58 | >> $APPCACHE
59 |
60 | find packages/ace/src | \
61 | grep -f ~/repos/ice-code-editor/bin/ace_src_files.txt | \
62 | sed 's/^\.\///' \
63 | >> $APPCACHE
64 |
65 |
66 | ##
67 | # Network
68 | cat <> $APPCACHE
69 |
70 | NETWORK:
71 | *
72 | EOF
73 |
--------------------------------------------------------------------------------
/bin/ice-compile.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | dart2js -m -o main.dart.js $1
4 |
--------------------------------------------------------------------------------
/bin/ice-de-symlink-packages.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # For deployment targets such as GitHub pages, this script replaces
4 | # the symbolic links in Dart's packages directory with the actual
5 | # contents of those libraries.
6 |
7 | cd packages
8 | for file in *; do
9 | if [ ! -L $file ]; then
10 | echo "Symlinked packages already replaced."
11 | exit 1
12 | fi
13 |
14 | link=`readlink $file`
15 | echo "$file ($link)"
16 | rm $file
17 | cp -r $link $file
18 | done
19 |
--------------------------------------------------------------------------------
/ice_code_editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eee-c/ice-code-editor/9fc0e4d254a33e3da48527f9e2ccfbe17e6e45aa/ice_code_editor.png
--------------------------------------------------------------------------------
/lib/css/ice.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'inconsolata';
3 | src: url('../fonts/inconsolata.woff') format('woff');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | .ice-code-editor-editor .ace_content {
9 | /* font-family: inconsolata; */
10 | /* Don't set font-size here. Use ACE setFontSize() method to get proper */
11 | /* line spacing */
12 | }
13 |
14 | .ice-code-editor-editor, .ice-code-editor-preview,
15 | .ice-code-editor-editor.ace_editor {
16 | margin: 0;
17 | top: 0;
18 | bottom: 0;
19 | left: 0;
20 | right: 0;
21 | }
22 |
23 | .ice-code-editor-editor,
24 | .ice-code-editor-editor.ace-chrome .ace_scroller,
25 | .ice-code-editor-editor.ace-tm .ace_scroller {
26 | background-color: rgba(255,255,255,0.0);
27 | }
28 |
29 | .ace_content {
30 | background: rgba(255,255,255,0.75);
31 | }
32 |
33 | .ace-chrome .ace_content .ace_marker-layer .ace_selection {
34 | background: rgba(0,0,255,0.1);
35 | }
36 |
37 | button {
38 | font-family: sans-serif;
39 | font-size: 10px;
40 | line-height: 12px;
41 | text-transform: uppercase;
42 | text-decoration: none;
43 | color: rgba(0,0,0,0.5);
44 | border: 1px solid rgba(0,0,0,0.25);
45 | border-radius: 2px;
46 | background-color: rgba(255,255,255,0.5);
47 | margin: 2px;
48 | padding: 8px 10px;
49 | cursor: pointer;
50 | }
51 |
52 | button:hover {
53 | background-color: rgba(0,0,0,0.66);
54 | color: white;
55 | }
56 |
57 | button input {
58 | margin: -10px 4px;
59 | }
60 |
61 | .ice-menu, .ice-dialog {
62 | margin: 2px;
63 | padding: 5px;
64 | border: 1px solid rgba(0,0,0,0.25);
65 | border-radius: 2px;
66 | font-family: Arial;
67 | font-size: 12px;
68 | text-transform: uppercase;
69 | text-decoration: none;
70 | color: rgba(0,0,0,0.5);
71 | background-color: rgba(255,255,255,0.5);
72 |
73 | position: absolute;
74 | margin: 2px;
75 | right: 20px;
76 | top: 45px;
77 | z-index: 999;
78 | }
79 |
80 | div.ice-menu h1 {
81 | display: none;
82 | }
83 |
84 | div.ice-menu ul {
85 | margin: 2px;
86 | padding: 5px;
87 | text-transform: none;
88 | font-size: 13px;
89 | }
90 |
91 | .ice-menu li {
92 | display: block;
93 | padding: 4px;
94 | cursor: pointer;
95 | }
96 |
97 | .ice-menu li.empty {
98 | display: block;
99 | padding: 0px;
100 | cursor: auto;
101 | }
102 |
103 | .ice-menu li:hover, .ice-menu li:focus {
104 | color: white;
105 | background-color: rgba(0,0,0,0.66);
106 | }
107 |
108 | .ice-menu li.empty:hover, .ice-menu li.empty:focus {
109 | color: rgba(0,0,0,0.5);
110 | background-color: white;
111 | }
112 |
113 | .ice-menu a {
114 | text-decoration: none;
115 | color: rgba(0,0,0,0.5);
116 | }
117 |
118 | .ice-menu li:hover a, .ice-menu li:focus a {
119 | color: white;
120 | }
121 |
122 | .ice-menu li.highlighted {
123 | font-weight: bold;
124 | color: red;
125 | }
126 |
127 | .ice-menu li:hover.highlighted {
128 | color: white;
129 | background-color: red;
130 | }
131 |
132 | .ice-dialog h1 {
133 | margin: 0px 0px 5px;
134 | font-size: 100%;
135 | }
136 |
137 | .ice-menu .instructions {
138 | text-transform: none;
139 | padding: 0px;
140 | }
141 |
142 | .ice-dialog .instructions {
143 | padding-top: 0.25em;
144 | float: right;
145 | text-transform: lowercase;
146 | }
147 |
148 | #info {
149 | position: absolute;
150 | top: 10px;
151 | opacity: 0.8;
152 | border-radius: 4px;
153 | z-index: 999;
154 | background-color: black;
155 | color: white;
156 | padding: 10px;
157 | font-family: sans-serif;
158 | left: 50%;
159 | -webkit-transform: translateX(-50%);
160 | transform: translateX(-50%);
161 | }
162 |
163 | img.clipboard {
164 | height: 17px;
165 | vertical-align: bottom;
166 | padding-left: 3px;
167 | }
168 |
--------------------------------------------------------------------------------
/lib/editor.dart:
--------------------------------------------------------------------------------
1 | part of ice;
2 |
3 | class Editor {
4 | bool autoupdate = true;
5 | bool _edit_only = false;
6 | bool _read_only = false;
7 |
8 | var _el;
9 | Element __el, _editor_el, _preview_el;
10 |
11 | var _ace;
12 | Completer _waitForAce;
13 |
14 | static bool disableJavaScriptWorker = false;
15 |
16 | Editor(this._el, {preview_el}) {
17 | if (preview_el != null) {
18 | this._preview_el = preview_el
19 | ..classes.add('ice-code-editor-preview');
20 | }
21 | this._startAce();
22 | this.applyStyles();
23 | }
24 |
25 | set content(String data) {
26 | if (!_waitForAce.isCompleted) {
27 | editorReady.then((_) => this.content = data);
28 | return;
29 | }
30 |
31 | var original_autoupdate = autoupdate;
32 | autoupdate = false;
33 | _ace.value = data;
34 | _ace.focus();
35 | updatePreview();
36 |
37 | var subscribe;
38 | subscribe = onPreviewChange.listen((_) {
39 | this.autoupdate = original_autoupdate;
40 | subscribe.cancel();
41 | });
42 | }
43 |
44 | Timer _update_timer;
45 | void delayedUpdatePreview() {
46 | if (!this.autoupdate) return;
47 | if (_update_timer != null) _update_timer.cancel();
48 |
49 | var wait = new Duration(seconds: 2);
50 | _update_timer = new Timer(wait, (){
51 | this.updatePreview();
52 | _update_timer = null;
53 | });
54 | }
55 |
56 | void _extendDelayedUpdatePreview() {
57 | if (_update_timer == null) return;
58 | delayedUpdatePreview();
59 | }
60 |
61 | bool get edit_only => _edit_only;
62 | void set edit_only(v) {
63 | _edit_only = v;
64 | if (v) removePreview();
65 | }
66 |
67 | bool get read_only => _read_only;
68 | void set read_only(v) {
69 | _read_only = v;
70 | if (v) {
71 | _ace.readOnly = true;
72 | }
73 | }
74 |
75 | // worry about waitForAce?
76 | String get content => _ace.value;
77 | Future get editorReady => _waitForAce.future;
78 |
79 | int get lineNumber => _ace.lineNumber;
80 | set lineNumber(int v) { _ace.lineNumber = v; }
81 | String get lineContent => _ace.lineContent;
82 |
83 | /// Update the preview layer with the current contents of the editor
84 | /// layer.
85 | updatePreview() {
86 | if (this.edit_only) return;
87 |
88 | this.removePreview();
89 |
90 | var iframe = this.createPreviewIframe();
91 | iframe.onLoad.first.then((_) {
92 | if (iframe.contentWindow == null) return;
93 |
94 | iframe
95 | ..height = "${this.preview_el.clientHeight}";
96 |
97 | var url = new RegExp(r'^file://').hasMatch(window.location.href)
98 | ? '*': window.location.href;
99 | iframe.contentWindow.postMessage(_ace.value, url);
100 |
101 | _previewChangeController.add(true);
102 | });
103 | }
104 |
105 | removePreview() {
106 | while (this.preview_el.children.length > 0) {
107 | this.preview_el.children.first.remove();
108 | }
109 | }
110 |
111 | createPreviewIframe() {
112 | var src = "code${window.navigator.onLine ? '' : '-OFFLINE'}";
113 |
114 | var iframe = new IFrameElement();
115 | iframe
116 | ..width = "${this.preview_el.clientWidth}"
117 | ..height = "${this.preview_el.clientHeight}"
118 | ..style.border = '0'
119 | ..src = 'packages/ice_code_editor/html/${src}.html';
120 |
121 | this.preview_el.children.add( iframe );
122 |
123 | return iframe;
124 | }
125 |
126 | Stream get onChange => _ace.session.onChange;
127 | Stream get onPreviewChange =>
128 | _previewChangeController.stream.asBroadcastStream();
129 |
130 | StreamController __previewChangeController;
131 | StreamController get _previewChangeController {
132 | if (__previewChangeController != null) return __previewChangeController;
133 | return __previewChangeController = new StreamController.broadcast();
134 | }
135 |
136 | /// Show the code layer, calling the ACE resize methods to ensure that
137 | /// the display is correct.
138 | // worry about waitForAce?
139 | showCode() {
140 | editor_el.style.visibility = 'visible';
141 | querySelectorAll('.ace_print-margin').forEach((e) { e.style.visibility = 'visible'; });
142 |
143 | _ace.resize();
144 | focus();
145 | }
146 |
147 | /// Hide the code layer
148 | hideCode() {
149 | editor_el.style.visibility = 'hidden';
150 | querySelector('.ace_print-margin').style.visibility = 'hidden';
151 |
152 | if (this.edit_only) return;
153 | focus();
154 | }
155 |
156 | focus() {
157 | if (isCodeVisible) {
158 | _ace.focus();
159 | }
160 | else {
161 | preview_el.children.first.focus();
162 | }
163 | }
164 |
165 | bool get isCodeVisible=> editor_el.style.visibility != 'hidden';
166 |
167 | Element get el {
168 | if (__el != null) return __el;
169 |
170 | if (this._el.runtimeType.toString().contains('Element')) {
171 | __el = _el;
172 | }
173 | else {
174 | __el = document.querySelector(_el);
175 | }
176 | return __el;
177 | }
178 |
179 | Element get editor_el {
180 | if (_editor_el != null) return _editor_el;
181 |
182 | _editor_el = new DivElement()
183 | ..classes.add('ice-code-editor-editor');
184 | this.el.children.add(_editor_el);
185 | return _editor_el;
186 | }
187 |
188 | Element get preview_el {
189 | if (_preview_el != null) return _preview_el;
190 |
191 | _preview_el = new DivElement()
192 | ..classes.add('ice-code-editor-preview');
193 |
194 | if (!this.edit_only) {
195 | this.el.append(_preview_el);
196 | }
197 |
198 | return _preview_el;
199 | }
200 |
201 | static List _scripts;
202 | static bool get _isAceJsAttached => (_scripts != null);
203 | static _attachScripts() {
204 | if (_scripts != null) return [];
205 |
206 | var script_paths = [
207 | "packages/ace/src/js/ace.js",
208 | "packages/ace/src/js/keybinding-emacs.js",
209 | "packages/ice_code_editor/js/deflate/rawdeflate.js",
210 | "packages/ice_code_editor/js/deflate/rawinflate.js"
211 | ];
212 |
213 | var scripts = script_paths.
214 | map((path) {
215 | var script = new ScriptElement()
216 | ..async = false
217 | ..src = path;
218 | document.head.nodes.add(script);
219 | return script;
220 | }).
221 | toList();
222 |
223 | return _scripts = scripts;
224 | }
225 |
226 | static Completer _waitForJS;
227 | static Future get jsReady {
228 | if (!_isAceJsAttached) {
229 | _waitForJS = new Completer();
230 | _attachScripts().
231 | first.
232 | onLoad.
233 | listen((_)=> _waitForJS.complete());
234 | }
235 |
236 | return _waitForJS.future;
237 | }
238 |
239 | _startAce() {
240 | this._waitForAce = new Completer();
241 | jsReady.then((_)=> _startJsAce());
242 | _attachKeyHandlersForAce();
243 | }
244 |
245 | _startJsAce() {
246 |
247 | _ace = Ace.edit(editor_el);
248 |
249 | _ace
250 | ..theme = "ace/theme/chrome"
251 | ..fontSize = '18px'
252 | ..printMarginColumn = false
253 | ..displayIndentGuides = false;
254 |
255 | if (!disableJavaScriptWorker) {
256 | _ace.session
257 | ..mode = "ace/mode/javascript"
258 | ..useWrapMode = true
259 | ..useSoftTabs = true
260 | ..tabSize = 2;
261 | }
262 |
263 | _ace.session.onChange.listen((e)=> this.delayedUpdatePreview());
264 |
265 | _waitForAce.complete();
266 | }
267 |
268 | _attachKeyHandlersForAce() {
269 | // Using keyup b/c ACE swallows keydown events
270 | document.onKeyUp.listen((event) {
271 | // only handling arrow keys
272 | if (event.keyCode < 37) return;
273 | if (event.keyCode > 40) return;
274 | _extendDelayedUpdatePreview();
275 | });
276 | }
277 |
278 | applyStyles() {
279 | var style = new LinkElement()
280 | ..type = "text/css"
281 | ..rel = "stylesheet"
282 | ..href = "packages/ice_code_editor/css/ice.css";
283 | document.head.nodes.add(style);
284 |
285 | this.el.style
286 | ..position = 'relative';
287 |
288 | this.editor_el.style
289 | ..position = 'absolute'
290 | ..zIndex = '20';
291 |
292 | var offset = this.el.documentOffset;
293 | this.preview_el.style
294 | ..position = 'absolute'
295 | ..width = this.el.style.width
296 | ..height = this.el.style.height
297 | ..top = '${offset.y}'
298 | ..left = '${offset.x}'
299 | ..zIndex = '10';
300 | }
301 | }
302 |
303 | class Ace {
304 | // This line won't work in Dartium because of a Polymer.dart bug
305 | static Ace edit(Element el) =>
306 | new Ace(js.context['ace'].callMethod('edit', [el]));
307 |
308 | var jsAce;
309 |
310 | Ace(this.jsAce) {
311 | js.context['ace']['config'].
312 | callMethod('set', ["workerPath", "packages/ice_code_editor/js/ace"]);
313 | jsAce[r'$blockScrolling'] = double.INFINITY;
314 | }
315 |
316 | set fontSize(String size) =>
317 | jsAce.callMethod('setFontSize', [size]);
318 | set theme(String theme) =>
319 | jsAce.callMethod('setTheme', [theme]);
320 | set printMarginColumn(bool b) =>
321 | jsAce.callMethod('setPrintMarginColumn', [b]);
322 | set displayIndentGuides(bool b) =>
323 | jsAce.callMethod('setDisplayIndentGuides', [b]);
324 | set readOnly(bool b) =>
325 | jsAce.callMethod('setReadOnly', [b]);
326 |
327 | String get value => jsAce.callMethod('getValue');
328 | set value(String content) {
329 | jsAce.callMethod('setValue', [content, -1]);
330 |
331 | var UndoManager = js.context['ace'].callMethod('require', ['ace/undomanager'])['UndoManager'];
332 | session.undoManager = new js.JsObject(UndoManager);
333 | }
334 |
335 | void focus() => jsAce.callMethod('focus');
336 |
337 | // This is way crazy, but... getLine() and getCursorPosition() are zero
338 | // indexed while gotoLine() and scrollToLine() are 1 indexed o_O
339 | String get lineContent => session.getLine(lineNumber - 1);
340 |
341 | int get lineNumber => jsAce.callMethod('getCursorPosition')['row'] + 1;
342 |
343 | set lineNumber(int row) {
344 | jsAce.callMethod('gotoLine', [row, 0, false]);
345 | jsAce.callMethod('scrollToLine', [row-1, false, false]);
346 | }
347 |
348 | get renderer => jsAce['renderer'];
349 |
350 | int resize() => renderer.callMethod('onResize');
351 |
352 | var _session;
353 | AceSession get session {
354 | if (_session != null) return _session;
355 | return _session = new AceSession(jsAce.callMethod('getSession'));
356 | }
357 |
358 | void toggleEmacs() {
359 | if (jsAce.callMethod('getKeyboardHandler') == commandManager) {
360 | jsAce.callMethod('setKeyboardHandler', [emacsManager]);
361 | }
362 | else {
363 | jsAce.callMethod('setKeyboardHandler', [commandManager]);
364 | }
365 | }
366 |
367 | var _commandManager;
368 | get commandManager {
369 | if (_commandManager != null) return _commandManager;
370 | _commandManager = jsAce.callMethod('getKeyboardHandler');
371 | return _commandManager;
372 | }
373 |
374 | var _emacsManager;
375 | get emacsManager {
376 | if (_emacsManager != null) return _emacsManager;
377 | _emacsManager = js.context['ace'].callMethod('require', ["ace/keyboard/emacs"])['handler'];
378 | return _emacsManager;
379 | }
380 | }
381 |
382 | class AceSession {
383 | var jsSession;
384 | AceSession(this.jsSession);
385 |
386 | set mode(String m) => jsSession.callMethod('setMode', [m]);
387 | set useWrapMode(bool b) {
388 | jsSession.callMethod('setUseWrapMode', [b]);
389 | }
390 | set useSoftTabs(bool b) => jsSession.callMethod('setUseSoftTabs', [b]);
391 | set tabSize(int size) => jsSession.callMethod('setTabSize', [size]);
392 |
393 | get undoManager => jsSession.callMethod('getUndoManager', [false]);
394 | set undoManager(u) {
395 | jsSession.callMethod('setUndoManager', [u]);
396 | }
397 |
398 | void insertAt(String content, int row, int col) {
399 | var pos = jsSession.callMethod('screenToDocumentPosition', [row, col]);
400 | jsSession.callMethod('insert', [pos, content]);
401 | }
402 |
403 | String getLine(int row) => jsSession.callMethod('getLine', [row]);
404 |
405 | StreamController _onChange;
406 | get onChange {
407 | if (_onChange != null) return _onChange.stream;
408 |
409 | _onChange = new StreamController.broadcast();
410 |
411 | jsSession.callMethod('on', [
412 | 'change',
413 | (e,a){ _onChange.add(e); }
414 | ]);
415 |
416 | return _onChange.stream;
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/lib/fonts/inconsolata.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eee-c/ice-code-editor/9fc0e4d254a33e3da48527f9e2ccfbe17e6e45aa/lib/fonts/inconsolata.woff
--------------------------------------------------------------------------------
/lib/full.dart:
--------------------------------------------------------------------------------
1 | part of ice;
2 |
3 | class Full {
4 | Element el;
5 | Editor ice;
6 | Store store;
7 | Snapshotter snapshotter;
8 | Settings settings;
9 |
10 | String mode;
11 | String compressedContent;
12 |
13 | Full({mode, this.compressedContent:''}) {
14 | el = new Element.html('');
15 | document.body.nodes.add(el);
16 |
17 | ice = new Editor('#ice');
18 | store = new Store();
19 | settings = new Settings();
20 | this.mode = mode?.replaceFirst(new RegExp(r'^\?'), '');
21 |
22 | _attachKeyboardHandlers();
23 | _attachMouseHandlers();
24 | _attachMessageHandlers();
25 | _attachDropHandlers();
26 |
27 | _fullScreenStyles();
28 |
29 | editorReady
30 | ..then((_)=> _startSnapshotter())
31 | ..then((_)=> _attachCodeToolbar())
32 | ..then((_)=> _attachPreviewToolbar())
33 | ..then((_)=> _startAutoSave())
34 | ..then((_)=> _openProject())
35 | ..then((_)=> _initializeSettingForFirstUse())
36 | ..then((_)=> _initializeMode())
37 | ..then((_)=> _applyStyles());
38 | }
39 |
40 | Stream get onPreviewChange => ice.onPreviewChange;
41 | Future get editorReady => Future.wait([ice.editorReady, Gzip._ready]);
42 | String get content => ice.content;
43 | void set content(data) { ice.content = data; }
44 |
45 | String get lineContent => ice.lineContent;
46 | int get lineNumber => ice.lineNumber;
47 | set lineNumber(int v) { ice.lineNumber = v; }
48 |
49 | void remove() {
50 | Keys.cancel();
51 | el.remove();
52 | }
53 |
54 | _attachCodeToolbar() {
55 | var toolbar = new Element.html('