wisp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/public/wisp.css ADDED
@@ -0,0 +1,137 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ }
5
+
6
+ body {
7
+ background: #000000;
8
+ color: #ffffff;
9
+ }
10
+
11
+ p {
12
+ margin: 0;
13
+ }
14
+
15
+ header {
16
+ padding: 0.2em 0.4em 0.2em 0.4em;
17
+ border-bottom: 1px solid #cccccc;
18
+ margin-bottom: 1em;
19
+ }
20
+ header h1 {
21
+ font-size: 2.0em;
22
+ }
23
+
24
+ footer {
25
+ padding: 0.5em 1em 0.5em 1em;
26
+ border-top: 1px solid #cccccc;
27
+ margin-top: 1em;
28
+ clear: both;
29
+ }
30
+
31
+ section {
32
+ line-height: 1.1em;
33
+ }
34
+
35
+ section {
36
+ margin: 1em;
37
+ }
38
+ section section {
39
+ margin: 0em;
40
+ }
41
+
42
+ section h1 {
43
+ color: #cccccc;
44
+ }
45
+
46
+ #__output_ {
47
+ word-break: break-all;
48
+ height: 11em;
49
+ overflow-y: auto;
50
+ border: 1px solid #cccccc;
51
+ border-radius: 0.3em;
52
+ margin: 1em;
53
+ padding: 0.4em;
54
+ list-style-type: none;
55
+ }
56
+
57
+ #__output_ li {
58
+ font-family: monospace;
59
+ white-space: pre-wrap;
60
+ }
61
+
62
+ #__output_ li.error {
63
+ color: #ff6666;
64
+ }
65
+
66
+ #__output_ li.info {
67
+ color: #999999;
68
+ }
69
+ #__output_ li.info:before {
70
+ content: "== ";
71
+ }
72
+ #__output_ li.info:after {
73
+ content: " ==";
74
+ }
75
+
76
+
77
+ #__sources_ {
78
+ /*display: box;
79
+ display: -webkit-box;
80
+ display: -moz-box;*/
81
+ position: relative;
82
+ z-index: 10;
83
+ }
84
+
85
+ canvas.__wisp_canvas_ {
86
+ position: absolute;
87
+ z-index: 1;
88
+ }
89
+
90
+ section.code {
91
+ font-family: monospace;
92
+ margin: 1em 0em 0em 1em;
93
+ border: 1px solid #cccccc;
94
+ border-radius: 0.3em;
95
+ box-shadow: none;
96
+ word-break: break-all;
97
+ display: inline-block;
98
+ vertical-align: top;
99
+ }
100
+
101
+ section.code h1 {
102
+ font-size: 1.0em;
103
+ margin: 0em;
104
+ padding: 0.3em 0.4em 0.1em 0.4em;
105
+ border-bottom: 1px solid #cccccc;
106
+ color: #ffffff;
107
+ }
108
+
109
+ div.code {
110
+ padding: 0.2em 0.5em 0.4em 0.5em;
111
+ counter-reset: line-counter;
112
+
113
+ box-shadow: none;
114
+ position: relative;
115
+ }
116
+
117
+ div.code span {
118
+ width: 5em;
119
+ }
120
+
121
+ code {
122
+ border-radius: 1em;
123
+ white-space: pre-wrap;
124
+ margin-left: 4em;
125
+ z-index: 20;
126
+ }
127
+ div.code code:before {
128
+ content: counter(line-counter);
129
+ counter-increment: line-counter;
130
+ position: absolute;
131
+ left: 1em;
132
+ width: 2em;
133
+ text-align: right;
134
+ color: #999999;
135
+ }
136
+ div.code code:after {
137
+ }
data/public/wisp.js ADDED
@@ -0,0 +1,459 @@
1
+ /**
2
+ constructor of WispAnim
3
+ target: jQuery object (line)
4
+ fontColor: color of text-shadow
5
+ wispColor: color of line-shadow
6
+ */
7
+ var WispAnim = function(target) {
8
+ // const
9
+ this.DELAY = 30; // msec
10
+ this.CANVAS_ELEMCLS = "__wisp_canvas_";
11
+
12
+ // variable
13
+ this.frameCnt = 0;
14
+ this.maxFrame = 16;
15
+
16
+ this.target = target;
17
+ this.target.addClass("wisp");
18
+ this.target.wisp = this;
19
+ this.canvasId = this.target.attr("id") + "_canvas";
20
+
21
+ this.fontColor = null; // [r,g,b] ex) [255, 255, 200]
22
+ this.wispColor = null; // [r,g,b,a] ex) [204, 255, 0, 0.75]
23
+ this.particleColor = null;
24
+ this.wispStyles = null;
25
+ };
26
+
27
+ WispAnim.prototype = {
28
+ createId: function() {
29
+ var date = new Date();
30
+ var dateStr = "" + date.getHours() +
31
+ date.getMinutes() + date.getSeconds() +
32
+ date.getMilliseconds();
33
+ return dateStr;
34
+ },
35
+
36
+ fillRoundRect: function(ctx, l, t, r, b, rad) {
37
+ ctx.moveTo(l+rad, t);
38
+ ctx.lineTo(r-rad, t);
39
+ ctx.arc(r-rad, t+rad, rad, 1.5*Math.PI, 0, false);
40
+ ctx.lineTo(r, b-rad);
41
+ ctx.arc(r-rad, b-rad, rad, 0, 0.5*Math.PI, false);
42
+ ctx.lineTo(l+rad, b);
43
+ ctx.arc(l+rad, b-rad, rad, 0.5*Math.PI, Math.PI, false);
44
+ ctx.lineTo(l, t+rad);
45
+ ctx.arc(l+rad, t+rad, rad, Math.PI, 1.5*Math.PI, false);
46
+ ctx.fill();
47
+ },
48
+
49
+ /** start animation */
50
+ start: function(fontColor, wispColor, particleColor, wispStyles) {
51
+ this.fontColor = fontColor;
52
+ this.wispColor = wispColor;
53
+ this.particleColor = particleColor;
54
+ this.wispStyles = wispStyles;
55
+
56
+ var animId = this.createId();
57
+ var delay = this.DELAY;
58
+ var frameCnt = 0;
59
+ var maxFrame = this.maxFrame;
60
+
61
+ this.target.wisp_animid = animId;
62
+ var wispNoStyle = {
63
+ textShadow: "none",
64
+ boxShadow: "none",
65
+ backgroundColor: "transparent"
66
+ };
67
+
68
+ // canvas setting
69
+ var canvas = $("#"+ this.canvasId);
70
+ if(canvas.length == 0) {
71
+ canvas = $("<canvas class=\"" + this.CANVAS_ELEMCLS + "\" id=\"" + this.canvasId + "\"></canvas>");
72
+ $(document.body).append(canvas);
73
+ }
74
+
75
+ var cm = 30; // margin of line
76
+ var lx = this.target.offset().left; // line xpos
77
+ var ly = this.target.offset().top; // line ypos
78
+ var lw = this.target.width(); // line width
79
+ var lh = this.target.height(); // line height
80
+ ly += lh * 0.75; lh *= 0.25;
81
+ var cw = lw + cm*2; // canvas width
82
+ var ch = lh + cm*2; // canvas height
83
+ var r = lh / 2.0;
84
+ if(r > lw / 2.0) r = lw / 2.0;
85
+
86
+ canvas.css({
87
+ left: lx - cm, top: ly - cm,
88
+ width: cw, height: ch
89
+ });
90
+ canvas.attr("width", cw);
91
+ canvas.attr("height", ch);
92
+
93
+ // particle settings
94
+ var emitNum = Math.round(1/this.maxFrame * 200);
95
+ var emitter = new Emitter();
96
+ emitter.canvasId = this.canvasId;
97
+ emitter.gravityX = 0.6; emitter.gravityY = 0;
98
+ emitter.velocityMin = 3; emitter.velocityMax = 10;
99
+ emitter.lifeMin = 2; emitter.lifeMax = 4;
100
+ emitter.radiusMin = 0.3; emitter.radiusMax = 2;
101
+ emitter.fgColor = this.particleColor;
102
+ emitter.bgColor = [0, 0, 0, 0];
103
+ emitter.afterImageNum = 0;
104
+
105
+ var end = function(wisp) {
106
+ var ctx = canvas.get(0).getContext("2d");
107
+ ctx.globalCompositeOperation = "source-over";
108
+ var width = canvas.width();
109
+ var height = canvas.height();
110
+
111
+ // clear
112
+ ctx.clearRect(0, 0, width, height);
113
+ ctx.fillStyle = "rgba(0, 0, 0, 0)";
114
+ ctx.fillRect(0, 0, width, height);
115
+
116
+ emitter.clear();
117
+
118
+ wisp.target.css(wispNoStyle);
119
+ };
120
+
121
+ var repeat = function(wisp) {
122
+ emitter.update();
123
+
124
+ var ctx = canvas.get(0).getContext("2d");
125
+ ctx.globalCompositeOperation = "source-over";
126
+ var width = canvas.width();
127
+ var height = canvas.height();
128
+
129
+ // clear
130
+ ctx.clearRect(0, 0, width, height);
131
+ ctx.fillStyle = "rgba(0, 0, 0, 0)";
132
+ ctx.fillRect(0, 0, width, height);
133
+ ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0;
134
+
135
+ if(frameCnt < maxFrame) {
136
+ var x = frameCnt / maxFrame * lw + cm;
137
+ var c = wispColor.slice(0, 3).join(",");
138
+ var a = (1-frameCnt/maxFrame) * wispColor[3];
139
+
140
+ ctx.globalCompositeOperation = "lighter";
141
+ ctx.shadowColor = "rgba(" + c + "," + a + ")";
142
+ ctx.shadowBlur = 10;
143
+ ctx.fillStyle = "rgba(" + c + "," + a + ")";
144
+
145
+ wisp.fillRoundRect(ctx, cm-1, cm-1, x+2, cm+lh+2, r+1);
146
+ wisp.fillRoundRect(ctx, cm, cm, x, cm+lh, r);
147
+
148
+ ctx.shadowColor = "rgba(0, 0, 0, 0)";
149
+ ctx.shadowBlur = 0;
150
+
151
+ emitter.emit(emitNum, x, height/2.0);
152
+
153
+ wisp.target.css(wispStyles[frameCnt]);
154
+ } else {
155
+ wisp.target.css(wispNoStyle);
156
+ }
157
+
158
+ emitter.render();
159
+
160
+ frameCnt++;
161
+ if(wisp.target.wisp_animid != animId) {
162
+ end(wisp);
163
+ } else if(frameCnt < maxFrame || !emitter.isEmpty()) {
164
+ setTimeout(repeat, delay, wisp);
165
+ } else {
166
+ setTimeout(end, delay, wisp);
167
+ }
168
+ };
169
+
170
+ repeat(this);
171
+ }
172
+
173
+ };
174
+
175
+ /** constructor of Wisp */
176
+ var Wisp = function() {
177
+ // const
178
+ this.OUTPUT_ELEMID = "__output_";
179
+ this.OUTPUT_MAXLINE = 50;
180
+
181
+ this.SRC_ELEMID = "__sources_";
182
+
183
+ this.OUTTYPE_OUTPUT = 0;
184
+ this.OUTTYPE_ERROR = 1;
185
+ this.OUTTYPE_INFO = 2;
186
+
187
+ this.ANIM_MAX_FRAME = 16;
188
+
189
+ // variables
190
+ this.lines = [];
191
+ this.filename = null;
192
+
193
+ this.idHash = { }; // filename => id
194
+ this.idCount = 0; // current id count
195
+
196
+ this.threadColors = { }; // thread name => color([font color, wisp color])
197
+ this.curColors = null;
198
+ this.curH = 0;
199
+ this.curHDiv = 720;
200
+ };
201
+
202
+ Wisp.convertHsvToRgb = function(hsv) {
203
+ var h = hsv[0]; var s = hsv[1]; var v = hsv[2];
204
+ var hi = Math.floor(h / 60.0) % 6;
205
+ var f = h/60.0-hi;
206
+ var p = Math.round(v*(1-s) * 255);
207
+ var q = Math.round(v*(1-f*s) * 255);
208
+ var t = Math.round(v*(1-(1-f)*s) * 255);
209
+ v = Math.round(v * 255);
210
+ switch(hi) {
211
+ case 0:
212
+ return [v,t,p];
213
+ case 1:
214
+ return [q,v,p];
215
+ case 2:
216
+ return [p,v,t];
217
+ case 3:
218
+ return [p,q,v];
219
+ case 4:
220
+ return [t,p,v];
221
+ case 5:
222
+ return [v,p,q];
223
+ }
224
+ return [0,0,0];
225
+ };
226
+
227
+ /* instance method */
228
+ Wisp.prototype = {
229
+
230
+ /** get next hue (color) */
231
+ nextHue: function() {
232
+ var h = this.curH % 360;
233
+ this.curH += this.curHDiv;
234
+ if(this.curH >= 360) {
235
+ this.curHDiv /= 2.0;
236
+ this.curH = this.curHDiv / 2.0;
237
+ }
238
+ return h;
239
+ },
240
+
241
+ /** get thread color from thread name */
242
+ getThreadColors: function(thname) {
243
+ if(this.threadColors[thname] != null) {
244
+ return this.threadColors[thname];
245
+
246
+ } else { // generate new color
247
+ var h = this.nextHue();
248
+ var fc = [255, 255, 255];
249
+ var wc = Wisp.convertHsvToRgb([h, 0.8, 0.6]);
250
+ wc.push(1.0);
251
+ var pc = Wisp.convertHsvToRgb([h, 0.8, 0.9]);
252
+
253
+ // generate text-shadow properties
254
+ var ts = [];
255
+ var col = "rgb(" + fc.join(",") + ")";
256
+ for(var i=0; i<this.ANIM_MAX_FRAME; i++) {
257
+ ts.unshift("0 0 " + (i*2+1) + "px " + col);
258
+ }
259
+ for(var i=ts.length-2; i>=0; i--) {
260
+ ts[i] = ts[i] + "," + ts[i+1];
261
+ }
262
+
263
+ // generate background & box-shadow colors
264
+ // generate wisp styles
265
+ var ws = [];
266
+ for(var i=0; i<this.ANIM_MAX_FRAME; i++) {
267
+ ws.push({ textShadow: ts[i] });
268
+ }
269
+
270
+ var col = [ fc, wc, pc, ws ];
271
+ this.threadColors[thname] = col;
272
+ return col;
273
+ }
274
+ },
275
+
276
+ /** get file id from filename */
277
+ getFileId : function(filename) {
278
+ if(this.idHash[filename] != null) {
279
+ return this.idHash[filename];
280
+ } else {
281
+ var id = "__" + this.idCount;
282
+ this.idHash[filename] = id;
283
+ this.idCount++;
284
+ return id;
285
+ }
286
+ },
287
+
288
+ /** get line id from filename & lineno */
289
+ getLineId : function(filename, id) {
290
+ return this.getFileId(filename) + "_l" + id;
291
+ },
292
+
293
+ /** add new file */
294
+ addFile : function(filename, codes) {
295
+ console.log("addFile: " + filename + ", " + codes.length + " lines");
296
+
297
+ var id = this.getFileId(filename);
298
+ var sec = $("<section class=\"code\" id=\"" + id + "\"></section>");
299
+ var header = $("<h1>" + filename + "</h1>");
300
+ var codeDiv = $("<div class=\"code\"></div>");
301
+
302
+ // lines
303
+ for(var i=0; i<codes.length; i++) {
304
+ var lineObj = $("<code id=\"" + this.getLineId(filename, i) + "\"></code>");
305
+ lineObj.text(codes[i]);
306
+ codeDiv.append(lineObj);
307
+ if(i != codes.length-1) {
308
+ codeDiv.append("<br />");
309
+ }
310
+ }
311
+ sec.append(header);
312
+ sec.append(codeDiv);
313
+ $("#" + this.SRC_ELEMID).append(sec);
314
+
315
+ this.setFile(filename);
316
+ },
317
+
318
+ /** set file */
319
+ setFile : function(filename) {
320
+ console.log("setFile: " + filename);
321
+
322
+ if(this.prevWispFile) {
323
+ this.prevWispFile.removeClass("wisp");
324
+ }
325
+
326
+ var id = this.getFileId(filename);
327
+ this.filename = filename;
328
+ this.lines = $("#" + id + " code");
329
+
330
+ var fdiv = $("#" + id);
331
+ var fcol = "rgb(" + this.curColors[2].join(",") + ")";
332
+ fdiv.addClass("wisp");
333
+
334
+ this.prevWispFile = fdiv;
335
+ },
336
+
337
+ /** set line */
338
+ setLine : function(lineNo) {
339
+ console.log("setLine: " + lineNo);
340
+
341
+ var line = this.lines[lineNo];
342
+ if(line) {
343
+ var lineObj = $(line);
344
+ var wispAnim = lineObj.wisp;
345
+ if(wispAnim == null) {
346
+ wispAnim = new WispAnim(lineObj);
347
+ wispAnim.maxFrame = this.ANIM_MAX_FRAME;
348
+ } else {
349
+ console.log("reuse");
350
+ }
351
+ wispAnim.start(this.curColors[0],
352
+ this.curColors[1],
353
+ this.curColors[2],
354
+ this.curColors[3]);
355
+ }
356
+ },
357
+
358
+ /** set thread */
359
+ setThread : function(threadName) {
360
+ console.log("setThread: " + threadName);
361
+ this.curColors = this.getThreadColors(threadName);
362
+ },
363
+
364
+ /** add output */
365
+ addOutput : function(type, data) {
366
+ var cls = "";
367
+ switch(type) {
368
+ case this.OUTTYPE_INFO:
369
+ cls = "info"; break;
370
+ case this.OUTTYPE_OUTPUT:
371
+ cls = "output"; break;
372
+ case this.OUTTYPE_ERROR:
373
+ cls = "error"; break;
374
+ }
375
+ console.log("addOutput: (" + cls + ") " + data);
376
+
377
+ var sd = data;
378
+ sd = sd.split("<").join("&lt;");
379
+ sd = sd.split(">").join("&gt;");
380
+ var li = "#" + this.OUTPUT_ELEMID + " li";
381
+ $("#" + this.OUTPUT_ELEMID).append($("<li class=\"" + cls + "\">" + sd + "</li>"));
382
+
383
+ if(type != this.OUTTYPE_INFO) {
384
+ if($(li).size() > this.OUTPUT_MAXLINE) {
385
+ $(li + ":first").remove();
386
+ }
387
+ }
388
+
389
+ $("#" + this.OUTPUT_ELEMID)[0].scrollTop = $("#" + this.OUTPUT_ELEMID)[0].scrollHeight;
390
+ },
391
+
392
+ /** proc web socket message
393
+ data: sended message */
394
+ procMessage : function(data) {
395
+ var op = data.charAt(0);
396
+ var arg = data.slice(1);
397
+
398
+ switch(data.charAt(0)) {
399
+ case "F":
400
+ var obj = JSON.parse(arg);
401
+ if(obj.name) {
402
+ if(obj.code) {
403
+ this.addFile(obj.name, obj.code);
404
+ } else {
405
+ this.setFile(obj.name);
406
+ }
407
+ }
408
+ return false;
409
+
410
+ case "L":
411
+ var no = parseInt(arg, 10);
412
+ this.setLine(no - 1);
413
+ return true;
414
+
415
+ case "O":
416
+ this.addOutput(this.OUTTYPE_OUTPUT, arg);
417
+ return false;
418
+ case "E":
419
+ this.addOutput(this.OUTTYPE_ERROR, arg);
420
+ return false;
421
+
422
+ case "T":
423
+ this.setThread(arg);
424
+ return false;
425
+ }
426
+ return true;
427
+ },
428
+
429
+ onLoad : function() {
430
+ },
431
+
432
+ onSocketOpen: function() {
433
+ this.addOutput(this.OUTTYPE_INFO, "Socket opened");
434
+ },
435
+
436
+ onSocketClose: function() {
437
+ this.addOutput(this.OUTTYPE_INFO, "Socket closed");
438
+ }
439
+
440
+ };
441
+
442
+ var wisp = new Wisp();
443
+
444
+ $(document).ready(function() {
445
+ wisp.onLoad();
446
+
447
+ var ws = new WebSocket('ws://' + window.location.host + window.location.pathname);
448
+ ws.onopen = function() {
449
+ wisp.onSocketOpen();
450
+ };
451
+ ws.onclose = function() {
452
+ wisp.onSocketClose();
453
+ };
454
+
455
+ ws.onmessage = function(m) {
456
+ if(wisp.procMessage(m.data))
457
+ ws.send("ok");
458
+ };
459
+ });