source: pro-violet-viettel/www/deploy/20150304/assets/js/jquery.knob.js @ 794

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