uikit-rails 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1641 @@
1
+ var ui = {};
2
+
3
+ ;(function(exports){
4
+ /**
5
+ * Expose `Emitter`.
6
+ */
7
+
8
+ exports.Emitter = Emitter;
9
+
10
+ /**
11
+ * Initialize a new `Emitter`.
12
+ *
13
+ * @api public
14
+ */
15
+
16
+ function Emitter() {
17
+ this.callbacks = {};
18
+ };
19
+
20
+ /**
21
+ * Listen on the given `event` with `fn`.
22
+ *
23
+ * @param {String} event
24
+ * @param {Function} fn
25
+ * @return {Emitter}
26
+ * @api public
27
+ */
28
+
29
+ Emitter.prototype.on = function(event, fn){
30
+ (this.callbacks[event] = this.callbacks[event] || [])
31
+ .push(fn);
32
+ return this;
33
+ };
34
+
35
+ /**
36
+ * Adds an `event` listener that will be invoked a single
37
+ * time then automatically removed.
38
+ *
39
+ * @param {String} event
40
+ * @param {Function} fn
41
+ * @return {Emitter}
42
+ * @api public
43
+ */
44
+
45
+ Emitter.prototype.once = function(event, fn){
46
+ var self = this;
47
+
48
+ function on() {
49
+ self.off(event, on);
50
+ fn.apply(this, arguments);
51
+ }
52
+
53
+ this.on(event, on);
54
+ return this;
55
+ };
56
+
57
+ /**
58
+ * Remove the given callback for `event` or all
59
+ * registered callbacks.
60
+ *
61
+ * @param {String} event
62
+ * @param {Function} fn
63
+ * @return {Emitter}
64
+ * @api public
65
+ */
66
+
67
+ Emitter.prototype.off = function(event, fn){
68
+ var callbacks = this.callbacks[event];
69
+ if (!callbacks) return this;
70
+
71
+ // remove all handlers
72
+ if (1 == arguments.length) {
73
+ delete this.callbacks[event];
74
+ return this;
75
+ }
76
+
77
+ // remove specific handler
78
+ var i = callbacks.indexOf(fn);
79
+ callbacks.splice(i, 1);
80
+ return this;
81
+ };
82
+
83
+ /**
84
+ * Emit `event` with the given args.
85
+ *
86
+ * @param {String} event
87
+ * @param {Mixed} ...
88
+ * @return {Emitter}
89
+ */
90
+
91
+ Emitter.prototype.emit = function(event){
92
+ var args = [].slice.call(arguments, 1)
93
+ , callbacks = this.callbacks[event];
94
+
95
+ if (callbacks) {
96
+ for (var i = 0, len = callbacks.length; i < len; ++i) {
97
+ callbacks[i].apply(this, args);
98
+ }
99
+ }
100
+
101
+ return this;
102
+ };
103
+
104
+ })(ui);
105
+ ;(function(exports, html){
106
+
107
+ /**
108
+ * Active dialog.
109
+ */
110
+
111
+ var active;
112
+
113
+ /**
114
+ * Expose `Dialog`.
115
+ */
116
+
117
+ exports.Dialog = Dialog;
118
+
119
+ /**
120
+ * Return a new `Dialog` with the given
121
+ * (optional) `title` and `msg`.
122
+ *
123
+ * @param {String} title or msg
124
+ * @param {String} msg
125
+ * @return {Dialog}
126
+ * @api public
127
+ */
128
+
129
+ exports.dialog = function(title, msg){
130
+ switch (arguments.length) {
131
+ case 2:
132
+ return new Dialog({ title: title, message: msg });
133
+ case 1:
134
+ return new Dialog({ message: title });
135
+ }
136
+ };
137
+
138
+ /**
139
+ * Initialize a new `Dialog`.
140
+ *
141
+ * Options:
142
+ *
143
+ * - `title` dialog title
144
+ * - `message` a message to display
145
+ *
146
+ * Emits:
147
+ *
148
+ * - `show` when visible
149
+ * - `hide` when hidden
150
+ *
151
+ * @param {Object} options
152
+ * @api public
153
+ */
154
+
155
+ function Dialog(options) {
156
+ ui.Emitter.call(this);
157
+ options = options || {};
158
+ this.template = html;
159
+ this.el = $(this.template);
160
+ this.render(options);
161
+ if (active && !active.hiding) active.hide();
162
+ if (Dialog.effect) this.effect(Dialog.effect);
163
+ active = this;
164
+ };
165
+
166
+ /**
167
+ * Inherit from `Emitter.prototype`.
168
+ */
169
+
170
+ Dialog.prototype = new ui.Emitter;
171
+
172
+ /**
173
+ * Render with the given `options`.
174
+ *
175
+ * @param {Object} options
176
+ * @api public
177
+ */
178
+
179
+ Dialog.prototype.render = function(options){
180
+ var el = this.el
181
+ , title = options.title
182
+ , msg = options.message
183
+ , self = this;
184
+
185
+ el.find('.close').click(function(){
186
+ self.emit('close');
187
+ self.hide();
188
+ return false;
189
+ });
190
+
191
+ el.find('h1').text(title);
192
+ if (!title) el.find('h1').remove();
193
+
194
+ // message
195
+ if ('string' == typeof msg) {
196
+ el.find('p').text(msg);
197
+ } else if (msg) {
198
+ el.find('p').replaceWith(msg.el || msg);
199
+ }
200
+
201
+ setTimeout(function(){
202
+ el.removeClass('hide');
203
+ }, 0);
204
+ };
205
+
206
+ /**
207
+ * Enable the dialog close link.
208
+ *
209
+ * @return {Dialog} for chaining
210
+ * @api public
211
+ */
212
+
213
+ Dialog.prototype.closable = function(){
214
+ this.el.addClass('closable');
215
+ return this;
216
+ };
217
+
218
+ /**
219
+ * Set the effect to `type`.
220
+ *
221
+ * @param {String} type
222
+ * @return {Dialog} for chaining
223
+ * @api public
224
+ */
225
+
226
+ Dialog.prototype.effect = function(type){
227
+ this._effect = type;
228
+ this.el.addClass(type);
229
+ return this;
230
+ };
231
+
232
+ /**
233
+ * Make it modal!
234
+ *
235
+ * @return {Dialog} for chaining
236
+ * @api public
237
+ */
238
+
239
+ Dialog.prototype.modal = function(){
240
+ this._overlay = ui.overlay();
241
+ return this;
242
+ };
243
+
244
+ /**
245
+ * Add an overlay.
246
+ *
247
+ * @return {Dialog} for chaining
248
+ * @api public
249
+ */
250
+
251
+ Dialog.prototype.overlay = function(){
252
+ var self = this;
253
+ this._overlay = ui
254
+ .overlay({ closable: true })
255
+ .on('hide', function(){
256
+ self.closedOverlay = true;
257
+ self.hide();
258
+ });
259
+ return this;
260
+ };
261
+
262
+ /**
263
+ * Close the dialog when the escape key is pressed.
264
+ *
265
+ * @api private
266
+ */
267
+
268
+ Dialog.prototype.escapable = function(){
269
+ var self = this;
270
+ $(document).bind('keydown.dialog', function(e){
271
+ if (27 != e.which) return;
272
+ $(this).unbind('keydown.dialog');
273
+ self.hide();
274
+ });
275
+ };
276
+
277
+ /**
278
+ * Show the dialog.
279
+ *
280
+ * Emits "show" event.
281
+ *
282
+ * @return {Dialog} for chaining
283
+ * @api public
284
+ */
285
+
286
+ Dialog.prototype.show = function(){
287
+ var overlay = this._overlay;
288
+
289
+ this.emit('show');
290
+
291
+ if (overlay) {
292
+ overlay.show();
293
+ this.el.addClass('modal');
294
+ }
295
+
296
+ // escape
297
+ if (!overlay || overlay.closable) this.escapable();
298
+
299
+ this.el.appendTo('body');
300
+ this.el.css({ marginLeft: -(this.el.width() / 2) + 'px' });
301
+ this.emit('show');
302
+ return this;
303
+ };
304
+
305
+ /**
306
+ * Hide the dialog with optional delay of `ms`,
307
+ * otherwise the dialog is removed immediately.
308
+ *
309
+ * Emits "hide" event.
310
+ *
311
+ * @return {Number} ms
312
+ * @return {Dialog} for chaining
313
+ * @api public
314
+ */
315
+
316
+ Dialog.prototype.hide = function(ms){
317
+ var self = this;
318
+
319
+ // prevent thrashing
320
+ this.hiding = true;
321
+
322
+ // duration
323
+ if (ms) {
324
+ setTimeout(function(){
325
+ self.hide();
326
+ }, ms);
327
+ return this;
328
+ }
329
+
330
+ // hide / remove
331
+ this.el.addClass('hide');
332
+ if (this._effect) {
333
+ setTimeout(function(self){
334
+ self.remove();
335
+ }, 500, this);
336
+ } else {
337
+ self.remove();
338
+ }
339
+
340
+ // modal
341
+ if (this._overlay && !self.closedOverlay) this._overlay.hide();
342
+
343
+ return this;
344
+ };
345
+
346
+ /**
347
+ * Hide the dialog without potential animation.
348
+ *
349
+ * @return {Dialog} for chaining
350
+ * @api public
351
+ */
352
+
353
+ Dialog.prototype.remove = function(){
354
+ this.emit('hide');
355
+ this.el.remove();
356
+ return this;
357
+ };
358
+
359
+ })(ui, "<div id=\"dialog\" class=\"hide\">\n <div class=\"content\">\n <h1>Title</h1>\n <a href=\"#\" class=\"close\">×</a>\n <p>Message</p>\n </div>\n</div>");
360
+ ;(function(exports, html){
361
+
362
+ /**
363
+ * Expose `Overlay`.
364
+ */
365
+
366
+ exports.Overlay = Overlay;
367
+
368
+ /**
369
+ * Return a new `Overlay` with the given `options`.
370
+ *
371
+ * @param {Object} options
372
+ * @return {Overlay}
373
+ * @api public
374
+ */
375
+
376
+ exports.overlay = function(options){
377
+ return new Overlay(options);
378
+ };
379
+
380
+ /**
381
+ * Initialize a new `Overlay`.
382
+ *
383
+ * @param {Object} options
384
+ * @api public
385
+ */
386
+
387
+ function Overlay(options) {
388
+ ui.Emitter.call(this);
389
+ var self = this;
390
+ options = options || {};
391
+ this.closable = options.closable;
392
+ this.el = $(html);
393
+ this.el.appendTo('body');
394
+ if (this.closable) {
395
+ this.el.click(function(){
396
+ self.hide();
397
+ });
398
+ }
399
+ }
400
+
401
+ /**
402
+ * Inherit from `Emitter.prototype`.
403
+ */
404
+
405
+ Overlay.prototype = new ui.Emitter;
406
+
407
+ /**
408
+ * Show the overlay.
409
+ *
410
+ * Emits "show" event.
411
+ *
412
+ * @return {Overlay} for chaining
413
+ * @api public
414
+ */
415
+
416
+ Overlay.prototype.show = function(){
417
+ this.emit('show');
418
+ this.el.removeClass('hide');
419
+ return this;
420
+ };
421
+
422
+ /**
423
+ * Hide the overlay.
424
+ *
425
+ * Emits "hide" event.
426
+ *
427
+ * @return {Overlay} for chaining
428
+ * @api public
429
+ */
430
+
431
+ Overlay.prototype.hide = function(){
432
+ var self = this;
433
+ this.emit('hide');
434
+ this.el.addClass('hide');
435
+ setTimeout(function(){
436
+ self.el.remove();
437
+ }, 2000);
438
+ return this;
439
+ };
440
+
441
+ })(ui, "<div id=\"overlay\" class=\"hide\"></div>");
442
+ ;(function(exports, html){
443
+
444
+ /**
445
+ * Expose `Confirmation`.
446
+ */
447
+
448
+ exports.Confirmation = Confirmation;
449
+
450
+ /**
451
+ * Return a new `Confirmation` dialog with the given
452
+ * `title` and `msg`.
453
+ *
454
+ * @param {String} title or msg
455
+ * @param {String} msg
456
+ * @return {Dialog}
457
+ * @api public
458
+ */
459
+
460
+ exports.confirm = function(title, msg){
461
+ switch (arguments.length) {
462
+ case 2:
463
+ return new Confirmation({ title: title, message: msg });
464
+ case 1:
465
+ return new Confirmation({ message: title });
466
+ }
467
+ };
468
+
469
+ /**
470
+ * Initialize a new `Confirmation` dialog.
471
+ *
472
+ * Options:
473
+ *
474
+ * - `title` dialog title
475
+ * - `message` a message to display
476
+ *
477
+ * Emits:
478
+ *
479
+ * - `cancel` the user pressed cancel or closed the dialog
480
+ * - `ok` the user clicked ok
481
+ * - `show` when visible
482
+ * - `hide` when hidden
483
+ *
484
+ * @param {Object} options
485
+ * @api public
486
+ */
487
+
488
+ function Confirmation(options) {
489
+ ui.Dialog.call(this, options);
490
+ };
491
+
492
+ /**
493
+ * Inherit from `Dialog.prototype`.
494
+ */
495
+
496
+ Confirmation.prototype = new ui.Dialog;
497
+
498
+ /**
499
+ * Change "cancel" button `text`.
500
+ *
501
+ * @param {String} text
502
+ * @return {Confirmation}
503
+ * @api public
504
+ */
505
+
506
+ Confirmation.prototype.cancel = function(text){
507
+ this.el.find('.cancel').text(text);
508
+ return this;
509
+ };
510
+
511
+ /**
512
+ * Change "ok" button `text`.
513
+ *
514
+ * @param {String} text
515
+ * @return {Confirmation}
516
+ * @api public
517
+ */
518
+
519
+ Confirmation.prototype.ok = function(text){
520
+ this.el.find('.ok').text(text);
521
+ return this;
522
+ };
523
+
524
+ /**
525
+ * Show the confirmation dialog and invoke `fn(ok)`.
526
+ *
527
+ * @param {Function} fn
528
+ * @return {Confirmation} for chaining
529
+ * @api public
530
+ */
531
+
532
+ Confirmation.prototype.show = function(fn){
533
+ ui.Dialog.prototype.show.call(this);
534
+ this.el.find('.ok').focus();
535
+ this.callback = fn || function(){};
536
+ return this;
537
+ };
538
+
539
+ /**
540
+ * Render with the given `options`.
541
+ *
542
+ * Emits "cancel" event.
543
+ * Emits "ok" event.
544
+ *
545
+ * @param {Object} options
546
+ * @api public
547
+ */
548
+
549
+ Confirmation.prototype.render = function(options){
550
+ ui.Dialog.prototype.render.call(this, options);
551
+ var self = this
552
+ , actions = $(html);
553
+
554
+ this.el.addClass('confirmation');
555
+ this.el.append(actions);
556
+
557
+ this.on('close', function(){
558
+ self.emit('cancel');
559
+ self.callback(false);
560
+ });
561
+
562
+ actions.find('.cancel').click(function(e){
563
+ e.preventDefault();
564
+ self.emit('cancel');
565
+ self.callback(false);
566
+ self.hide();
567
+ });
568
+
569
+ actions.find('.ok').click(function(e){
570
+ e.preventDefault();
571
+ self.emit('ok');
572
+ self.callback(true);
573
+ self.hide();
574
+ });
575
+ };
576
+
577
+ })(ui, "<div class=\"actions\">\n <button class=\"cancel\">Cancel</button>\n <button class=\"ok main\">Ok</button>\n</div>");
578
+ ;(function(exports, html){
579
+
580
+ /**
581
+ * Expose `ColorPicker`.
582
+ */
583
+
584
+ exports.ColorPicker = ColorPicker;
585
+
586
+ /**
587
+ * RGB util.
588
+ */
589
+
590
+ function rgb(r,g,b) {
591
+ return 'rgb(' + r + ', ' + g + ', ' + b + ')';
592
+ }
593
+
594
+ /**
595
+ * RGBA util.
596
+ */
597
+
598
+ function rgba(r,g,b,a) {
599
+ return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + a + ')';
600
+ }
601
+
602
+ /**
603
+ * Mouse position util.
604
+ */
605
+
606
+ function localPos(e) {
607
+ var offset = $(e.target).offset();
608
+ return {
609
+ x: e.pageX - offset.left
610
+ , y: e.pageY - offset.top
611
+ };
612
+ }
613
+
614
+ /**
615
+ * Initialize a new `ColorPicker`.
616
+ *
617
+ * Emits:
618
+ *
619
+ * - `change` with the given color object
620
+ *
621
+ * @api public
622
+ */
623
+
624
+ function ColorPicker() {
625
+ ui.Emitter.call(this);
626
+ this._colorPos = {};
627
+ this.el = $(html);
628
+ this.main = this.el.find('.main').get(0);
629
+ this.spectrum = this.el.find('.spectrum').get(0);
630
+ $(this.main).bind('selectstart', function(e){ e.preventDefault() });
631
+ $(this.spectrum).bind('selectstart', function(e){ e.preventDefault() });
632
+ this.hue(rgb(255, 0, 0));
633
+ this.spectrumEvents();
634
+ this.mainEvents();
635
+ this.w = 180;
636
+ this.h = 180;
637
+ this.render();
638
+ }
639
+
640
+ /**
641
+ * Inherit from `Emitter.prototype`.
642
+ */
643
+
644
+ ColorPicker.prototype = new ui.Emitter;
645
+
646
+ /**
647
+ * Set width / height to `n`.
648
+ *
649
+ * @param {Number} n
650
+ * @return {ColorPicker} for chaining
651
+ * @api public
652
+ */
653
+
654
+ ColorPicker.prototype.size = function(n){
655
+ return this
656
+ .width(n)
657
+ .height(n);
658
+ };
659
+
660
+ /**
661
+ * Set width to `n`.
662
+ *
663
+ * @param {Number} n
664
+ * @return {ColorPicker} for chaining
665
+ * @api public
666
+ */
667
+
668
+ ColorPicker.prototype.width = function(n){
669
+ this.w = n;
670
+ this.render();
671
+ return this;
672
+ };
673
+
674
+ /**
675
+ * Set height to `n`.
676
+ *
677
+ * @param {Number} n
678
+ * @return {ColorPicker} for chaining
679
+ * @api public
680
+ */
681
+
682
+ ColorPicker.prototype.height = function(n){
683
+ this.h = n;
684
+ this.render();
685
+ return this;
686
+ };
687
+
688
+ /**
689
+ * Spectrum related events.
690
+ *
691
+ * @api private
692
+ */
693
+
694
+ ColorPicker.prototype.spectrumEvents = function(){
695
+ var self = this
696
+ , canvas = $(this.spectrum)
697
+ , down;
698
+
699
+ function update(e) {
700
+ var offsetY = localPos(e).y
701
+ , color = self.hueAt(offsetY - 4);
702
+ self.hue(color.toString());
703
+ self.emit('change', color);
704
+ self._huePos = offsetY;
705
+ self.render();
706
+ }
707
+
708
+ canvas.mousedown(function(e){
709
+ e.preventDefault();
710
+ down = true;
711
+ update(e);
712
+ });
713
+
714
+ canvas.mousemove(function(e){
715
+ if (down) update(e);
716
+ });
717
+
718
+ canvas.mouseup(function(){
719
+ down = false;
720
+ });
721
+ };
722
+
723
+ /**
724
+ * Hue / lightness events.
725
+ *
726
+ * @api private
727
+ */
728
+
729
+ ColorPicker.prototype.mainEvents = function(){
730
+ var self = this
731
+ , canvas = $(this.main)
732
+ , down;
733
+
734
+ function update(e) {
735
+ var color;
736
+ self._colorPos = localPos(e);
737
+ color = self.colorAt(self._colorPos.x, self._colorPos.y);
738
+ self.color(color.toString());
739
+ self.emit('change', color);
740
+
741
+ self.render();
742
+ }
743
+
744
+ canvas.mousedown(function(e){
745
+ down = true;
746
+ update(e);
747
+ });
748
+
749
+ canvas.mousemove(function(e){
750
+ if (down) update(e);
751
+ });
752
+
753
+ canvas.mouseup(function(){
754
+ down = false;
755
+ });
756
+ };
757
+
758
+ /**
759
+ * Get the RGB color at `(x, y)`.
760
+ *
761
+ * @param {Number} x
762
+ * @param {Number} y
763
+ * @return {Object}
764
+ * @api private
765
+ */
766
+
767
+ ColorPicker.prototype.colorAt = function(x, y){
768
+ var data = this.main.getContext('2d').getImageData(x, y, 1, 1).data;
769
+ return {
770
+ r: data[0]
771
+ , g: data[1]
772
+ , b: data[2]
773
+ , toString: function(){
774
+ return rgb(this.r, this.g, this.b);
775
+ }
776
+ };
777
+ };
778
+
779
+ /**
780
+ * Get the RGB value at `y`.
781
+ *
782
+ * @param {Type} name
783
+ * @return {Type}
784
+ * @api private
785
+ */
786
+
787
+ ColorPicker.prototype.hueAt = function(y){
788
+ var data = this.spectrum.getContext('2d').getImageData(0, y, 1, 1).data;
789
+ return {
790
+ r: data[0]
791
+ , g: data[1]
792
+ , b: data[2]
793
+ , toString: function(){
794
+ return rgb(this.r, this.g, this.b);
795
+ }
796
+ };
797
+ };
798
+
799
+ /**
800
+ * Get or set `color`.
801
+ *
802
+ * @param {String} color
803
+ * @return {String|ColorPicker}
804
+ * @api public
805
+ */
806
+
807
+ ColorPicker.prototype.color = function(color){
808
+ // TODO: update pos
809
+ if (0 == arguments.length) return this._color;
810
+ this._color = color;
811
+ return this;
812
+ };
813
+
814
+ /**
815
+ * Get or set hue `color`.
816
+ *
817
+ * @param {String} color
818
+ * @return {String|ColorPicker}
819
+ * @api public
820
+ */
821
+
822
+ ColorPicker.prototype.hue = function(color){
823
+ // TODO: update pos
824
+ if (0 == arguments.length) return this._hue;
825
+ this._hue = color;
826
+ return this;
827
+ };
828
+
829
+ /**
830
+ * Render with the given `options`.
831
+ *
832
+ * @param {Object} options
833
+ * @api public
834
+ */
835
+
836
+ ColorPicker.prototype.render = function(options){
837
+ options = options || {};
838
+ this.renderMain(options);
839
+ this.renderSpectrum(options);
840
+ };
841
+
842
+ /**
843
+ * Render spectrum.
844
+ *
845
+ * @api private
846
+ */
847
+
848
+ ColorPicker.prototype.renderSpectrum = function(options){
849
+ var el = this.el
850
+ , canvas = this.spectrum
851
+ , ctx = canvas.getContext('2d')
852
+ , pos = this._huePos
853
+ , w = this.w * .12
854
+ , h = this.h;
855
+
856
+ canvas.width = w;
857
+ canvas.height = h;
858
+
859
+ var grad = ctx.createLinearGradient(0, 0, 0, h);
860
+ grad.addColorStop(0, rgb(255, 0, 0));
861
+ grad.addColorStop(.15, rgb(255, 0, 255));
862
+ grad.addColorStop(.33, rgb(0, 0, 255));
863
+ grad.addColorStop(.49, rgb(0, 255, 255));
864
+ grad.addColorStop(.67, rgb(0, 255, 0));
865
+ grad.addColorStop(.84, rgb(255, 255, 0));
866
+ grad.addColorStop(1, rgb(255, 0, 0));
867
+
868
+ ctx.fillStyle = grad;
869
+ ctx.fillRect(0, 0, w, h);
870
+
871
+ // pos
872
+ if (!pos) return;
873
+ ctx.fillStyle = rgba(0,0,0, .3);
874
+ ctx.fillRect(0, pos, w, 1);
875
+ ctx.fillStyle = rgba(255,255,255, .3);
876
+ ctx.fillRect(0, pos + 1, w, 1);
877
+ };
878
+
879
+ /**
880
+ * Render hue/luminosity canvas.
881
+ *
882
+ * @api private
883
+ */
884
+
885
+ ColorPicker.prototype.renderMain = function(options){
886
+ var el = this.el
887
+ , canvas = this.main
888
+ , ctx = canvas.getContext('2d')
889
+ , w = this.w
890
+ , h = this.h
891
+ , x = (this._colorPos.x || w) + .5
892
+ , y = (this._colorPos.y || 0) + .5;
893
+
894
+ canvas.width = w;
895
+ canvas.height = h;
896
+
897
+ var grad = ctx.createLinearGradient(0, 0, w, 0);
898
+ grad.addColorStop(0, rgb(255, 255, 255));
899
+ grad.addColorStop(1, this._hue);
900
+
901
+ ctx.fillStyle = grad;
902
+ ctx.fillRect(0, 0, w, h);
903
+
904
+ grad = ctx.createLinearGradient(0, 0, 0, h);
905
+ grad.addColorStop(0, rgba(255, 255, 255, 0));
906
+ grad.addColorStop(1, rgba(0, 0, 0, 1));
907
+
908
+ ctx.fillStyle = grad;
909
+ ctx.fillRect(0, 0, w, h);
910
+
911
+ // pos
912
+ var rad = 10;
913
+ ctx.save();
914
+ ctx.beginPath();
915
+ ctx.lineWidth = 1;
916
+
917
+ // outer dark
918
+ ctx.strokeStyle = rgba(0,0,0,.5);
919
+ ctx.arc(x, y, rad / 2, 0, Math.PI * 2, false);
920
+ ctx.stroke();
921
+
922
+ // outer light
923
+ ctx.strokeStyle = rgba(255,255,255,.5);
924
+ ctx.arc(x, y, rad / 2 - 1, 0, Math.PI * 2, false);
925
+ ctx.stroke();
926
+
927
+ ctx.beginPath();
928
+ ctx.restore();
929
+ };
930
+ })(ui, "<div class=\"color-picker\">\n <canvas class=\"main\"></canvas>\n <canvas class=\"spectrum\"></canvas>\n</div>");
931
+ ;(function(exports, html){
932
+
933
+ /**
934
+ * Notification list.
935
+ */
936
+
937
+ var list;
938
+
939
+ /**
940
+ * Expose `Notification`.
941
+ */
942
+
943
+ exports.Notification = Notification;
944
+
945
+ // list
946
+
947
+ $(function(){
948
+ list = $('<ul id="notifications">');
949
+ list.appendTo('body');
950
+ })
951
+
952
+ /**
953
+ * Return a new `Notification` with the given
954
+ * (optional) `title` and `msg`.
955
+ *
956
+ * @param {String} title or msg
957
+ * @param {String} msg
958
+ * @return {Dialog}
959
+ * @api public
960
+ */
961
+
962
+ exports.notify = function(title, msg){
963
+ switch (arguments.length) {
964
+ case 2:
965
+ return new Notification({ title: title, message: msg })
966
+ .show()
967
+ .hide(4000);
968
+ case 1:
969
+ return new Notification({ message: title })
970
+ .show()
971
+ .hide(4000);
972
+ }
973
+ };
974
+
975
+ /**
976
+ * Construct a notification function for `type`.
977
+ *
978
+ * @param {String} type
979
+ * @return {Function}
980
+ * @api private
981
+ */
982
+
983
+ function type(type) {
984
+ return function(title, msg){
985
+ return exports.notify.apply(this, arguments)
986
+ .type(type);
987
+ }
988
+ }
989
+
990
+ /**
991
+ * Notification methods.
992
+ */
993
+
994
+ exports.info = exports.notify;
995
+ exports.warn = type('warn');
996
+ exports.error = type('error');
997
+
998
+ /**
999
+ * Initialize a new `Notification`.
1000
+ *
1001
+ * Options:
1002
+ *
1003
+ * - `title` dialog title
1004
+ * - `message` a message to display
1005
+ *
1006
+ * @param {Object} options
1007
+ * @api public
1008
+ */
1009
+
1010
+ function Notification(options) {
1011
+ ui.Emitter.call(this);
1012
+ options = options || {};
1013
+ this.template = html;
1014
+ this.el = $(this.template);
1015
+ this.render(options);
1016
+ if (Notification.effect) this.effect(Notification.effect);
1017
+ };
1018
+
1019
+ /**
1020
+ * Inherit from `Emitter.prototype`.
1021
+ */
1022
+
1023
+ Notification.prototype = new ui.Emitter;
1024
+
1025
+ /**
1026
+ * Render with the given `options`.
1027
+ *
1028
+ * @param {Object} options
1029
+ * @api public
1030
+ */
1031
+
1032
+ Notification.prototype.render = function(options){
1033
+ var el = this.el
1034
+ , title = options.title
1035
+ , msg = options.message
1036
+ , self = this;
1037
+
1038
+ el.find('.close').click(function(){
1039
+ self.hide();
1040
+ return false;
1041
+ });
1042
+
1043
+ el.click(function(e){
1044
+ e.preventDefault();
1045
+ self.emit('click', e);
1046
+ });
1047
+
1048
+ el.find('h1').text(title);
1049
+ if (!title) el.find('h1').remove();
1050
+
1051
+ // message
1052
+ if ('string' == typeof msg) {
1053
+ el.find('p').text(msg);
1054
+ } else if (msg) {
1055
+ el.find('p').replaceWith(msg.el || msg);
1056
+ }
1057
+
1058
+ setTimeout(function(){
1059
+ el.removeClass('hide');
1060
+ }, 0);
1061
+ };
1062
+
1063
+ /**
1064
+ * Enable the dialog close link.
1065
+ *
1066
+ * @return {Notification} for chaining
1067
+ * @api public
1068
+ */
1069
+
1070
+ Notification.prototype.closable = function(){
1071
+ this.el.addClass('closable');
1072
+ return this;
1073
+ };
1074
+
1075
+ /**
1076
+ * Set the effect to `type`.
1077
+ *
1078
+ * @param {String} type
1079
+ * @return {Notification} for chaining
1080
+ * @api public
1081
+ */
1082
+
1083
+ Notification.prototype.effect = function(type){
1084
+ this._effect = type;
1085
+ this.el.addClass(type);
1086
+ return this;
1087
+ };
1088
+
1089
+ /**
1090
+ * Show the notification.
1091
+ *
1092
+ * @return {Notification} for chaining
1093
+ * @api public
1094
+ */
1095
+
1096
+ Notification.prototype.show = function(){
1097
+ this.el.appendTo(list);
1098
+ return this;
1099
+ };
1100
+
1101
+ /**
1102
+ * Set the notification `type`.
1103
+ *
1104
+ * @param {String} type
1105
+ * @return {Notification} for chaining
1106
+ * @api public
1107
+ */
1108
+
1109
+ Notification.prototype.type = function(type){
1110
+ this._type = type;
1111
+ this.el.addClass(type);
1112
+ return this;
1113
+ };
1114
+
1115
+ /**
1116
+ * Make it stick (clear hide timer), and make it closable.
1117
+ *
1118
+ * @return {Notification} for chaining
1119
+ * @api public
1120
+ */
1121
+
1122
+ Notification.prototype.sticky = function(){
1123
+ return this.hide(0).closable();
1124
+ };
1125
+
1126
+ /**
1127
+ * Hide the dialog with optional delay of `ms`,
1128
+ * otherwise the notification is removed immediately.
1129
+ *
1130
+ * @return {Number} ms
1131
+ * @return {Notification} for chaining
1132
+ * @api public
1133
+ */
1134
+
1135
+ Notification.prototype.hide = function(ms){
1136
+ var self = this;
1137
+
1138
+ // duration
1139
+ if ('number' == typeof ms) {
1140
+ clearTimeout(this.timer);
1141
+ if (!ms) return this;
1142
+ this.timer = setTimeout(function(){
1143
+ self.hide();
1144
+ }, ms);
1145
+ return this;
1146
+ }
1147
+
1148
+ // hide / remove
1149
+ this.el.addClass('hide');
1150
+ if (this._effect) {
1151
+ setTimeout(function(self){
1152
+ self.remove();
1153
+ }, 500, this);
1154
+ } else {
1155
+ self.remove();
1156
+ }
1157
+
1158
+ return this;
1159
+ };
1160
+
1161
+ /**
1162
+ * Hide the notification without potential animation.
1163
+ *
1164
+ * @return {Dialog} for chaining
1165
+ * @api public
1166
+ */
1167
+
1168
+ Notification.prototype.remove = function(){
1169
+ this.el.remove();
1170
+ return this;
1171
+ };
1172
+ })(ui, "<li class=\"notification hide\">\n <div class=\"content\">\n <h1>Title</h1>\n <a href=\"#\" class=\"close\">×</a>\n <p>Message</p>\n </div>\n</li>");
1173
+ ;(function(exports, html){
1174
+
1175
+ /**
1176
+ * Expose `SplitButton`.
1177
+ */
1178
+
1179
+ exports.SplitButton = SplitButton;
1180
+
1181
+ /**
1182
+ * Initialize a new `SplitButton`
1183
+ * with an optional `label`.
1184
+ *
1185
+ * @param {String} label
1186
+ * @api public
1187
+ */
1188
+
1189
+ function SplitButton(label) {
1190
+ ui.Emitter.call(this);
1191
+ this.el = $(html);
1192
+ this.events();
1193
+ this.render({ label: label });
1194
+ this.state = 'hidden';
1195
+ }
1196
+
1197
+ /**
1198
+ * Inherit from `Emitter.prototype`.
1199
+ */
1200
+
1201
+ SplitButton.prototype = new ui.Emitter;
1202
+
1203
+ /**
1204
+ * Register event handlers.
1205
+ *
1206
+ * @api private
1207
+ */
1208
+
1209
+ SplitButton.prototype.events = function(){
1210
+ var self = this
1211
+ , el = this.el;
1212
+
1213
+ el.find('.button').click(function(e){
1214
+ e.preventDefault();
1215
+ self.emit('click', e);
1216
+ });
1217
+
1218
+ el.find('.toggle').click(function(e){
1219
+ e.preventDefault();
1220
+ self.toggle();
1221
+ });
1222
+ };
1223
+
1224
+ /**
1225
+ * Toggle the drop-down contents.
1226
+ *
1227
+ * @return {SplitButton}
1228
+ * @api public
1229
+ */
1230
+
1231
+ SplitButton.prototype.toggle = function(){
1232
+ return 'hidden' == this.state
1233
+ ? this.show()
1234
+ : this.hide();
1235
+ };
1236
+
1237
+ /**
1238
+ * Show the drop-down contents.
1239
+ *
1240
+ * @return {SplitButton}
1241
+ * @api public
1242
+ */
1243
+
1244
+ SplitButton.prototype.show = function(){
1245
+ this.state = 'visible';
1246
+ this.emit('show');
1247
+ this.el.addClass('show');
1248
+ return this;
1249
+ };
1250
+
1251
+ /**
1252
+ * Hide the drop-down contents.
1253
+ *
1254
+ * @return {SplitButton}
1255
+ * @api public
1256
+ */
1257
+
1258
+ SplitButton.prototype.hide = function(){
1259
+ this.state = 'hidden';
1260
+ this.emit('hide');
1261
+ this.el.removeClass('show');
1262
+ return this;
1263
+ };
1264
+
1265
+ /**
1266
+ * Render the split-button with the given `options`.
1267
+ *
1268
+ * @param {Object} options
1269
+ * @return {SplitButton}
1270
+ * @api private
1271
+ */
1272
+
1273
+ SplitButton.prototype.render = function(options){
1274
+ var options = options || {}
1275
+ , button = this.el.find('.button')
1276
+ , label = options.label;
1277
+
1278
+ if ('string' == label) button.text(label);
1279
+ else button.text('').append(label);
1280
+ return this;
1281
+ };
1282
+
1283
+ })(ui, "<div class=\"split-button\">\n <a class=\"button\" href=\"#\">Action</a>\n <a class=\"toggle\" href=\"#\"><span></span></a>\n</div>");
1284
+ ;(function(exports, html){
1285
+
1286
+ /**
1287
+ * Expose `Menu`.
1288
+ */
1289
+
1290
+ exports.Menu = Menu;
1291
+
1292
+ /**
1293
+ * Create a new `Menu`.
1294
+ *
1295
+ * @return {Menu}
1296
+ * @api public
1297
+ */
1298
+
1299
+ exports.menu = function(){
1300
+ return new Menu();
1301
+ };
1302
+
1303
+ /**
1304
+ * Initialize a new `Menu`.
1305
+ *
1306
+ * Emits:
1307
+ *
1308
+ * - "show" when shown
1309
+ * - "hide" when hidden
1310
+ * - "remove" with the item name when an item is removed
1311
+ * - * menu item events are emitted when clicked
1312
+ *
1313
+ * @api public
1314
+ */
1315
+
1316
+ function Menu() {
1317
+ ui.Emitter.call(this);
1318
+ this.items = {};
1319
+ this.el = $(html).hide().appendTo('body');
1320
+ this.el.hover(this.deselect.bind(this));
1321
+ $('html').click(this.hide.bind(this));
1322
+ this.on('show', this.bindKeyboardEvents.bind(this));
1323
+ this.on('hide', this.unbindKeyboardEvents.bind(this));
1324
+ };
1325
+
1326
+ /**
1327
+ * Inherit from `Emitter.prototype`.
1328
+ */
1329
+
1330
+ Menu.prototype = new ui.Emitter;
1331
+
1332
+ /**
1333
+ * Deselect selected menu items.
1334
+ *
1335
+ * @api private
1336
+ */
1337
+
1338
+ Menu.prototype.deselect = function(){
1339
+ this.el.find('.selected').removeClass('selected');
1340
+ };
1341
+
1342
+ /**
1343
+ * Bind keyboard events.
1344
+ *
1345
+ * @api private
1346
+ */
1347
+
1348
+ Menu.prototype.bindKeyboardEvents = function(){
1349
+ $(document).bind('keydown.menu', this.onkeydown.bind(this));
1350
+ return this;
1351
+ };
1352
+
1353
+ /**
1354
+ * Unbind keyboard events.
1355
+ *
1356
+ * @api private
1357
+ */
1358
+
1359
+ Menu.prototype.unbindKeyboardEvents = function(){
1360
+ $(document).unbind('keydown.menu');
1361
+ return this;
1362
+ };
1363
+
1364
+ /**
1365
+ * Handle keydown events.
1366
+ *
1367
+ * @api private
1368
+ */
1369
+
1370
+ Menu.prototype.onkeydown = function(e){
1371
+ switch (e.keyCode) {
1372
+ // up
1373
+ case 38:
1374
+ e.preventDefault();
1375
+ this.move('prev');
1376
+ break;
1377
+ // down
1378
+ case 40:
1379
+ e.preventDefault();
1380
+ this.move('next');
1381
+ break;
1382
+ }
1383
+ };
1384
+
1385
+ /**
1386
+ * Focus on the next menu item in `direction`.
1387
+ *
1388
+ * @param {String} direction "prev" or "next"
1389
+ * @api public
1390
+ */
1391
+
1392
+ Menu.prototype.move = function(direction){
1393
+ var prev = this.el.find('.selected').eq(0);
1394
+
1395
+ var next = prev.length
1396
+ ? prev[direction]()
1397
+ : this.el.find('li:first-child');
1398
+
1399
+ if (next.length) {
1400
+ prev.removeClass('selected');
1401
+ next.addClass('selected');
1402
+ next.find('a').focus();
1403
+ }
1404
+ };
1405
+
1406
+ /**
1407
+ * Add menu item with the given `text` and optional callback `fn`.
1408
+ *
1409
+ * When the item is clicked `fn()` will be invoked
1410
+ * and the `Menu` is immediately closed. When clicked
1411
+ * an event of the name `text` is emitted regardless of
1412
+ * the callback function being present.
1413
+ *
1414
+ * @param {String} text
1415
+ * @param {Function} fn
1416
+ * @return {Menu}
1417
+ * @api public
1418
+ */
1419
+
1420
+ Menu.prototype.add = function(text, fn){
1421
+ var self = this
1422
+ , el = $('<li><a href="#">' + text + '</a></li>')
1423
+ .addClass(slug(text))
1424
+ .appendTo(this.el)
1425
+ .click(function(e){
1426
+ e.preventDefault();
1427
+ e.stopPropagation();
1428
+ self.hide();
1429
+ self.emit(text);
1430
+ fn && fn();
1431
+ });
1432
+
1433
+ this.items[text] = el;
1434
+ return this;
1435
+ };
1436
+
1437
+ /**
1438
+ * Remove menu item with the given `text`.
1439
+ *
1440
+ * @param {String} text
1441
+ * @return {Menu}
1442
+ * @api public
1443
+ */
1444
+
1445
+ Menu.prototype.remove = function(text){
1446
+ var item = this.items[text];
1447
+ if (!item) throw new Error('no menu item named "' + text + '"');
1448
+ this.emit('remove', text);
1449
+ item.remove();
1450
+ delete this.items[text];
1451
+ return this;
1452
+ };
1453
+
1454
+ /**
1455
+ * Check if this menu has an item with the given `text`.
1456
+ *
1457
+ * @param {String} text
1458
+ * @return {Boolean}
1459
+ * @api public
1460
+ */
1461
+
1462
+ Menu.prototype.has = function(text){
1463
+ return !! this.items[text];
1464
+ };
1465
+
1466
+ /**
1467
+ * Move context menu to `(x, y)`.
1468
+ *
1469
+ * @param {Number} x
1470
+ * @param {Number} y
1471
+ * @return {Menu}
1472
+ * @api public
1473
+ */
1474
+
1475
+ Menu.prototype.moveTo = function(x, y){
1476
+ this.el.css({
1477
+ top: y,
1478
+ left: x
1479
+ });
1480
+ return this;
1481
+ };
1482
+
1483
+ /**
1484
+ * Show the menu.
1485
+ *
1486
+ * @return {Menu}
1487
+ * @api public
1488
+ */
1489
+
1490
+ Menu.prototype.show = function(){
1491
+ this.emit('show');
1492
+ this.el.show();
1493
+ return this;
1494
+ };
1495
+
1496
+ /**
1497
+ * Hide the menu.
1498
+ *
1499
+ * @return {Menu}
1500
+ * @api public
1501
+ */
1502
+
1503
+ Menu.prototype.hide = function(){
1504
+ this.emit('hide');
1505
+ this.el.hide();
1506
+ return this;
1507
+ };
1508
+
1509
+ /**
1510
+ * Generate a slug from `str`.
1511
+ *
1512
+ * @param {String} str
1513
+ * @return {String}
1514
+ * @api private
1515
+ */
1516
+
1517
+ function slug(str) {
1518
+ return str
1519
+ .toLowerCase()
1520
+ .replace(/ +/g, '-')
1521
+ .replace(/[^a-z0-9-]/g, '');
1522
+ }
1523
+
1524
+ })(ui, "<div class=\"menu\">\n</div>");
1525
+ ;(function(exports, html){
1526
+
1527
+ /**
1528
+ * Expose `Card`.
1529
+ */
1530
+
1531
+ exports.Card = Card;
1532
+
1533
+ /**
1534
+ * Create a new `Card`.
1535
+ *
1536
+ * @param {Mixed} front
1537
+ * @param {Mixed} back
1538
+ * @return {Card}
1539
+ * @api public
1540
+ */
1541
+
1542
+ exports.card = function(front, back){
1543
+ return new Card(front, back);
1544
+ };
1545
+
1546
+ /**
1547
+ * Initialize a new `Card` with content
1548
+ * for face `front` and `back`.
1549
+ *
1550
+ * Emits "flip" event.
1551
+ *
1552
+ * @param {Mixed} front
1553
+ * @param {Mixed} back
1554
+ * @api public
1555
+ */
1556
+
1557
+ function Card(front, back) {
1558
+ ui.Emitter.call(this);
1559
+ this._front = front || $('<p>front</p>');
1560
+ this._back = back || $('<p>back</p>');
1561
+ this.template = html;
1562
+ this.render();
1563
+ };
1564
+
1565
+ /**
1566
+ * Inherit from `Emitter.prototype`.
1567
+ */
1568
+
1569
+ Card.prototype = new ui.Emitter;
1570
+
1571
+ /**
1572
+ * Set front face `val`.
1573
+ *
1574
+ * @param {Mixed} val
1575
+ * @return {Card}
1576
+ * @api public
1577
+ */
1578
+
1579
+ Card.prototype.front = function(val){
1580
+ this._front = val;
1581
+ this.render();
1582
+ return this;
1583
+ };
1584
+
1585
+ /**
1586
+ * Set back face `val`.
1587
+ *
1588
+ * @param {Mixed} val
1589
+ * @return {Card}
1590
+ * @api public
1591
+ */
1592
+
1593
+ Card.prototype.back = function(val){
1594
+ this._back = val;
1595
+ this.render();
1596
+ return this;
1597
+ };
1598
+
1599
+ /**
1600
+ * Flip the card.
1601
+ *
1602
+ * @return {Card} for chaining
1603
+ * @api public
1604
+ */
1605
+
1606
+ Card.prototype.flip = function(){
1607
+ this.emit('flip');
1608
+ this.el.toggleClass('flipped');
1609
+ return this;
1610
+ };
1611
+
1612
+ /**
1613
+ * Set the effect to `type`.
1614
+ *
1615
+ * @param {String} type
1616
+ * @return {Dialog} for chaining
1617
+ * @api public
1618
+ */
1619
+
1620
+ Card.prototype.effect = function(type){
1621
+ this.el.addClass(type);
1622
+ return this;
1623
+ };
1624
+
1625
+ /**
1626
+ * Render with the given `options`.
1627
+ *
1628
+ * @param {Object} options
1629
+ * @api public
1630
+ */
1631
+
1632
+ Card.prototype.render = function(options){
1633
+ var self = this
1634
+ , el = this.el = $(this.template);
1635
+ el.find('.front').empty().append(this._front.el || $(this._front));
1636
+ el.find('.back').empty().append(this._back.el || $(this._back));
1637
+ el.click(function(){
1638
+ self.flip();
1639
+ });
1640
+ };
1641
+ })(ui, "<div class=\"card\">\n <div class=\"wrapper\">\n <div class=\"face front\">1</div>\n <div class=\"face back\">2</div>\n </div>\n</div>");