riemann-dash 0.0.5 → 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.
@@ -0,0 +1,30 @@
1
+ // Provides persistent storage for dashboard configuration.
2
+ var persistence = (function() {
3
+ // Saves configuration to persistent store. Calls success() or error() when
4
+ // complete.
5
+ var save = function(config, success, error) {
6
+ jQuery.ajax('/ws/config', {
7
+ type: 'POST',
8
+ success: success,
9
+ error: error,
10
+ contentType: 'application/json',
11
+ data: JSON.stringify(config),
12
+ dataType: 'json'
13
+ });
14
+ };
15
+
16
+ // Returns configuration from persistent store.
17
+ var load = function(success, error) {
18
+ jQuery.ajax('/ws/config', {
19
+ type: 'GET',
20
+ success: success,
21
+ error: error,
22
+ dataType: 'json'
23
+ });
24
+ };
25
+
26
+ return {
27
+ save: save,
28
+ load: load
29
+ }
30
+ })();
@@ -0,0 +1,28 @@
1
+ var subs = (function() {
2
+ // Loads index with query, calling f with each received event.
3
+ var subscribe = function(query, f) {
4
+ var queryString = "query=" + encodeURI(query);
5
+ var uri = "ws://127.0.0.1:5556/index?subscribe=true&" + queryString;
6
+ var ws = new WebSocket(uri);
7
+ var $ws = $(ws);
8
+
9
+ $ws.bind('open', function() {
10
+ console.log("connected", query);
11
+ });
12
+
13
+ $ws.bind('close', function() {
14
+ console.log("closed", query);
15
+ });
16
+
17
+ $ws.bind('message', function(e) {
18
+ f(JSON.parse(e.originalEvent.data));
19
+ });
20
+
21
+ $(window).unload(function() { ws.close; ws = null });
22
+ return ws;
23
+ }
24
+
25
+ return {
26
+ "subscribe": subscribe
27
+ };
28
+ })();
@@ -0,0 +1,33 @@
1
+ (function() {
2
+ var fitopts = {min: 6, max: 400};
3
+
4
+ var Title = function(json) {
5
+ view.View.call(this, json);
6
+ this.title = json.title;
7
+ this.clickFocusable = true;
8
+ this.el.addClass("title");
9
+ this.el.text(this.title);
10
+ this.reflow();
11
+ }
12
+
13
+ view.inherit(view.View, Title);
14
+ view.Title = Title;
15
+ view.types.Title = Title;
16
+
17
+ Title.prototype.json = function() {
18
+ return $.extend(view.View.prototype.json.call(this), {
19
+ type: 'Title',
20
+ title: this.title
21
+ });
22
+ }
23
+
24
+ Title.prototype.editForm = function() {
25
+ return Mustache.render('<label for="title">Title</label>' +
26
+ '<input type="title" name="title" value="{{title}}" />',
27
+ this);
28
+ }
29
+
30
+ Title.prototype.reflow = function() {
31
+ this.el.quickfit(fitopts);
32
+ }
33
+ })();
@@ -0,0 +1,684 @@
1
+ var view = (function() {
2
+ var types = {};
3
+ var focused = null;
4
+ var focusOverlay = $('<div class="focusOverlay"></div>');
5
+ $('body').append(focusOverlay);
6
+
7
+ // Unfocus all views.
8
+ var unfocus = function() {
9
+ if (focused) {
10
+ focused.unfocus();
11
+ }
12
+ }
13
+
14
+ function createObject(parent) {
15
+ function TempClass() {}
16
+ TempClass.prototype = parent;
17
+ var child = new TempClass();
18
+ return child;
19
+ }
20
+
21
+ function inherit(sup, sub) {
22
+ var newSubPrototype = createObject(sup.prototype);
23
+ newSubPrototype.constructor = sub;
24
+ sub.prototype = newSubPrototype;
25
+ }
26
+
27
+ // Create *some* type of view from json
28
+ var reify = function(json) {
29
+ var t = types[json.type];
30
+ return(new t(json));
31
+ }
32
+
33
+ // Initialize keybindings
34
+ function setKeyBindings() {
35
+ var focusedBindings = {
36
+ // left
37
+ 37: function(ev) {
38
+ if (ev.ctrlKey === true) {
39
+ focused.split('HStack', -1);
40
+ } else {
41
+ focused.moveHorizontal(-1);
42
+ }
43
+ },
44
+
45
+ // up
46
+ 38: function(ev) {
47
+ if (ev.ctrlKey === true) {
48
+ focused.split('VStack', -1);
49
+ } else {
50
+ focused.moveVertical(-1);
51
+ }
52
+ },
53
+
54
+ // right
55
+ 39: function(ev) {
56
+ if (ev.ctrlKey === true) {
57
+ focused.split('HStack', 1);
58
+ } else {
59
+ focused.moveHorizontal(1);
60
+ }
61
+ },
62
+
63
+ // down
64
+ 40: function(ev) {
65
+ if (ev.ctrlKey === true) {
66
+ focused.split('VStack', 1);
67
+ } else {
68
+ focused.moveVertical(1);
69
+ }
70
+ },
71
+
72
+ 27: function() { focused.unfocus() }, // escape
73
+ 33: function() { if (focused.parent) { // pgup
74
+ focused.parent.focus();
75
+ } },
76
+ 46: function() { focused.delete() }, // delete
77
+ 69: function() { focused.edit() }, // e
78
+ 86: function() { focused.split('VStack', 1) }, // v
79
+ 87: function() { focused.split('HStack', 1) }, // h
80
+
81
+ 187: function() { focused.grow(); }, // +
82
+ 189: function() { focused.shrink(); } // -
83
+
84
+ };
85
+
86
+ var bindings = {};
87
+
88
+ // Set up focused bindings.
89
+ for (code in focusedBindings) {
90
+ (function(f) {
91
+ keys.bind(code, function(ev) {
92
+ if (focused) { f(ev) }
93
+ });
94
+ })(focusedBindings[code]);
95
+ }
96
+
97
+ // Set up unfocused bindings.
98
+ for (code in bindings) {
99
+ (function(f) {
100
+ keys.bind(code, function(ev) {
101
+ if (! focused) { f(ev) }
102
+ });
103
+ })(bindings[code]);
104
+ }
105
+ }
106
+ setKeyBindings()
107
+
108
+ // View ////////////////////////////////////////////////////////////////////
109
+
110
+ var View = function(json) {
111
+ this.type = json.type;
112
+ this.el = $('<div class="view">');
113
+ this.weight = json.weight || 1;
114
+ // this.el.css('background', 'rgb(' +
115
+ // Math.floor(Math.random() * 256) + ',' +
116
+ // Math.floor(Math.random() * 256) + ',' +
117
+ // Math.floor(Math.random() * 256) + ')');
118
+
119
+ var self = this;
120
+ this.clickFocusable = true;
121
+ this.el.click(function() {
122
+ if (self.clickFocusable) {
123
+ self.focus();
124
+ }
125
+ });
126
+ };
127
+ types.View = View;
128
+
129
+ View.prototype.width = function(w) {
130
+ if (w !== undefined) {
131
+ return this.el.width(w);
132
+ } else {
133
+ return this.el.width();
134
+ }
135
+ };
136
+
137
+ View.prototype.height = function(h) {
138
+ if (h !== undefined) {
139
+ return this.el.height(h);
140
+ } else {
141
+ return this.el.height();
142
+ }
143
+ };
144
+
145
+ View.prototype.top = function(t) {
146
+ if(t !== undefined) {
147
+ return this.el.css("top", t);
148
+ } else {
149
+ return this.el.css("top");
150
+ }
151
+ };
152
+
153
+ View.prototype.left = function(l) {
154
+ if (l !== undefined) {
155
+ return this.el.css("left", l);
156
+ } else {
157
+ return this.el.css("left");
158
+ }
159
+ };
160
+
161
+ View.prototype.reflow = function() {
162
+ };
163
+
164
+ View.prototype.grow = function() {
165
+ this.weight *= 2;
166
+ this.parent.reflow();
167
+ focused.refocus();
168
+ };
169
+
170
+ View.prototype.shrink = function() {
171
+ this.weight *= 0.5;
172
+ this.parent.reflow();
173
+ focused.refocus();
174
+ };
175
+
176
+ // Replace this view with a different one. Returns replacement.
177
+ View.prototype.replace = function(replacement) {
178
+ console.log("Replacing", this, "with", replacement);
179
+ console.log("Parent is", this.parent);
180
+ var p = this.parent;
181
+
182
+ if (p == null) {
183
+ throw "Sorry, can't replace top-level views.";
184
+ }
185
+
186
+ if (p.replaceChild == null) {
187
+ throw "Sorry, can't replace unless parent can replace child.";
188
+ }
189
+
190
+ p.replaceChild(this, replacement);
191
+
192
+ return replacement;
193
+ }
194
+
195
+ // Unfocus this view and delete it permanently.
196
+ View.prototype.delete = function() {
197
+ this.unfocus();
198
+ var p = this.parent;
199
+ if (p){
200
+ if (p.removeChild) {
201
+ p.removeChild(this);
202
+ }
203
+ p.reflow();
204
+ }
205
+ this.el.remove();
206
+ this.el = null;
207
+ }
208
+
209
+ // Remove us from our parent, and have them reflow.
210
+ View.prototype.removeFromParent = function() {
211
+ this.unfocus();
212
+ var oldParent = this.parent;
213
+ if (oldParent && oldParent.removeChild) {
214
+ oldParent.removeChild(this);
215
+ oldParent.reflow();
216
+ }
217
+ }
218
+
219
+ // Split in a parent stack that already exists. i is either -1 (place the
220
+ // new view before us) or +1 (place the new view after us).
221
+ View.prototype.splitParentStack = function(i) {
222
+ console.log("Split parent");
223
+ if (i === null) { i = 0 }
224
+ var index = this.parent.indexOf(this) - Math.min(i, 0);
225
+ console.log(this.parent.indexOf(this), index);
226
+ this.parent.insertChild(
227
+ index,
228
+ reify({type: 'View'})
229
+ );
230
+ this.parent.reflow();
231
+ this.refocus();
232
+ }
233
+
234
+ // Split into a stackType. i is either -1 (place the new view before us) or
235
+ // +1 (place the new view after us.
236
+ View.prototype.split = function(stackType, i) {
237
+ var parent = this.parent;
238
+ if (parent) {
239
+ if (parent.type === stackType) {
240
+ this.splitParentStack(i);
241
+ } else if (parent.replaceChild) {
242
+ // Replace self with stack
243
+ var stack = reify({
244
+ type: stackType,
245
+ weight: this.weight
246
+ });
247
+ parent.replaceChild(this, stack);
248
+
249
+ // Add self to stack
250
+ this.weight = 1;
251
+ if (i === -1) {
252
+ stack.addChild(this);
253
+ stack.addChild(new View({type: 'View'}));
254
+ } else {
255
+ stack.addChild(new View({type: 'View'}));
256
+ stack.addChild(this);
257
+ }
258
+
259
+ // Redraw
260
+ parent.reflow();
261
+ this.refocus();
262
+ } else {
263
+ console.log("Can't split: parent can't replace child.");
264
+ }
265
+ } else {
266
+ console.log("Can't split: no parent");
267
+ }
268
+ }
269
+
270
+ // Redraw the focus indicator
271
+ View.prototype.refocus = function() {
272
+ focusOverlay.width(this.el.width());
273
+ focusOverlay.height(this.el.height());
274
+ focusOverlay.css('top', this.el.offset().top);
275
+ focusOverlay.css('left', this.el.offset().left);
276
+ focusOverlay.show();
277
+ }
278
+
279
+ // Focus this view
280
+ View.prototype.focus = function() {
281
+ if (focused !== null) {
282
+ focused.unfocus();
283
+ }
284
+ this.el.addClass("focused");
285
+ this.refocus();
286
+ focused = this;
287
+ }
288
+
289
+ // Unfocus this view
290
+ View.prototype.unfocus = function() {
291
+ focusOverlay.hide();
292
+ this.el.removeClass("focused");
293
+ if (focused === this) {
294
+ focused = null;
295
+ }
296
+ }
297
+
298
+ // Returns the nearest parent hstack
299
+ View.prototype.enclosingHStack = function() {
300
+ try {
301
+ if (this.parent.isHStack) {
302
+ return {
303
+ i: this.parent.indexOf(this),
304
+ stack: this.parent
305
+ };
306
+ } else {
307
+ return this.parent.enclosingHStack();
308
+ }
309
+ } catch(e) {
310
+ return null;
311
+ }
312
+ }
313
+
314
+ // Returns the nearest parent vstack
315
+ View.prototype.enclosingVStack = function() {
316
+ try {
317
+ if (this.parent.isVStack) {
318
+ return {
319
+ i: this.parent.indexOf(this),
320
+ stack: this.parent
321
+ };
322
+ } else {
323
+ return this.parent.enclosingVStack();
324
+ }
325
+ } catch(e) {
326
+ return null;
327
+ }
328
+ }
329
+
330
+ // Move a view horizontally, by delta -1 = left, +1 = right).
331
+ View.prototype.move = function(parentFinder, delta) {
332
+ var enclosing = this[parentFinder]();
333
+
334
+ if (enclosing) {
335
+ // Get the stack we'll move in to, and our (possibly our parent's) index
336
+ // in it.
337
+ var stack = enclosing.stack;
338
+ var i = enclosing.i;
339
+ console.log("I am", this);
340
+ console.log("Enclosing is", stack, i);
341
+
342
+ var newI = i + delta;
343
+ if (newI < 0) {
344
+ console.log("Sorry, at start.");
345
+ newI = 0;
346
+ } else if (newI >= stack.children.length) {
347
+ console.log("Sorry, at end.");
348
+ newI = stack.children.length;
349
+ } else {
350
+ // What's there now?
351
+ var neighbor = stack.children[newI];
352
+ if (neighbor && neighbor.addChild) {
353
+ // We can enter our neighbor
354
+ this.removeFromParent();
355
+ neighbor.addChild(this);
356
+ neighbor.reflow();
357
+ this.focus();
358
+ return;
359
+ }
360
+ }
361
+
362
+ if (this.parent === stack &&
363
+ stack.children.length === 1) {
364
+ // An special case: we can't leave our parent and then re-enter it,
365
+ // because removeFromParent() would *destroy* our parent after we
366
+ // left. Nothing *needs* to happen, so we return immediately.
367
+ return;
368
+ }
369
+
370
+ // We're moving to a new position inside the enclosing stack.
371
+ this.removeFromParent();
372
+ stack.insertChild(newI, this);
373
+ stack.reflow();
374
+ this.focus();
375
+ } else {
376
+ console.log("Sorry, not yet");
377
+ }
378
+ }
379
+
380
+ View.prototype.moveHorizontal = function(delta) {
381
+ this.move('enclosingHStack', delta);
382
+ }
383
+
384
+ View.prototype.moveVertical = function(delta) {
385
+ this.move('enclosingVStack', delta);
386
+ }
387
+
388
+ // A jquery node inserted into the edit modal, for changing the
389
+ // properties of a view.
390
+ View.prototype.editForm = function() {
391
+ }
392
+
393
+ // Show a dialog for changing this view.
394
+ View.prototype.edit = function() {
395
+ var dialog = $('<div><h1></h1><form>' +
396
+ '<select name="type" />' +
397
+ '<div class="edit-form"></div>' +
398
+ '<button name="apply">Apply</button>' +
399
+ '</form></div>');
400
+ dialog.find('h1').text("Edit " + this.type);
401
+
402
+ // Build type selector.
403
+ var typeSelector = dialog.find('select[name=type]');
404
+ var editForm = dialog.find('.edit-form');
405
+ var type;
406
+ for (type in types) {
407
+ if (type == this.json().type) {
408
+ typeSelector.append("<option selected>" + type + "</option>");
409
+ } else {
410
+ typeSelector.append("<option>" + type + "</option>");
411
+ }
412
+ }
413
+
414
+ // The serialized representation of a view that we're editing.
415
+ // Carried between various view types; when Apply is clicked, projected
416
+ // into an actual view.
417
+ var replacementJson = this.json();
418
+
419
+ // Update the replacement structure with the current values.
420
+ var mergeCurrentValues = function() {
421
+ dialog.find('form').first().serializeArray().forEach(function(input) {
422
+ replacementJson[input.name] = input.value;
423
+ });
424
+ }
425
+
426
+ // Add the edit form itself.
427
+ editForm.append(reify(replacementJson).editForm());
428
+
429
+ // Handle type changes.
430
+ typeSelector.change(function() {
431
+ // Read fields
432
+ mergeCurrentValues();
433
+
434
+ // Replace edit form
435
+ editForm.empty();
436
+ editForm.append(reify(replacementJson).editForm());
437
+ });
438
+
439
+ // Apply button.
440
+ var me = this;
441
+ dialog.find('button[name=apply]').click(function(e) {
442
+ // Don't submit the form.
443
+ e.preventDefault();
444
+
445
+ // Read fields
446
+ mergeCurrentValues();
447
+
448
+ // Replace view.
449
+ replacement = me.replace(reify(replacementJson));
450
+ replacement.parent.reflow();
451
+ me.delete();
452
+ replacement.focus();
453
+
454
+ // Close dialog.
455
+ $.modal.close();
456
+ });
457
+
458
+ // Show dialog.
459
+ keys.disable();
460
+ dialog.modal({onClose: function() {
461
+ keys.enable();
462
+ $.modal.close();
463
+ }});
464
+ }
465
+
466
+ // Serialize this view to JSON.
467
+ View.prototype.json = function() {
468
+ return {type: 'View', weight: this.weight};
469
+ }
470
+
471
+ // Balloon /////////////////////////////////////////////////////////////////
472
+
473
+ var Balloon = function(json) {
474
+ View.call(this, json);
475
+ this.clickFocusable = false;
476
+ this.child = reify(json.child);
477
+ this.child.parent = this;
478
+ this.el.append(this.child.el);
479
+ this.parent = this.el.parent();
480
+ }
481
+ inherit(View, Balloon);
482
+ types.Balloon = Balloon;
483
+
484
+ Balloon.prototype.json = function() {
485
+ return $.extend(View.prototype.json.call(this), {
486
+ type: 'Balloon',
487
+ child: this.child.json()
488
+ });
489
+ }
490
+
491
+ Balloon.prototype.replaceChild = function(v1, v2) {
492
+ this.child.parent = null;
493
+ this.child.el.detach();
494
+
495
+ this.child = v2;
496
+ v2.parent = this;
497
+ this.el.append(this.child.el);
498
+ }
499
+
500
+ Balloon.prototype.reflow = function() {
501
+ var p = this.parent;
502
+ this.width(p.width());
503
+ this.height(p.height());
504
+ this.child.width(p.width());
505
+ this.child.height(p.height());
506
+ this.child.reflow();
507
+ }
508
+
509
+ // Fullscreen //////////////////////////////////////////////////////////////
510
+
511
+ var Fullscreen = function(json) {
512
+ Balloon.call(this, json);
513
+ this.el.detach();
514
+ this.el.css('position', 'fixed');
515
+ this.el.appendTo($('body'));
516
+ this.parent = null;
517
+ }
518
+ inherit(Balloon, Fullscreen);
519
+ types.Fullscreen = Fullscreen;
520
+
521
+ Fullscreen.prototype.json = function() {
522
+ return $.extend(
523
+ Balloon.prototype.json.call(this),
524
+ {type: 'Fullscreen'});
525
+ }
526
+
527
+ Fullscreen.prototype.reflow = function() {
528
+ var p = $(window);
529
+ this.width(p.width());
530
+ this.height(p.height());
531
+ this.child.width(p.width());
532
+ this.child.height(p.height());
533
+ this.child.reflow();
534
+ }
535
+
536
+ // Stack ///////////////////////////////////////////////////////////////////
537
+
538
+ var Stack = function(json) {
539
+ View.call(this, json);
540
+ this.clickFocusable = false;
541
+ this.children = [];
542
+ var self = this;
543
+ if (json.children !== undefined) {
544
+ json.children.map(reify).forEach(function(c) {
545
+ self.addChild(c);
546
+ });
547
+ }
548
+ };
549
+ inherit(View, Stack);
550
+
551
+ Stack.prototype.json = function() {
552
+ return $.extend(View.prototype.json.call(this), {
553
+ type: 'Stack',
554
+ children: $.map(this.children, function(x) { return x.json(); })
555
+ });
556
+ }
557
+
558
+ Stack.prototype.addChild = function(view) {
559
+ view.parent = this;
560
+ this.children.push(view);
561
+ this.el.append(view.el);
562
+ }
563
+
564
+ Stack.prototype.insertChild = function(i, view) {
565
+ view.parent = this;
566
+ this.children.splice(i, 0, view);
567
+ this.el.append(view.el);
568
+ }
569
+
570
+ // Replace v1 with v2
571
+ Stack.prototype.replaceChild = function(v1, v2) {
572
+ v1.parent = null;
573
+ v2.parent = this;
574
+ v1.el.detach();
575
+ var i = this.children.indexOf(v1);
576
+ this.children[i] = v2;
577
+ this.el.append(v2.el);
578
+ }
579
+
580
+ Stack.prototype.removeChild = function(view) {
581
+ view.parent = null;
582
+ var i = this.children.indexOf(view);
583
+ view.el.detach();
584
+ this.children.splice(i, 1);
585
+
586
+ // Delete self if empty
587
+ if (this.children.length === 0) {
588
+ this.delete();
589
+ }
590
+ }
591
+
592
+ Stack.prototype.indexOf = function(child) {
593
+ return this.children.indexOf(child);
594
+ }
595
+
596
+ Stack.prototype.delete = function() {
597
+ this.children.forEach(function(c) {
598
+ c.delete();
599
+ });
600
+ View.prototype.delete.call(this);
601
+ }
602
+
603
+ // HStack //////////////////////////////////////////////////////////////////
604
+
605
+ var HStack = function(json) {
606
+ Stack.call(this, json);
607
+ };
608
+ inherit(Stack, HStack);
609
+ types.HStack = HStack;
610
+
611
+ HStack.prototype.json = function() {
612
+ return $.extend(Stack.prototype.json.call(this), {type: 'HStack'});
613
+ }
614
+
615
+ HStack.prototype.isHStack = true;
616
+
617
+ HStack.prototype.reflow = function() {
618
+ if (this.el === null) {
619
+ // We're gone.
620
+ return;
621
+ }
622
+
623
+ var width = this.width();
624
+ var height = this.height();
625
+ var left = 0;
626
+ var weightSum = this.children.reduce(function(acc, c) {
627
+ return acc + c.weight;
628
+ }, 0);
629
+
630
+ this.children.forEach(function(c) {
631
+ c.height(height);
632
+ c.width(width * (c.weight / weightSum));
633
+ c.top(0);
634
+ c.left(left);
635
+ left = left + c.width();
636
+ c.reflow();
637
+ });
638
+ }
639
+
640
+ // VStack //////////////////////////////////////////////////////////////////
641
+
642
+ var VStack = function(json) {
643
+ Stack.call(this, json);
644
+ };
645
+ inherit(Stack, VStack);
646
+ types.VStack = VStack;
647
+
648
+ VStack.prototype.json = function() {
649
+ return $.extend(Stack.prototype.json.call(this), {type: 'VStack'});
650
+ }
651
+
652
+ VStack.prototype.isVStack = true;
653
+
654
+ VStack.prototype.reflow = function() {
655
+ if (this.el === null) {
656
+ // We're gone.
657
+ return;
658
+ }
659
+
660
+ var width = this.width();
661
+ var height = this.height();
662
+ var top = 0;
663
+ var weightSum = this.children.reduce(function(acc, c) {
664
+ return acc + c.weight;
665
+ }, 0);
666
+
667
+ this.children.forEach(function(c) {
668
+ c.width(width);
669
+ c.height(height * (c.weight / weightSum));
670
+ c.left(0);
671
+ c.top(top);
672
+ top = top + c.height();
673
+ c.reflow();
674
+ });
675
+ };
676
+
677
+ return $.extend({
678
+ types: types,
679
+ reify: reify,
680
+ inherit: inherit,
681
+ unfocus: unfocus,
682
+ focused: function() { return focused; }
683
+ }, types);
684
+ })();