1/*!jQuery Knob*/ 2/** 3 * Downward compatible, touchable dial 4 * 5 * Version: 1.2.12 6 * Requires: jQuery v1.7+ 7 * 8 * Copyright (c) 2012 Anthony Terrien 9 * Under MIT License (http://www.opensource.org/licenses/mit-license.php) 10 * 11 * Thanks to vor, eskimoblood, spiffistan, FabrizioC 12 */ 13(function (factory) { 14 if (typeof exports === 'object') { 15 // CommonJS 16 module.exports = factory(require('jquery')); 17 } else if (typeof define === 'function' && define.amd) { 18 // AMD. Register as an anonymous module. 19 define(['jquery'], factory); 20 } else { 21 // Browser globals 22 factory(jQuery); 23 } 24}(function ($) { 25 26 /** 27 * Kontrol library 28 */ 29 "use strict"; 30 31 /** 32 * Definition of globals and core 33 */ 34 var k = {}, // kontrol 35 max = Math.max, 36 min = Math.min; 37 38 k.c = {}; 39 k.c.d = $(document); 40 k.c.t = function (e) { 41 return e.originalEvent.touches.length - 1; 42 }; 43 44 /** 45 * Kontrol Object 46 * 47 * Definition of an abstract UI control 48 * 49 * Each concrete component must call this one. 50 * <code> 51 * k.o.call(this); 52 * </code> 53 */ 54 k.o = function () { 55 var s = this; 56 57 this.o = null; // array of options 58 this.$ = null; // jQuery wrapped element 59 this.i = null; // mixed HTMLInputElement or array of HTMLInputElement 60 this.g = null; // deprecated 2D graphics context for 'pre-rendering' 61 this.v = null; // value ; mixed array or integer 62 this.cv = null; // change value ; not commited value 63 this.x = 0; // canvas x position 64 this.y = 0; // canvas y position 65 this.w = 0; // canvas width 66 this.h = 0; // canvas height 67 this.$c = null; // jQuery canvas element 68 this.c = null; // rendered canvas context 69 this.t = 0; // touches index 70 this.isInit = false; 71 this.fgColor = null; // main color 72 this.pColor = null; // previous color 73 this.dH = null; // draw hook 74 this.cH = null; // change hook 75 this.eH = null; // cancel hook 76 this.rH = null; // release hook 77 this.scale = 1; // scale factor 78 this.relative = false; 79 this.relativeWidth = false; 80 this.relativeHeight = false; 81 this.$div = null; // component div 82 83 this.run = function () { 84 var cf = function (e, conf) { 85 var k; 86 for (k in conf) { 87 s.o[k] = conf[k]; 88 } 89 s._carve().init(); 90 s._configure() 91 ._draw(); 92 }; 93 94 if (this.$.data('kontroled')) return; 95 this.$.data('kontroled', true); 96 97 this.extend(); 98 this.o = $.extend({ 99 // Config 100 min: this.$.data('min') !== undefined ? this.$.data('min') : 0, 101 max: this.$.data('max') !== undefined ? this.$.data('max') : 100, 102 stopper: true, 103 readOnly: this.$.data('readonly') || (this.$.attr('readonly') === 'readonly'), 104 105 // UI 106 cursor: this.$.data('cursor') === true && 30 107 || this.$.data('cursor') || 0, 108 thickness: this.$.data('thickness') 109 && Math.max(Math.min(this.$.data('thickness'), 1), 0.01) 110 || 0.35, 111 lineCap: this.$.data('linecap') || 'butt', 112 width: this.$.data('width') || 200, 113 height: this.$.data('height') || 200, 114 displayInput: this.$.data('displayinput') == null || this.$.data('displayinput'), 115 displayPrevious: this.$.data('displayprevious'), 116 fgColor: this.$.data('fgcolor') || '#87CEEB', 117 inputColor: this.$.data('inputcolor'), 118 font: this.$.data('font') || 'Arial', 119 fontWeight: this.$.data('font-weight') || 'bold', 120 inline: false, 121 step: this.$.data('step') || 1, 122 rotation: this.$.data('rotation'), 123 124 // Hooks 125 draw: null, // function () {} 126 change: null, // function (value) {} 127 cancel: null, // function () {} 128 release: null, // function (value) {} 129 130 // Output formatting, allows to add unit: %, ms ... 131 format: function(v) { 132 return v; 133 }, 134 parse: function (v) { 135 return parseFloat(v); 136 } 137 }, this.o 138 ); 139 140 // finalize options 141 this.o.flip = this.o.rotation === 'anticlockwise' || this.o.rotation === 'acw'; 142 if (!this.o.inputColor) { 143 this.o.inputColor = this.o.fgColor; 144 } 145 146 // routing value 147 if (this.$.is('fieldset')) { 148 149 // fieldset = array of integer 150 this.v = {}; 151 this.i = this.$.find('input'); 152 this.i.each(function(k) { 153 var $this = $(this); 154 s.i[k] = $this; 155 s.v[k] = s.o.parse($this.val()); 156 157 $this.bind( 158 'change blur', 159 function () { 160 var val = {}; 161 val[k] = $this.val(); 162 s.val(s._validate(val)); 163 } 164 ); 165 }); 166 this.$.find('legend').remove(); 167 } else { 168 169 // input = integer 170 this.i = this.$; 171 this.v = this.o.parse(this.$.val()); 172 this.v === '' && (this.v = this.o.min); 173 this.$.bind( 174 'change blur', 175 function () { 176 s.val(s._validate(s.o.parse(s.$.val()))); 177 } 178 ); 179 180 } 181 182 !this.o.displayInput && this.$.hide(); 183 184 // adds needed DOM elements (canvas, div) 185 this.$c = $(document.createElement('canvas')).attr({ 186 width: this.o.width, 187 height: this.o.height 188 }); 189 190 // wraps all elements in a div 191 // add to DOM before Canvas init is triggered
192 this.$div = $('<div style="' 193 + (this.o.inline ? 'display:inline;' : '') 194 + 'width:' + this.o.width + 'px;height:' + this.o.height + 'px;' 195 + '"></div>'); 196 197 this.$.wrap(this.$div).before(this.$c); 198 this.$div = this.$.parent(); 199 200 if (typeof G_vmlCanvasManager !== 'undefined') { 201 G_vmlCanvasManager.initElement(this.$c[0]); 202 } 203 204 this.c = this.$c[0].getContext ? this.$c[0].getContext('2d') : null; 205 206 if (!this.c) { 207 throw { 208 name: "CanvasNotSupportedException", 209 message: "Canvas not supported. Please use excanvas on IE8.0.", 210 toString: function(){return this.name + ": " + this.message} 211 } 212 } 213 214 // hdpi support 215 this.scale = (window.devicePixelRatio || 1) / ( 216 this.c.webkitBackingStorePixelRatio || 217 this.c.mozBackingStorePixelRatio || 218 this.c.msBackingStorePixelRatio || 219 this.c.oBackingStorePixelRatio || 220 this.c.backingStorePixelRatio || 1 221 ); 222 223 // detects relative width / height 224 this.relativeWidth = this.o.width % 1 !== 0 225 && this.o.width.indexOf('%'); 226 this.relativeHeight = this.o.height % 1 !== 0 227 && this.o.height.indexOf('%'); 228 this.relative = this.relativeWidth || this.relativeHeight; 229 230 // computes size and carves the component 231 this._carve(); 232 233 // prepares props for transaction 234 if (this.v instanceof Object) { 235 this.cv = {}; 236 this.copy(this.v, this.cv); 237 } else { 238 this.cv = this.v; 239 } 240 241 // binds configure event 242 this.$ 243 .bind("configure", cf) 244 .parent() 245 .bind("configure", cf); 246 247 // finalize init 248 this._listen() 249 ._configure() 250 ._xy() 251 .init(); 252 253 this.isInit = true; 254 255 this.$.val(this.o.format(this.v)); 256 this._draw(); 257 258 return this; 259 }; 260 261 this._carve = function() { 262 if (this.relative) { 263 var w = this.relativeWidth ? 264 this.$div.parent().width() * 265 parseInt(this.o.width) / 100 266 : this.$div.parent().width(), 267 h = this.relativeHeight ? 268 this.$div.parent().height() * 269 parseInt(this.o.height) / 100 270 : this.$div.parent().height(); 271 272 // apply relative 273 this.w = this.h = Math.min(w, h); 274 } else { 275 this.w = this.o.width; 276 this.h = this.o.height; 277 } 278 279 // finalize div 280 this.$div.css({ 281 'width': this.w + 'px', 282 'height': this.h + 'px' 283 }); 284 285 // finalize canvas with computed width 286 this.$c.attr({ 287 width: this.w, 288 height: this.h 289 }); 290 291 // scaling 292 if (this.scale !== 1) { 293 this.$c[0].width = this.$c[0].width * this.scale; 294 this.$c[0].height = this.$c[0].height * this.scale; 295 this.$c.width(this.w); 296 this.$c.height(this.h); 297 } 298 299 return this; 300 }; 301 302 this._draw = function () { 303 304 // canvas pre-rendering 305 var d = true; 306 307 s.g = s.c; 308 309 s.clear(); 310 311 s.dH && (d = s.dH()); 312 313 d !== false && s.draw(); 314 }; 315 316 this._touch = function (e) { 317 var touchMove = function (e) { 318 var v = s.xy2val( 319 e.originalEvent.touches[s.t].pageX, 320 e.originalEvent.touches[s.t].pageY 321 ); 322 323 if (v == s.cv) return; 324 325 if (s.cH && s.cH(v) === false) return; 326 327 s.change(s._validate(v)); 328 s._draw(); 329 }; 330 331 // get touches index 332 this.t = k.c.t(e); 333 334 // First touch 335 touchMove(e); 336 337 // Touch events listeners 338 k.c.d 339 .bind("touchmove.k", touchMove) 340 .bind( 341 "touchend.k", 342 function () { 343 k.c.d.unbind('touchmove.k touchend.k'); 344 s.val(s.cv); 345 } 346 ); 347 348 return this; 349 }; 350 351 this._mouse = function (e) { 352 var mouseMove = function (e) { 353 var v = s.xy2val(e.pageX, e.pageY); 354 355 if (v == s.cv) return; 356 357 if (s.cH && (s.cH(v) === false)) return; 358 359 s.change(s._validate(v)); 360 s._draw(); 361 }; 362 363 // First click 364 mouseMove(e); 365 366 // Mouse events listeners 367 k.c.d 368 .bind("mousemove.k", mouseMove) 369 .bind( 370 // Escape key cancel current change 371 "keyup.k", 372 function (e) { 373 if (e.keyCode === 27) { 374 k.c.d.unbind("mouseup.k mousemove.k keyup.k"); 375 376 if (s.eH && s.eH() === false) 377 return; 378 379 s.cancel(); 380 } 381 } 382 ) 383 .bind( 384 "mouseup.k", 385 function (e) { 386 k.c.d.unbind('mousemove.k mouseup.k keyup.k'); 387 s.val(s.cv); 388 } 389 ); 390 391 return this; 392 }; 393 394 this._xy = function () { 395 var o = this.$c.offset(); 396 this.x = o.left; 397 this.y = o.top; 398 399 return this; 400 }; 401 402 this._listen = function () { 403 if (!this.o.readOnly) { 404 this.$c 405 .bind( 406 "mousedown", 407 function (e) { 408 e.preventDefault(); 409 s._xy()._mouse(e); 410 } 411 ) 412 .bind( 413 "touchstart", 414 function (e) { 415 e.preventDefault(); 416 s._xy()._touch(e); 417 } 418 ); 419 420 this.listen(); 421 } else { 422 this.$.attr('readonly', 'readonly'); 423 } 424 425 if (this.relative) { 426 $(window).resize(function() { 427 s._carve().init(); 428 s._draw(); 429 }); 430 } 431 432 return this; 433 }; 434 435 this._configure = function () { 436 437 // Hooks 438 if (this.o.draw) this.dH = this.o.draw; 439 if (this.o.change) this.cH = this.o.change; 440 if (this.o.cancel) this.eH = this.o.cancel; 441 if (this.o.release) this.rH = this.o.release; 442 443 if (this.o.displayPrevious) { 444 this.pColor = this.h2rgba(this.o.fgColor, "0.4"); 445 this.fgColor = this.h2rgba(this.o.fgColor, "0.6"); 446 } else { 447 this.fgColor = this.o.fgColor; 448 } 449 450 return this; 451 }; 452 453 this._clear = function () { 454 this.$c[0].width = this.$c[0].width; 455 }; 456
457 this._validate = function (v) { 458 var val = (~~ (((v < 0) ? -0.5 : 0.5) + (v/this.o.step))) * this.o.step; 459 return Math.round(val * 100) / 100; 460 }; 461 462 // Abstract methods 463 this.listen = function () {}; // on start, one time 464 this.extend = function () {}; // each time configure triggered 465 this.init = function () {}; // each time configure triggered 466 this.change = function (v) {}; // on change 467 this.val = function (v) {}; // on release 468 this.xy2val = function (x, y) {}; // 469 this.draw = function () {}; // on change / on release 470 this.clear = function () { this._clear(); }; 471 472 // Utils 473 this.h2rgba = function (h, a) { 474 var rgb; 475 h = h.substring(1,7); 476 rgb = [ 477 parseInt(h.substring(0,2), 16), 478 parseInt(h.substring(2,4), 16), 479 parseInt(h.substring(4,6), 16) 480 ]; 481 482 return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")"; 483 }; 484 485 this.copy = function (f, t) { 486 for (var i in f) { 487 t[i] = f[i]; 488 } 489 }; 490 }; 491 492 493 /** 494 * k.Dial 495 */ 496 k.Dial = function () { 497 k.o.call(this); 498 499 this.startAngle = null; 500 this.xy = null; 501 this.radius = null; 502 this.lineWidth = null; 503 this.cursorExt = null; 504 this.w2 = null; 505 this.PI2 = 2*Math.PI; 506 507 this.extend = function () { 508 this.o = $.extend({ 509 bgColor: this.$.data('bgcolor') || '#e4e4e4', 510 angleOffset: this.$.data('angleoffset') || 0, 511 angleArc: this.$.data('anglearc') || 360, 512 inline: true 513 }, this.o); 514 }; 515 516 this.val = function (v, triggerRelease) { 517 if (null != v) { 518 519 // reverse format 520 v = this.o.parse(v); 521 522 if (triggerRelease !== false 523 && v != this.v 524 && this.rH 525 && this.rH(v) === false) { return; } 526 527 this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v; 528 this.v = this.cv; 529 this.$.val(this.o.format(this.v)); 530 this._draw(); 531 } else { 532 return this.v; 533 } 534 }; 535 536 this.xy2val = function (x, y) { 537 var a, ret; 538 539 a = Math.atan2( 540 x - (this.x + this.w2), 541 - (y - this.y - this.w2) 542 ) - this.angleOffset; 543 544 if (this.o.flip) { 545 a = this.angleArc - a - this.PI2; 546 } 547 548 if (this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) { 549 550 // if isset angleArc option, set to min if .5 under min 551 a = 0; 552 } else if (a < 0) { 553 a += this.PI2; 554 } 555 556 ret = (a * (this.o.max - this.o.min) / this.angleArc) + this.o.min; 557 558 this.o.stopper && (ret = max(min(ret, this.o.max), this.o.min)); 559 560 return ret; 561 }; 562 563 this.listen = function () { 564 565 // bind MouseWheel 566 var s = this, mwTimerStop, 567 mwTimerRelease, 568 mw = function (e) { 569 e.preventDefault(); 570 571 var ori = e.originalEvent, 572 deltaX = ori.detail || ori.wheelDeltaX, 573 deltaY = ori.detail || ori.wheelDeltaY, 574 v = s._validate(s.o.parse(s.$.val())) 575 + ( 576 deltaX > 0 || deltaY > 0 577 ? s.o.step 578 : deltaX < 0 || deltaY < 0 ? -s.o.step : 0 579 ); 580 581 v = max(min(v, s.o.max), s.o.min); 582 583 s.val(v, false); 584 585 if (s.rH) { 586 // Handle mousewheel stop 587 clearTimeout(mwTimerStop); 588 mwTimerStop = setTimeout(function () { 589 s.rH(v); 590 mwTimerStop = null; 591 }, 100); 592 593 // Handle mousewheel releases 594 if (!mwTimerRelease) { 595 mwTimerRelease = setTimeout(function () { 596 if (mwTimerStop) 597 s.rH(v); 598 mwTimerRelease = null; 599 }, 200); 600 } 601 } 602 }, 603 kval, 604 to, 605 m = 1, 606 kv = { 607 37: -s.o.step, 608 38: s.o.step, 609 39: s.o.step, 610 40: -s.o.step 611 }; 612 613 this.$ 614 .bind( 615 "keydown", 616 function (e) { 617 var kc = e.keyCode; 618 619 // numpad support 620 if (kc >= 96 && kc <= 105) { 621 kc = e.keyCode = kc - 48; 622 } 623 624 kval = parseInt(String.fromCharCode(kc)); 625 626 if (isNaN(kval)) { 627 (kc !== 13) // enter 628 && kc !== 8 // bs 629 && kc !== 9 // tab 630 && kc !== 189 // - 631 && (kc !== 190 632 || s.$.val().match(/\./)) // . allowed once 633 && e.preventDefault(); 634 635 // arrows 636 if ($.inArray(kc,[37,38,39,40]) > -1) { 637 e.preventDefault(); 638 639 var v = s.o.parse(s.$.val()) + kv[kc] * m; 640 s.o.stopper && (v = max(min(v, s.o.max), s.o.min)); 641 642 s.change(s._validate(v)); 643 s._draw(); 644 645 // long time keydown speed-up 646 to = window.setTimeout(function () { 647 m *= 2; 648 }, 30); 649 } 650 } 651 } 652 ) 653 .bind( 654 "keyup", 655 function (e) { 656 if (isNaN(kval)) { 657 if (to) { 658 window.clearTimeout(to); 659 to = null; 660 m = 1; 661 s.val(s.$.val()); 662 } 663 } else { 664 // kval postcond 665 (s.$.val() >
665 s.o.max && s.$.val(s.o.max)) 666 || (s.$.val() < s.o.min && s.$.val(s.o.min)); 667 } 668 } 669 ); 670 671 //this.$c.bind("mousewheel DOMMouseScroll", mw); 672 //this.$.bind("mousewheel DOMMouseScroll", mw); 673 }; 674 675 this.init = function () { 676 if (this.v < this.o.min 677 || this.v > this.o.max) { this.v = this.o.min; } 678 679 this.$.val(this.v); 680 this.w2 = this.w / 2; 681 this.cursorExt = this.o.cursor / 100; 682 this.xy = this.w2 * this.scale; 683 this.lineWidth = this.xy * this.o.thickness; 684 this.lineCap = this.o.lineCap; 685 this.radius = this.xy - this.lineWidth / 2; 686 687 this.o.angleOffset 688 && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset); 689 690 this.o.angleArc 691 && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc); 692 693 // deg to rad 694 this.angleOffset = this.o.angleOffset * Math.PI / 180; 695 this.angleArc = this.o.angleArc * Math.PI / 180; 696 697 // compute start and end angles 698 this.startAngle = 1.5 * Math.PI + this.angleOffset; 699 this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc; 700 701 var s = max( 702 String(Math.abs(this.o.max)).length, 703 String(Math.abs(this.o.min)).length, 704 2 705 ) + 2; 706 707 this.o.displayInput 708 && this.i.css({ 709 'width' : ((this.w / 2 + 4) >> 0) + 'px', 710 'height' : ((this.w / 3) >> 0) + 'px', 711 'position' : 'absolute', 712 'vertical-align' : 'middle', 713 'margin-top' : ((this.w / 3) >> 0) + 'px', 714 'margin-left' : '-' + ((this.w * 3 / 4 + 2) >> 0) + 'px', 715 'border' : 0, 716 'background' : 'none', 717 'font' : this.o.fontWeight + ' ' + ((this.w / s) >> 0) + 'px ' + this.o.font, 718 'text-align' : 'center', 719 'color' : this.o.inputColor || this.o.fgColor, 720 'padding' : '0px', 721 '-webkit-appearance': 'none' 722 }) || this.i.css({ 723 'width': '0px', 724 'visibility': 'hidden' 725 }); 726 }; 727 728 this.change = function (v) { 729 this.cv = v; 730 this.$.val(this.o.format(v)); 731 }; 732 733 this.angle = function (v) { 734 return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min); 735 }; 736 737 this.arc = function (v) { 738 var sa, ea; 739 v = this.angle(v); 740 if (this.o.flip) { 741 sa = this.endAngle + 0.00001; 742 ea = sa - v - 0.00001; 743 } else { 744 sa = this.startAngle - 0.00001; 745 ea = sa + v + 0.00001; 746 } 747 this.o.cursor 748 && (sa = ea - this.cursorExt) 749 && (ea = ea + this.cursorExt); 750 751 return { 752 s: sa, 753 e: ea, 754 d: this.o.flip && !this.o.cursor 755 }; 756 }; 757 758 this.draw = function () { 759 var c = this.g, // context 760 a = this.arc(this.cv), // Arc 761 pa, // Previous arc 762 r = 1; 763 764 c.lineWidth = this.lineWidth; 765 c.lineCap = this.lineCap; 766 767 if (this.o.bgColor !== "none") { 768 c.beginPath(); 769 c.strokeStyle = this.o.bgColor; 770 c.arc(this.xy, this.xy, this.radius, this.endAngle - 0.00001, this.startAngle + 0.00001, true); 771 c.stroke(); 772 } 773 774 if (this.o.displayPrevious) { 775 pa = this.arc(this.v); 776 c.beginPath(); 777 c.strokeStyle = this.pColor; 778 c.arc(this.xy, this.xy, this.radius, pa.s, pa.e, pa.d); 779 c.stroke(); 780 r = this.cv == this.v; 781 } 782 783 c.beginPath(); 784 c.strokeStyle = r ? this.o.fgColor : this.fgColor ; 785 c.arc(this.xy, this.xy, this.radius, a.s, a.e, a.d); 786 c.stroke(); 787 }; 788 789 this.cancel = function () { 790 this.val(this.v); 791 }; 792 }; 793 794 $.fn.dial = $.fn.knob = function (o) { 795 return this.each( 796 function () { 797 var d = new k.Dial(); 798 d.o = o; 799 d.$ = $(this); 800 d.run(); 801 } 802 ).parent(); 803 }; 804 805}));
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.