source: pro-violet-viettel/docs/template/assets/js/uncompressed/jquery.knob.js @ 400

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