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() >
526 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 - 0.00001, this.startAngle + 0.00001, 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})(jQuery);
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.