Spaces:
Runtime error
Runtime error
| /** | |
| * Knob - jQuery Plugin | |
| * Downward compatible, touchable dial | |
| * | |
| * Version: 1.1.2 (22/05/2012) | |
| * Requires: jQuery v1.7+ | |
| * | |
| * Copyright (c) 2011 Anthony Terrien | |
| * Under MIT and GPL licenses: | |
| * http://www.opensource.org/licenses/mit-license.php | |
| * http://www.gnu.org/licenses/gpl.html | |
| * | |
| * Thanks to vor, eskimoblood, spiffistan | |
| */ | |
| $(function () { | |
| // Dial logic | |
| var Dial = function (c, opt) { | |
| var v = null | |
| ,ctx = c[0].getContext("2d") | |
| ,PI2 = 2 * Math.PI | |
| ,mx ,my ,x ,y | |
| ,self = this; | |
| this.onChange = function () {}; | |
| this.onCancel = function () {}; | |
| this.onRelease = function () {}; | |
| this.val = function (nv) { | |
| if (null != nv) { | |
| opt.stopper && (nv = Math.max(Math.min(nv, opt.max), opt.min)); | |
| v = nv; | |
| this.onChange(nv); | |
| if(opt.dynamicDraw) this.dynamicDraw(nv); | |
| else this.draw(nv); | |
| } else { | |
| var b, a; | |
| b = a = Math.atan2(mx - x, -(my - y - opt.width / 2)) - opt.angleOffset; | |
| (a < 0) && (b = a + PI2); | |
| nv = Math.round(b * (opt.max - opt.min) / PI2) + opt.min; | |
| return (nv > opt.max) ? opt.max : nv; | |
| } | |
| }; | |
| this.change = function (nv) { | |
| opt.stopper && (nv = Math.max(Math.min(nv, opt.max), opt.min)); | |
| this.onChange(nv); | |
| this.draw(nv); | |
| }; | |
| this.angle = function (nv) { | |
| return (nv - opt.min) * PI2 / (opt.max - opt.min); | |
| }; | |
| this.draw = function (nv) { | |
| var a = this.angle(nv) // Angle | |
| ,sa = 1.5 * Math.PI + opt.angleOffset // Previous start angle | |
| ,sat = sa // Start angle | |
| ,ea = sa + this.angle(v) // Previous end angle | |
| ,eat = sat + a // End angle | |
| ,r = opt.width / 2 // Radius | |
| ,lw = r * opt.thickness // Line width | |
| ,cgcolor = Dial.getCgColor(opt.cgColor) | |
| ,tick | |
| ; | |
| ctx.clearRect(0, 0, opt.width, opt.width); | |
| ctx.lineWidth = lw; | |
| // Hook draw | |
| if (opt.draw(a, v, opt, ctx)) { return; } | |
| for (tick = 0; tick < opt.ticks; tick++) { | |
| ctx.beginPath(); | |
| if (a > (((2 * Math.PI) / opt.ticks) * tick) && opt.tickColorizeValues) { | |
| ctx.strokeStyle = opt.fgColor; | |
| } else { | |
| ctx.strokeStyle = opt.tickColor; | |
| } | |
| var tick_sa = (((2 * Math.PI) / opt.ticks) * tick) - (0.5 * Math.PI); | |
| ctx.arc( r, r, r-lw-opt.tickLength, tick_sa, tick_sa+opt.tickWidth , false); | |
| ctx.stroke(); | |
| } | |
| opt.cursor | |
| && (sa = ea - 0.3) | |
| && (ea = ea + 0.3) | |
| && (sat = eat - 0.3) | |
| && (eat = eat + 0.3); | |
| switch (opt.skin) { | |
| case 'default' : | |
| ctx.beginPath(); | |
| ctx.strokeStyle = opt.bgColor; | |
| ctx.arc(r, r, r - lw / 2, 0, PI2, true); | |
| ctx.stroke(); | |
| if (opt.displayPrevious) { | |
| ctx.beginPath(); | |
| ctx.strokeStyle = (v == nv) ? opt.fgColor : cgcolor; | |
| ctx.arc(r, r, r - lw / 2, sa, ea, false); | |
| ctx.stroke(); | |
| } | |
| ctx.beginPath(); | |
| ctx.strokeStyle = opt.fgColor; | |
| ctx.arc(r, r, r - lw / 2, sat, eat, false); | |
| ctx.stroke(); | |
| break; | |
| case 'tron' : | |
| if (opt.displayPrevious) { | |
| ctx.beginPath(); | |
| ctx.strokeStyle = (v == nv) ? opt.fgColor : cgcolor; | |
| ctx.arc( r, r, r - lw, sa, ea, false); | |
| ctx.stroke(); | |
| } | |
| ctx.beginPath(); | |
| ctx.strokeStyle = opt.fgColor; | |
| ctx.arc( r, r, r - lw, sat, eat, false); | |
| ctx.stroke(); | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.strokeStyle = opt.fgColor; | |
| ctx.arc( r, r, r - lw + 1 + lw * 2 / 3, 0, 2 * Math.PI, false); | |
| ctx.stroke(); | |
| break; | |
| } | |
| }; | |
| var dynamicDrawIndex; | |
| var dynamicDrawInterval; | |
| this.dynamicDraw = function (nv) { | |
| var instanceOfThis = this; | |
| dynamicDrawIndex = opt.min; | |
| dynamicDrawInterval = setInterval(function() { | |
| instanceOfThis.animateDraw(nv); | |
| }, 20); | |
| }; | |
| this.animateDraw = function () { | |
| if(dynamicDrawIndex > v) { | |
| clearInterval(dynamicDrawInterval); | |
| v = dynamicDrawIndex; | |
| } else { | |
| this.draw(dynamicDrawIndex); | |
| this.change(dynamicDrawIndex); | |
| dynamicDrawIndex++; | |
| } | |
| }; | |
| this.capture = function (e) { | |
| switch (e.type) { | |
| case 'mousemove' : | |
| case 'mousedown' : | |
| mx = e.pageX; | |
| my = e.pageY; | |
| break; | |
| case 'touchmove' : | |
| case 'touchstart' : | |
| mx = e.originalEvent.touches[0].pageX; | |
| my = e.originalEvent.touches[0].pageY; | |
| break; | |
| } | |
| this.change( this.val() ); | |
| }; | |
| this.cancel = function () { | |
| self.val(v); | |
| self.onCancel(); | |
| }; | |
| this.startDrag = function (e) { | |
| var p = c.offset() | |
| ,$doc = $(document); | |
| x = p.left + (opt.width / 2); | |
| y = p.top; | |
| this.capture(e); | |
| // Listen mouse and touch events | |
| $doc.bind( | |
| "mousemove.dial touchmove.dial" | |
| ,function (e) { | |
| self.capture(e); | |
| } | |
| ) | |
| .bind( | |
| // Escape | |
| "keyup.dial" | |
| ,function (e) { | |
| if(e.keyCode === 27) { | |
| $doc.unbind("mouseup.dial mousemove.dial keyup.dial"); | |
| self.cancel(); | |
| } | |
| } | |
| ) | |
| .bind( | |
| "mouseup.dial touchend.dial" | |
| ,function (e) { | |
| $doc.unbind('mousemove.dial touchmove.dial mouseup.dial touchend.dial keyup.dial'); | |
| self.val(self.val()); | |
| self.onRelease(v); | |
| } | |
| ); | |
| }; | |
| }; | |
| // Dial static func | |
| Dial.getCgColor = function (h) { | |
| h = h.substring(1,7); | |
| var rgb = [parseInt(h.substring(0,2),16) | |
| ,parseInt(h.substring(2,4),16) | |
| ,parseInt(h.substring(4,6),16)]; | |
| return "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+",.5)"; | |
| }; | |
| // jQuery plugin | |
| $.fn.knob = $.fn.dial = function (gopt) { | |
| return this.each( | |
| function () { | |
| var $this = $(this), opt; | |
| if ($this.data('dialed')) { return $this; } | |
| $this.data('dialed', true); | |
| opt = $.extend( | |
| { | |
| // Config | |
| 'min' : $this.data('min') || 0 | |
| ,'max' : $this.data('max') || 100 | |
| ,'stopper' : true | |
| ,'readOnly' : $this.data('readonly') | |
| // UI | |
| ,'cursor' : $this.data('cursor') | |
| ,'thickness' : $this.data('thickness') || 0.35 | |
| ,'width' : $this.data('width') || 200 | |
| ,'displayInput' : $this.data('displayinput') == null || $this.data('displayinput') | |
| ,'displayPrevious' : $this.data('displayprevious') | |
| ,'fgColor' : $this.data('fgcolor') || '#87CEEB' | |
| ,'cgColor' : $this.data('cgcolor') || $this.data('fgcolor') || '#87CEEB' | |
| ,'bgColor' : $this.data('bgcolor') || '#EEEEEE' | |
| ,'tickColor' : $this.data('tickColor') || $this.data('fgcolor') || '#DDDDDD' | |
| ,'ticks' : $this.data('ticks') || 0 | |
| ,'tickLength' : $this.data('tickLength') || 0 | |
| ,'tickWidth' : $this.data('tickWidth') || 0.02 | |
| ,'tickColorizeValues' : $this.data('tickColorizeValues') || true | |
| ,'skin' : $this.data('skin') || 'default' | |
| ,'angleOffset': degreeToRadians($this.data('angleoffset')) | |
| ,'dynamicDraw': $this.data('dynamicdraw') || false | |
| // Hooks | |
| ,'draw' : | |
| /** | |
| * @param int a angle | |
| * @param int v current value | |
| * @param array opt plugin options | |
| * @param context ctx Canvas context 2d | |
| * @return bool true:bypass default draw methode | |
| */ | |
| function (a, v, opt, ctx) {} | |
| ,'change' : | |
| /** | |
| * @param int v Current value | |
| */ | |
| function (v) {} | |
| ,'release' : | |
| /** | |
| * @param int v Current value | |
| * @param jQuery ipt Input | |
| */ | |
| function (v, ipt) {} | |
| } | |
| ,gopt | |
| ); | |
| var c = $('<canvas width="' + opt.width + '" height="' + opt.width + '"></canvas>') | |
| ,wd = $('<div style=width:' + opt.width + 'px;display:inline;"></div>') | |
| ,k | |
| ,vl = $this.val() | |
| ,initStyle = function () { | |
| opt.displayInput | |
| && $this.css({ | |
| 'width' : opt.width / 2 + 'px' | |
| ,'position' : 'absolute' | |
| ,'margin-top' : (opt.width * 5 / 14) + 'px' | |
| ,'margin-left' : '-' + (opt.width * 3 / 4) + 'px' | |
| ,'font-size' : (opt.width / 4) + 'px' | |
| ,'border' : 'none' | |
| ,'background' : 'none' | |
| ,'font-family' : 'Arial' | |
| ,'font-weight' : 'bold' | |
| ,'text-align' : 'center' | |
| ,'color' : opt.fgColor | |
| ,'padding' : '0px' | |
| ,'-webkit-appearance': 'none' | |
| }) | |
| || $this.css({ | |
| 'width' : '0px' | |
| ,'visibility' : 'hidden' | |
| }); | |
| }; | |
| // Canvas insert | |
| $this.wrap(wd).before(c); | |
| initStyle(); | |
| // Invoke dial logic | |
| k = new Dial(c, opt); | |
| vl || (vl = opt.min); | |
| $this.val(vl); | |
| k.val(vl); | |
| k.onRelease = function (v) { | |
| opt.release(v, $this); | |
| }; | |
| k.onChange = function (v) { | |
| $this.val(v); | |
| opt.change(v); | |
| }; | |
| // bind change on input | |
| $this.bind( | |
| 'change' | |
| ,function (e) { | |
| k.val($this.val()); | |
| } | |
| ); | |
| if (!opt.readOnly) { | |
| // canvas | |
| c.bind( | |
| "mousedown touchstart" | |
| ,function (e) { | |
| e.preventDefault(); | |
| k.startDrag(e); | |
| } | |
| ) | |
| .bind( | |
| "mousewheel DOMMouseScroll" | |
| ,mw = function (e) { | |
| e.preventDefault(); | |
| var ori = e.originalEvent | |
| ,deltaX = ori.detail || ori.wheelDeltaX | |
| ,deltaY = ori.detail || ori.wheelDeltaY | |
| ,val = parseInt($this.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0); | |
| k.val(val); | |
| } | |
| ); | |
| // input | |
| var kval, val, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1}; | |
| $this | |
| .bind( | |
| "configure" | |
| ,function (e, aconf) { | |
| var kconf; | |
| for (kconf in aconf) { opt[kconf] = aconf[kconf]; } | |
| initStyle(); | |
| k.val($this.val()); | |
| } | |
| ) | |
| .bind( | |
| "keydown" | |
| ,function (e) { | |
| var kc = e.keyCode; | |
| if (kc >= 96 && kc <= 105) kc -= 48; //numpad | |
| kval = parseInt(String.fromCharCode(kc)); | |
| if (isNaN(kval)) { | |
| (kc !== 13) // enter | |
| && (kc !== 8) // bs | |
| && (kc !== 9) // tab | |
| && (kc !== 189) // - | |
| && e.preventDefault(); | |
| // arrows | |
| if ($.inArray(kc,[37,38,39,40]) > -1) { | |
| k.change(parseInt($this.val()) + kv[kc] * m); | |
| // long time keydown speed-up | |
| to = window.setTimeout( | |
| function () { m < 20 && m++; } | |
| ,50 | |
| ); | |
| e.preventDefault(); | |
| } | |
| } | |
| } | |
| ) | |
| .bind( | |
| "keyup" | |
| ,function(e) { | |
| if (isNaN(kval)) { | |
| if (to) { | |
| window.clearTimeout(to); | |
| to = null; | |
| m = 1; | |
| k.val($this.val()); | |
| k.onRelease($this.val(), $this); | |
| } else { | |
| // enter | |
| (e.keyCode === 13) | |
| && k.onRelease($this.val(), $this); | |
| } | |
| } else { | |
| // kval postcond | |
| ($this.val() > opt.max && $this.val(opt.max)) | |
| || ($this.val() < opt.min && $this.val(opt.min)); | |
| } | |
| } | |
| ) | |
| .bind( | |
| "mousewheel DOMMouseScroll" | |
| ,mw | |
| ); | |
| } else { | |
| $this.attr('readonly', 'readonly'); | |
| } | |
| } | |
| ).parent(); | |
| }; | |
| function degreeToRadians (angle) { | |
| return $.isNumeric(angle) ? angle * Math.PI / 180 : 0; | |
| } | |
| }); |