Initial commit

This commit is contained in:
Khanh Ngo
2015-12-13 16:34:12 +07:00
commit 2dac8205f6
3113 changed files with 514935 additions and 0 deletions

View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 Anthony Terrien
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,135 @@
jQuery Knob
=============
- canvas based ; no png or jpg sprites.
- touch, mouse and mousewheel, keyboard events implemented.
- downward compatible ; overloads an input element.
Example
-------
<input type="text" value="75" class="dial">
<script>
$(function() {
$(".dial").knob();
});
</script>
Options
-------
Options are provided as attributes 'data-option':
<input type="text" class="dial" data-min="-50" data-max="50">
... or in the "knob()" call :
$(".dial").knob({
'min':-50
,'max':50
});
The following options are supported :
Behaviors :
* min : min value | default=0.
* max : max value | default=100.
* step : step size | default=1.
* angleOffset : starting angle in degrees | default=0.
* angleArc : arc size in degrees | default=360.
* stopper : stop at min & max on keydown/mousewheel | default=true.
* readOnly : disable input and events | default=false.
* rotation : direction of progression | default=clockwise.
UI :
* cursor : display mode "cursor", cursor size could be changed passing a numeric value to the option, default width is used when passing boolean value "true" | default=gauge.
* thickness : gauge thickness.
* lineCap : gauge stroke endings. | default=butt, round=rounded line endings
* width : dial width.
* displayInput : default=true | false=hide input.
* displayPrevious : default=false | true=displays the previous value with transparency.
* fgColor : foreground color.
* inputColor : input value (number) color.
* font : font family.
* fontWeight : font weight.
* bgColor : background color.
Hooks
-------
<script>
$(".dial").knob({
'release' : function (v) { /*make something*/ }
});
</script>
* 'release' : executed on release
Parameters :
+ value : int, current value
* 'change' : executed at each change of the value
Parameters :
+ value : int, current value
* 'draw' : when drawing the canvas
Context :
- this.g : canvas context 2D (see Canvas documentation)
- this.$ : jQuery wrapped element
- this.o : options
- this.i : input
- ... console.log(this);
* 'cancel' : triggered on [esc] keydown
* 'format' : allows to format output (add unit %, ms ...)
The scope (this) of each hook function is the current Knob instance (refer to the demo code).
Example
-------
<input type="text" value="75" class="dial">
<script>
$(".dial").knob({
'change' : function (v) { console.log(v); }
});
</script>
Dynamically configure
-------
<script>
$('.dial')
.trigger(
'configure',
{
"min":10,
"max":40,
"fgColor":"#FF0000",
"skin":"tron",
"cursor":true
}
);
</script>
Set the value
-------
<script>
$('.dial')
.val(27)
.trigger('change');
</script>
Supported browser
-------
Tested on Chrome, Safari, Firefox, IE>=8.0 (IE8.0 with excanvas).
![secretplan](https://raw.github.com/aterrien/jQuery-Knob/master/secretplan.jpg)

View File

@ -0,0 +1,791 @@
/*!jQuery Knob*/
/**
* Downward compatible, touchable dial
*
* Version: 1.2.8
* Requires: jQuery v1.7+
*
* Copyright (c) 2012 Anthony Terrien
* Under MIT License (http://www.opensource.org/licenses/mit-license.php)
*
* Thanks to vor, eskimoblood, spiffistan, FabrizioC
*/
(function($) {
/**
* Kontrol library
*/
"use strict";
/**
* Definition of globals and core
*/
var k = {}, // kontrol
max = Math.max,
min = Math.min;
k.c = {};
k.c.d = $(document);
k.c.t = function (e) {
return e.originalEvent.touches.length - 1;
};
/**
* Kontrol Object
*
* Definition of an abstract UI control
*
* Each concrete component must call this one.
* <code>
* k.o.call(this);
* </code>
*/
k.o = function () {
var s = this;
this.o = null; // array of options
this.$ = null; // jQuery wrapped element
this.i = null; // mixed HTMLInputElement or array of HTMLInputElement
this.g = null; // deprecated 2D graphics context for 'pre-rendering'
this.v = null; // value ; mixed array or integer
this.cv = null; // change value ; not commited value
this.x = 0; // canvas x position
this.y = 0; // canvas y position
this.w = 0; // canvas width
this.h = 0; // canvas height
this.$c = null; // jQuery canvas element
this.c = null; // rendered canvas context
this.t = 0; // touches index
this.isInit = false;
this.fgColor = null; // main color
this.pColor = null; // previous color
this.dH = null; // draw hook
this.cH = null; // change hook
this.eH = null; // cancel hook
this.rH = null; // release hook
this.scale = 1; // scale factor
this.relative = false;
this.relativeWidth = false;
this.relativeHeight = false;
this.$div = null; // component div
this.run = function () {
var cf = function (e, conf) {
var k;
for (k in conf) {
s.o[k] = conf[k];
}
s._carve().init();
s._configure()
._draw();
};
if(this.$.data('kontroled')) return;
this.$.data('kontroled', true);
this.extend();
this.o = $.extend(
{
// Config
min : this.$.data('min') !== undefined ? this.$.data('min') : 0,
max : this.$.data('max') !== undefined ? this.$.data('max') : 100,
stopper : true,
readOnly : this.$.data('readonly') || (this.$.attr('readonly') === 'readonly'),
// UI
cursor : (this.$.data('cursor') === true && 30) ||
this.$.data('cursor') || 0,
thickness : (
this.$.data('thickness') &&
Math.max(Math.min(this.$.data('thickness'), 1), 0.01)
) || 0.35,
lineCap : this.$.data('linecap') || 'butt',
width : this.$.data('width') || 200,
height : this.$.data('height') || 200,
displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'),
displayPrevious : this.$.data('displayprevious'),
fgColor : this.$.data('fgcolor') || '#87CEEB',
inputColor: this.$.data('inputcolor'),
font: this.$.data('font') || 'Arial',
fontWeight: this.$.data('font-weight') || 'bold',
inline : false,
step : this.$.data('step') || 1,
rotation: this.$.data('rotation'),
// Hooks
draw : null, // function () {}
change : null, // function (value) {}
cancel : null, // function () {}
release : null, // function (value) {}
// Output formatting, allows to add unit: %, ms ...
format: function(v) {
return v;
},
parse: function (v) {
return parseFloat(v);
}
}, this.o
);
// finalize options
this.o.flip = this.o.rotation === 'anticlockwise' || this.o.rotation === 'acw';
if(!this.o.inputColor) {
this.o.inputColor = this.o.fgColor;
}
// routing value
if(this.$.is('fieldset')) {
// fieldset = array of integer
this.v = {};
this.i = this.$.find('input');
this.i.each(function(k) {
var $this = $(this);
s.i[k] = $this;
s.v[k] = s.o.parse($this.val());
$this.bind(
'change blur'
, function () {
var val = {};
val[k] = $this.val();
s.val(val);
}
);
});
this.$.find('legend').remove();
} else {
// input = integer
this.i = this.$;
this.v = this.o.parse(this.$.val());
(this.v === '') && (this.v = this.o.min);
this.$.bind(
'change blur'
, function () {
s.val(s._validate(s.o.parse(s.$.val())));
}
);
}
(!this.o.displayInput) && this.$.hide();
// adds needed DOM elements (canvas, div)
this.$c = $(document.createElement('canvas')).attr({
width: this.o.width,
height: this.o.height
});
// wraps all elements in a div
// add to DOM before Canvas init is triggered
this.$div = $('<div style="'
+ (this.o.inline ? 'display:inline;' : '')
+ 'width:' + this.o.width + 'px;height:' + this.o.height + 'px;'
+ '"></div>');
this.$.wrap(this.$div).before(this.$c);
this.$div = this.$.parent();
if (typeof G_vmlCanvasManager !== 'undefined') {
G_vmlCanvasManager.initElement(this.$c[0]);
}
this.c = this.$c[0].getContext ? this.$c[0].getContext('2d') : null;
if (!this.c) {
throw {
name: "CanvasNotSupportedException",
message: "Canvas not supported. Please use excanvas on IE8.0.",
toString: function(){return this.name + ": " + this.message}
}
}
// hdpi support
this.scale = (window.devicePixelRatio || 1) /
(
this.c.webkitBackingStorePixelRatio ||
this.c.mozBackingStorePixelRatio ||
this.c.msBackingStorePixelRatio ||
this.c.oBackingStorePixelRatio ||
this.c.backingStorePixelRatio || 1
);
// detects relative width / height
this.relativeWidth = ((this.o.width % 1 !== 0) &&
this.o.width.indexOf('%'));
this.relativeHeight = ((this.o.height % 1 !== 0) &&
this.o.height.indexOf('%'));
this.relative = (this.relativeWidth || this.relativeHeight);
// computes size and carves the component
this._carve();
// prepares props for transaction
if (this.v instanceof Object) {
this.cv = {};
this.copy(this.v, this.cv);
} else {
this.cv = this.v;
}
// binds configure event
this.$
.bind("configure", cf)
.parent()
.bind("configure", cf);
// finalize init
this._listen()
._configure()
._xy()
.init();
this.isInit = true;
this.$.val(this.o.format(this.v));
this._draw();
return this;
};
this._carve = function() {
if(this.relative) {
var w = this.relativeWidth ?
this.$div.parent().width() *
parseInt(this.o.width) / 100 :
this.$div.parent().width(),
h = this.relativeHeight ?
this.$div.parent().height() *
parseInt(this.o.height) / 100 :
this.$div.parent().height();
// apply relative
this.w = this.h = Math.min(w, h);
} else {
this.w = this.o.width;
this.h = this.o.height;
}
// finalize div
this.$div.css({
'width': this.w + 'px',
'height': this.h + 'px'
});
// finalize canvas with computed width
this.$c.attr({
width: this.w,
height: this.h
});
// scaling
if (this.scale !== 1) {
this.$c[0].width = this.$c[0].width * this.scale;
this.$c[0].height = this.$c[0].height * this.scale;
this.$c.width(this.w);
this.$c.height(this.h);
}
return this;
}
this._draw = function () {
// canvas pre-rendering
var d = true;
s.g = s.c;
s.clear();
s.dH
&& (d = s.dH());
(d !== false) && s.draw();
};
this._touch = function (e) {
var touchMove = function (e) {
var v = s.xy2val(
e.originalEvent.touches[s.t].pageX,
e.originalEvent.touches[s.t].pageY
);
if (v == s.cv) return;
if (s.cH && (s.cH(v) === false)) return;
s.change(s._validate(v));
s._draw();
};
// get touches index
this.t = k.c.t(e);
// First touch
touchMove(e);
// Touch events listeners
k.c.d
.bind("touchmove.k", touchMove)
.bind(
"touchend.k"
, function () {
k.c.d.unbind('touchmove.k touchend.k');
s.val(s.cv);
}
);
return this;
};
this._mouse = function (e) {
var mouseMove = function (e) {
var v = s.xy2val(e.pageX, e.pageY);
if (v == s.cv) return;
if (s.cH && (s.cH(v) === false)) return;
s.change(s._validate(v));
s._draw();
};
// First click
mouseMove(e);
// Mouse events listeners
k.c.d
.bind("mousemove.k", mouseMove)
.bind(
// Escape key cancel current change
"keyup.k"
, function (e) {
if (e.keyCode === 27) {
k.c.d.unbind("mouseup.k mousemove.k keyup.k");
if (
s.eH
&& (s.eH() === false)
) return;
s.cancel();
}
}
)
.bind(
"mouseup.k"
, function (e) {
k.c.d.unbind('mousemove.k mouseup.k keyup.k');
s.val(s.cv);
}
);
return this;
};
this._xy = function () {
var o = this.$c.offset();
this.x = o.left;
this.y = o.top;
return this;
};
this._listen = function () {
if (!this.o.readOnly) {
this.$c
.bind(
"mousedown"
, function (e) {
e.preventDefault();
s._xy()._mouse(e);
}
)
.bind(
"touchstart"
, function (e) {
e.preventDefault();
s._xy()._touch(e);
}
);
this.listen();
} else {
this.$.attr('readonly', 'readonly');
}
if(this.relative) {
$(window).resize(function() {
s._carve()
.init();
s._draw();
});
}
return this;
};
this._configure = function () {
// Hooks
if (this.o.draw) this.dH = this.o.draw;
if (this.o.change) this.cH = this.o.change;
if (this.o.cancel) this.eH = this.o.cancel;
if (this.o.release) this.rH = this.o.release;
if (this.o.displayPrevious) {
this.pColor = this.h2rgba(this.o.fgColor, "0.4");
this.fgColor = this.h2rgba(this.o.fgColor, "0.6");
} else {
this.fgColor = this.o.fgColor;
}
return this;
};
this._clear = function () {
this.$c[0].width = this.$c[0].width;
};
this._validate = function(v) {
return (~~ (((v < 0) ? -0.5 : 0.5) + (v/this.o.step))) * this.o.step;
};
// Abstract methods
this.listen = function () {}; // on start, one time
this.extend = function () {}; // each time configure triggered
this.init = function () {}; // each time configure triggered
this.change = function (v) {}; // on change
this.val = function (v) {}; // on release
this.xy2val = function (x, y) {}; //
this.draw = function () {}; // on change / on release
this.clear = function () { this._clear(); };
// Utils
this.h2rgba = function (h, a) {
var rgb;
h = h.substring(1,7)
rgb = [parseInt(h.substring(0,2),16)
,parseInt(h.substring(2,4),16)
,parseInt(h.substring(4,6),16)];
return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")";
};
this.copy = function (f, t) {
for (var i in f) { t[i] = f[i]; }
};
};
/**
* k.Dial
*/
k.Dial = function () {
k.o.call(this);
this.startAngle = null;
this.xy = null;
this.radius = null;
this.lineWidth = null;
this.cursorExt = null;
this.w2 = null;
this.PI2 = 2*Math.PI;
this.extend = function () {
this.o = $.extend(
{
bgColor : this.$.data('bgcolor') || '#EEEEEE',
angleOffset : this.$.data('angleoffset') || 0,
angleArc : this.$.data('anglearc') || 360,
inline : true
}, this.o
);
};
this.val = function (v, triggerRelease) {
if (null != v) {
// reverse format
v = this.o.parse(v);
if (
triggerRelease !== false && (v != this.v) && this.rH &&
(this.rH(v) === false)
) return;
this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;
this.v = this.cv;
this.$.val(this.o.format(this.v));
this._draw();
} else {
return this.v;
}
};
this.xy2val = function (x, y) {
var a, ret;
a = Math.atan2(
x - (this.x + this.w2)
, - (y - this.y - this.w2)
) - this.angleOffset;
if (this.o.flip) {
a = this.angleArc - a - this.PI2;
}
if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {
// if isset angleArc option, set to min if .5 under min
a = 0;
} else if (a < 0) {
a += this.PI2;
}
ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc))
+ this.o.min;
this.o.stopper && (ret = max(min(ret, this.o.max), this.o.min));
return ret;
};
this.listen = function () {
// bind MouseWheel
var s = this, mwTimerStop, mwTimerRelease,
mw = function (e) {
e.preventDefault();
var ori = e.originalEvent
,deltaX = ori.detail || ori.wheelDeltaX
,deltaY = ori.detail || ori.wheelDeltaY
,v = s._validate(s.o.parse(s.$.val()))
+ (deltaX>0 || deltaY>0 ? s.o.step : deltaX<0 || deltaY<0 ? -s.o.step : 0);
v = max(min(v, s.o.max), s.o.min);
s.val(v, false);
if(s.rH) {
// Handle mousewheel stop
clearTimeout(mwTimerStop);
mwTimerStop = setTimeout(function() {
s.rH(v);
mwTimerStop = null;
}, 100);
// Handle mousewheel releases
if(!mwTimerRelease) {
mwTimerRelease = setTimeout(function() {
if(mwTimerStop) s.rH(v);
mwTimerRelease = null;
}, 200);
}
}
}
, kval, to, m = 1, kv = {37:-s.o.step, 38:s.o.step, 39:s.o.step, 40:-s.o.step};
this.$
.bind(
"keydown"
,function (e) {
var kc = e.keyCode;
// numpad support
if(kc >= 96 && kc <= 105) {
kc = e.keyCode = kc - 48;
}
kval = parseInt(String.fromCharCode(kc));
if (isNaN(kval)) {
(kc !== 13) // enter
&& (kc !== 8) // bs
&& (kc !== 9) // tab
&& (kc !== 189) // -
&& (kc !== 190 || s.$.val().match(/\./)) // . only allowed once
&& e.preventDefault();
// arrows
if ($.inArray(kc,[37,38,39,40]) > -1) {
e.preventDefault();
var v = s.o.parse(s.$.val()) + kv[kc] * m;
s.o.stopper && (v = max(min(v, s.o.max), s.o.min));
s.change(v);
s._draw();
// long time keydown speed-up
to = window.setTimeout(
function () { m *= 2; }, 30
);
}
}
}
)
.bind(
"keyup"
,function (e) {
if (isNaN(kval)) {
if (to) {
window.clearTimeout(to);
to = null;
m = 1;
s.val(s.$.val());
}
} else {
// kval postcond
(s.$.val() > s.o.max && s.$.val(s.o.max))
|| (s.$.val() < s.o.min && s.$.val(s.o.min));
}
}
);
//this.$c.bind("mousewheel DOMMouseScroll", mw);
//this.$.bind("mousewheel DOMMouseScroll", mw)
};
this.init = function () {
if (
this.v < this.o.min
|| this.v > this.o.max
) this.v = this.o.min;
this.$.val(this.v);
this.w2 = this.w / 2;
this.cursorExt = this.o.cursor / 100;
this.xy = this.w2 * this.scale;
this.lineWidth = this.xy * this.o.thickness;
this.lineCap = this.o.lineCap;
this.radius = this.xy - this.lineWidth / 2;
this.o.angleOffset
&& (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);
this.o.angleArc
&& (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);
// deg to rad
this.angleOffset = this.o.angleOffset * Math.PI / 180;
this.angleArc = this.o.angleArc * Math.PI / 180;
// compute start and end angles
this.startAngle = 1.5 * Math.PI + this.angleOffset;
this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;
var s = max(
String(Math.abs(this.o.max)).length
, String(Math.abs(this.o.min)).length
, 2
) + 2;
this.o.displayInput
&& this.i.css({
'width' : ((this.w / 2 + 4) >> 0) + 'px'
,'height' : ((this.w / 3) >> 0) + 'px'
,'position' : 'absolute'
,'vertical-align' : 'middle'
,'margin-top' : ((this.w / 3) >> 0) + 'px'
,'margin-left' : '-' + ((this.w * 3 / 4 + 2) >> 0) + 'px'
,'border' : 0
,'background' : 'none'
,'font' : this.o.fontWeight + ' ' + ((this.w / s) >> 0) + 'px ' + this.o.font
,'text-align' : 'center'
,'color' : this.o.inputColor || this.o.fgColor
,'padding' : '0px'
,'-webkit-appearance': 'none'
})
|| this.i.css({
'width' : '0px'
,'visibility' : 'hidden'
});
};
this.change = function (v) {
this.cv = v;
this.$.val(this.o.format(v));
};
this.angle = function (v) {
return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);
};
this.arc = function (v) {
var sa, ea;
v = this.angle(v);
if (this.o.flip) {
sa = this.endAngle + 0.00001;
ea = sa - v - 0.00001;
} else {
sa = this.startAngle - 0.00001;
ea = sa + v + 0.00001;
}
this.o.cursor
&& (sa = ea - this.cursorExt)
&& (ea = ea + this.cursorExt);
return {
s: sa,
e: ea,
d: this.o.flip && !this.o.cursor
};
};
this.draw = function () {
var c = this.g, // context
a = this.arc(this.cv) // Arc
, pa // Previous arc
, r = 1;
c.lineWidth = this.lineWidth;
c.lineCap = this.lineCap;
c.beginPath();
c.strokeStyle = this.o.bgColor;
c.arc(this.xy, this.xy, this.radius, this.endAngle - 0.00001, this.startAngle + 0.00001, true);
c.stroke();
if (this.o.displayPrevious) {
pa = this.arc(this.v);
c.beginPath();
c.strokeStyle = this.pColor;
c.arc(this.xy, this.xy, this.radius, pa.s, pa.e, pa.d);
c.stroke();
r = (this.cv == this.v);
}
c.beginPath();
c.strokeStyle = r ? this.o.fgColor : this.fgColor ;
c.arc(this.xy, this.xy, this.radius, a.s, a.e, a.d);
c.stroke();
};
this.cancel = function () {
this.val(this.v);
};
};
$.fn.dial = $.fn.knob = function (o) {
return this.each(
function () {
var d = new k.Dial();
d.o = o;
d.$ = $(this);
d.run();
}
).parent();
};
})(jQuery);

View File

@ -0,0 +1,37 @@
{
"name": "knob",
"title": "jQuery Knob",
"description": "Nice, downward compatible, touchable, jQuery dial.",
"keywords": [
"dial",
"button",
"knob",
"ui",
"input"
],
"version": "1.2.8",
"author": {
"name": "Anthony Terrien",
"url": "https://github.com/aterrien"
},
"maintainers": [
{
"name": "Anthony Terrien",
"email": "kontrol@anthonyterrien.com",
"url": "http://anthonyterrien.com/knob"
}
],
"licenses": [
{
"type": "MIT",
"url": "http://opensource.org/licenses/mit-license.php"
}
],
"bugs": "https://github.com/aterrien/jQuery-Knob/issues",
"homepage": "https://github.com/aterrien/jQuery-Knob",
"docs": "https://github.com/aterrien/jQuery-Knob",
"download": "https://github.com/aterrien/jQuery-Knob/tags",
"dependencies": {
"jquery": ">=1.7.0"
}
}