├── README.md ├── index.html └── js └── jquery.kontrol.js /README.md: -------------------------------------------------------------------------------- 1 | jQuery Kontrol 2 | ============= 3 | 4 | Library of UI controls ; dial (was 'knob'), XY pad, bars control ... 5 | 6 | - canvas based ; no png or jpg sprites. 7 | - touch, mousewheel, keyboard events implemented. 8 | - downward compatible ; overloads inputs. 9 | 10 | Demo : http://anthonyterrien.com/kontrol/ 11 | 12 | Controls 13 | ------- 14 | 15 | - Dial (was 'Knob') : $('#id').dial() 16 | - XY : $('#id').xy() 17 | - Bars : $('#id').bars() 18 | 19 | Example 20 | ------- 21 | 22 | 23 | 24 | 29 | 30 | Options 31 | ------- 32 | 33 | Options are provided as attributes 'data-option': 34 | 35 | 36 | 37 | ... or in the plugin method call : 38 | 39 | $(".dial").dial({ 40 | 'min':-50 41 | ,'max':50 42 | }) 43 | 44 | The following options are supported on controls : 45 | 46 | Behaviors : 47 | * min : min value || default=0. 48 | * max : max value || default=100. 49 | * stopper : stop at 0 & 100 on keydown/mousewheel || default=true. 50 | * readOnly : disable input and events. 51 | * flatMouse : the dial responds to up-down mouse movement instead of radial mouse movement. 52 | * noScroll : disable the mouse wheel for the dial. 53 | 54 | UI : 55 | * cursor : display mode "cursor" | default=gauge. 56 | * thickness : gauge thickness. 57 | * width : dial width. 58 | * displayInput : default=true | false=hide input. 59 | * displayPrevious : default=false | true=displays the previous value with transparency. 60 | * fgColor : foreground color. 61 | * bgColor : background color. 62 | 63 | Hooks 64 | ------- 65 | 66 | 71 | 72 | * 'release' : executed on release 73 | 74 | Parameters : 75 | + value : int, current value 76 | 77 | * 'change' : executed at each change of the value 78 | 79 | Parameters : 80 | + value : int, current value 81 | 82 | * 'draw' : when drawing the canvas 83 | 84 | * 'cancel' : on [esc] keydown 85 | 86 | * 'start' : executed on mousedown or touchStart 87 | 88 | The scope (this) of each hook function is an instance of Kontrol component. 89 | 90 | 91 | Example 92 | ------- 93 | 94 | 95 | 96 | 103 | 104 | 105 | Dynamically configure 106 | ------- 107 | 108 | 111 | 112 | Set the value 113 | ------- 114 | 115 | 120 | 121 | Make your own component 122 | ------- 123 | 124 | $(function () { 125 | 126 | /** 127 | * 128 | * Kontrol core 129 | * 130 | */ 131 | 132 | 133 | // Component logic 134 | k.NewComponent = function () { 135 | 136 | // extends Kontrol Object 137 | k.o.call(this); 138 | 139 | /** 140 | * 141 | * your code 142 | * 143 | */ 144 | }; 145 | 146 | // jQuery plugin 147 | $.fn.newcomponent = function (o) { 148 | return this.each( 149 | function () { 150 | var k = new k.NewComponent(); 151 | k.o = o; 152 | k.$ = $(this); 153 | k.run(); 154 | } 155 | ).parent(); 156 | }; 157 | 158 | }); 159 | 160 | 161 | Supported browsers 162 | ------- 163 | 164 | Chrome / Safari / Firefox / IE 9.0 165 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | jQuery Kontrol 5 | 6 | 7 | 168 | 197 | 198 | 199 |
200 |
201 |

jQuery Kontrol

202 |
203 |

Library of user interface components : 204 | Dial (dial, circular gauge), XY (XY pad), Bars (mixer, level meter ...). 205 | 206 | Flattr this 207 | 208 |

209 |
210 |
211 |
colors
212 | 219 |
220 |
cursor
221 | 227 |
228 |
displayPrevious
229 | 233 |
234 |
235 |
236 |
237 |
Dial
238 | 239 |
240 |
241 |
XY
242 |
243 | XY Pad 244 | x : 245 | y : 246 |
247 |
248 |
249 |
Dial with angleOffset
250 | 251 |
252 |
253 |
Bars ( animate !)
254 |
255 | 16 tracks 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 |
273 |
274 | 296 |
297 |
298 |
299 |

jQuery Kontrol is © 2012 Anthony Terrien and dual licensed under the MIT or GPL licenses.

300 |
301 |
302 | 307 | 316 | 317 | -------------------------------------------------------------------------------- /js/jquery.kontrol.js: -------------------------------------------------------------------------------- 1 | /*!jQuery Kontrol*/ 2 | /** 3 | * Small extensible jQuery library of new UI controls ; 4 | * Dial (was jQuery Knob), XY pad, Bars. 5 | * 6 | * Version: 0.9.0 (15/07/2012) 7 | * Requires: jQuery v1.7+ 8 | * 9 | * Copyright (c) 2012 Anthony Terrien 10 | * Under MIT and GPL licenses: 11 | * http://www.opensource.org/licenses/mit-license.php 12 | * http://www.gnu.org/licenses/gpl.html 13 | * 14 | * Thanks to vor, eskimoblood, spiffistan, FabrizioC 15 | */ 16 | (function($) { 17 | 18 | /** 19 | * Kontrol library 20 | */ 21 | "use strict"; 22 | 23 | /** 24 | * Definition of globals and core 25 | */ 26 | var k = {}, // kontrol 27 | max = Math.max, 28 | min = Math.min; 29 | 30 | k.c = {}; 31 | k.c.d = $(document); 32 | k.c.t = function (e) { 33 | return e.originalEvent.touches.length - 1; 34 | }; 35 | 36 | /** 37 | * Kontrol Object 38 | * 39 | * Definition of an abstract UI control 40 | * 41 | * Each concrete component must call this one. 42 | * 43 | * k.o.call(this); 44 | * 45 | */ 46 | k.o = function () { 47 | var s = this; 48 | 49 | this.o = null; // array of options 50 | this.$ = null; // jQuery wrapped element 51 | this.i = null; // mixed HTMLInputElement or array of HTMLInputElement 52 | this.g = null; // 2D graphics context for 'pre-rendering' 53 | this.v = null; // value ; mixed array or integer 54 | this.cv = null; // change value ; not commited value 55 | this.x = 0; // canvas x position 56 | this.y = 0; // canvas y position 57 | this.mx = 0; // x value of mouse down point of the current mouse move 58 | this.my = 0; // y value of mouse down point of the current mose move 59 | this.$c = null; // jQuery canvas element 60 | this.c = null; // rendered canvas context 61 | this.t = 0; // touches index 62 | this.isInit = false; 63 | this.fgColor = null; // main color 64 | this.pColor = null; // previous color 65 | this.sH = null; // start hook 66 | this.dH = null; // draw hook 67 | this.cH = null; // change hook 68 | this.eH = null; // cancel hook 69 | this.rH = null; // release hook 70 | 71 | this.run = function () { 72 | var cf = function (e, conf) { 73 | var k; 74 | for (k in conf) { 75 | s.o[k] = conf[k]; 76 | } 77 | s.init(); 78 | s._configure() 79 | ._draw(); 80 | }; 81 | 82 | if(this.$.data('kontroled')) return; 83 | this.$.data('kontroled', true); 84 | 85 | this.extend(); 86 | this.o = $.extend( 87 | { 88 | // Config 89 | min : this.$.data('min') || 0, 90 | max : this.$.data('max') || 100, 91 | stopper : true, 92 | readOnly : this.$.data('readonly'), 93 | noScroll : this.$.data('noScroll'), 94 | 95 | // UI 96 | cursor : (this.$.data('cursor') === true && 30) 97 | || this.$.data('cursor') 98 | || 0, 99 | thickness : this.$.data('thickness') || 0.35, 100 | width : this.$.data('width') || 200, 101 | height : this.$.data('height') || 200, 102 | displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'), 103 | displayPrevious : this.$.data('displayprevious'), 104 | fgColor : this.$.data('fgcolor') || '#87CEEB', 105 | inline : false, 106 | //context : {'lineCap' : 'butt'}, 107 | 108 | // Hooks 109 | start:null, // function () {} 110 | draw : null, // function () {} 111 | change : null, // function (value) {} 112 | cancel : null, // function () {} 113 | release : null // function (value) {} 114 | }, this.o 115 | ); 116 | 117 | // routing value 118 | if(this.$.is('fieldset')) { 119 | 120 | // fieldset = array of integer 121 | this.v = {}; 122 | this.i = this.$.find('input') 123 | this.i.each(function(k) { 124 | var $this = $(this); 125 | s.i[k] = $this; 126 | s.v[k] = $this.val(); 127 | 128 | $this.bind( 129 | 'change' 130 | , function () { 131 | var val = {}; 132 | val[k] = $this.val(); 133 | s.val(val); 134 | } 135 | ); 136 | }); 137 | this.$.find('legend').remove(); 138 | 139 | } else { 140 | // input = integer 141 | this.i = this.$; 142 | this.v = this.$.val(); 143 | (this.v == '') && (this.v = this.o.min); 144 | 145 | this.$.bind( 146 | 'change' 147 | , function () { 148 | s.val(s.$.val()); 149 | } 150 | ); 151 | } 152 | 153 | (!this.o.displayInput) && this.$.hide(); 154 | 155 | this.$c = $(''); 158 | this.c = this.$c[0].getContext("2d"); 159 | 160 | this.$ 161 | .wrap($('
')) 164 | .before(this.$c); 165 | 166 | if (this.v instanceof Object) { 167 | this.cv = {}; 168 | this.copy(this.v, this.cv); 169 | } else { 170 | this.cv = this.v; 171 | } 172 | 173 | this.$ 174 | .bind("configure", cf) 175 | .parent() 176 | .bind("configure", cf); 177 | 178 | this._listen() 179 | ._configure() 180 | ._xy() 181 | .init(); 182 | 183 | this.isInit = true; 184 | 185 | this._draw(); 186 | 187 | return this; 188 | }; 189 | 190 | this._draw = function () { 191 | 192 | // canvas pre-rendering 193 | var d = true, 194 | c = document.createElement('canvas'); 195 | 196 | c.width = s.o.width; 197 | c.height = s.o.height; 198 | s.g = c.getContext('2d'); 199 | 200 | s.clear(); 201 | 202 | s.dH 203 | && (d = s.dH()); 204 | 205 | (d !== false) && s.draw(); 206 | 207 | s.c.drawImage(c, 0, 0); 208 | c = null; 209 | }; 210 | 211 | this._touch = function (e) { 212 | 213 | var touchMove = function (e) { 214 | 215 | var v = s.xy2val( 216 | e.originalEvent.touches[s.t].pageX, 217 | e.originalEvent.touches[s.t].pageY, 218 | 'touch' 219 | ); 220 | 221 | if (v == s.cv) return; 222 | 223 | if ( 224 | s.cH 225 | && (s.cH(v) === false) 226 | ) return; 227 | 228 | 229 | s.change(v); 230 | s._draw(); 231 | }; 232 | 233 | // get touches index 234 | this.t = k.c.t(e); 235 | 236 | if ( 237 | this.sH 238 | && (this.sH() === false) 239 | ) return; 240 | 241 | // First touch 242 | touchMove(e); 243 | 244 | // Touch events listeners 245 | k.c.d 246 | .bind("touchmove.k", touchMove) 247 | .bind( 248 | "touchend.k" 249 | , function () { 250 | k.c.d.unbind('touchmove.k touchend.k'); 251 | 252 | if ( 253 | s.rH 254 | && (s.rH(s.cv) === false) 255 | ) return; 256 | 257 | s.val(s.cv); 258 | } 259 | ); 260 | 261 | return this; 262 | }; 263 | 264 | this._mouse = function (e) { 265 | 266 | var mouseMove = function (e) { 267 | var v = s.xy2val(e.pageX, e.pageY, 'mouse'); 268 | if (v == s.cv) return; 269 | 270 | if ( 271 | s.cH 272 | && (s.cH(v) === false) 273 | ) return; 274 | 275 | s.change(v); 276 | s._draw(); 277 | }; 278 | 279 | if ( 280 | this.sH 281 | && (this.sH() === false) 282 | ) return; 283 | 284 | // First click 285 | s.mx = e.pageX; 286 | s.my = e.pageY; 287 | mouseMove(e); 288 | 289 | // Mouse events listeners 290 | k.c.d 291 | .bind("mousemove.k", mouseMove) 292 | .bind( 293 | // Escape key cancel current change 294 | "keyup.k" 295 | , function (e) { 296 | if (e.keyCode === 27) { 297 | k.c.d.unbind("mouseup.k mousemove.k keyup.k"); 298 | 299 | if ( 300 | s.eH 301 | && (s.eH() === false) 302 | ) return; 303 | 304 | s.cancel(); 305 | } 306 | } 307 | ) 308 | .bind( 309 | "mouseup.k" 310 | , function (e) { 311 | k.c.d.unbind('mousemove.k mouseup.k keyup.k'); 312 | 313 | if ( 314 | s.rH 315 | && (s.rH(s.cv) === false) 316 | ) return; 317 | 318 | s.val(s.cv); 319 | } 320 | ); 321 | 322 | return this; 323 | }; 324 | 325 | this._xy = function () { 326 | var o = this.$c.offset(); 327 | this.x = o.left; 328 | this.y = o.top; 329 | return this; 330 | }; 331 | 332 | this._listen = function () { 333 | 334 | if (!this.o.readOnly) { 335 | this.$c 336 | .bind( 337 | "mousedown" 338 | , function (e) { 339 | e.preventDefault(); 340 | s._xy()._mouse(e); 341 | } 342 | ) 343 | .bind( 344 | "touchstart" 345 | , function (e) { 346 | e.preventDefault(); 347 | s._xy()._touch(e); 348 | } 349 | ); 350 | this.listen(); 351 | } else { 352 | this.$.attr('readonly', 'readonly'); 353 | } 354 | 355 | return this; 356 | }; 357 | 358 | this._configure = function () { 359 | 360 | // Hooks 361 | if (this.o.start) this.sH = this.o.start; 362 | if (this.o.draw) this.dH = this.o.draw; 363 | if (this.o.change) this.cH = this.o.change; 364 | if (this.o.cancel) this.eH = this.o.cancel; 365 | if (this.o.release) this.rH = this.o.release; 366 | 367 | if (this.o.displayPrevious) { 368 | this.pColor = this.h2rgba(this.o.fgColor, "0.4"); 369 | this.fgColor = this.h2rgba(this.o.fgColor, "0.6"); 370 | } else { 371 | this.fgColor = this.o.fgColor; 372 | } 373 | 374 | return this; 375 | }; 376 | 377 | this._clear = function () { 378 | this.$c[0].width = this.$c[0].width; 379 | }; 380 | 381 | // Abstract methods 382 | this.listen = function () {}; // on start, one time 383 | this.extend = function () {}; // each time configure triggered 384 | this.init = function () {}; // each time configure triggered 385 | this.change = function (v) {}; // on change 386 | this.val = function (v) {}; // on release 387 | this.xy2val = function (x, y, method) {}; // 388 | this.draw = function () {}; // on change / on release 389 | this.clear = function () { this._clear(); }; 390 | 391 | // Utils 392 | this.h2rgba = function (h, a) { 393 | var rgb; 394 | h = h.substring(1,7) 395 | rgb = [parseInt(h.substring(0,2),16) 396 | ,parseInt(h.substring(2,4),16) 397 | ,parseInt(h.substring(4,6),16)]; 398 | return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")"; 399 | }; 400 | 401 | this.copy = function (f, t) { 402 | for (var i in f) { t[i] = f[i]; } 403 | }; 404 | }; 405 | 406 | 407 | /** 408 | * k.Dial 409 | */ 410 | k.Dial = function () { 411 | k.o.call(this); 412 | 413 | this.startAngle = null; 414 | this.xy = null; 415 | this.radius = null; 416 | this.lineWidth = null; 417 | this.cursorExt = null; 418 | this.w2 = null; 419 | this.PI2 = 2*Math.PI; 420 | 421 | this.extend = function () { 422 | this.o = $.extend( 423 | { 424 | bgColor : this.$.data('bgcolor') || '#EEEEEE', 425 | angleOffset : this.$.data('angleoffset') || 0, 426 | angleArc : this.$.data('anglearc') || 360, 427 | flatMouse : this.$.data('flatMouse'), 428 | inline : true 429 | }, this.o 430 | ); 431 | }; 432 | 433 | this.val = function (v) { 434 | if (null != v) { 435 | this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v; 436 | this.v = this.cv; 437 | this.$.val(this.v); 438 | this._draw(); 439 | } else { 440 | return this.v; 441 | } 442 | }; 443 | 444 | this.xy2val = function (x, y, m) { 445 | var a, ret; 446 | 447 | if ((m === 'mouse') && (this.o.flatMouse)) { 448 | a = ((this.my - y) + (x - this.mx)) / (this.o.height); 449 | ret = ~~ (a * (this.o.max - this.o.min) + parseFloat(this.v)); 450 | ret = max(min(ret, this.o.max), this.o.min); 451 | } else { 452 | a = Math.atan2( 453 | x - (this.x + this.w2) 454 | , - (y - this.y - this.w2) 455 | ) - this.angleOffset; 456 | 457 | if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) { 458 | // if isset angleArc option, set to min if .5 under min 459 | a = 0; 460 | } else if (a < 0) { 461 | a += this.PI2; 462 | } 463 | 464 | ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc)) 465 | + this.o.min; 466 | } 467 | 468 | this.o.stopper 469 | && (ret = max(min(ret, this.o.max), this.o.min)); 470 | 471 | return ret; 472 | }; 473 | 474 | this.listen = function () { 475 | // bind MouseWheel 476 | var s = this, 477 | mw = function (e) { 478 | if(s.o.noScroll) 479 | return; 480 | 481 | e.preventDefault(); 482 | 483 | var ori = e.originalEvent 484 | ,deltaX = ori.detail || ori.wheelDeltaX 485 | ,deltaY = ori.detail || ori.wheelDeltaY 486 | ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? 1 : deltaX<0 || deltaY<0 ? -1 : 0); 487 | 488 | if ( 489 | s.cH 490 | && (s.cH(v) === false) 491 | ) return; 492 | 493 | s.val(v); 494 | } 495 | , kval, to, m = 1, kv = {37:-1, 38:1, 39:1, 40:-1}; 496 | 497 | this.$ 498 | .bind( 499 | "keydown" 500 | ,function (e) { 501 | 502 | var kc = e.keyCode; 503 | 504 | // numpad support 505 | if(kc >= 96 && kc <= 105) { 506 | kc = e.keyCode = kc - 48; 507 | } 508 | 509 | kval = parseInt(String.fromCharCode(kc)); 510 | 511 | if (isNaN(kval)) { 512 | 513 | (kc !== 13) // enter 514 | && (kc !== 8) // bs 515 | && (kc !== 9) // tab 516 | && (kc !== 189) // - 517 | && e.preventDefault(); 518 | 519 | // arrows 520 | if ($.inArray(kc,[37,38,39,40]) > -1) { 521 | e.preventDefault(); 522 | 523 | var v = parseInt(s.$.val()) + kv[kc] * m; 524 | 525 | s.o.stopper 526 | && (v = max(min(v, s.o.max), s.o.min)); 527 | 528 | s.change(v); 529 | s._draw(); 530 | 531 | // long time keydown speed-up 532 | to = window.setTimeout( 533 | function () { m*=2; } 534 | ,30 535 | ); 536 | } 537 | } 538 | } 539 | ) 540 | .bind( 541 | "keyup" 542 | ,function (e) { 543 | if (isNaN(kval)) { 544 | if (to) { 545 | window.clearTimeout(to); 546 | to = null; 547 | m = 1; 548 | s.val(s.$.val()); 549 | } 550 | } else { 551 | // kval postcond 552 | (s.$.val() > s.o.max && s.$.val(s.o.max)) 553 | || (s.$.val() < s.o.min && s.$.val(s.o.min)); 554 | } 555 | 556 | } 557 | ); 558 | 559 | this.$c.bind("mousewheel DOMMouseScroll", mw); 560 | this.$.bind("mousewheel DOMMouseScroll", mw) 561 | }; 562 | 563 | this.init = function () { 564 | 565 | if ( 566 | this.v < this.o.min 567 | || this.v > this.o.max 568 | ) this.v = this.o.min; 569 | 570 | this.$.val(this.v); 571 | this.w2 = this.o.width / 2; 572 | this.cursorExt = this.o.cursor / 100; 573 | this.xy = this.w2; 574 | this.lineWidth = this.xy * this.o.thickness; 575 | this.radius = this.xy - this.lineWidth / 2; 576 | 577 | this.o.angleOffset 578 | && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset); 579 | 580 | this.o.angleArc 581 | && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc); 582 | 583 | // deg to rad 584 | this.angleOffset = this.o.angleOffset * Math.PI / 180; 585 | this.angleArc = this.o.angleArc * Math.PI / 180; 586 | 587 | // compute start and end angles 588 | this.startAngle = 1.5 * Math.PI + this.angleOffset; 589 | this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc; 590 | 591 | var s = max( 592 | String(Math.abs(this.o.max)).length 593 | , String(Math.abs(this.o.min)).length 594 | , 2 595 | ) + 2; 596 | 597 | this.o.displayInput 598 | && this.i.css({ 599 | 'width' : ((this.o.width / 2 + 4) >> 0) + 'px' 600 | ,'height' : ((this.o.width / 3) >> 0) + 'px' 601 | ,'position' : 'absolute' 602 | ,'vertical-align' : 'middle' 603 | ,'margin-top' : ((this.o.width / 3) >> 0) + 'px' 604 | ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px' 605 | ,'border' : 0 606 | ,'background' : 'none' 607 | ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial' 608 | ,'text-align' : 'center' 609 | ,'color' : this.o.fgColor 610 | ,'padding' : '0px' 611 | ,'-webkit-appearance': 'none' 612 | }) 613 | || this.i.css({ 614 | 'width' : '0px' 615 | ,'visibility' : 'hidden' 616 | }); 617 | }; 618 | 619 | this.change = function (v) { 620 | this.cv = v; 621 | this.$.val(v); 622 | }; 623 | 624 | this.angle = function (v) { 625 | return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min); 626 | }; 627 | 628 | this.draw = function () { 629 | 630 | var c = this.g, // context 631 | a = this.angle(this.cv) // Angle 632 | , sat = this.startAngle // Start angle 633 | , eat = sat + a // End angle 634 | , sa, ea // Previous angles 635 | , r = 1; 636 | 637 | c.lineWidth = this.lineWidth; 638 | 639 | /*for(o in this.o.context) { 640 | c[o] = this.o.context[o]; 641 | }*/ 642 | 643 | this.o.cursor 644 | && (sat = eat - this.cursorExt) 645 | && (eat = eat + this.cursorExt); 646 | 647 | c.beginPath(); 648 | c.strokeStyle = this.o.bgColor; 649 | c.arc(this.xy, this.xy, this.radius, this.endAngle, this.startAngle, true); 650 | c.stroke(); 651 | 652 | if (this.o.displayPrevious) { 653 | ea = this.startAngle + this.angle(this.v); 654 | sa = this.startAngle; 655 | this.o.cursor 656 | && (sa = ea - this.cursorExt) 657 | && (ea = ea + this.cursorExt); 658 | 659 | c.beginPath(); 660 | c.strokeStyle = this.pColor; 661 | c.arc(this.xy, this.xy, this.radius, sa, ea, false); 662 | c.stroke(); 663 | r = (this.cv == this.v); 664 | } 665 | 666 | c.beginPath(); 667 | c.strokeStyle = r ? this.o.fgColor : this.fgColor ; 668 | c.arc(this.xy, this.xy, this.radius, sat, eat, false); 669 | c.stroke(); 670 | }; 671 | 672 | this.cancel = function () { 673 | this.val(this.v); 674 | }; 675 | }; 676 | 677 | $.fn.dial = $.fn.knob = function (o) { 678 | return this.each( 679 | function () { 680 | var d = new k.Dial(); 681 | d.o = o; 682 | d.$ = $(this); 683 | d.run(); 684 | } 685 | ).parent(); 686 | }; 687 | 688 | 689 | /** 690 | * k.XY 691 | */ 692 | k.XY = function () { 693 | k.o.call(this); 694 | 695 | this.m = []; 696 | this.p = []; 697 | this.f = []; // factor 698 | this.s = {0:1,1:-1}; 699 | this.cur2 = 0; 700 | this.cursor = 0; 701 | this.v = {}; 702 | this.div = null; 703 | 704 | this.extend = function () { 705 | this.o = $.extend( 706 | { 707 | min : this.$.data('min') || 0, 708 | max : this.$.data('max') || 100, 709 | width : this.$.data('width') || 200, 710 | height : this.$.data('height') || 200 711 | }, this.o 712 | ); 713 | }; 714 | 715 | this._coord = function() { 716 | for(var i in this.v) { 717 | this.m[i] = ~~ (0.5 + ((this.s[i] * this.v[i] - this.o.min) / this.f[i]) + this.cur2) ; 718 | this.p[i] = this.m[i]; 719 | } 720 | }; 721 | 722 | this.init = function () { 723 | this.cursor = this.o.cursor || 30; 724 | this.cur2 = this.cursor / 2; 725 | 726 | this.f[0] = (this.o.max - this.o.min) / (this.o.width - this.cursor); 727 | this.f[1] = (this.o.max - this.o.min) / (this.o.height - this.cursor); 728 | 729 | if (!this.isInit) { 730 | this._coord(); 731 | } 732 | 733 | if(this.o.displayInput) { 734 | var s = this; 735 | this.$.css({ 736 | 'margin-top' : '-30px' 737 | , 'border' : 0 738 | , 'font' : '11px Arial' 739 | }); 740 | 741 | this.i.each( 742 | function (){ 743 | $(this).css({ 744 | 'width' : (s.o.width / 4) + 'px' 745 | ,'border' : 0 746 | ,'background' : 'none' 747 | ,'color' : s.o.fgColor 748 | ,'padding' : '0px' 749 | ,'-webkit-appearance': 'none' 750 | }); 751 | }); 752 | } else { 753 | this.$.css({ 754 | 'width' : '0px' 755 | ,'visibility' : 'hidden' 756 | }); 757 | } 758 | }; 759 | 760 | this.xy2val = function (x, y) { 761 | this.m[0] = max(this.cur2, min(x - this.x, this.o.width - this.cur2)); 762 | this.m[1] = max(this.cur2, min(y - this.y, this.o.height - this.cur2)); 763 | 764 | return { 765 | 0 : ~~ (this.o.min + (this.m[0] - this.cur2) * this.f[0]), 766 | 1 : ~~ (this.o.min + (this.o.height - this.m[1] - this.cur2) * this.f[1]) 767 | }; 768 | }; 769 | 770 | this.change = function (v) { 771 | this.cv = v; 772 | this.i[0].val(this.cv[0]); 773 | this.i[1].val(this.cv[1]); 774 | }; 775 | 776 | this.val = function (v) { 777 | if (null !== v) { 778 | this.cv = v; 779 | this.copy(this.cv, this.v); 780 | this._coord(); 781 | this._draw(); 782 | } else { 783 | return this.v; 784 | } 785 | }; 786 | 787 | this.cancel = function () { 788 | this.copy(this.v, this.cv); 789 | this.i[0].val(this.cv[0]); 790 | this.i[1].val(this.cv[1]); 791 | this.m[0] = this.p[0]; 792 | this.m[1] = this.p[1]; 793 | this._draw(); 794 | }; 795 | 796 | this.draw = function () { 797 | 798 | var c = this.g 799 | , r = 1; 800 | 801 | if (this.o.displayPrevious) { 802 | c.beginPath(); 803 | c.lineWidth = this.cursor; 804 | c.strokeStyle = this.pColor; 805 | c.moveTo(this.p[0], this.p[1] + this.cur2); 806 | c.lineTo(this.p[0], this.p[1] - this.cur2); 807 | c.stroke(); 808 | r = (this.cv[0] == this.v[0] && this.cv[1] == this.v[1]); 809 | } 810 | 811 | c.beginPath(); 812 | c.lineWidth = this.cursor; 813 | c.strokeStyle = r ? this.o.fgColor : this.fgColor; 814 | c.moveTo(this.m[0], this.m[1] + this.cur2); 815 | c.lineTo(this.m[0], this.m[1] - this.cur2); 816 | c.stroke(); 817 | }; 818 | }; 819 | 820 | $.fn.xy = function (o) { 821 | return this.each( 822 | function () { 823 | var x = new k.XY(); 824 | x.$ = $(this); 825 | x.o = o; 826 | x.run(); 827 | } 828 | ).parent(); 829 | }; 830 | 831 | 832 | /** 833 | * k.Bars 834 | */ 835 | k.Bars = function () { 836 | k.o.call(this); 837 | 838 | this.bar = null; 839 | this.mid = null; 840 | this.col = null; 841 | this.colWidth = null; 842 | this.fontSize = null; 843 | this.displayMidLine = false; 844 | 845 | this.extend = function () { 846 | 847 | this.o = $.extend( 848 | { 849 | min : this.$.data('min') || 0, 850 | max : this.$.data('max') || 100, 851 | width : this.$.data('width') || 600, 852 | displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'), 853 | height : (this.$.data('height') || 200), 854 | fgColor : this.$.data('fgcolor') || '#87CEEB', 855 | bgColor : this.$.data('bgcolor') || '#CCCCCC', 856 | cols : this.$.data('cols') || 8, 857 | spacing : this.$.data('spacing') || 1 858 | } 859 | ,this.o 860 | ); 861 | 862 | // initialize colWith 863 | (this.o.cols == 1) && (this.o.spacing = 0); 864 | this.colWidth = (((this.o.width - this.o.spacing * this.o.cols) / this.o.cols) >> 0); 865 | 866 | if(this.o.displayInput) { 867 | this.fontSize = max(~~ (this.colWidth/3), 10); 868 | this.o.height -= this.fontSize; 869 | } 870 | }; 871 | 872 | this.xy2val = function (x, y) { 873 | var cw = this.colWidth + this.o.spacing 874 | ,val = ( 875 | max(this.o.min 876 | , min(this.o.max, - ( - this.mid + (y - this.y)) / this.bar)) 877 | ) >> 0 878 | ,ret = {}; 879 | 880 | this.col = max(0, min(this.o.cols-1, ((x - this.x) / cw) >> 0)); 881 | ret[this.col] = val; 882 | return ret; 883 | }; 884 | 885 | this.init = function () { 886 | 887 | this.bar = this.o.height / (this.o.max - this.o.min); 888 | this.mid = (this.o.max * this.bar) >> 0; 889 | this.displayMidLine = this.o.cursor && this.o.min < 0; 890 | 891 | if(this.o.displayInput) { 892 | var s = this; 893 | this.$.css({ 894 | 'margin' : '0px' 895 | ,'border' : 0 896 | ,'padding' : '0px' 897 | }); 898 | 899 | this.i.each( 900 | function (){ 901 | $(this).css({ 902 | 'width' : (s.colWidth - 4 + s.o.spacing) + 'px' 903 | ,'border' : 0 904 | ,'background' : 'none' 905 | ,'font' : s.fontSize+'px Arial' //this.fontSize 906 | ,'color' : s.o.fgColor 907 | ,'margin' : '0px' 908 | ,'padding' : '0px' 909 | ,'-webkit-appearance': 'none' 910 | ,'text-align' : 'center' 911 | }); 912 | }); 913 | } else { 914 | this.$.css({ 915 | 'width' : '0px' 916 | ,'visibility' : 'hidden' 917 | }); 918 | } 919 | }; 920 | 921 | this.change = function (v) { 922 | for (var i in v) { 923 | this.cv[i] = v[i]; 924 | this.i[i].val(this.cv[i]); 925 | } 926 | }; 927 | 928 | this.val = function (v) { 929 | if (null !== v) { 930 | this.copy(v, this.cv); 931 | this.copy(this.cv, this.v); 932 | 933 | // reset cur col 934 | this.col = null; 935 | this._draw(); 936 | } else { 937 | return this.v; 938 | } 939 | }; 940 | 941 | this.cancel = function () { 942 | this.copy(this.v, this.cv); 943 | 944 | // reset cur col 945 | this.col = null; 946 | this._draw(); 947 | }; 948 | 949 | this._bar = function (col) { 950 | 951 | var x = (col * (this.colWidth + this.o.spacing) + this.colWidth / 2); 952 | 953 | if (this.displayMidLine) { 954 | this.g.beginPath(); 955 | this.g.lineWidth = this.colWidth; 956 | this.g.strokeStyle = this.o.fgColor; 957 | this.g.moveTo(x, this.mid); 958 | this.g.lineTo(x, this.mid + 1); 959 | this.g.stroke(); 960 | } 961 | 962 | if (this.o.displayPrevious) { 963 | this.g.beginPath(); 964 | this.g.lineWidth = this.colWidth; 965 | this.g.strokeStyle = (this.cv[col] == this.v[col]) ? this.o.fgColor : this.pColor; 966 | if (this.o.cursor) { 967 | this.g.lineTo(x, this.mid - ((this.v[col] * this.bar) >> 0) + this.o.cursor / 2); 968 | } else { 969 | this.g.moveTo(x, this.mid); 970 | } 971 | this.g.lineTo(x, this.mid - ((this.v[col] * this.bar) >> 0) - this.o.cursor / 2); 972 | this.g.stroke(); 973 | } 974 | 975 | this.g.beginPath(); 976 | this.g.lineWidth = this.colWidth; 977 | this.g.strokeStyle = this.fgColor; 978 | if (this.o.cursor) { 979 | this.g.lineTo(x, this.mid - ((this.cv[col] * this.bar) >> 0) + this.o.cursor / 2); 980 | } else { 981 | this.g.moveTo(x, this.mid); 982 | } 983 | this.g.lineTo(x, this.mid - ((this.cv[col] * this.bar) >> 0) - this.o.cursor / 2); 984 | this.g.stroke(); 985 | }; 986 | 987 | this.clear = function () { 988 | if (this.col) { 989 | // current col 990 | this.c.clearRect( 991 | this.col * (this.colWidth + this.o.spacing) 992 | , 0 993 | , this.colWidth + this.o.spacing 994 | , this.o.height 995 | ); 996 | } else { 997 | this._clear(); 998 | } 999 | } 1000 | 1001 | this.draw = function () { 1002 | if (this.col) { 1003 | this._bar(this.col); 1004 | } else { 1005 | for (var i = 0; i < this.o.cols; i++) { 1006 | this._bar(i); 1007 | } 1008 | } 1009 | }; 1010 | }; 1011 | 1012 | $.fn.bars = function (o) { 1013 | return this.each( 1014 | function () { 1015 | var b = new k.Bars(); 1016 | b.$ = $(this); 1017 | b.o = o; 1018 | b.run(); 1019 | } 1020 | ).parent(); 1021 | }; 1022 | })(jQuery); --------------------------------------------------------------------------------