1/** 2 * Knob - jQuery Plugin 3 * Downward compatible, touchable dial 4 * 5 * Version: 1.1.2 (22/05/2012) 6 * Requires: jQuery v1.7+ 7 * 8 * Copyright (c) 2011 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 14 */ 15$(function () { 16 17 // Dial logic 18 var Dial = function (c, opt) { 19 20 var v = null 21 ,ctx = c[0].getContext("2d") 22 ,PI2 = 2 * Math.PI 23 ,mx ,my ,x ,y 24 ,self = this; 25 26 this.onChange = function () {}; 27 this.onCancel = function () {}; 28 this.onRelease = function () {}; 29 30 this.val = function (nv) { 31 if (null != nv) { 32 opt.stopper && (nv = Math.max(Math.min(nv, opt.max), opt.min)); 33 v = nv; 34 this.onChange(nv); 35 if(opt.dynamicDraw) this.dynamicDraw(nv); 36 else this.draw(nv); 37 } else { 38 var b, a; 39 b = a = Math.atan2(mx - x, -(my - y - opt.width / 2)) - opt.angleOffset; 40 (a < 0) && (b = a + PI2); 41 nv = Math.round(b * (opt.max - opt.min) / PI2) + opt.min; 42 return (nv > opt.max) ? opt.max : nv; 43 } 44 }; 45 46 this.change = function (nv) { 47 opt.stopper && (nv = Math.max(Math.min(nv, opt.max), opt.min)); 48 this.onChange(nv); 49 this.draw(nv); 50 }; 51 52 this.angle = function (nv) { 53 return (nv - opt.min) * PI2 / (opt.max - opt.min); 54 }; 55 56 this.draw = function (nv) { 57 58 var a = this.angle(nv) // Angle 59 ,sa = 1.5 * Math.PI + opt.angleOffset // Previous start angle 60 ,sat = sa // Start angle 61 ,ea = sa + this.angle(v) // Previous end angle 62 ,eat = sat + a // End angle 63 ,r = opt.width / 2 // Radius 64 ,lw = r * opt.thickness // Line width 65 ,cgcolor = Dial.getCgColor(opt.cgColor) 66 ,tick 67 ; 68 69 ctx.clearRect(0, 0, opt.width, opt.width); 70 ctx.lineWidth = lw; 71 72 // Hook draw 73 if (opt.draw(a, v, opt, ctx)) { return; } 74 75 for (tick = 0; tick < opt.ticks; tick++) { 76 77 ctx.beginPath(); 78 79 if (a > (((2 * Math.PI) / opt.ticks) * tick) && opt.tickColorizeValues) { 80 ctx.strokeStyle = opt.fgColor; 81 } else { 82 ctx.strokeStyle = opt.tickColor; 83 } 84 85 var tick_sa = (((2 * Math.PI) / opt.ticks) * tick) - (0.5 * Math.PI); 86 ctx.arc( r, r, r-lw-opt.tickLength, tick_sa, tick_sa+opt.tickWidth , false); 87 ctx.stroke(); 88 } 89 90 opt.cursor 91 && (sa = ea - 0.3) 92 && (ea = ea + 0.3) 93 && (sat = eat - 0.3) 94 && (eat = eat + 0.3); 95 96 switch (opt.skin) { 97 98 case 'default' : 99 100 ctx.beginPath(); 101 ctx.strokeStyle = opt.bgColor; 102 ctx.arc(r, r, r - lw / 2, 0, PI2, true); 103 ctx.stroke(); 104 105 if (opt.displayPrevious) { 106 ctx.beginPath(); 107 ctx.strokeStyle = (v == nv) ? opt.fgColor : cgcolor; 108 ctx.arc(r, r, r - lw / 2, sa, ea, false); 109 ctx.stroke(); 110 } 111 112 ctx.beginPath(); 113 ctx.strokeStyle = opt.fgColor; 114 ctx.arc(r, r, r - lw / 2, sat, eat, false); 115 ctx.stroke(); 116 117 break; 118 119 case 'tron' : 120 121 if (opt.displayPrevious) { 122 ctx.beginPath(); 123 ctx.strokeStyle = (v == nv) ? opt.fgColor : cgcolor; 124 ctx.arc( r, r, r - lw, sa, ea, false); 125 ctx.stroke(); 126 } 127 128 ctx.beginPath(); 129 ctx.strokeStyle = opt.fgColor; 130 ctx.arc( r, r, r - lw, sat, eat, false); 131 ctx.stroke(); 132 133 ctx.lineWidth = 2; 134 ctx.beginPath(); 135 ctx.strokeStyle = opt.fgColor; 136 ctx.arc( r, r, r - lw + 1 + lw * 2 / 3, 0, 2 * Math.PI, false); 137 ctx.stroke(); 138 139 break; 140 } 141 }; 142 143 var dynamicDrawIndex; 144 var dynamicDrawInterval; 145 this.dynamicDraw = function (nv) { 146 var instanceOfThis = this; 147 dynamicDrawIndex = opt.min; 148 dynamicDrawInterval = setInterval(function() { 149 instanceOfThis.animateDraw(nv); 150 }, 20); 151 }; 152 153 this.animateDraw = function () { 154 if(dynamicDrawIndex > v) { 155 clearInterval(dynamicDrawInterval); 156 v = dynamicDrawIndex; 157 } else { 158 this.draw(dynamicDrawIndex); 159 this.change(dynamicDrawIndex); 160 dynamicDrawIndex++; 161 } 162 }; 163 164 this.capture = function (e) { 165 switch (e.type) { 166 case 'mousemove' : 167 case 'mousedown' : 168 mx = e.pageX; 169 my = e.pageY; 170 break; 171 case 'touchmove' : 172 case 'touchstart' : 173 mx = e.originalEvent.touches[0].pageX; 174 my = e.originalEvent.touches[0].pageY; 175 break; 176 } 177 this.change( this.val() ); 178 }; 179 180 this.cancel = function () { 181 self.val(v); 182 self.onCancel(); 183 }; 184 185 this.startDrag = function (e) { 186 187 var p = c.offset() 188 ,$doc = $(document); 189 190 x = p.left + (opt.width / 2); 191 y = p.top; 192 193 this.capture(e); 194 195 // Listen mouse and touch events 196 $doc.bind( 197 "mousemove.dial touchmove.dial" 198 ,function (e) { 199 self.capture(e); 200 } 201 ) 202 .bind( 203 // Escape 204 "keyup.dial" 205 ,function (e) { 206 if(e.keyCode === 27) { 207 $doc.unbind("mouseup.dial mousemove.dial keyup.dial"); 208 self.cancel(); 209 } 210 } 211 ) 212 .bind( 213 "mouseup.dial touchend.dial" 214 ,function (e) { 215 $doc.unbind('mousemove.dial touchmove.dial mouseup.dial touchend.dial keyup.dial'); 216 self.val(self.val()); 217 self.onRelease(v); 218 } 219 ); 220 }; 221 }; 222 223 // Dial static func 224 Dial.getCgColor = function (h) { 225 h = h.substring(1,7); 226 var rgb = [parseInt(h.substring(0,2),16) 227 ,parseInt(h.substring(2,4),16) 228 ,parseInt(h.substring(4,6),16)]; 229 return "rgba("+rgb[0]+","+rgb[1]+","+rgb[2]+",.5)"; 230 }; 231 232 // jQuery plugin 233 $.fn.knob = $.fn.dial = function (gopt) { 234 235 return this.each( 236 237 function () { 238 239 var $this = $(this), opt; 240 241 if ($this.data('dialed')) { return $this; } 242 $this.data('dialed', true); 243 244 opt = $.extend( 245 { 246 // Config 247 'min' : $this.data('min') || 0 248 ,'max' : $this.data('max') || 100 249 ,'stopper' : true 250 ,'readOnly' : $this.data('readonly') 251 252 // UI 253 ,'cursor' : $this.data('cursor') 254 ,'thickness' : $this.data('thickness') || 0.35 255 ,'width' : $this.data('width') || 200 256 ,'displayInput' : $this.data('displayinput') == null || $this.data('displayinput') 257 ,'displayPrevious' : $this.data('displayprevious') 258 ,'fgColor' : $this.data('fgcolor') || '#87CEEB' 259 ,'cgColor' : $this.data('cgcolor') || $this.data('fgcolor') || '#87CEEB' 260 ,'bgColor' : $this.data('bgcolor') || '#EEEEEE' 261 ,'tickColor' : $this.data('tickColor') || $this.data('fgcolor') || '#DDDDDD' 262 ,'ticks' : $this.data('ticks') || 0 263 ,'tickLength' : $this.data('tickLength') || 0 264 ,'tickWidth' : $this.data('tickWidth') || 0.02 265 ,'tickColorizeValues' : $this.data('tickColorizeValues') || true 266 ,'skin' : $this.data('skin') || 'default' 267 ,'angleOffset': degreeToRadians($this.data('angleoffset')) 268 ,'dynamicDraw': $this.data('dynamicdraw') || false 269 270 // Hooks 271 ,'draw' : 272 /** 273 * @param int a angle 274 * @param int v current value 275 * @param array opt plugin options 276 * @param context ctx Canvas context 2d 277 * @return bool true:bypass default draw methode 278 */ 279 function (a, v, opt, ctx) {} 280 ,'change' : 281 /** 282 * @param int v Current value 283 */ 284 function (v) {} 285 ,'release' : 286 /** 287 * @param int v Current value 288 * @param jQuery ipt Input 289 */ 290 function (v, ipt) {} 291 } 292 ,gopt 293 ); 294 295 var c = $('<canvas width="' + opt.width + '" height="' + opt.width + '"></canvas>') 296 ,wd = $('<div style=width:' + opt.width + 'px;display:inline;"></div>') 297 ,k 298 ,vl = $this.val() 299 ,initStyle = function () { 300 opt.displayInput 301 && $this.css({ 302 'width' : opt.width / 2 + 'px' 303 ,'position' : 'absolute' 304 ,'margin-top' : (opt.width * 5 / 14) + 'px' 305 ,'margin-left' : '-' + (opt.width * 3 / 4) + 'px' 306 ,'font-size' : (opt.width / 4) + 'px' 307 ,'border' : 'none' 308 ,'background' : 'none' 309 ,'font-family' : 'Arial' 310 ,'font-weight' : 'bold' 311 ,'text-align' : 'center' 312 ,'color' : opt.fgColor 313 ,'padding' : '2px 0px 0px 0px'
314 ,'-webkit-appearance': 'none' 315 }) 316 || $this.css({ 317 'width' : '0px' 318 ,'visibility' : 'hidden' 319 }); 320 }; 321 322 // Canvas insert 323 $this.wrap(wd).before(c); 324 325 initStyle(); 326 327 // Invoke dial logic 328 k = new Dial(c, opt); 329 vl || (vl = opt.min); 330 $this.val(vl); 331 k.val(vl); 332 333 k.onRelease = function (v) { 334 opt.release(v, $this); 335 }; 336 k.onChange = function (v) { 337 $this.val(v); 338 opt.change(v); 339 }; 340 341 // bind change on input 342 $this.bind( 343 'change' 344 ,function (e) { 345 k.val($this.val()); 346 } 347 ); 348 349 if (!opt.readOnly) { 350 351 // canvas 352 c.bind( 353 "mousedown touchstart" 354 ,function (e) { 355 e.preventDefault(); 356 k.startDrag(e); 357 } 358 ) 359 .bind( 360 "mousewheel DOMMouseScroll" 361 ,mw = function (e) { 362 e.preventDefault(); 363 var ori = e.originalEvent 364 ,deltaX = ori.detail || ori.wheelDeltaX 365 ,deltaY = ori.detail || ori.wheelDeltaY 366 ,val = parseInt($this.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0); 367 k.val(val); 368 } 369 ); 370 371 // input 372 var kval, val, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1}; 373 $this 374 .bind( 375 "configure" 376 ,function (e, aconf) { 377 var kconf; 378 for (kconf in aconf) { opt[kconf] = aconf[kconf]; } 379 initStyle(); 380 k.val($this.val()); 381 } 382 ) 383 .bind( 384 "keydown" 385 ,function (e) { 386 var kc = e.keyCode; 387 if (kc >= 96 && kc <= 105) kc -= 48; //numpad 388 kval = parseInt(String.fromCharCode(kc)); 389 390 if (isNaN(kval)) { 391 392 (kc !== 13) // enter 393 && (kc !== 8) // bs 394 && (kc !== 9) // tab 395 && (kc !== 189) // - 396 && e.preventDefault(); 397 398 // arrows 399 if ($.inArray(kc,[37,38,39,40]) > -1) { 400 k.change(parseInt($this.val()) + kv[kc] * m); 401 402 // long time keydown speed-up 403 to = window.setTimeout( 404 function () { m < 20 && m++; } 405 ,50 406 ); 407 408 e.preventDefault(); 409 } 410 } 411 } 412 ) 413 .bind( 414 "keyup" 415 ,function(e) { 416 if (isNaN(kval)) { 417 if (to) { 418 window.clearTimeout(to); 419 to = null; 420 m = 1; 421 k.val($this.val()); 422 k.onRelease($this.val(), $this); 423 } else { 424 // enter 425 (e.keyCode === 13) 426 && k.onRelease($this.val(), $this); 427 } 428 } else { 429 // kval postcond 430 ($this.val() > opt.max && $this.val(opt.max)) 431 || ($this.val() < opt.min && $this.val(opt.min)); 432 } 433 434 } 435 ) 436 .bind( 437 "mousewheel DOMMouseScroll" 438 ,mw 439 ); 440 } else { 441 $this.attr('readonly', 'readonly'); 442 } 443 } 444 ).parent(); 445 }; 446 447 function degreeToRadians (angle) { 448 return $.isNumeric(angle) ? angle * Math.PI / 180 : 0; 449 } 450});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.