├── 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 |Library of user interface components :
204 | Dial (dial, circular gauge), XY (XY pad), Bars (mixer, level meter ...).
205 |
206 |
207 |
208 |
jQuery Kontrol is © 2012 Anthony Terrien and dual licensed under the MIT or GPL licenses.
300 |
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);
--------------------------------------------------------------------------------