├── Makefile
├── Readme.md
├── config.json
├── package.json
├── public
├── addon
│ └── simplescrollbars.js
├── index.html
├── lib
│ ├── codemirror.css
│ └── codemirror.js
├── mode
│ └── javascript
│ │ ├── index.html
│ │ ├── javascript.js
│ │ ├── json-ld.html
│ │ ├── test.js
│ │ └── typescript.html
├── search.svg
├── src
│ ├── form.js
│ ├── index.js
│ ├── item.html
│ └── routes.js
└── style.css
├── server.js
└── webpack.config.js
/Makefile:
--------------------------------------------------------------------------------
1 |
2 | @build:
3 | @webpack -w -d
4 |
5 | .PHONY: build
6 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Mockapi
2 |
3 | Mockapi helps you create a mock api service with JSON response quickly, so you
4 | can test your client with this fake service.
5 |
6 |
7 |
8 | TODO: custom response status and headers
9 |
10 | Warning: only tested on current Chrome.
11 |
12 | ## Features
13 |
14 | * Create, remove, filter, delete and sort api
15 | * Import and export the api list
16 | * Shortcut, use `⌘ s` to save current api
17 |
18 | ## Install
19 |
20 | Make sure you have [nodejs](https://nodejs.org) and [redis](http://redis.io/)
21 | installed.
22 |
23 | git clone git@github.com:chemzqm/mockapi.git
24 | npm install && npm run build
25 |
26 | ## Usage
27 |
28 | Configure your redis and server port in `config.json` if needed, run command:
29 |
30 | ```
31 | node server.js
32 | ```
33 |
34 | Open your browser at `http://localhost:4000` (default port)
35 |
36 | You can make use of http://www.json-generator.com/ to generate the json data,
37 | and https://www.getpostman.com/ to check the http api.
38 |
39 | ## MIT license
40 | Copyright (c) 2016 chemzqm@gmail.com
41 |
42 | 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:
43 |
44 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
45 |
46 | 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.
47 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "redis": {
3 | "host": "127.0.0.1",
4 | "port": 6379
5 | },
6 | "port": 4000
7 | }
8 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "api-service",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "build": "./node_modules/.bin/webpack"
8 | },
9 | "author": "chemzqm@gmail.com",
10 | "license": "MIT",
11 | "dependencies": {
12 | "co": "^4.6.0",
13 | "co-body": "^4.0.0",
14 | "co-redis": "^2.1.0",
15 | "component-classes": "^1.2.5",
16 | "component-closest": "^1.0.0",
17 | "component-delegate": "^0.2.3",
18 | "component-emitter": "^1.2.0",
19 | "component-event": "^0.1.4",
20 | "component-file-picker": "^0.2.1",
21 | "component-notice": "0.0.15",
22 | "component-spin": "^0.1.1",
23 | "domify": "^1.4.0",
24 | "js-base64": "^2.1.9",
25 | "koa": "^1.2.0",
26 | "koa-cors": "0.0.16",
27 | "koa-liveload": "0.0.2",
28 | "koa-logger": "^1.3.1",
29 | "koa-router": "^5.4.0",
30 | "koa-static": "^2.0.0",
31 | "nprogress": "^0.2.0",
32 | "radio-component": "0.0.2",
33 | "redis": "^2.5.0",
34 | "request-component": "0.0.1",
35 | "sweet-sortable": "^0.3.2",
36 | "transitionend-property": "0.0.2"
37 | },
38 | "browser": {
39 | "request": "request-component",
40 | "notice": "component-notice",
41 | "emitter": "component-emitter",
42 | "event": "component-event",
43 | "delegate": "component-delegate",
44 | "closest": "component-closest",
45 | "classes": "component-classes",
46 | "spin": "component-spin",
47 | "radio": "radio-component",
48 | "file-picker": "component-file-picker"
49 | },
50 | "devDependencies": {
51 | "html-loader": "^0.4.3",
52 | "json-loader": "^0.5.4",
53 | "webpack": "^1.12.14"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/public/addon/simplescrollbars.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | (function(mod) {
5 | if (typeof exports == "object" && typeof module == "object") // CommonJS
6 | mod(require("../../lib/codemirror"));
7 | else if (typeof define == "function" && define.amd) // AMD
8 | define(["../../lib/codemirror"], mod);
9 | else // Plain browser env
10 | mod(CodeMirror);
11 | })(function(CodeMirror) {
12 | "use strict";
13 |
14 | function Bar(cls, orientation, scroll) {
15 | this.orientation = orientation;
16 | this.scroll = scroll;
17 | this.screen = this.total = this.size = 1;
18 | this.pos = 0;
19 |
20 | this.node = document.createElement("div");
21 | this.node.className = cls + "-" + orientation;
22 | this.inner = this.node.appendChild(document.createElement("div"));
23 |
24 | var self = this;
25 | CodeMirror.on(this.inner, "mousedown", function(e) {
26 | if (e.which != 1) return;
27 | CodeMirror.e_preventDefault(e);
28 | var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
29 | var start = e[axis], startpos = self.pos;
30 | function done() {
31 | CodeMirror.off(document, "mousemove", move);
32 | CodeMirror.off(document, "mouseup", done);
33 | }
34 | function move(e) {
35 | if (e.which != 1) return done();
36 | self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
37 | }
38 | CodeMirror.on(document, "mousemove", move);
39 | CodeMirror.on(document, "mouseup", done);
40 | });
41 |
42 | CodeMirror.on(this.node, "click", function(e) {
43 | CodeMirror.e_preventDefault(e);
44 | var innerBox = self.inner.getBoundingClientRect(), where;
45 | if (self.orientation == "horizontal")
46 | where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
47 | else
48 | where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
49 | self.moveTo(self.pos + where * self.screen);
50 | });
51 |
52 | function onWheel(e) {
53 | var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
54 | var oldPos = self.pos;
55 | self.moveTo(self.pos + moved);
56 | if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
57 | }
58 | CodeMirror.on(this.node, "mousewheel", onWheel);
59 | CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
60 | }
61 |
62 | Bar.prototype.moveTo = function(pos, update) {
63 | if (pos < 0) pos = 0;
64 | if (pos > this.total - this.screen) pos = this.total - this.screen;
65 | if (pos == this.pos) return;
66 | this.pos = pos;
67 | this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
68 | (pos * (this.size / this.total)) + "px";
69 | if (update !== false) this.scroll(pos, this.orientation);
70 | };
71 |
72 | var minButtonSize = 10;
73 |
74 | Bar.prototype.update = function(scrollSize, clientSize, barSize) {
75 | this.screen = clientSize;
76 | this.total = scrollSize;
77 | this.size = barSize;
78 |
79 | var buttonSize = this.screen * (this.size / this.total);
80 | if (buttonSize < minButtonSize) {
81 | this.size -= minButtonSize - buttonSize;
82 | buttonSize = minButtonSize;
83 | }
84 | this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
85 | buttonSize + "px";
86 | this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
87 | this.pos * (this.size / this.total) + "px";
88 | };
89 |
90 | function SimpleScrollbars(cls, place, scroll) {
91 | this.addClass = cls;
92 | this.horiz = new Bar(cls, "horizontal", scroll);
93 | place(this.horiz.node);
94 | this.vert = new Bar(cls, "vertical", scroll);
95 | place(this.vert.node);
96 | this.width = null;
97 | }
98 |
99 | SimpleScrollbars.prototype.update = function(measure) {
100 | if (this.width == null) {
101 | var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
102 | if (style) this.width = parseInt(style.height);
103 | }
104 | var width = this.width || 0;
105 |
106 | var needsH = measure.scrollWidth > measure.clientWidth + 1;
107 | var needsV = measure.scrollHeight > measure.clientHeight + 1;
108 | this.vert.node.style.display = needsV ? "block" : "none";
109 | this.horiz.node.style.display = needsH ? "block" : "none";
110 |
111 | if (needsV) {
112 | this.vert.update(measure.scrollHeight, measure.clientHeight,
113 | measure.viewHeight - (needsH ? width : 0));
114 | this.vert.node.style.display = "block";
115 | this.vert.node.style.bottom = needsH ? width + "px" : "0";
116 | }
117 | if (needsH) {
118 | this.horiz.update(measure.scrollWidth, measure.clientWidth,
119 | measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
120 | this.horiz.node.style.right = needsV ? width + "px" : "0";
121 | this.horiz.node.style.left = measure.barLeft + "px";
122 | }
123 |
124 | return {right: needsV ? width : 0, bottom: needsH ? width : 0};
125 | };
126 |
127 | SimpleScrollbars.prototype.setScrollTop = function(pos) {
128 | this.vert.moveTo(pos, false);
129 | };
130 |
131 | SimpleScrollbars.prototype.setScrollLeft = function(pos) {
132 | this.horiz.moveTo(pos, false);
133 | };
134 |
135 | SimpleScrollbars.prototype.clear = function() {
136 | var parent = this.horiz.node.parentNode;
137 | parent.removeChild(this.horiz.node);
138 | parent.removeChild(this.vert.node);
139 | };
140 |
141 | CodeMirror.scrollbarModel.simple = function(place, scroll) {
142 | return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
143 | };
144 | CodeMirror.scrollbarModel.overlay = function(place, scroll) {
145 | return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
146 | };
147 | });
148 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | API 配置
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
50 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/public/lib/codemirror.css:
--------------------------------------------------------------------------------
1 | /* BASICS */
2 |
3 | .CodeMirror {
4 | /* Set height, width, borders, and global font properties here */
5 | font-family: monospace;
6 | height: 300px;
7 | color: black;
8 | }
9 |
10 | /* PADDING */
11 |
12 | .CodeMirror-lines {
13 | padding: 4px 0; /* Vertical padding around content */
14 | }
15 | .CodeMirror pre {
16 | padding: 0 4px; /* Horizontal padding of content */
17 | }
18 |
19 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
20 | background-color: white; /* The little square between H and V scrollbars */
21 | }
22 |
23 | /* GUTTER */
24 |
25 | .CodeMirror-gutters {
26 | border-right: 1px solid #ddd;
27 | background-color: #f7f7f7;
28 | white-space: nowrap;
29 | }
30 | .CodeMirror-linenumbers {}
31 | .CodeMirror-linenumber {
32 | padding: 0 3px 0 5px;
33 | min-width: 20px;
34 | text-align: right;
35 | color: #999;
36 | white-space: nowrap;
37 | }
38 |
39 | .CodeMirror-guttermarker { color: black; }
40 | .CodeMirror-guttermarker-subtle { color: #999; }
41 |
42 | /* CURSOR */
43 |
44 | .CodeMirror-cursor {
45 | border-left: 1px solid black;
46 | border-right: none;
47 | width: 0;
48 | }
49 | /* Shown when moving in bi-directional text */
50 | .CodeMirror div.CodeMirror-secondarycursor {
51 | border-left: 1px solid silver;
52 | }
53 | .cm-fat-cursor .CodeMirror-cursor {
54 | width: auto;
55 | border: 0;
56 | background: #7e7;
57 | }
58 | .cm-fat-cursor div.CodeMirror-cursors {
59 | z-index: 1;
60 | }
61 |
62 | .cm-animate-fat-cursor {
63 | width: auto;
64 | border: 0;
65 | -webkit-animation: blink 1.06s steps(1) infinite;
66 | -moz-animation: blink 1.06s steps(1) infinite;
67 | animation: blink 1.06s steps(1) infinite;
68 | background-color: #7e7;
69 | }
70 | @-moz-keyframes blink {
71 | 0% {}
72 | 50% { background-color: transparent; }
73 | 100% {}
74 | }
75 | @-webkit-keyframes blink {
76 | 0% {}
77 | 50% { background-color: transparent; }
78 | 100% {}
79 | }
80 | @keyframes blink {
81 | 0% {}
82 | 50% { background-color: transparent; }
83 | 100% {}
84 | }
85 |
86 | /* Can style cursor different in overwrite (non-insert) mode */
87 | .CodeMirror-overwrite .CodeMirror-cursor {}
88 |
89 | .cm-tab { display: inline-block; text-decoration: inherit; }
90 |
91 | .CodeMirror-ruler {
92 | border-left: 1px solid #ccc;
93 | position: absolute;
94 | }
95 |
96 | /* DEFAULT THEME */
97 |
98 | .cm-s-default .cm-header {color: blue;}
99 | .cm-s-default .cm-quote {color: #090;}
100 | .cm-negative {color: #d44;}
101 | .cm-positive {color: #292;}
102 | .cm-header, .cm-strong {font-weight: bold;}
103 | .cm-em {font-style: italic;}
104 | .cm-link {text-decoration: underline;}
105 | .cm-strikethrough {text-decoration: line-through;}
106 |
107 | .cm-s-default .cm-keyword {color: #708;}
108 | .cm-s-default .cm-atom {color: #219;}
109 | .cm-s-default .cm-number {color: #164;}
110 | .cm-s-default .cm-def {color: #00f;}
111 | .cm-s-default .cm-variable,
112 | .cm-s-default .cm-punctuation,
113 | .cm-s-default .cm-property,
114 | .cm-s-default .cm-operator {}
115 | .cm-s-default .cm-variable-2 {color: #05a;}
116 | .cm-s-default .cm-variable-3 {color: #085;}
117 | .cm-s-default .cm-comment {color: #a50;}
118 | .cm-s-default .cm-string {color: #a11;}
119 | .cm-s-default .cm-string-2 {color: #f50;}
120 | .cm-s-default .cm-meta {color: #555;}
121 | .cm-s-default .cm-qualifier {color: #555;}
122 | .cm-s-default .cm-builtin {color: #30a;}
123 | .cm-s-default .cm-bracket {color: #997;}
124 | .cm-s-default .cm-tag {color: #170;}
125 | .cm-s-default .cm-attribute {color: #00c;}
126 | .cm-s-default .cm-hr {color: #999;}
127 | .cm-s-default .cm-link {color: #00c;}
128 |
129 | .cm-s-default .cm-error {color: #f00;}
130 | .cm-invalidchar {color: #f00;}
131 |
132 | .CodeMirror-composing { border-bottom: 2px solid; }
133 |
134 | /* Default styles for common addons */
135 |
136 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
137 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
138 | .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
139 | .CodeMirror-activeline-background {background: #e8f2ff;}
140 |
141 | /* STOP */
142 |
143 | /* The rest of this file contains styles related to the mechanics of
144 | the editor. You probably shouldn't touch them. */
145 |
146 | .CodeMirror {
147 | position: relative;
148 | overflow: hidden;
149 | background: white;
150 | }
151 |
152 | .CodeMirror-scroll {
153 | overflow: scroll !important; /* Things will break if this is overridden */
154 | /* 30px is the magic margin used to hide the element's real scrollbars */
155 | /* See overflow: hidden in .CodeMirror */
156 | margin-bottom: -30px; margin-right: -30px;
157 | padding-bottom: 30px;
158 | height: 100%;
159 | outline: none; /* Prevent dragging from highlighting the element */
160 | position: relative;
161 | }
162 | .CodeMirror-sizer {
163 | position: relative;
164 | border-right: 30px solid transparent;
165 | }
166 |
167 | /* The fake, visible scrollbars. Used to force redraw during scrolling
168 | before actual scrolling happens, thus preventing shaking and
169 | flickering artifacts. */
170 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
171 | position: absolute;
172 | z-index: 6;
173 | display: none;
174 | }
175 | .CodeMirror-vscrollbar {
176 | right: 0; top: 0;
177 | overflow-x: hidden;
178 | overflow-y: scroll;
179 | }
180 | .CodeMirror-hscrollbar {
181 | bottom: 0; left: 0;
182 | overflow-y: hidden;
183 | overflow-x: scroll;
184 | }
185 | .CodeMirror-scrollbar-filler {
186 | right: 0; bottom: 0;
187 | }
188 | .CodeMirror-gutter-filler {
189 | left: 0; bottom: 0;
190 | }
191 |
192 | .CodeMirror-gutters {
193 | position: absolute; left: 0; top: 0;
194 | z-index: 3;
195 | }
196 | .CodeMirror-gutter {
197 | white-space: normal;
198 | height: 100%;
199 | display: inline-block;
200 | vertical-align: top;
201 | margin-bottom: -30px;
202 | /* Hack to make IE7 behave */
203 | *zoom:1;
204 | *display:inline;
205 | }
206 | .CodeMirror-gutter-wrapper {
207 | position: absolute;
208 | z-index: 4;
209 | background: none !important;
210 | border: none !important;
211 | }
212 | .CodeMirror-gutter-background {
213 | position: absolute;
214 | top: 0; bottom: 0;
215 | z-index: 4;
216 | }
217 | .CodeMirror-gutter-elt {
218 | position: absolute;
219 | cursor: default;
220 | z-index: 4;
221 | }
222 | .CodeMirror-gutter-wrapper {
223 | -webkit-user-select: none;
224 | -moz-user-select: none;
225 | user-select: none;
226 | }
227 |
228 | .CodeMirror-lines {
229 | cursor: text;
230 | min-height: 1px; /* prevents collapsing before first draw */
231 | }
232 | .CodeMirror pre {
233 | /* Reset some styles that the rest of the page might have set */
234 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
235 | border-width: 0;
236 | background: transparent;
237 | font-family: inherit;
238 | font-size: inherit;
239 | margin: 0;
240 | white-space: pre;
241 | word-wrap: normal;
242 | line-height: inherit;
243 | color: inherit;
244 | z-index: 2;
245 | position: relative;
246 | overflow: visible;
247 | -webkit-tap-highlight-color: transparent;
248 | }
249 | .CodeMirror-wrap pre {
250 | word-wrap: break-word;
251 | white-space: pre-wrap;
252 | word-break: normal;
253 | }
254 |
255 | .CodeMirror-linebackground {
256 | position: absolute;
257 | left: 0; right: 0; top: 0; bottom: 0;
258 | z-index: 0;
259 | }
260 |
261 | .CodeMirror-linewidget {
262 | position: relative;
263 | z-index: 2;
264 | overflow: auto;
265 | }
266 |
267 | .CodeMirror-widget {}
268 |
269 | .CodeMirror-code {
270 | outline: none;
271 | }
272 |
273 | /* Force content-box sizing for the elements where we expect it */
274 | .CodeMirror-scroll,
275 | .CodeMirror-sizer,
276 | .CodeMirror-gutter,
277 | .CodeMirror-gutters,
278 | .CodeMirror-linenumber {
279 | -moz-box-sizing: content-box;
280 | box-sizing: content-box;
281 | }
282 |
283 | .CodeMirror-measure {
284 | position: absolute;
285 | width: 100%;
286 | height: 0;
287 | overflow: hidden;
288 | visibility: hidden;
289 | }
290 |
291 | .CodeMirror-cursor { position: absolute; }
292 | .CodeMirror-measure pre { position: static; }
293 |
294 | div.CodeMirror-cursors {
295 | visibility: hidden;
296 | position: relative;
297 | z-index: 3;
298 | }
299 | div.CodeMirror-dragcursors {
300 | visibility: visible;
301 | }
302 |
303 | .CodeMirror-focused div.CodeMirror-cursors {
304 | visibility: visible;
305 | }
306 |
307 | .CodeMirror-selected { background: #d9d9d9; }
308 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
309 | .CodeMirror-crosshair { cursor: crosshair; }
310 | .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
311 | .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
312 |
313 | .cm-searching {
314 | background: #ffa;
315 | background: rgba(255, 255, 0, .4);
316 | }
317 |
318 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
319 | .CodeMirror span { *vertical-align: text-bottom; }
320 |
321 | /* Used to force a border model for a node */
322 | .cm-force-border { padding-right: .1px; }
323 |
324 | @media print {
325 | /* Hide the cursor when printing */
326 | .CodeMirror div.CodeMirror-cursors {
327 | visibility: hidden;
328 | }
329 | }
330 |
331 | /* See issue #2901 */
332 | .cm-tab-wrap-hack:after { content: ''; }
333 |
334 | /* Help users use markselection to safely style text background */
335 | span.CodeMirror-selectedtext { background: none; }
336 |
--------------------------------------------------------------------------------
/public/mode/javascript/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: JavaScript mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | JavaScript mode
30 |
31 |
32 |
81 |
82 |
90 |
91 |
92 | JavaScript mode supports several configuration options:
93 |
94 | json
which will set the mode to expect JSON
95 | data rather than a JavaScript program.
96 | jsonld
which will set the mode to expect
97 | JSON-LD linked data rather
98 | than a JavaScript program (demo ).
99 | typescript
which will activate additional
100 | syntax highlighting and some other things for TypeScript code
101 | (demo ).
102 | statementIndent
which (given a number) will
103 | determine the amount of indentation to use for statements
104 | continued on a new line.
105 | wordCharacters
, a regexp that indicates which
106 | characters should be considered part of an identifier.
107 | Defaults to /[\w$]/
, which does not handle
108 | non-ASCII identifiers. Can be set to something more elaborate
109 | to improve Unicode support.
110 |
111 |
112 |
113 | MIME types defined: text/javascript
, application/json
, application/ld+json
, text/typescript
, application/typescript
.
114 |
115 |
--------------------------------------------------------------------------------
/public/mode/javascript/javascript.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | // TODO actually recognize syntax of TypeScript constructs
5 |
6 | (function(mod) {
7 | if (typeof exports == "object" && typeof module == "object") // CommonJS
8 | mod(require("../../lib/codemirror"));
9 | else if (typeof define == "function" && define.amd) // AMD
10 | define(["../../lib/codemirror"], mod);
11 | else // Plain browser env
12 | mod(CodeMirror);
13 | })(function(CodeMirror) {
14 | "use strict";
15 |
16 | function expressionAllowed(stream, state, backUp) {
17 | return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
18 | (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
19 | }
20 |
21 | CodeMirror.defineMode("javascript", function(config, parserConfig) {
22 | var indentUnit = config.indentUnit;
23 | var statementIndent = parserConfig.statementIndent;
24 | var jsonldMode = parserConfig.jsonld;
25 | var jsonMode = parserConfig.json || jsonldMode;
26 | var isTS = parserConfig.typescript;
27 | var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
28 |
29 | // Tokenizer
30 |
31 | var keywords = function(){
32 | function kw(type) {return {type: type, style: "keyword"};}
33 | var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
34 | var operator = kw("operator"), atom = {type: "atom", style: "atom"};
35 |
36 | var jsKeywords = {
37 | "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
38 | "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
39 | "var": kw("var"), "const": kw("var"), "let": kw("var"),
40 | "function": kw("function"), "catch": kw("catch"),
41 | "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
42 | "in": operator, "typeof": operator, "instanceof": operator,
43 | "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
44 | "this": kw("this"), "class": kw("class"), "super": kw("atom"),
45 | "yield": C, "export": kw("export"), "import": kw("import"), "extends": C
46 | };
47 |
48 | // Extend the 'normal' keywords with the TypeScript language extensions
49 | if (isTS) {
50 | var type = {type: "variable", style: "variable-3"};
51 | var tsKeywords = {
52 | // object-like things
53 | "interface": kw("class"),
54 | "implements": C,
55 | "namespace": C,
56 | "module": kw("module"),
57 | "enum": kw("module"),
58 |
59 | // scope modifiers
60 | "public": kw("modifier"),
61 | "private": kw("modifier"),
62 | "protected": kw("modifier"),
63 | "abstract": kw("modifier"),
64 |
65 | // operators
66 | "as": operator,
67 |
68 | // types
69 | "string": type, "number": type, "boolean": type, "any": type
70 | };
71 |
72 | for (var attr in tsKeywords) {
73 | jsKeywords[attr] = tsKeywords[attr];
74 | }
75 | }
76 |
77 | return jsKeywords;
78 | }();
79 |
80 | var isOperatorChar = /[+\-*&%=<>!?|~^]/;
81 | var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
82 |
83 | function readRegexp(stream) {
84 | var escaped = false, next, inSet = false;
85 | while ((next = stream.next()) != null) {
86 | if (!escaped) {
87 | if (next == "/" && !inSet) return;
88 | if (next == "[") inSet = true;
89 | else if (inSet && next == "]") inSet = false;
90 | }
91 | escaped = !escaped && next == "\\";
92 | }
93 | }
94 |
95 | // Used as scratch variables to communicate multiple values without
96 | // consing up tons of objects.
97 | var type, content;
98 | function ret(tp, style, cont) {
99 | type = tp; content = cont;
100 | return style;
101 | }
102 | function tokenBase(stream, state) {
103 | var ch = stream.next();
104 | if (ch == '"' || ch == "'") {
105 | state.tokenize = tokenString(ch);
106 | return state.tokenize(stream, state);
107 | } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
108 | return ret("number", "number");
109 | } else if (ch == "." && stream.match("..")) {
110 | return ret("spread", "meta");
111 | } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
112 | return ret(ch);
113 | } else if (ch == "=" && stream.eat(">")) {
114 | return ret("=>", "operator");
115 | } else if (ch == "0" && stream.eat(/x/i)) {
116 | stream.eatWhile(/[\da-f]/i);
117 | return ret("number", "number");
118 | } else if (ch == "0" && stream.eat(/o/i)) {
119 | stream.eatWhile(/[0-7]/i);
120 | return ret("number", "number");
121 | } else if (ch == "0" && stream.eat(/b/i)) {
122 | stream.eatWhile(/[01]/i);
123 | return ret("number", "number");
124 | } else if (/\d/.test(ch)) {
125 | stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
126 | return ret("number", "number");
127 | } else if (ch == "/") {
128 | if (stream.eat("*")) {
129 | state.tokenize = tokenComment;
130 | return tokenComment(stream, state);
131 | } else if (stream.eat("/")) {
132 | stream.skipToEnd();
133 | return ret("comment", "comment");
134 | } else if (expressionAllowed(stream, state, 1)) {
135 | readRegexp(stream);
136 | stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
137 | return ret("regexp", "string-2");
138 | } else {
139 | stream.eatWhile(isOperatorChar);
140 | return ret("operator", "operator", stream.current());
141 | }
142 | } else if (ch == "`") {
143 | state.tokenize = tokenQuasi;
144 | return tokenQuasi(stream, state);
145 | } else if (ch == "#") {
146 | stream.skipToEnd();
147 | return ret("error", "error");
148 | } else if (isOperatorChar.test(ch)) {
149 | stream.eatWhile(isOperatorChar);
150 | return ret("operator", "operator", stream.current());
151 | } else if (wordRE.test(ch)) {
152 | stream.eatWhile(wordRE);
153 | var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
154 | return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
155 | ret("variable", "variable", word);
156 | }
157 | }
158 |
159 | function tokenString(quote) {
160 | return function(stream, state) {
161 | var escaped = false, next;
162 | if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
163 | state.tokenize = tokenBase;
164 | return ret("jsonld-keyword", "meta");
165 | }
166 | while ((next = stream.next()) != null) {
167 | if (next == quote && !escaped) break;
168 | escaped = !escaped && next == "\\";
169 | }
170 | if (!escaped) state.tokenize = tokenBase;
171 | return ret("string", "string");
172 | };
173 | }
174 |
175 | function tokenComment(stream, state) {
176 | var maybeEnd = false, ch;
177 | while (ch = stream.next()) {
178 | if (ch == "/" && maybeEnd) {
179 | state.tokenize = tokenBase;
180 | break;
181 | }
182 | maybeEnd = (ch == "*");
183 | }
184 | return ret("comment", "comment");
185 | }
186 |
187 | function tokenQuasi(stream, state) {
188 | var escaped = false, next;
189 | while ((next = stream.next()) != null) {
190 | if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
191 | state.tokenize = tokenBase;
192 | break;
193 | }
194 | escaped = !escaped && next == "\\";
195 | }
196 | return ret("quasi", "string-2", stream.current());
197 | }
198 |
199 | var brackets = "([{}])";
200 | // This is a crude lookahead trick to try and notice that we're
201 | // parsing the argument patterns for a fat-arrow function before we
202 | // actually hit the arrow token. It only works if the arrow is on
203 | // the same line as the arguments and there's no strange noise
204 | // (comments) in between. Fallback is to only notice when we hit the
205 | // arrow, and not declare the arguments as locals for the arrow
206 | // body.
207 | function findFatArrow(stream, state) {
208 | if (state.fatArrowAt) state.fatArrowAt = null;
209 | var arrow = stream.string.indexOf("=>", stream.start);
210 | if (arrow < 0) return;
211 |
212 | var depth = 0, sawSomething = false;
213 | for (var pos = arrow - 1; pos >= 0; --pos) {
214 | var ch = stream.string.charAt(pos);
215 | var bracket = brackets.indexOf(ch);
216 | if (bracket >= 0 && bracket < 3) {
217 | if (!depth) { ++pos; break; }
218 | if (--depth == 0) break;
219 | } else if (bracket >= 3 && bracket < 6) {
220 | ++depth;
221 | } else if (wordRE.test(ch)) {
222 | sawSomething = true;
223 | } else if (/["'\/]/.test(ch)) {
224 | return;
225 | } else if (sawSomething && !depth) {
226 | ++pos;
227 | break;
228 | }
229 | }
230 | if (sawSomething && !depth) state.fatArrowAt = pos;
231 | }
232 |
233 | // Parser
234 |
235 | var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
236 |
237 | function JSLexical(indented, column, type, align, prev, info) {
238 | this.indented = indented;
239 | this.column = column;
240 | this.type = type;
241 | this.prev = prev;
242 | this.info = info;
243 | if (align != null) this.align = align;
244 | }
245 |
246 | function inScope(state, varname) {
247 | for (var v = state.localVars; v; v = v.next)
248 | if (v.name == varname) return true;
249 | for (var cx = state.context; cx; cx = cx.prev) {
250 | for (var v = cx.vars; v; v = v.next)
251 | if (v.name == varname) return true;
252 | }
253 | }
254 |
255 | function parseJS(state, style, type, content, stream) {
256 | var cc = state.cc;
257 | // Communicate our context to the combinators.
258 | // (Less wasteful than consing up a hundred closures on every call.)
259 | cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
260 |
261 | if (!state.lexical.hasOwnProperty("align"))
262 | state.lexical.align = true;
263 |
264 | while(true) {
265 | var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
266 | if (combinator(type, content)) {
267 | while(cc.length && cc[cc.length - 1].lex)
268 | cc.pop()();
269 | if (cx.marked) return cx.marked;
270 | if (type == "variable" && inScope(state, content)) return "variable-2";
271 | return style;
272 | }
273 | }
274 | }
275 |
276 | // Combinator utils
277 |
278 | var cx = {state: null, column: null, marked: null, cc: null};
279 | function pass() {
280 | for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
281 | }
282 | function cont() {
283 | pass.apply(null, arguments);
284 | return true;
285 | }
286 | function register(varname) {
287 | function inList(list) {
288 | for (var v = list; v; v = v.next)
289 | if (v.name == varname) return true;
290 | return false;
291 | }
292 | var state = cx.state;
293 | cx.marked = "def";
294 | if (state.context) {
295 | if (inList(state.localVars)) return;
296 | state.localVars = {name: varname, next: state.localVars};
297 | } else {
298 | if (inList(state.globalVars)) return;
299 | if (parserConfig.globalVars)
300 | state.globalVars = {name: varname, next: state.globalVars};
301 | }
302 | }
303 |
304 | // Combinators
305 |
306 | var defaultVars = {name: "this", next: {name: "arguments"}};
307 | function pushcontext() {
308 | cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
309 | cx.state.localVars = defaultVars;
310 | }
311 | function popcontext() {
312 | cx.state.localVars = cx.state.context.vars;
313 | cx.state.context = cx.state.context.prev;
314 | }
315 | function pushlex(type, info) {
316 | var result = function() {
317 | var state = cx.state, indent = state.indented;
318 | if (state.lexical.type == "stat") indent = state.lexical.indented;
319 | else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
320 | indent = outer.indented;
321 | state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
322 | };
323 | result.lex = true;
324 | return result;
325 | }
326 | function poplex() {
327 | var state = cx.state;
328 | if (state.lexical.prev) {
329 | if (state.lexical.type == ")")
330 | state.indented = state.lexical.indented;
331 | state.lexical = state.lexical.prev;
332 | }
333 | }
334 | poplex.lex = true;
335 |
336 | function expect(wanted) {
337 | function exp(type) {
338 | if (type == wanted) return cont();
339 | else if (wanted == ";") return pass();
340 | else return cont(exp);
341 | };
342 | return exp;
343 | }
344 |
345 | function statement(type, value) {
346 | if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
347 | if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
348 | if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
349 | if (type == "{") return cont(pushlex("}"), block, poplex);
350 | if (type == ";") return cont();
351 | if (type == "if") {
352 | if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
353 | cx.state.cc.pop()();
354 | return cont(pushlex("form"), expression, statement, poplex, maybeelse);
355 | }
356 | if (type == "function") return cont(functiondef);
357 | if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
358 | if (type == "variable") return cont(pushlex("stat"), maybelabel);
359 | if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
360 | block, poplex, poplex);
361 | if (type == "case") return cont(expression, expect(":"));
362 | if (type == "default") return cont(expect(":"));
363 | if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
364 | statement, poplex, popcontext);
365 | if (type == "class") return cont(pushlex("form"), className, poplex);
366 | if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
367 | if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
368 | if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
369 | return pass(pushlex("stat"), expression, expect(";"), poplex);
370 | }
371 | function expression(type) {
372 | return expressionInner(type, false);
373 | }
374 | function expressionNoComma(type) {
375 | return expressionInner(type, true);
376 | }
377 | function expressionInner(type, noComma) {
378 | if (cx.state.fatArrowAt == cx.stream.start) {
379 | var body = noComma ? arrowBodyNoComma : arrowBody;
380 | if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
381 | else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
382 | }
383 |
384 | var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
385 | if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
386 | if (type == "function") return cont(functiondef, maybeop);
387 | if (type == "keyword c") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
388 | if (type == "(") return cont(pushlex(")"), maybeexpression, comprehension, expect(")"), poplex, maybeop);
389 | if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
390 | if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
391 | if (type == "{") return contCommasep(objprop, "}", null, maybeop);
392 | if (type == "quasi") return pass(quasi, maybeop);
393 | if (type == "new") return cont(maybeTarget(noComma));
394 | return cont();
395 | }
396 | function maybeexpression(type) {
397 | if (type.match(/[;\}\)\],]/)) return pass();
398 | return pass(expression);
399 | }
400 | function maybeexpressionNoComma(type) {
401 | if (type.match(/[;\}\)\],]/)) return pass();
402 | return pass(expressionNoComma);
403 | }
404 |
405 | function maybeoperatorComma(type, value) {
406 | if (type == ",") return cont(expression);
407 | return maybeoperatorNoComma(type, value, false);
408 | }
409 | function maybeoperatorNoComma(type, value, noComma) {
410 | var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
411 | var expr = noComma == false ? expression : expressionNoComma;
412 | if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
413 | if (type == "operator") {
414 | if (/\+\+|--/.test(value)) return cont(me);
415 | if (value == "?") return cont(expression, expect(":"), expr);
416 | return cont(expr);
417 | }
418 | if (type == "quasi") { return pass(quasi, me); }
419 | if (type == ";") return;
420 | if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
421 | if (type == ".") return cont(property, me);
422 | if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
423 | }
424 | function quasi(type, value) {
425 | if (type != "quasi") return pass();
426 | if (value.slice(value.length - 2) != "${") return cont(quasi);
427 | return cont(expression, continueQuasi);
428 | }
429 | function continueQuasi(type) {
430 | if (type == "}") {
431 | cx.marked = "string-2";
432 | cx.state.tokenize = tokenQuasi;
433 | return cont(quasi);
434 | }
435 | }
436 | function arrowBody(type) {
437 | findFatArrow(cx.stream, cx.state);
438 | return pass(type == "{" ? statement : expression);
439 | }
440 | function arrowBodyNoComma(type) {
441 | findFatArrow(cx.stream, cx.state);
442 | return pass(type == "{" ? statement : expressionNoComma);
443 | }
444 | function maybeTarget(noComma) {
445 | return function(type) {
446 | if (type == ".") return cont(noComma ? targetNoComma : target);
447 | else return pass(noComma ? expressionNoComma : expression);
448 | };
449 | }
450 | function target(_, value) {
451 | if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
452 | }
453 | function targetNoComma(_, value) {
454 | if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
455 | }
456 | function maybelabel(type) {
457 | if (type == ":") return cont(poplex, statement);
458 | return pass(maybeoperatorComma, expect(";"), poplex);
459 | }
460 | function property(type) {
461 | if (type == "variable") {cx.marked = "property"; return cont();}
462 | }
463 | function objprop(type, value) {
464 | if (type == "variable" || cx.style == "keyword") {
465 | cx.marked = "property";
466 | if (value == "get" || value == "set") return cont(getterSetter);
467 | return cont(afterprop);
468 | } else if (type == "number" || type == "string") {
469 | cx.marked = jsonldMode ? "property" : (cx.style + " property");
470 | return cont(afterprop);
471 | } else if (type == "jsonld-keyword") {
472 | return cont(afterprop);
473 | } else if (type == "modifier") {
474 | return cont(objprop)
475 | } else if (type == "[") {
476 | return cont(expression, expect("]"), afterprop);
477 | } else if (type == "spread") {
478 | return cont(expression);
479 | }
480 | }
481 | function getterSetter(type) {
482 | if (type != "variable") return pass(afterprop);
483 | cx.marked = "property";
484 | return cont(functiondef);
485 | }
486 | function afterprop(type) {
487 | if (type == ":") return cont(expressionNoComma);
488 | if (type == "(") return pass(functiondef);
489 | }
490 | function commasep(what, end) {
491 | function proceed(type) {
492 | if (type == ",") {
493 | var lex = cx.state.lexical;
494 | if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
495 | return cont(what, proceed);
496 | }
497 | if (type == end) return cont();
498 | return cont(expect(end));
499 | }
500 | return function(type) {
501 | if (type == end) return cont();
502 | return pass(what, proceed);
503 | };
504 | }
505 | function contCommasep(what, end, info) {
506 | for (var i = 3; i < arguments.length; i++)
507 | cx.cc.push(arguments[i]);
508 | return cont(pushlex(end, info), commasep(what, end), poplex);
509 | }
510 | function block(type) {
511 | if (type == "}") return cont();
512 | return pass(statement, block);
513 | }
514 | function maybetype(type) {
515 | if (isTS && type == ":") return cont(typedef);
516 | }
517 | function maybedefault(_, value) {
518 | if (value == "=") return cont(expressionNoComma);
519 | }
520 | function typedef(type) {
521 | if (type == "variable") {cx.marked = "variable-3"; return cont();}
522 | }
523 | function vardef() {
524 | return pass(pattern, maybetype, maybeAssign, vardefCont);
525 | }
526 | function pattern(type, value) {
527 | if (type == "modifier") return cont(pattern)
528 | if (type == "variable") { register(value); return cont(); }
529 | if (type == "spread") return cont(pattern);
530 | if (type == "[") return contCommasep(pattern, "]");
531 | if (type == "{") return contCommasep(proppattern, "}");
532 | }
533 | function proppattern(type, value) {
534 | if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
535 | register(value);
536 | return cont(maybeAssign);
537 | }
538 | if (type == "variable") cx.marked = "property";
539 | if (type == "spread") return cont(pattern);
540 | if (type == "}") return pass();
541 | return cont(expect(":"), pattern, maybeAssign);
542 | }
543 | function maybeAssign(_type, value) {
544 | if (value == "=") return cont(expressionNoComma);
545 | }
546 | function vardefCont(type) {
547 | if (type == ",") return cont(vardef);
548 | }
549 | function maybeelse(type, value) {
550 | if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
551 | }
552 | function forspec(type) {
553 | if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
554 | }
555 | function forspec1(type) {
556 | if (type == "var") return cont(vardef, expect(";"), forspec2);
557 | if (type == ";") return cont(forspec2);
558 | if (type == "variable") return cont(formaybeinof);
559 | return pass(expression, expect(";"), forspec2);
560 | }
561 | function formaybeinof(_type, value) {
562 | if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
563 | return cont(maybeoperatorComma, forspec2);
564 | }
565 | function forspec2(type, value) {
566 | if (type == ";") return cont(forspec3);
567 | if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
568 | return pass(expression, expect(";"), forspec3);
569 | }
570 | function forspec3(type) {
571 | if (type != ")") cont(expression);
572 | }
573 | function functiondef(type, value) {
574 | if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
575 | if (type == "variable") {register(value); return cont(functiondef);}
576 | if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, statement, popcontext);
577 | }
578 | function funarg(type) {
579 | if (type == "spread") return cont(funarg);
580 | return pass(pattern, maybetype, maybedefault);
581 | }
582 | function className(type, value) {
583 | if (type == "variable") {register(value); return cont(classNameAfter);}
584 | }
585 | function classNameAfter(type, value) {
586 | if (value == "extends") return cont(expression, classNameAfter);
587 | if (type == "{") return cont(pushlex("}"), classBody, poplex);
588 | }
589 | function classBody(type, value) {
590 | if (type == "variable" || cx.style == "keyword") {
591 | if (value == "static") {
592 | cx.marked = "keyword";
593 | return cont(classBody);
594 | }
595 | cx.marked = "property";
596 | if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
597 | return cont(functiondef, classBody);
598 | }
599 | if (value == "*") {
600 | cx.marked = "keyword";
601 | return cont(classBody);
602 | }
603 | if (type == ";") return cont(classBody);
604 | if (type == "}") return cont();
605 | }
606 | function classGetterSetter(type) {
607 | if (type != "variable") return pass();
608 | cx.marked = "property";
609 | return cont();
610 | }
611 | function afterExport(_type, value) {
612 | if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
613 | if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
614 | return pass(statement);
615 | }
616 | function afterImport(type) {
617 | if (type == "string") return cont();
618 | return pass(importSpec, maybeFrom);
619 | }
620 | function importSpec(type, value) {
621 | if (type == "{") return contCommasep(importSpec, "}");
622 | if (type == "variable") register(value);
623 | if (value == "*") cx.marked = "keyword";
624 | return cont(maybeAs);
625 | }
626 | function maybeAs(_type, value) {
627 | if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
628 | }
629 | function maybeFrom(_type, value) {
630 | if (value == "from") { cx.marked = "keyword"; return cont(expression); }
631 | }
632 | function arrayLiteral(type) {
633 | if (type == "]") return cont();
634 | return pass(expressionNoComma, maybeArrayComprehension);
635 | }
636 | function maybeArrayComprehension(type) {
637 | if (type == "for") return pass(comprehension, expect("]"));
638 | if (type == ",") return cont(commasep(maybeexpressionNoComma, "]"));
639 | return pass(commasep(expressionNoComma, "]"));
640 | }
641 | function comprehension(type) {
642 | if (type == "for") return cont(forspec, comprehension);
643 | if (type == "if") return cont(expression, comprehension);
644 | }
645 |
646 | function isContinuedStatement(state, textAfter) {
647 | return state.lastType == "operator" || state.lastType == "," ||
648 | isOperatorChar.test(textAfter.charAt(0)) ||
649 | /[,.]/.test(textAfter.charAt(0));
650 | }
651 |
652 | // Interface
653 |
654 | return {
655 | startState: function(basecolumn) {
656 | var state = {
657 | tokenize: tokenBase,
658 | lastType: "sof",
659 | cc: [],
660 | lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
661 | localVars: parserConfig.localVars,
662 | context: parserConfig.localVars && {vars: parserConfig.localVars},
663 | indented: basecolumn || 0
664 | };
665 | if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
666 | state.globalVars = parserConfig.globalVars;
667 | return state;
668 | },
669 |
670 | token: function(stream, state) {
671 | if (stream.sol()) {
672 | if (!state.lexical.hasOwnProperty("align"))
673 | state.lexical.align = false;
674 | state.indented = stream.indentation();
675 | findFatArrow(stream, state);
676 | }
677 | if (state.tokenize != tokenComment && stream.eatSpace()) return null;
678 | var style = state.tokenize(stream, state);
679 | if (type == "comment") return style;
680 | state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
681 | return parseJS(state, style, type, content, stream);
682 | },
683 |
684 | indent: function(state, textAfter) {
685 | if (state.tokenize == tokenComment) return CodeMirror.Pass;
686 | if (state.tokenize != tokenBase) return 0;
687 | var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
688 | // Kludge to prevent 'maybelse' from blocking lexical scope pops
689 | if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
690 | var c = state.cc[i];
691 | if (c == poplex) lexical = lexical.prev;
692 | else if (c != maybeelse) break;
693 | }
694 | if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
695 | if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
696 | lexical = lexical.prev;
697 | var type = lexical.type, closing = firstChar == type;
698 |
699 | if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
700 | else if (type == "form" && firstChar == "{") return lexical.indented;
701 | else if (type == "form") return lexical.indented + indentUnit;
702 | else if (type == "stat")
703 | return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
704 | else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
705 | return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
706 | else if (lexical.align) return lexical.column + (closing ? 0 : 1);
707 | else return lexical.indented + (closing ? 0 : indentUnit);
708 | },
709 |
710 | electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
711 | blockCommentStart: jsonMode ? null : "/*",
712 | blockCommentEnd: jsonMode ? null : "*/",
713 | lineComment: jsonMode ? null : "//",
714 | fold: "brace",
715 | closeBrackets: "()[]{}''\"\"``",
716 |
717 | helperType: jsonMode ? "json" : "javascript",
718 | jsonldMode: jsonldMode,
719 | jsonMode: jsonMode,
720 |
721 | expressionAllowed: expressionAllowed,
722 | skipExpression: function(state) {
723 | var top = state.cc[state.cc.length - 1]
724 | if (top == expression || top == expressionNoComma) state.cc.pop()
725 | }
726 | };
727 | });
728 |
729 | CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
730 |
731 | CodeMirror.defineMIME("text/javascript", "javascript");
732 | CodeMirror.defineMIME("text/ecmascript", "javascript");
733 | CodeMirror.defineMIME("application/javascript", "javascript");
734 | CodeMirror.defineMIME("application/x-javascript", "javascript");
735 | CodeMirror.defineMIME("application/ecmascript", "javascript");
736 | CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
737 | CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
738 | CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
739 | CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
740 | CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
741 |
742 | });
743 |
--------------------------------------------------------------------------------
/public/mode/javascript/json-ld.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: JSON-LD mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
27 |
28 |
29 | JSON-LD mode
30 |
31 |
32 |
61 |
62 |
70 |
71 | This is a specialization of the JavaScript mode .
72 |
73 |
--------------------------------------------------------------------------------
/public/mode/javascript/test.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | (function() {
5 | var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
6 | function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
7 |
8 | MT("locals",
9 | "[keyword function] [def foo]([def a], [def b]) { [keyword var] [def c] [operator =] [number 10]; [keyword return] [variable-2 a] [operator +] [variable-2 c] [operator +] [variable d]; }");
10 |
11 | MT("comma-and-binop",
12 | "[keyword function](){ [keyword var] [def x] [operator =] [number 1] [operator +] [number 2], [def y]; }");
13 |
14 | MT("destructuring",
15 | "([keyword function]([def a], [[[def b], [def c] ]]) {",
16 | " [keyword let] {[def d], [property foo]: [def c][operator =][number 10], [def x]} [operator =] [variable foo]([variable-2 a]);",
17 | " [[[variable-2 c], [variable y] ]] [operator =] [variable-2 c];",
18 | "})();");
19 |
20 | MT("destructure_trailing_comma",
21 | "[keyword let] {[def a], [def b],} [operator =] [variable foo];",
22 | "[keyword let] [def c];"); // Parser still in good state?
23 |
24 | MT("class_body",
25 | "[keyword class] [def Foo] {",
26 | " [property constructor]() {}",
27 | " [property sayName]() {",
28 | " [keyword return] [string-2 `foo${][variable foo][string-2 }oo`];",
29 | " }",
30 | "}");
31 |
32 | MT("class",
33 | "[keyword class] [def Point] [keyword extends] [variable SuperThing] {",
34 | " [property get] [property prop]() { [keyword return] [number 24]; }",
35 | " [property constructor]([def x], [def y]) {",
36 | " [keyword super]([string 'something']);",
37 | " [keyword this].[property x] [operator =] [variable-2 x];",
38 | " }",
39 | "}");
40 |
41 | MT("import",
42 | "[keyword function] [def foo]() {",
43 | " [keyword import] [def $] [keyword from] [string 'jquery'];",
44 | " [keyword import] { [def encrypt], [def decrypt] } [keyword from] [string 'crypto'];",
45 | "}");
46 |
47 | MT("const",
48 | "[keyword function] [def f]() {",
49 | " [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
50 | "}");
51 |
52 | MT("for/of",
53 | "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}");
54 |
55 | MT("generator",
56 | "[keyword function*] [def repeat]([def n]) {",
57 | " [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
58 | " [keyword yield] [variable-2 i];",
59 | "}");
60 |
61 | MT("quotedStringAddition",
62 | "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
63 |
64 | MT("quotedFatArrow",
65 | "[keyword let] [def f] [operator =] [variable a] [operator +] [string '=>'] [operator +] [variable c];");
66 |
67 | MT("fatArrow",
68 | "[variable array].[property filter]([def a] [operator =>] [variable-2 a] [operator +] [number 1]);",
69 | "[variable a];", // No longer in scope
70 | "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
71 | "[variable c];");
72 |
73 | MT("spread",
74 | "[keyword function] [def f]([def a], [meta ...][def b]) {",
75 | " [variable something]([variable-2 a], [meta ...][variable-2 b]);",
76 | "}");
77 |
78 | MT("comprehension",
79 | "[keyword function] [def f]() {",
80 | " [[([variable x] [operator +] [number 1]) [keyword for] ([keyword var] [def x] [keyword in] [variable y]) [keyword if] [variable pred]([variable-2 x]) ]];",
81 | " ([variable u] [keyword for] ([keyword var] [def u] [keyword of] [variable generateValues]()) [keyword if] ([variable-2 u].[property color] [operator ===] [string 'blue']));",
82 | "}");
83 |
84 | MT("quasi",
85 | "[variable re][string-2 `fofdlakj${][variable x] [operator +] ([variable re][string-2 `foo`]) [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
86 |
87 | MT("quasi_no_function",
88 | "[variable x] [operator =] [string-2 `fofdlakj${][variable x] [operator +] [string-2 `foo`] [operator +] [number 1][string-2 }fdsa`] [operator +] [number 2]");
89 |
90 | MT("indent_statement",
91 | "[keyword var] [def x] [operator =] [number 10]",
92 | "[variable x] [operator +=] [variable y] [operator +]",
93 | " [atom Infinity]",
94 | "[keyword debugger];");
95 |
96 | MT("indent_if",
97 | "[keyword if] ([number 1])",
98 | " [keyword break];",
99 | "[keyword else] [keyword if] ([number 2])",
100 | " [keyword continue];",
101 | "[keyword else]",
102 | " [number 10];",
103 | "[keyword if] ([number 1]) {",
104 | " [keyword break];",
105 | "} [keyword else] [keyword if] ([number 2]) {",
106 | " [keyword continue];",
107 | "} [keyword else] {",
108 | " [number 10];",
109 | "}");
110 |
111 | MT("indent_for",
112 | "[keyword for] ([keyword var] [def i] [operator =] [number 0];",
113 | " [variable i] [operator <] [number 100];",
114 | " [variable i][operator ++])",
115 | " [variable doSomething]([variable i]);",
116 | "[keyword debugger];");
117 |
118 | MT("indent_c_style",
119 | "[keyword function] [def foo]()",
120 | "{",
121 | " [keyword debugger];",
122 | "}");
123 |
124 | MT("indent_else",
125 | "[keyword for] (;;)",
126 | " [keyword if] ([variable foo])",
127 | " [keyword if] ([variable bar])",
128 | " [number 1];",
129 | " [keyword else]",
130 | " [number 2];",
131 | " [keyword else]",
132 | " [number 3];");
133 |
134 | MT("indent_funarg",
135 | "[variable foo]([number 10000],",
136 | " [keyword function]([def a]) {",
137 | " [keyword debugger];",
138 | "};");
139 |
140 | MT("indent_below_if",
141 | "[keyword for] (;;)",
142 | " [keyword if] ([variable foo])",
143 | " [number 1];",
144 | "[number 2];");
145 |
146 | MT("multilinestring",
147 | "[keyword var] [def x] [operator =] [string 'foo\\]",
148 | "[string bar'];");
149 |
150 | MT("scary_regexp",
151 | "[string-2 /foo[[/]]bar/];");
152 |
153 | MT("indent_strange_array",
154 | "[keyword var] [def x] [operator =] [[",
155 | " [number 1],,",
156 | " [number 2],",
157 | "]];",
158 | "[number 10];");
159 |
160 | MT("param_default",
161 | "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
162 | " [keyword return] [variable-2 x];",
163 | "}");
164 |
165 | MT("new_target",
166 | "[keyword function] [def F]([def target]) {",
167 | " [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {",
168 | " [keyword return] [keyword new]",
169 | " .[keyword target];",
170 | " }",
171 | "}");
172 |
173 | var jsonld_mode = CodeMirror.getMode(
174 | {indentUnit: 2},
175 | {name: "javascript", jsonld: true}
176 | );
177 | function LD(name) {
178 | test.mode(name, jsonld_mode, Array.prototype.slice.call(arguments, 1));
179 | }
180 |
181 | LD("json_ld_keywords",
182 | '{',
183 | ' [meta "@context"]: {',
184 | ' [meta "@base"]: [string "http://example.com"],',
185 | ' [meta "@vocab"]: [string "http://xmlns.com/foaf/0.1/"],',
186 | ' [property "likesFlavor"]: {',
187 | ' [meta "@container"]: [meta "@list"]',
188 | ' [meta "@reverse"]: [string "@beFavoriteOf"]',
189 | ' },',
190 | ' [property "nick"]: { [meta "@container"]: [meta "@set"] },',
191 | ' [property "nick"]: { [meta "@container"]: [meta "@index"] }',
192 | ' },',
193 | ' [meta "@graph"]: [[ {',
194 | ' [meta "@id"]: [string "http://dbpedia.org/resource/John_Lennon"],',
195 | ' [property "name"]: [string "John Lennon"],',
196 | ' [property "modified"]: {',
197 | ' [meta "@value"]: [string "2010-05-29T14:17:39+02:00"],',
198 | ' [meta "@type"]: [string "http://www.w3.org/2001/XMLSchema#dateTime"]',
199 | ' }',
200 | ' } ]]',
201 | '}');
202 |
203 | LD("json_ld_fake",
204 | '{',
205 | ' [property "@fake"]: [string "@fake"],',
206 | ' [property "@contextual"]: [string "@identifier"],',
207 | ' [property "user@domain.com"]: [string "@graphical"],',
208 | ' [property "@ID"]: [string "@@ID"]',
209 | '}');
210 | })();
211 |
--------------------------------------------------------------------------------
/public/mode/javascript/typescript.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | CodeMirror: TypeScript mode
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
24 |
25 |
26 | TypeScript mode
27 |
28 |
29 |
51 |
52 |
59 |
60 | This is a specialization of the JavaScript mode .
61 |
62 |
--------------------------------------------------------------------------------
/public/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/src/form.js:
--------------------------------------------------------------------------------
1 | /*global CodeMirror*/
2 | var event = require('event')
3 | var request = require('request')
4 | var Notice = require('notice')
5 | var Emitter = require('emitter')
6 | var Nprogress = require('nprogress')
7 |
8 | var editor = CodeMirror.fromTextArea(document.getElementById('content'), {
9 | tabsize: 2,
10 | autofocus: true,
11 | lineNumbers: true,
12 | matchBrackets: true,
13 | lineWrapping: true,
14 | scrollbarStyle: 'simple'
15 | })
16 |
17 | function Panel(root) {
18 | this.root = root
19 | this.input = this.root.querySelector('[name="route"]')
20 | var save = this.root.querySelector('.save')
21 | event.bind(save, 'click', this.save.bind(this))
22 | }
23 |
24 | Emitter(Panel.prototype)
25 |
26 | Panel.prototype.save = function () {
27 | var route = this.input.value
28 | if (!route) {
29 | new Notice('route should not be empty', {
30 | type: 'error'
31 | })
32 | return
33 | }
34 | if (!/^(GET|POST|PUT|DELETE|PATCH)\s/.test(route)) {
35 | new Notice('Need GET/POST/PUT/DELETE/PATH method', {
36 | type: 'error'
37 | })
38 | return
39 | }
40 | if (/^\w+\shttp/.test(route)) {
41 | var method = route.match(/^\w+/)[0]
42 | route = method + ' '+ parseUrl(route.replace(method, ''))
43 | this.input.value = route
44 | } else if (!/\w+\s\//.test(route)) {
45 | new Notice('Invalid request url', {
46 | type: 'error'
47 | })
48 | return
49 | }
50 | var content = editor.getValue()
51 | var self = this
52 | Nprogress.start()
53 | request
54 | .post('/_addapi')
55 | .type('form')
56 | .accept('json')
57 | .send({route: route, content: content})
58 | .end(function (res) {
59 | Nprogress.done()
60 | if (res.error) {
61 | if (res.body && res.body.error) {
62 | new Notice(res.body.error, {
63 | type: 'error'
64 | })
65 | } else {
66 | new Notice('request error ' + res.status, {
67 | type: 'error'
68 | })
69 | }
70 | } else {
71 | new Notice('Saved:' + route, {
72 | type: 'success',
73 | duration: 2000
74 | })
75 | self.emit('save', route)
76 | }
77 | })
78 | }
79 |
80 | Panel.prototype.load = function (route) {
81 | Nprogress.start()
82 | var parts = route.split(' ')
83 | var self = this
84 | request(parts[0], parts[1])
85 | .end(function (res) {
86 | Nprogress.done()
87 | if (res.error) {
88 | if (res.body && res.body.error) {
89 | new Notice(res.body.error, {
90 | type: 'error'
91 | })
92 | } else {
93 | new Notice('request error ' + res.status, {
94 | type: 'error'
95 | })
96 | }
97 | } else {
98 | self.input.value = route
99 | editor.setValue(res.text)
100 | }
101 | })
102 | }
103 |
104 | Panel.prototype.empty = function () {
105 | this.input.value = ''
106 | editor.setValue('')
107 | }
108 |
109 | function parseUrl(url) {
110 | var parser = document.createElement('a')
111 | parser.href = url
112 | return parser.pathname + parser.search
113 | }
114 |
115 | module.exports = Panel
116 |
--------------------------------------------------------------------------------
/public/src/index.js:
--------------------------------------------------------------------------------
1 | var event = require('event')
2 | var Form = require('./form')
3 | var Routes = require('./routes')
4 | var Base64 = require('js-base64').Base64;
5 | var Notice = require('notice')
6 | var filePicker = require('file-picker')
7 | var request = require('request')
8 |
9 | var routes = new Routes(document.querySelector('.left-panel'))
10 | var form = new Form(document.querySelector('.right-panel'))
11 |
12 | routes.on('active', function (li) {
13 | var route = li.querySelector('.text').textContent
14 | form.load(route)
15 | })
16 |
17 | form.on('save', function (route) {
18 | var id = Base64.encode(route)
19 | if (routes.hasItem(route)) {
20 | routes.active(id, false)
21 | } else {
22 | routes.addItem(route, true)
23 | routes.active(id, false)
24 | }
25 | })
26 |
27 | routes.on('empty', function () {
28 | form.empty()
29 | })
30 |
31 | event.bind(document.getElementById('import'), 'click', function (e) {
32 | filePicker(function(files){
33 | var file = files[0]
34 | var reader = new FileReader()
35 | reader.onload = function (e) {
36 | try {
37 | var obj = JSON.parse(reader.result)
38 | } catch(e) {
39 | new Notice('not valid json file', {
40 | type: 'error'
41 | })
42 | }
43 | importApis(obj)
44 | }
45 | reader.readAsText(file)
46 | })
47 | })
48 |
49 | function importApis(data) {
50 | request
51 | .post('/_importapi')
52 | .type('json')
53 | .accept('json')
54 | .send(data)
55 | .end(function (res) {
56 | if (res.error) {
57 | if (res.body && res.body.error) {
58 | new Notice(res.body.error, {
59 | type: 'error'
60 | })
61 | } else {
62 | new Notice('request error ' + res.status, {
63 | type: 'error'
64 | })
65 | }
66 | } else {
67 | window.location.reload()
68 | }
69 | })
70 | }
71 |
72 | event.bind(document, 'keydown', function (e) {
73 | if (e.keyCode == 83 && e.metaKey) {
74 | e.preventDefault()
75 | form.save()
76 | }
77 | })
78 |
--------------------------------------------------------------------------------
/public/src/item.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/public/src/routes.js:
--------------------------------------------------------------------------------
1 | var radio = require('radio')
2 | var Notice = require('notice')
3 | var request = require('request')
4 | var Emitter = require('emitter')
5 | var classes = require('classes')
6 | var event = require('event')
7 | var delegate = require('delegate')
8 | var closest = require('closest')
9 | var transitionEnd = require('transitionend-property')
10 | var spin = require('spin')
11 | var domify = require('domify')
12 | var template = require('./item.html')
13 | var Base64 = require('js-base64').Base64
14 | var Sortable = require('sweet-sortable')
15 |
16 | function Routes(root) {
17 | var search = root.querySelector('.search')
18 | this.root = root
19 | this.list = document.querySelector('#routes > ul')
20 | event.bind(search, 'input', this.filter.bind(this))
21 | delegate.bind(this.list, '.close', 'click', this.removeItem.bind(this))
22 | delegate.bind(this.list, '.item', 'click', this.activeItem.bind(this))
23 | this.loadItems()
24 | var sortable = new Sortable(this.list)
25 | sortable.bind('li')
26 | sortable.on('update', this.saveOrder.bind(this))
27 | }
28 |
29 | Emitter(Routes.prototype)
30 |
31 | Routes.prototype.saveOrder = function () {
32 | var lis = this.list.children
33 | var ids = []
34 | for (var i = 0, l = lis.length; i < l; i++) {
35 | var id = lis[i].getAttribute('data-id')
36 | if (id) ids.push(id)
37 | }
38 | window.localStorage.setItem('routes', JSON.stringify(ids))
39 | }
40 |
41 | Routes.prototype.loadItems = function () {
42 | var s = spin(this.root)
43 | var self = this
44 | request
45 | .get('/_listapi')
46 | .end(function (res) {
47 | s.remove()
48 | if (res.error) {
49 | if (res.body && res.body.error) {
50 | new Notice(res.body.error, {
51 | type: 'error'
52 | })
53 | } else {
54 | new Notice('request error ' + res.status, {
55 | type: 'error'
56 | })
57 | }
58 | } else {
59 | var keys = self.orderItems(res.body.keys)
60 | keys.forEach(function (key) {
61 | self.addItem(key)
62 | })
63 | var active_id = window.localStorage.getItem('active')
64 | if (active_id != null) {
65 | self.active(active_id, true)
66 | } else {
67 | var li = self.list.children[0]
68 | if (li) self.active(li, true)
69 | }
70 | }
71 | })
72 | }
73 |
74 | Routes.prototype.orderItems = function (keys) {
75 | var res = []
76 | var routes = JSON.parse(window.localStorage.getItem('routes')) || []
77 | for (var i = 0, l = routes.length; i < l; i++) {
78 | var route = Base64.decode(routes[i])
79 | if (keys.indexOf(route) != -1) {
80 | res.push(route)
81 | }
82 | }
83 | keys.forEach(function (key) {
84 | if (res.indexOf(key) == -1) {
85 | res.unshift(key)
86 | }
87 | })
88 | return res
89 | }
90 |
91 | Routes.prototype.addItem = function (route, prepend) {
92 | var el = domify(template.replace('{{route}}', route))
93 | el.setAttribute('data-id', Base64.encode(route))
94 | if (prepend) {
95 | classes(el).add('adding')
96 | if (this.list.children.length) {
97 | this.list.insertBefore(el, this.list.firstElementChild)
98 | } else {
99 | this.list.appendChild(el)
100 | }
101 | } else {
102 | this.list.appendChild(el)
103 | }
104 | setTimeout(function () {
105 | classes(el).remove('adding')
106 | }, 1000)
107 | if (prepend) this.saveOrder()
108 | }
109 |
110 | Routes.prototype.hasItem = function (route) {
111 | var id = Base64.encode(route)
112 | return this.list.querySelector('[data-id="' + id + '"]') != null
113 | }
114 |
115 | Routes.prototype.filter = function (e) {
116 | var val = e.target.value
117 | var lis = [].slice.call(this.list.querySelectorAll('li'))
118 | lis.forEach(function (li) {
119 | var text = li.textContent
120 | if (val == '' || text.indexOf(val) !== -1) {
121 | li.style.display = 'block'
122 | } else {
123 | li.style.display = 'none'
124 | }
125 | })
126 | }
127 |
128 | Routes.prototype.removeItem = function (e) {
129 | var li = closest(e.target, 'li')
130 | if (classes(li).has('removing')) return
131 | e.stopPropagation()
132 | e.preventDefault()
133 | classes(li).add('removing')
134 | var route = li.querySelector('.text').textContent
135 | var self = this
136 | request
137 | .del('/_deleteapi')
138 | .query({route: route})
139 | .end(function (res) {
140 | if (res.error) {
141 | if (res.body.error) {
142 | new Notice(res.body.error, {
143 | type: 'error'
144 | })
145 | } else {
146 | new Notice('request error ' + res.status, {
147 | type: 'error'
148 | })
149 | }
150 | } else {
151 | new Notice('Removed: ' + route, {
152 | type: 'success',
153 | duration: 2000
154 | })
155 | self.emit('remove', route)
156 | event.bind(li, transitionEnd, function () {
157 | if (li.parentNode) li.parentNode.removeChild(li)
158 | self.saveOrder()
159 | })
160 | classes(li).add('remove')
161 | if (self.list.children.length === 1) {
162 | return self.emit('empty')
163 | }
164 | if (classes(li).has('active')) {
165 | var lis = self.list.children
166 | for (var i = 0, l = lis.length; i < l; i++) {
167 | if (!classes(lis[i]).has('removing')) {
168 | self.active(lis[i], true)
169 | break;
170 | }
171 | }
172 | }
173 | }
174 | })
175 | }
176 |
177 | Routes.prototype.active = function (li, emit) {
178 | if (typeof li === 'string') {
179 | li = this.list.querySelector('[data-id="'+li+'"]')
180 | if (!li && this.list.children.length) {
181 | li = this.list.children[0]
182 | }
183 | }
184 | if (!li) return
185 | if (classes(li).has('active')) return
186 | radio(li)
187 | if (emit) {
188 | window.localStorage.setItem('active', li.getAttribute('data-id'))
189 | this.emit('active', li)
190 | }
191 | }
192 |
193 | Routes.prototype.activeItem = function (e) {
194 | var li = closest(e.target, 'li')
195 | if (e.defaultPrevented) return
196 | if (!li) return
197 | this.active(li, true)
198 | }
199 |
200 | module.exports = Routes
201 |
--------------------------------------------------------------------------------
/public/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | font: 500 14px/1.6 "Helvetica Neue", Helvetica, sans-serif;
3 | padding: 0px;
4 | margin: 0px;
5 | overflow: hidden;
6 | -webkit-touch-callout: none;
7 | -webkit-text-size-adjust: 100%;
8 | -ms-text-size-adjust: 100%;
9 | text-size-adjust: 100%;
10 | -webkit-font-smoothing: antialiased;
11 | }
12 | *,*::after,*::before {
13 | -webkit-box-sizing: border-box;
14 | -moz-box-sizing: border-box;
15 | box-sizing: border-box;
16 | }
17 | a {
18 | color: #fff;
19 | text-decoration: none;
20 | }
21 | a:visited {
22 | color: #fff;
23 | }
24 | a:hover {
25 | color: #fff;
26 | }
27 |
28 | blockquote {
29 | padding: 10px 20px;
30 | margin: 0 0 20px;
31 | font-size: 17.5px;
32 | border-left: 5px solid #EEE;
33 | }
34 | code {
35 | font-family: Menlo,Monaco,Consolas,"Courier New",monospace;
36 | padding: 2px 4px;
37 | font-size: 90%;
38 | color: #C7254E;
39 | background-color: #F9F2F4;
40 | border-radius: 4px;
41 | }
42 | code, pre {
43 | font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
44 | }
45 | pre {
46 | display: block;
47 | padding: 9.5px;
48 | margin: 0 0 10px;
49 | font-size: 13px;
50 | line-height: 1.428571429;
51 | color: #333333;
52 | word-break: break-all;
53 | word-wrap: break-word;
54 | background-color: #f5f5f5;
55 | border: 1px solid #cccccc;
56 | border-radius: 4px;
57 | white-space: pre-wrap;
58 | }
59 | .container {
60 | height: 100vh;
61 | position: relative;
62 | }
63 | .left-panel {
64 | position: absolute;
65 | left: 0px;
66 | top: 0px;
67 | bottom: 0px;
68 | width: 300px;
69 | overflow-y: auto;
70 | padding: 1.5em;
71 | border-right: 1px solid #f2f2f2;
72 | }
73 | .left-panel .header {
74 | position: relative;
75 | height: 60px;
76 | display: flex;
77 | align-items: center;
78 | justify-content: center;
79 | }
80 | .left-panel .header .search{
81 | display: block;
82 | width: 180px;
83 | }
84 | .right-panel .body {
85 | overflow: hidden;
86 | padding: 5px 10px;
87 | }
88 | .right-panel {
89 | padding: 1.5em;
90 | position: absolute;
91 | right: 0px;
92 | left: 300px;
93 | top: 0px;
94 | bottom: 0px;
95 | flex: 0 1 auto;
96 | }
97 | .right-panel .header {
98 | height: 60px;
99 | display: flex;
100 | align-items: center;
101 | padding: 0 10px;
102 | }
103 | .right-panel .header a{
104 | display: inline-block;
105 | }
106 | .btn {
107 | padding: 0.5em 1.5em;
108 | background-color: #000;
109 | color: #fff;
110 | margin-bottom: 0;
111 | font-weight: normal;
112 | text-align: center;
113 | vertical-align: middle;
114 | cursor: pointer;
115 | background-image: none;
116 | white-space: nowrap;
117 | font-size: 14px;
118 | line-height: 1.42857143;
119 | touch-action: manipulation;
120 | -webkit-user-select: none;
121 | user-select: none;
122 | border: none;
123 | border-radius: 8px;
124 | outline: none;
125 | }
126 | .btn:hover,
127 | .btn:active {
128 | color: #fff;
129 | background-color: #333;
130 | outline: none;
131 | }
132 | #routes {
133 | position: relative;
134 | padding: 10px 0px;
135 | margin: 2em auto;
136 | -webkit-user-select: none;
137 | user-select: none;
138 | -moz-user-select: none;
139 | -ms-user-select: none;
140 | -o-user-select: none;
141 | }
142 | #routes li {
143 | list-style: none;
144 | font-weight: lighter;
145 | line-height: 30px;
146 | padding: 5px 10px;
147 | margin: 0px;
148 | font-weight: 300;
149 | background-color: #fff;
150 | border-bottom: 1px solid #ccc;
151 | }
152 | #routes li .item{
153 | position: relative;
154 | }
155 | #routes li.remove {
156 | transition: all 200ms ease-in;
157 | transform: translateX(-90px)
158 | }
159 | #routes li.adding {
160 | animation: 0.5s ease-in reverse adding;
161 | }
162 | #routes li:hover {
163 | cursor: pointer;
164 | }
165 | #routes li.active {
166 | color: #07A2EA;
167 | }
168 | #routes ul {
169 | padding: 0px;
170 | margin: 0px;
171 | }
172 | input[type="search"],
173 | input[type="text"] {
174 | display: block;
175 | width: 100%;
176 | height: 34px;
177 | padding: 6px 12px;
178 | font-size: 14px;
179 | line-height: 1.42857143;
180 | color: #000;
181 | background-color: #fff;
182 | background-image: none;
183 | border-top: none;
184 | border-left: none;
185 | border-right: none;
186 | border-bottom: 1px solid #ccc;
187 | border-radius: 0px;
188 | -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
189 | -o-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
190 | transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
191 | }
192 | input[type="search"]:focus,
193 | input[type="text"]:focus {
194 | border-color: #66afe9;
195 | outline: 0;
196 | }
197 |
198 | /*
199 | * Simple Grid
200 | * Learn More - http://dallasbass.com/simple-grid-a-lightweight-responsive-css-grid/
201 | * Project Page - http://thisisdallas.github.com/Simple-Grid/
202 | * Author - Dallas Bass
203 | *
204 | * .grid.grid-pad>.col-4-12~.col-8-12
205 | */
206 | [class*='col-'] {
207 | float: left;
208 | padding-right: 20px;
209 | }
210 | [class*='col-']:last-of-type {
211 | padding-right: 0px;
212 | }
213 | .grid {
214 | width: 100%;
215 | max-width: 1140px;
216 | min-width: 755px;
217 | margin: 0 auto;
218 | overflow: hidden;
219 | }
220 | .grid:after {
221 | content: "";
222 | display: table;
223 | clear: both;
224 | }
225 | .grid-pad {
226 | padding: 20px 0 0px 20px;
227 | }
228 | .grid-pad > [class*='col-']:last-of-type {
229 | padding-right: 20px;
230 | }
231 | /* Content Columns */
232 | .col-1-1 {
233 | width: 100%;
234 | }
235 | .col-2-3, .col-8-12 {
236 | width: 66.66%;
237 | }
238 | .col-1-2, .col-6-12 {
239 | width: 50%;
240 | }
241 | .col-1-3, .col-4-12 {
242 | width: 33.33%;
243 | }
244 | .col-1-4, .col-3-12 {
245 | width: 25%;
246 | }
247 | .col-1-5 {
248 | width: 20%;
249 | }
250 | .col-1-6, .col-2-12 {
251 | width: 16.667%;
252 | }
253 | .col-1-7 {
254 | width: 14.28%;
255 | }
256 |
257 | .col-1-8 {
258 | width: 12.5%;
259 | }
260 | .col-1-9 {
261 | width: 11.1%;
262 | }
263 | .col-1-10 {
264 | width: 10%;
265 | }
266 | .col-1-11 {
267 | width: 9.09%;
268 | }
269 | .col-1-12 {
270 | width: 8.33%
271 | }
272 | /* Layout Columns */
273 | .col-11-12 {
274 | width: 91.66%
275 | }
276 | .col-10-12 {
277 | width: 83.333%;
278 | }
279 | .col-9-12 {
280 | width: 75%;
281 | }
282 | .col-5-12 {
283 | width: 41.66%;
284 | }
285 | .col-7-12 {
286 | width: 58.33%
287 | }
288 | @media handheld, only screen and (max-width: 767px) {
289 | .grid {
290 | width: 100%;
291 | min-width: 0;
292 | margin-left: 0px;
293 | margin-right: 0px;
294 | padding-left: 0px;
295 | padding-right: 0px;
296 | }
297 | [class*='col-'] {
298 | width: auto;
299 | float: none;
300 | margin-left: 0px;
301 | margin-right: 0px;
302 | margin-top: 10px;
303 | margin-bottom: 10px;
304 | padding-left: 20px;
305 | padding-right: 20px;
306 | }
307 | }
308 | .body .CodeMirror {
309 | height: calc(100vh - 100px)
310 | }
311 | a.close {
312 | position: absolute;
313 | top: -1px;
314 | right: 0px;
315 | text-decoration: none;
316 | color: #888;
317 | font-size: 16px;
318 | font-weight: bold;
319 | display: none;
320 | }
321 | li:hover a.close {
322 | display: block;
323 | }
324 | a.close:hover {
325 | color: black;
326 | }
327 |
328 | a.close:active {
329 | margin-top: 1px;
330 | }
331 | .notice-container {
332 | position: fixed;
333 | top: 0;
334 | left: 0;
335 | width: 100%;
336 | z-index: 999999;
337 | }
338 | .notice-container .notice-item {
339 | position: relative;
340 | font: 500 16px/1.8 "Georgia","Xin Gothic","Hiragino Sans GB","WenQuanYi Micro Hei",sans-serif;
341 | background: #fefefe;
342 | background: rgba(255,255,255,0.9);
343 | color: #565656;
344 | padding: 10px 20px;
345 | box-sizing: border-box;
346 | border-bottom: 1px solid #efefef;
347 | text-align: center;
348 | transition: all .2s ease-in-out;
349 | transform-origin: top;
350 | }
351 | /* colors from bootstrap */
352 | .notice-container .notice-warning {
353 | background: #fcf8e3;
354 | background: rgba(252, 248, 227, 0.9);
355 | border-color: #fbeed5;
356 | color: #c09853;
357 | }
358 |
359 | .notice-container .notice-success {
360 | background: #EAFBE3;
361 | background: rgba(221, 242, 210, 0.9);
362 | border-color: #D6E9C6;
363 | color: #3FB16F;
364 | }
365 |
366 | .notice-container .notice-danger,
367 | .notice-container .notice-error {
368 | background: #f2dede;
369 | background: rgba(242, 222, 222, 0.9);
370 | border-color: #ebccd1;
371 | color: #a94442;
372 | }
373 |
374 | .notice-container .notice-content {
375 | color: inherit;
376 | text-decoration: none;
377 | margin: 0 auto;
378 | max-width: 650px;
379 | }
380 | .notice-container .notice-close {
381 | position: absolute;
382 | top: 5px;
383 | height: 40px;
384 | right: 20px;
385 | width: 40px;
386 | cursor: pointer;
387 | font: 400 normal 22px/40px "Arial", sans-serif;
388 | color: rgba(231, 76, 60, 0.6);
389 | }
390 | .notice-container .notice-dismiss {
391 | transform: rotateX(-75deg);
392 | opacity: 0;
393 | }
394 | .CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
395 | position: absolute;
396 | background: #ccc;
397 | -moz-box-sizing: border-box;
398 | box-sizing: border-box;
399 | border: 1px solid #bbb;
400 | border-radius: 2px;
401 | }
402 |
403 | .CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
404 | position: absolute;
405 | z-index: 6;
406 | background: #eee;
407 | }
408 |
409 | .CodeMirror-simplescroll-horizontal {
410 | bottom: 0; left: 0;
411 | height: 8px;
412 | }
413 | .CodeMirror-simplescroll-horizontal div {
414 | bottom: 0;
415 | height: 100%;
416 | }
417 |
418 | .CodeMirror-simplescroll-vertical {
419 | right: 0; top: 0;
420 | width: 8px;
421 | }
422 | .CodeMirror-simplescroll-vertical div {
423 | right: 0;
424 | width: 100%;
425 | }
426 |
427 |
428 | .CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
429 | display: none;
430 | }
431 |
432 | .CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
433 | position: absolute;
434 | background: #bcd;
435 | border-radius: 3px;
436 | }
437 |
438 | .CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
439 | position: absolute;
440 | z-index: 6;
441 | }
442 |
443 | .CodeMirror-overlayscroll-horizontal {
444 | bottom: 0; left: 0;
445 | height: 6px;
446 | }
447 | .CodeMirror-overlayscroll-horizontal div {
448 | bottom: 0;
449 | height: 100%;
450 | }
451 |
452 | .CodeMirror-overlayscroll-vertical {
453 | right: 0; top: 0;
454 | width: 6px;
455 | }
456 | .CodeMirror-overlayscroll-vertical div {
457 | right: 0;
458 | width: 100%;
459 | }
460 | .icon-search {
461 | height: 14px;
462 | width: 14px;
463 | position: absolute;
464 | display: block;
465 | left: 40px;
466 | top: 23px;
467 | background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2Mi45OTMgNjIuOTkzIj48cGF0aCBkPSJNNjIuNTggNjAuNTk0TDQxLjMxMyAzOS4zM2MzLjcxMi00LjE4IDUuOTg4LTkuNjYgNS45ODgtMTUuNjc4QzQ3LjMgMTAuNjEgMzYuNjkzLjAwMiAyMy42NS4wMDIgMTAuNjEgMCAwIDEwLjYxIDAgMjMuNjVjMCAxMy4wNCAxMC42MSAyMy42NSAyMy42NSAyMy42NSA2LjAxNyAwIDExLjQ5OC0yLjI3NiAxNS42NzgtNS45ODhMNjAuNTkzIDYyLjU4YTEuNDAzIDEuNDAzIDAgMCAwIDEuOTg3IDBjLjU1LS41NS41NS0xLjQzNyAwLTEuOTg2ek0yMy42NSA0NC40OTJjLTExLjQ5IDAtMjAuODQtOS4zNDgtMjAuODQtMjAuODRzOS4zNS0yMC44NCAyMC44NC0yMC44NGMxMS40OTIgMCAyMC44NCA5LjM0OCAyMC44NCAyMC44NCAwIDUuMjQtMS45NTcgMTAuMDIzLTUuMTYyIDEzLjY5LS42Mi43MDUtMS4yOCAxLjM2Ny0xLjk4NyAxLjk4Ni0zLjY2NSAzLjIwNi04LjQ0OCA1LjE2NC0xMy42OSA1LjE2NHoiIGZpbGw9IiMwMTAwMDIiLz48L3N2Zz4=);
468 | }
469 |
470 | @keyframes adding {
471 | from {
472 | transform: translateX(0)
473 | }
474 | to {
475 | transform: translateX(-60px)
476 | }
477 | }
478 | /* Make clicks pass-through */
479 | #nprogress {
480 | pointer-events: none;
481 | }
482 |
483 | #nprogress .bar {
484 | background: #29d;
485 |
486 | position: fixed;
487 | z-index: 1031;
488 | top: 0;
489 | left: 0;
490 |
491 | width: 100%;
492 | height: 2px;
493 | }
494 |
495 | /* Fancy blur effect */
496 | #nprogress .peg {
497 | display: block;
498 | position: absolute;
499 | right: 0px;
500 | width: 100px;
501 | height: 100%;
502 | box-shadow: 0 0 10px #29d, 0 0 5px #29d;
503 | opacity: 1.0;
504 |
505 | -webkit-transform: rotate(3deg) translate(0px, -4px);
506 | -ms-transform: rotate(3deg) translate(0px, -4px);
507 | transform: rotate(3deg) translate(0px, -4px);
508 | }
509 |
510 | /* Remove these to get rid of the spinner */
511 | #nprogress .spinner {
512 | display: block;
513 | position: fixed;
514 | z-index: 1031;
515 | top: 15px;
516 | right: 15px;
517 | }
518 |
519 | #nprogress .spinner-icon {
520 | width: 18px;
521 | height: 18px;
522 | box-sizing: border-box;
523 |
524 | border: solid 2px transparent;
525 | border-top-color: #29d;
526 | border-left-color: #29d;
527 | border-radius: 50%;
528 |
529 | -webkit-animation: nprogress-spinner 400ms linear infinite;
530 | animation: nprogress-spinner 400ms linear infinite;
531 | }
532 |
533 | .nprogress-custom-parent {
534 | overflow: hidden;
535 | position: relative;
536 | }
537 |
538 | .nprogress-custom-parent #nprogress .spinner,
539 | .nprogress-custom-parent #nprogress .bar {
540 | position: absolute;
541 | }
542 |
543 | @-webkit-keyframes nprogress-spinner {
544 | 0% { -webkit-transform: rotate(0deg); }
545 | 100% { -webkit-transform: rotate(360deg); }
546 | }
547 | @keyframes nprogress-spinner {
548 | 0% { transform: rotate(0deg); }
549 | 100% { transform: rotate(360deg); }
550 | }
551 |
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | var config = require('./config.json')
2 | var redisClient = require('redis').createClient(config.redis.port, config.redis.host)
3 | var parse = require('co-body')
4 | var cors = require('koa-cors')
5 | var wrapper = require('co-redis')
6 | var redisCo = wrapper(redisClient)
7 | var liveload = require('koa-liveload')
8 | var koa = require('koa')
9 | var app = koa()
10 | var router = require('koa-router')()
11 | var logger = require('koa-logger')
12 |
13 |
14 | router.get('/_listapi', function* (next) {
15 | var data = yield redisCo.hkeys('apis')
16 | this.type = 'json'
17 | this.body = {keys: data || []}
18 | })
19 |
20 | router.delete('/_deleteapi', function* (next) {
21 | if (this.query && this.query.route) {
22 | yield redisCo.hdel('apis', this.query.route)
23 | this.status = 200
24 | } else {
25 | this.type = 'json'
26 | this.body = {error: 'route required'}
27 | this.status = 401
28 | }
29 | })
30 |
31 | router.post('/_addapi', function* (next) {
32 | var body = yield parse.form(this.req,{limit: '30mb'} )
33 | if (!body.route || !body.content || !/^(GET|PUT|POST|DELETE|PATCH)/.test(body.route)) {
34 | this.body = { error: 'route and content empty or invalid'}
35 | this.status = 401
36 | } else {
37 | yield redisCo.hset('apis', body.route, body.content)
38 | this.res.type = 'json'
39 | this.body = {success: true}
40 | }
41 | })
42 |
43 | router.get('/_exportapi', function* () {
44 | var data = yield redisCo.hgetall('apis')
45 | this.body = JSON.stringify({data: data || {}}, null, ' ')
46 | this.response.attachment('apis.json')
47 | })
48 |
49 | router.post('/_importapi', function* () {
50 | var body = yield parse.json(this.req, {limit: '30mb'})
51 | if (!body.data) {
52 | this.body = { error: 'no data found'}
53 | this.status = 401
54 | } else {
55 | var data = body.data
56 | for (var key in data) {
57 | yield redisCo.hset('apis', key, data[key])
58 | }
59 | this.res.type = 'json'
60 | this.body = {success: true}
61 | }
62 | })
63 |
64 | app.use(cors())
65 | app.use(router.routes())
66 |
67 | app.use(function* (next) {
68 | // static resource
69 | if (/^\/(.*\.(js|css))?$/.test(this.path)) {
70 | yield next
71 | } else {
72 | var key = this.method + ' ' + this.path
73 | if (this.querystring) {
74 | key = key + '?' + this.querystring
75 | }
76 | var content = yield redisCo.hget('apis', key)
77 | if (content) {
78 | this.type = 'json'
79 | this.body = content
80 | } else {
81 | yield next
82 | }
83 | }
84 | })
85 |
86 | var root = __dirname + '/public'
87 | app.use(logger())
88 | app.use(liveload(root))
89 | app.use(require('koa-static')(root, {
90 | index: 'index.html'
91 | }))
92 |
93 | app.listen(config.port, function () {
94 | console.log('server listening on ' + config.port)
95 | })
96 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | entry: './public/src/index.js',
3 | output: {
4 | path: 'public',
5 | filename: 'bundle.js'
6 | },
7 | module: {
8 | loaders: [
9 | {test: /\.html$/, loader: 'html'}
10 | ]
11 | },
12 | plugins: []
13 | }
14 |
--------------------------------------------------------------------------------