Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
21627 kshitij.so 1
/*!jQuery Knob*/
2
/**
3
 * Downward compatible, touchable dial
4
 *
5
 * Version: 1.2.0 (15/07/2012)
6
 * Requires: jQuery v1.7+
7
 *
8
 * Copyright (c) 2012 Anthony Terrien
9
 * Under MIT and GPL licenses:
10
 *  http://www.opensource.org/licenses/mit-license.php
11
 *  http://www.gnu.org/licenses/gpl.html
12
 *
13
 * Thanks to vor, eskimoblood, spiffistan, FabrizioC
14
 */
15
(function($) {
16
 
17
    /**
18
     * Kontrol library
19
     */
20
    "use strict";
21
 
22
    /**
23
     * Definition of globals and core
24
     */
25
    var k = {}, // kontrol
26
        max = Math.max,
27
        min = Math.min;
28
 
29
    k.c = {};
30
    k.c.d = $(document);
31
    k.c.t = function (e) {
32
        return e.originalEvent.touches.length - 1;
33
    };
34
 
35
    /**
36
     * Kontrol Object
37
     *
38
     * Definition of an abstract UI control
39
     *
40
     * Each concrete component must call this one.
41
     * <code>
42
     * k.o.call(this);
43
     * </code>
44
     */
45
    k.o = function () {
46
        var s = this;
47
 
48
        this.o = null; // array of options
49
        this.$ = null; // jQuery wrapped element
50
        this.i = null; // mixed HTMLInputElement or array of HTMLInputElement
51
        this.g = null; // 2D graphics context for 'pre-rendering'
52
        this.v = null; // value ; mixed array or integer
53
        this.cv = null; // change value ; not commited value
54
        this.x = 0; // canvas x position
55
        this.y = 0; // canvas y position
56
        this.$c = null; // jQuery canvas element
57
        this.c = null; // rendered canvas context
58
        this.t = 0; // touches index
59
        this.isInit = false;
60
        this.fgColor = null; // main color
61
        this.pColor = null; // previous color
62
        this.dH = null; // draw hook
63
        this.cH = null; // change hook
64
        this.eH = null; // cancel hook
65
        this.rH = null; // release hook
66
 
67
        this.run = function () {
68
            var cf = function (e, conf) {
69
                var k;
70
                for (k in conf) {
71
                    s.o[k] = conf[k];
72
                }
73
                s.init();
74
                s._configure()
75
                 ._draw();
76
            };
77
 
78
            if(this.$.data('kontroled')) return;
79
            this.$.data('kontroled', true);
80
 
81
            this.extend();
82
            this.o = $.extend(
83
                {
84
                    // Config
85
                    min : this.$.data('min') || 0,
86
                    max : this.$.data('max') || 100,
87
                    stopper : true,
88
                    readOnly : this.$.data('readonly'),
89
 
90
                    // UI
91
                    cursor : (this.$.data('cursor') === true && 30)
92
                                || this.$.data('cursor')
93
                                || 0,
94
                    thickness : this.$.data('thickness') || 0.35,
95
                    width : this.$.data('width') || 200,
96
                    height : this.$.data('height') || 200,
97
                    displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'),
98
                    displayPrevious : this.$.data('displayprevious'),
99
                    fgColor : this.$.data('fgcolor') || '#87CEEB',
100
                    inline : false,
101
 
102
                    // Hooks
103
                    draw : null, // function () {}
104
                    change : null, // function (value) {}
105
                    cancel : null, // function () {}
106
                    release : null // function (value) {}
107
                }, this.o
108
            );
109
 
110
            // routing value
111
            if(this.$.is('fieldset')) {
112
 
113
                // fieldset = array of integer
114
                this.v = {};
115
                this.i = this.$.find('input')
116
                this.i.each(function(k) {
117
                    var $this = $(this);
118
                    s.i[k] = $this;
119
                    s.v[k] = $this.val();
120
 
121
                    $this.bind(
122
                        'change'
123
                        , function () {
124
                            var val = {};
125
                            val[k] = $this.val();
126
                            s.val(val);
127
                        }
128
                    );
129
                });
130
                this.$.find('legend').remove();
131
 
132
            } else {
133
                // input = integer
134
                this.i = this.$;
135
                this.v = this.$.val();
136
                (this.v == '') && (this.v = this.o.min);
137
 
138
                this.$.bind(
139
                    'change'
140
                    , function () {
141
                        s.val(s.$.val());
142
                    }
143
                );
144
            }
145
 
146
            (!this.o.displayInput) && this.$.hide();
147
 
148
            this.$c = $('<canvas width="' +
149
                            this.o.width + 'px" height="' +
150
                            this.o.height + 'px"></canvas>');
151
            this.c = this.$c[0].getContext("2d");
152
 
153
            this.$
154
                .wrap($('<div style="' + (this.o.inline ? 'display:inline;' : '') +
155
                        'width:' + this.o.width + 'px;height:' +
156
                        this.o.height + 'px;"></div>'))
157
                .before(this.$c);
158
 
159
            if (this.v instanceof Object) {
160
                this.cv = {};
161
                this.copy(this.v, this.cv);
162
            } else {
163
                this.cv = this.v;
164
            }
165
 
166
            this.$
167
                .bind("configure", cf)
168
                .parent()
169
                .bind("configure", cf);
170
 
171
            this._listen()
172
                ._configure()
173
                ._xy()
174
                .init();
175
 
176
            this.isInit = true;
177
 
178
            this._draw();
179
 
180
            return this;
181
        };
182
 
183
        this._draw = function () {
184
 
185
            // canvas pre-rendering
186
            var d = true,
187
                c = document.createElement('canvas');
188
 
189
            c.width = s.o.width;
190
            c.height = s.o.height;
191
            s.g = c.getContext('2d');
192
 
193
            s.clear();
194
 
195
            s.dH
196
            && (d = s.dH());
197
 
198
            (d !== false) && s.draw();
199
 
200
            s.c.drawImage(c, 0, 0);
201
            c = null;
202
        };
203
 
204
        this._touch = function (e) {
205
 
206
            var touchMove = function (e) {
207
 
208
                var v = s.xy2val(
209
                            e.originalEvent.touches[s.t].pageX,
210
                            e.originalEvent.touches[s.t].pageY
211
                            );
212
 
213
                if (v == s.cv) return;
214
 
215
                if (
216
                    s.cH
217
                    && (s.cH(v) === false)
218
                ) return;
219
 
220
 
221
                s.change(v);
222
                s._draw();
223
            };
224
 
225
            // get touches index
226
            this.t = k.c.t(e);
227
 
228
            // First touch
229
            touchMove(e);
230
 
231
            // Touch events listeners
232
            k.c.d
233
                .bind("touchmove.k", touchMove)
234
                .bind(
235
                    "touchend.k"
236
                    , function () {
237
                        k.c.d.unbind('touchmove.k touchend.k');
238
 
239
                        if (
240
                            s.rH
241
                            && (s.rH(s.cv) === false)
242
                        ) return;
243
 
244
                        s.val(s.cv);
245
                    }
246
                );
247
 
248
            return this;
249
        };
250
 
251
        this._mouse = function (e) {
252
 
253
            var mouseMove = function (e) {
254
                var v = s.xy2val(e.pageX, e.pageY);
255
                if (v == s.cv) return;
256
 
257
                if (
258
                    s.cH
259
                    && (s.cH(v) === false)
260
                ) return;
261
 
262
                s.change(v);
263
                s._draw();
264
            };
265
 
266
            // First click
267
            mouseMove(e);
268
 
269
            // Mouse events listeners
270
            k.c.d
271
                .bind("mousemove.k", mouseMove)
272
                .bind(
273
                    // Escape key cancel current change
274
                    "keyup.k"
275
                    , function (e) {
276
                        if (e.keyCode === 27) {
277
                            k.c.d.unbind("mouseup.k mousemove.k keyup.k");
278
 
279
                            if (
280
                                s.eH
281
                                && (s.eH() === false)
282
                            ) return;
283
 
284
                            s.cancel();
285
                        }
286
                    }
287
                )
288
                .bind(
289
                    "mouseup.k"
290
                    , function (e) {
291
                        k.c.d.unbind('mousemove.k mouseup.k keyup.k');
292
 
293
                        if (
294
                            s.rH
295
                            && (s.rH(s.cv) === false)
296
                        ) return;
297
 
298
                        s.val(s.cv);
299
                    }
300
                );
301
 
302
            return this;
303
        };
304
 
305
        this._xy = function () {
306
            var o = this.$c.offset();
307
            this.x = o.left;
308
            this.y = o.top;
309
            return this;
310
        };
311
 
312
        this._listen = function () {
313
 
314
            if (!this.o.readOnly) {
315
                this.$c
316
                    .bind(
317
                        "mousedown"
318
                        , function (e) {
319
                            e.preventDefault();
320
                            s._xy()._mouse(e);
321
                         }
322
                    )
323
                    .bind(
324
                        "touchstart"
325
                        , function (e) {
326
                            e.preventDefault();
327
                            s._xy()._touch(e);
328
                         }
329
                    );
330
                this.listen();
331
            } else {
332
                this.$.attr('readonly', 'readonly');
333
            }
334
 
335
            return this;
336
        };
337
 
338
        this._configure = function () {
339
 
340
            // Hooks
341
            if (this.o.draw) this.dH = this.o.draw;
342
            if (this.o.change) this.cH = this.o.change;
343
            if (this.o.cancel) this.eH = this.o.cancel;
344
            if (this.o.release) this.rH = this.o.release;
345
 
346
            if (this.o.displayPrevious) {
347
                this.pColor = this.h2rgba(this.o.fgColor, "0.4");
348
                this.fgColor = this.h2rgba(this.o.fgColor, "0.6");
349
            } else {
350
                this.fgColor = this.o.fgColor;
351
            }
352
 
353
            return this;
354
        };
355
 
356
        this._clear = function () {
357
            this.$c[0].width = this.$c[0].width;
358
        };
359
 
360
        // Abstract methods
361
        this.listen = function () {}; // on start, one time
362
        this.extend = function () {}; // each time configure triggered
363
        this.init = function () {}; // each time configure triggered
364
        this.change = function (v) {}; // on change
365
        this.val = function (v) {}; // on release
366
        this.xy2val = function (x, y) {}; //
367
        this.draw = function () {}; // on change / on release
368
        this.clear = function () { this._clear(); };
369
 
370
        // Utils
371
        this.h2rgba = function (h, a) {
372
            var rgb;
373
            h = h.substring(1,7)
374
            rgb = [parseInt(h.substring(0,2),16)
375
                   ,parseInt(h.substring(2,4),16)
376
                   ,parseInt(h.substring(4,6),16)];
377
            return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")";
378
        };
379
 
380
        this.copy = function (f, t) {
381
            for (var i in f) { t[i] = f[i]; }
382
        };
383
    };
384
 
385
 
386
    /**
387
     * k.Dial
388
     */
389
    k.Dial = function () {
390
        k.o.call(this);
391
 
392
        this.startAngle = null;
393
        this.xy = null;
394
        this.radius = null;
395
        this.lineWidth = null;
396
        this.cursorExt = null;
397
        this.w2 = null;
398
        this.PI2 = 2*Math.PI;
399
 
400
        this.extend = function () {
401
            this.o = $.extend(
402
                {
403
                    bgColor : this.$.data('bgcolor') || '#EEEEEE',
404
                    angleOffset : this.$.data('angleoffset') || 0,
405
                    angleArc : this.$.data('anglearc') || 360,
406
                    inline : true
407
                }, this.o
408
            );
409
        };
410
 
411
        this.val = function (v) {
412
            if (null != v) {
413
                this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;
414
                this.v = this.cv;
415
                this.$.val(this.v);
416
                this._draw();
417
            } else {
418
                return this.v;
419
            }
420
        };
421
 
422
        this.xy2val = function (x, y) {
423
            var a, ret;
424
 
425
            a = Math.atan2(
426
                        x - (this.x + this.w2)
427
                        , - (y - this.y - this.w2)
428
                    ) - this.angleOffset;
429
 
430
            if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {
431
                // if isset angleArc option, set to min if .5 under min
432
                a = 0;
433
            } else if (a < 0) {
434
                a += this.PI2;
435
            }
436
 
437
            ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc))
438
                    + this.o.min;
439
 
440
            this.o.stopper
441
            && (ret = max(min(ret, this.o.max), this.o.min));
442
 
443
            return ret;
444
        };
445
 
446
        this.listen = function () {
447
            // bind MouseWheel
448
            var s = this,
449
                mw = function (e) {
450
                            e.preventDefault();
451
 
452
                            var ori = e.originalEvent
453
                                ,deltaX = ori.detail || ori.wheelDeltaX
454
                                ,deltaY = ori.detail || ori.wheelDeltaY
455
                                ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0);
456
 
457
                            if (
458
                                s.cH
459
                                && (s.cH(v) === false)
460
                            ) return;
461
 
462
                            s.val(v);
463
                        }
464
                , kval, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1};
465
 
466
            this.$
467
                .bind(
468
                    "keydown"
469
                    ,function (e) {
470
                        var kc = e.keyCode;
471
 
472
                        // numpad support
473
                        if(kc >= 96 && kc <= 105) {
474
                            kc = e.keyCode = kc - 48;
475
                        }
476
 
477
                        kval = parseInt(String.fromCharCode(kc));
478
 
479
                        if (isNaN(kval)) {
480
 
481
                            (kc !== 13)         // enter
482
                            && (kc !== 8)       // bs
483
                            && (kc !== 9)       // tab
484
                            && (kc !== 189)     // -
485
                            && e.preventDefault();
486
 
487
                            // arrows
488
                            if ($.inArray(kc,[37,38,39,40]) > -1) {
489
                                e.preventDefault();
490
 
491
                                var v = parseInt(s.$.val()) + kv[kc] * m;
492
 
493
                                s.o.stopper
494
                                && (v = max(min(v, s.o.max), s.o.min));
495
 
496
                                s.change(v);
497
                                s._draw();
498
 
499
                                // long time keydown speed-up
500
                                to = window.setTimeout(
501
                                    function () { m*=2; }
502
                                    ,30
503
                                );
504
                            }
505
                        }
506
                    }
507
                )
508
                .bind(
509
                    "keyup"
510
                    ,function (e) {
511
                        if (isNaN(kval)) {
512
                            if (to) {
513
                                window.clearTimeout(to);
514
                                to = null;
515
                                m = 1;
516
                                s.val(s.$.val());
517
                            }
518
                        } else {
519
                            // kval postcond
520
                            (s.$.val() > s.o.max && s.$.val(s.o.max))
521
                            || (s.$.val() < s.o.min && s.$.val(s.o.min));
522
                        }
523
 
524
                    }
525
                );
526
 
527
            this.$c.bind("mousewheel DOMMouseScroll", mw);
528
            this.$.bind("mousewheel DOMMouseScroll", mw)
529
        };
530
 
531
        this.init = function () {
532
 
533
            if (
534
                this.v < this.o.min
535
                || this.v > this.o.max
536
            ) this.v = this.o.min;
537
 
538
            this.$.val(this.v);
539
            this.w2 = this.o.width / 2;
540
            this.cursorExt = this.o.cursor / 100;
541
            this.xy = this.w2;
542
            this.lineWidth = this.xy * this.o.thickness;
543
            this.radius = this.xy - this.lineWidth / 2;
544
 
545
            this.o.angleOffset
546
            && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);
547
 
548
            this.o.angleArc
549
            && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);
550
 
551
            // deg to rad
552
            this.angleOffset = this.o.angleOffset * Math.PI / 180;
553
            this.angleArc = this.o.angleArc * Math.PI / 180;
554
 
555
            // compute start and end angles
556
            this.startAngle = 1.5 * Math.PI + this.angleOffset;
557
            this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;
558
 
559
            var s = max(
560
                            String(Math.abs(this.o.max)).length
561
                            , String(Math.abs(this.o.min)).length
562
                            , 2
563
                            ) + 2;
564
 
565
            this.o.displayInput
566
                && this.i.css({
567
                        'width' : ((this.o.width / 2 + 4) >> 0) + 'px'
568
                        ,'height' : ((this.o.width / 3) >> 0) + 'px'
569
                        ,'position' : 'absolute'
570
                        ,'vertical-align' : 'middle'
571
                        ,'margin-top' : ((this.o.width / 3) >> 0) + 'px'
572
                        ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px'
573
                        ,'border' : 0
574
                        ,'background' : 'none'
575
                        ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial'
576
                        ,'text-align' : 'center'
577
                        ,'color' : this.o.fgColor
578
                        ,'padding' : '0px'
579
                        ,'-webkit-appearance': 'none'
580
                        })
581
                || this.i.css({
582
                        'width' : '0px'
583
                        ,'visibility' : 'hidden'
584
                        });
585
        };
586
 
587
        this.change = function (v) {
588
            this.cv = v;
589
            this.$.val(v);
590
        };
591
 
592
        this.angle = function (v) {
593
            return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);
594
        };
595
 
596
        this.draw = function () {
597
 
598
            var c = this.g,                 // context
599
                a = this.angle(this.cv)    // Angle
600
                , sat = this.startAngle     // Start angle
601
                , eat = sat + a             // End angle
602
                , sa, ea                    // Previous angles
603
                , r = 1;
604
 
605
            c.lineWidth = this.lineWidth;
606
 
607
            this.o.cursor
608
                && (sat = eat - this.cursorExt)
609
                && (eat = eat + this.cursorExt);
610
 
611
            c.beginPath();
612
                c.strokeStyle = this.o.bgColor;
613
                c.arc(this.xy, this.xy, this.radius, this.endAngle, this.startAngle, true);
614
            c.stroke();
615
 
616
            if (this.o.displayPrevious) {
617
                ea = this.startAngle + this.angle(this.v);
618
                sa = this.startAngle;
619
                this.o.cursor
620
                    && (sa = ea - this.cursorExt)
621
                    && (ea = ea + this.cursorExt);
622
 
623
                c.beginPath();
624
                    c.strokeStyle = this.pColor;
625
                    c.arc(this.xy, this.xy, this.radius, sa, ea, false);
626
                c.stroke();
627
                r = (this.cv == this.v);
628
            }
629
 
630
            c.beginPath();
631
                c.strokeStyle = r ? this.o.fgColor : this.fgColor ;
632
                c.arc(this.xy, this.xy, this.radius, sat, eat, false);
633
            c.stroke();
634
        };
635
 
636
        this.cancel = function () {
637
            this.val(this.v);
638
        };
639
    };
640
 
641
    $.fn.dial = $.fn.knob = function (o) {
642
        return this.each(
643
            function () {
644
                var d = new k.Dial();
645
                d.o = o;
646
                d.$ = $(this);
647
                d.run();
648
            }
649
        ).parent();
650
    };
651
 
652
})(jQuery);