├── audio ├── synth.mp3 └── guitar.mp3 ├── libs ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.svg ├── css │ └── bootstrap-theme.min.css └── js │ ├── jquery.knob.js │ └── bootstrap.min.js ├── README.md ├── LICENSE ├── css └── style.css ├── js ├── gui.js └── main.js └── index.html /audio/synth.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zya/granular/HEAD/audio/synth.mp3 -------------------------------------------------------------------------------- /audio/guitar.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zya/granular/HEAD/audio/guitar.mp3 -------------------------------------------------------------------------------- /libs/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zya/granular/HEAD/libs/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /libs/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zya/granular/HEAD/libs/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /libs/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zya/granular/HEAD/libs/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTML5 Granular Synthesiser 1.0 # 2 | 3 | The demo is a granular synthesiser with multi-touch support which uses Web Audio API for sound sampling and Processing.js for drawing and interactivity. 4 | 5 | Runs on: Chrome, Chrome iOS, Safari, Safari iOS, Firefox 6 | 7 | 8 | ----- 9 | Libraries used: 10 | 11 | Web Audio API : http://www.w3.org/TR/webaudio/ 12 | 13 | Processing.js : http://processingjs.org/ 14 | 15 | jQuery Knob : http://anthonyterrien.com/knob/ 16 | 17 | 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ehsan Ziya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | html{ 2 | width: 100%; 3 | height: 100%; 4 | padding: 0; 5 | margin: 0; 6 | overflow:hidden; 7 | } 8 | body{ 9 | width: 100%; 10 | height: 100%; 11 | padding: 0; 12 | margin: 0; 13 | 14 | 15 | } 16 | #header{ 17 | background: white; 18 | width: 100%; 19 | height: 5%; 20 | padding: 0; 21 | margin: 0; 22 | font-family: 'Open Sans', sans-serif; 23 | font-weight: 100; 24 | font-size: 1.3em; 25 | font-weight: 300; 26 | color: black; 27 | padding-top: 1vh; 28 | border-bottom: 1px solid white; 29 | } 30 | #waveform{ 31 | background: black; 32 | width: 100%; 33 | height: 71%; 34 | padding: 0; 35 | margin: 0; 36 | -moz-user-select: -moz-none; 37 | -khtml-user-select: none; 38 | -webkit-user-select: none; 39 | -o-user-select: none; 40 | user-select: none; 41 | 42 | 43 | 44 | 45 | } 46 | #controls{ 47 | border-top: 1px solid white; 48 | background: white; 49 | width: 100%; 50 | height: 23%; 51 | padding: 0; 52 | margin: 0; 53 | padding-top: 1.5%; 54 | text-align: center; 55 | z-index: 3; 56 | overflow: hidden; 57 | 58 | } 59 | 60 | #canvas{ 61 | 62 | position: absolute; 63 | -moz-user-select: -moz-none; 64 | -khtml-user-select: none; 65 | -webkit-user-select: none; 66 | -o-user-select: none; 67 | user-select: none; 68 | cursor: pointer; 69 | 70 | opacity: 0.0; 71 | } 72 | #canvas2{ 73 | position: absolute; 74 | -moz-user-select: -moz-none; 75 | -khtml-user-select: none; 76 | -webkit-user-select: none; 77 | -o-user-select: none; 78 | user-select: none; 79 | z-index: 5; 80 | 81 | 82 | 83 | } 84 | 85 | 86 | 87 | 88 | .label{ 89 | font-size: 1.3em; 90 | text-align: center; 91 | font-family: 'Open Sans', sans-serif; 92 | -webkit-font-smoothing: antialiased; 93 | font-weight: 300; 94 | color: black; 95 | margin-bottom: -15px; 96 | } 97 | 98 | 99 | 100 | #transopse{ 101 | width: 100%; 102 | margin: auto; 103 | display: inline; 104 | } 105 | 106 | #plus{ 107 | background-color: #2a6496; 108 | width: 48%; 109 | cursor: pointer; 110 | float: right; 111 | color: white; 112 | } 113 | #minus{ 114 | background-color: #2a6496; 115 | width: 48%; 116 | height: 40%; 117 | float: left; 118 | cursor: pointer; 119 | color: white; 120 | 121 | } 122 | 123 | #github{ 124 | text-align: right; 125 | } 126 | 127 | 128 | #helpbutton{ 129 | font-size: 2em; 130 | color:#2a6496; 131 | cursor: pointer; 132 | } 133 | 134 | #help{ 135 | width: 100%; 136 | height: 100%; 137 | background-color: black; 138 | z-index: 3; 139 | text-align: center; 140 | font-family: 'Open Sans', sans-serif; 141 | color:white; 142 | z-index: 3; 143 | } 144 | 145 | #drop{ 146 | width: 50%; 147 | height: 12%; 148 | border: 2px dotted white; 149 | margin: auto; 150 | font-size: 1.5em; 151 | margin-top:4%; 152 | } 153 | 154 | .sample{ 155 | background-color: white; 156 | border-right: black 2px solid; 157 | border-left: black 2px solid; 158 | color: black; 159 | padding-top: 0.5em; 160 | padding-bottom: 0.5em; 161 | cursor: pointer; 162 | } 163 | 164 | #x{ 165 | font-size: 0.8em; 166 | color: #888888; 167 | margin-bottom: -40px; 168 | } 169 | 170 | #description{ 171 | text-align: justify; 172 | margin:0; 173 | padding:0; 174 | font-size: 0.85em; 175 | font-weight: 100; 176 | } 177 | 178 | #badge{ 179 | float: right; 180 | position: absolute; 181 | right: 15px; 182 | } 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /js/gui.js: -------------------------------------------------------------------------------- 1 | function guiinit(){ 2 | var dialwidth = parseInt($('.col-sm-2').css('width')) - ( parseInt($('.col-sm-2').css('width')) / 6); 3 | 4 | var settings = { 5 | 'min':0, 6 | 'max':100, 7 | 'width' : dialwidth, 8 | "displayInput" : false, 9 | "angleArc" : 180, 10 | "angleOffset" : -90 11 | }; 12 | 13 | var bg = '#E4E4E4'; 14 | var fg = '#2a6496'; 15 | $("#attack").knob({ 16 | 'min':1, 17 | 'max':100, 18 | 'width' : dialwidth, 19 | "displayInput" : false, 20 | "val": 50, 21 | "angleArc" : 180, 22 | "angleOffset" : -90, 23 | 'bgColor': bg, 24 | 'fgColor': fg, 25 | "change": function(v){ 26 | attack = v / 100; 27 | 28 | } 29 | }); 30 | 31 | $("#release").knob({ 32 | 'min':1, 33 | 'max':100, 34 | 'width' : dialwidth, 35 | "displayInput" : false, 36 | "val": 50, 37 | "angleArc" : 180, 38 | "angleOffset" : -90, 39 | 'bgColor': bg, 40 | 'fgColor': fg, 41 | "change": function(v){ 42 | release = v / 100; 43 | 44 | } 45 | }); 46 | $('#density').knob({ 47 | 'min':0, 48 | 'max':100, 49 | 'width' : dialwidth, 50 | "displayInput" : false, 51 | "val": 50, 52 | "angleArc" : 180, 53 | "angleOffset" : -90, 54 | 'bgColor': bg, 55 | 'fgColor': fg, 56 | "change": function(v){ 57 | density = v / 100; 58 | 59 | } 60 | }); 61 | $('#spread').knob({ 62 | 'min':0, 63 | 'max':200, 64 | 'width' : dialwidth, 65 | "displayInput" : false, 66 | "val": 50, 67 | "angleArc" : 180, 68 | "angleOffset" : -90, 69 | 'bgColor': bg, 70 | 'fgColor': fg, 71 | "change": function(v){ 72 | spread = v / 100; 73 | 74 | 75 | } 76 | }); 77 | $('#pan').knob({ 78 | 'min':0, 79 | 'max':200, 80 | 'width' : dialwidth, 81 | "displayInput" : false, 82 | "val": 50, 83 | "angleArc" : 180, 84 | "angleOffset" : -90, 85 | 'bgColor': bg, 86 | 'fgColor': fg, 87 | "change": function(v){ 88 | pan = v / 100; 89 | 90 | 91 | } 92 | }); 93 | 94 | $('#minus').click(function(){ 95 | trans = trans * 0.5; 96 | $('#minus').css('opacity',0.3); 97 | setTimeout(function(){ 98 | $('#minus').css('opacity',1); 99 | },200); 100 | }); 101 | 102 | $('#plus').click(function(){ 103 | trans = trans * 2; 104 | $('#plus').css('opacity',0.3); 105 | setTimeout(function(){ 106 | $('#plus').css('opacity',1); 107 | },200); 108 | }); 109 | 110 | var minus = document.getElementById('minus'); 111 | minus.addEventListener('touchstart',function(e){ 112 | e.preventDefault(); 113 | $('#minus').css('opacity',0.3); 114 | trans = trans * 0.5; 115 | }); 116 | minus.addEventListener('touchend',function(e){ 117 | e.preventDefault(); 118 | $('#minus').css('opacity',1); 119 | }); 120 | 121 | var plus = document.getElementById('plus'); 122 | plus.addEventListener('touchstart',function(e){ 123 | e.preventDefault(); 124 | $('#plus').css('opacity',0.3); 125 | trans = trans * 2; 126 | }); 127 | plus.addEventListener('touchend',function(e){ 128 | e.preventDefault(); 129 | $('#plus').css('opacity',1); 130 | }); 131 | 132 | 133 | function load(){ 134 | $('#canvas').show(); 135 | $('#canvas2').show(); 136 | 137 | $('#canvas').animate({ 138 | opacity : 1 139 | },1000); 140 | 141 | $('#canvas2').animate({ 142 | opacity : 1 143 | },1000); 144 | 145 | $('#help').animate({ 146 | opacity : 0 147 | },1000,function(){ 148 | $('#help').hide(); 149 | helpvisible = false; 150 | }); 151 | } 152 | 153 | $('#canvas2').hide(); 154 | $('#canvas').hide(); 155 | $('#helpbutton').click(function(){ 156 | if(helpvisible){ 157 | load(); 158 | helpvisible = false; 159 | 160 | }else{ 161 | //$('#help').css('opacity','0'); 162 | $('#canvas2').animate({ 163 | opacity:0.1 164 | },1000,function(){ 165 | $('#help').css('opacity',0); 166 | 167 | $('#canvas2').hide(); 168 | $('#help').animate({ 169 | opacity : 1 170 | },1000); 171 | 172 | $('#help').show(); 173 | 174 | }); 175 | 176 | $('#canvas').animate({ 177 | opacity:0.0 178 | },1000,function(){ 179 | $('#help').show(); 180 | $('#canvas').hide(); 181 | }); 182 | 183 | helpvisible = true; 184 | } 185 | 186 | }); 187 | 188 | $('.sample').hover(function(){ 189 | $(this).css('opacity','0.5'); 190 | },function(){ 191 | $(this).css('opacity','1'); 192 | }); 193 | 194 | 195 | $('#sample1').click(function(){ 196 | load(); 197 | }); 198 | 199 | $('#sample2').click(function(){ 200 | //loading the sound with XML HTTP REQUEST 201 | 202 | var request = new XMLHttpRequest(); 203 | request.open('GET','audio/synth.mp3',true); 204 | request.responseType = "arraybuffer"; 205 | request.onload = function(){ 206 | context.decodeAudioData(request.response,function(b){ 207 | buffer = b; //set the buffer 208 | data = buffer.getChannelData(0); 209 | isloaded = true; 210 | var canvas1 = document.getElementById('canvas'); 211 | //initialize the processing draw when the buffer is ready 212 | var processing = new Processing(canvas1,waveformdisplay); 213 | load(); 214 | 215 | },function(){ 216 | console.log('loading failed'); 217 | alert('loading failed'); 218 | 219 | }); 220 | }; 221 | request.send(); 222 | 223 | 224 | }); 225 | 226 | //drop 227 | var drop = document.getElementById('waveform'); 228 | 229 | drop.addEventListener("dragover",function(e){ 230 | //prevents from loading the file in a new page 231 | e.preventDefault(); 232 | },false); 233 | drop.addEventListener('drop',function(e){ 234 | e.preventDefault(); 235 | var file = e.dataTransfer.files[0]; 236 | var reader = new FileReader(); 237 | reader.onload = function(e){ 238 | var array = e.target.result; 239 | context.decodeAudioData(array,function(b){ 240 | 241 | buffer = b 242 | data = buffer.getChannelData(0); 243 | var canvas1 = document.getElementById('canvas'); 244 | var processing = new Processing(canvas1,waveformdisplay); 245 | load(); 246 | 247 | },function(){ 248 | console.log('loading failed'); 249 | alert('loading failed'); 250 | }); 251 | } 252 | reader.readAsArrayBuffer(file); 253 | },false); 254 | 255 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
42 | * k.o.call(this);
43 | *
44 | */
45 | k.o = function () {
46 | var s = this;
47 |
48 | this.o = null; // array of options
49 | this.$ = null; // jQuery wrapped element
50 | this.i = null; // mixed HTMLInputElement or array of HTMLInputElement
51 | this.g = null; // 2D graphics context for 'pre-rendering'
52 | this.v = null; // value ; mixed array or integer
53 | this.cv = null; // change value ; not commited value
54 | this.x = 0; // canvas x position
55 | this.y = 0; // canvas y position
56 | this.$c = null; // jQuery canvas element
57 | this.c = null; // rendered canvas context
58 | this.t = 0; // touches index
59 | this.isInit = false;
60 | this.fgColor = null; // main color
61 | this.pColor = null; // previous color
62 | this.dH = null; // draw hook
63 | this.cH = null; // change hook
64 | this.eH = null; // cancel hook
65 | this.rH = null; // release hook
66 |
67 | this.run = function () {
68 | var cf = function (e, conf) {
69 | var k;
70 | for (k in conf) {
71 | s.o[k] = conf[k];
72 | }
73 | s.init();
74 | s._configure()
75 | ._draw();
76 | };
77 |
78 | if(this.$.data('kontroled')) return;
79 | this.$.data('kontroled', true);
80 |
81 | this.extend();
82 | this.o = $.extend(
83 | {
84 | // Config
85 | min : this.$.data('min') || 0,
86 | max : this.$.data('max') || 100,
87 | stopper : true,
88 | readOnly : this.$.data('readonly'),
89 |
90 | // UI
91 | cursor : (this.$.data('cursor') === true && 30)
92 | || this.$.data('cursor')
93 | || 0,
94 | thickness : this.$.data('thickness') || 0.35,
95 | lineCap : this.$.data('linecap') || 'butt',
96 | width : this.$.data('width') || 200,
97 | height : this.$.data('height') || 200,
98 | displayInput : this.$.data('displayinput') == null || this.$.data('displayinput'),
99 | displayPrevious : this.$.data('displayprevious'),
100 | fgColor : this.$.data('fgcolor') || '#87CEEB',
101 | inputColor: this.$.data('inputcolor') || this.$.data('fgcolor') || '#87CEEB',
102 | inline : false,
103 | step : this.$.data('step') || 1,
104 |
105 | // Hooks
106 | draw : null, // function () {}
107 | change : null, // function (value) {}
108 | cancel : null, // function () {}
109 | release : null // function (value) {}
110 | }, this.o
111 | );
112 |
113 | // routing value
114 | if(this.$.is('fieldset')) {
115 |
116 | // fieldset = array of integer
117 | this.v = {};
118 | this.i = this.$.find('input')
119 | this.i.each(function(k) {
120 | var $this = $(this);
121 | s.i[k] = $this;
122 | s.v[k] = $this.val();
123 |
124 | $this.bind(
125 | 'change'
126 | , function () {
127 | var val = {};
128 | val[k] = $this.val();
129 | s.val(val);
130 | }
131 | );
132 | });
133 | this.$.find('legend').remove();
134 |
135 | } else {
136 | // input = integer
137 | this.i = this.$;
138 | this.v = this.$.val();
139 | (this.v == '') && (this.v = this.o.min);
140 |
141 | this.$.bind(
142 | 'change'
143 | , function () {
144 | s.val(s._validate(s.$.val()));
145 | }
146 | );
147 | }
148 |
149 | (!this.o.displayInput) && this.$.hide();
150 |
151 | this.$c = $('');
154 | this.c = this.$c[0].getContext("2d");
155 |
156 | this.$
157 | .wrap($(''))
160 | .before(this.$c);
161 |
162 | if (this.v instanceof Object) {
163 | this.cv = {};
164 | this.copy(this.v, this.cv);
165 | } else {
166 | this.cv = this.v;
167 | }
168 |
169 | this.$
170 | .bind("configure", cf)
171 | .parent()
172 | .bind("configure", cf);
173 |
174 | this._listen()
175 | ._configure()
176 | ._xy()
177 | .init();
178 |
179 | this.isInit = true;
180 |
181 | this._draw();
182 |
183 | return this;
184 | };
185 |
186 | this._draw = function () {
187 |
188 | // canvas pre-rendering
189 | var d = true,
190 | c = document.createElement('canvas');
191 |
192 | c.width = s.o.width;
193 | c.height = s.o.height;
194 | s.g = c.getContext('2d');
195 |
196 | s.clear();
197 |
198 | s.dH
199 | && (d = s.dH());
200 |
201 | (d !== false) && s.draw();
202 |
203 | s.c.drawImage(c, 0, 0);
204 | c = null;
205 | };
206 |
207 | this._touch = function (e) {
208 |
209 | var touchMove = function (e) {
210 |
211 | var v = s.xy2val(
212 | e.originalEvent.touches[s.t].pageX,
213 | e.originalEvent.touches[s.t].pageY
214 | );
215 |
216 | if (v == s.cv) return;
217 |
218 | if (
219 | s.cH
220 | && (s.cH(v) === false)
221 | ) return;
222 |
223 |
224 | s.change(s._validate(v));
225 | s._draw();
226 | };
227 |
228 | // get touches index
229 | this.t = k.c.t(e);
230 |
231 | // First touch
232 | touchMove(e);
233 |
234 | // Touch events listeners
235 | k.c.d
236 | .bind("touchmove.k", touchMove)
237 | .bind(
238 | "touchend.k"
239 | , function () {
240 | k.c.d.unbind('touchmove.k touchend.k');
241 |
242 | if (
243 | s.rH
244 | && (s.rH(s.cv) === false)
245 | ) return;
246 |
247 | s.val(s.cv);
248 | }
249 | );
250 |
251 | return this;
252 | };
253 |
254 | this._mouse = function (e) {
255 |
256 | var mouseMove = function (e) {
257 | var v = s.xy2val(e.pageX, e.pageY);
258 | if (v == s.cv) return;
259 |
260 | if (
261 | s.cH
262 | && (s.cH(v) === false)
263 | ) return;
264 |
265 | s.change(s._validate(v));
266 | s._draw();
267 | };
268 |
269 | // First click
270 | mouseMove(e);
271 |
272 | // Mouse events listeners
273 | k.c.d
274 | .bind("mousemove.k", mouseMove)
275 | .bind(
276 | // Escape key cancel current change
277 | "keyup.k"
278 | , function (e) {
279 | if (e.keyCode === 27) {
280 | k.c.d.unbind("mouseup.k mousemove.k keyup.k");
281 |
282 | if (
283 | s.eH
284 | && (s.eH() === false)
285 | ) return;
286 |
287 | s.cancel();
288 | }
289 | }
290 | )
291 | .bind(
292 | "mouseup.k"
293 | , function (e) {
294 | k.c.d.unbind('mousemove.k mouseup.k keyup.k');
295 |
296 | if (
297 | s.rH
298 | && (s.rH(s.cv) === false)
299 | ) return;
300 |
301 | s.val(s.cv);
302 | }
303 | );
304 |
305 | return this;
306 | };
307 |
308 | this._xy = function () {
309 | var o = this.$c.offset();
310 | this.x = o.left;
311 | this.y = o.top;
312 | return this;
313 | };
314 |
315 | this._listen = function () {
316 |
317 | if (!this.o.readOnly) {
318 | this.$c
319 | .bind(
320 | "mousedown"
321 | , function (e) {
322 | e.preventDefault();
323 | s._xy()._mouse(e);
324 | }
325 | )
326 | .bind(
327 | "touchstart"
328 | , function (e) {
329 | e.preventDefault();
330 | s._xy()._touch(e);
331 | }
332 | );
333 | this.listen();
334 | } else {
335 | this.$.attr('readonly', 'readonly');
336 | }
337 |
338 | return this;
339 | };
340 |
341 | this._configure = function () {
342 |
343 | // Hooks
344 | if (this.o.draw) this.dH = this.o.draw;
345 | if (this.o.change) this.cH = this.o.change;
346 | if (this.o.cancel) this.eH = this.o.cancel;
347 | if (this.o.release) this.rH = this.o.release;
348 |
349 | if (this.o.displayPrevious) {
350 | this.pColor = this.h2rgba(this.o.fgColor, "0.4");
351 | this.fgColor = this.h2rgba(this.o.fgColor, "0.6");
352 | } else {
353 | this.fgColor = this.o.fgColor;
354 | }
355 |
356 | return this;
357 | };
358 |
359 | this._clear = function () {
360 | this.$c[0].width = this.$c[0].width;
361 | };
362 |
363 | this._validate = function(v) {
364 | return (~~ (((v < 0) ? -0.5 : 0.5) + (v/this.o.step))) * this.o.step;
365 | };
366 |
367 | // Abstract methods
368 | this.listen = function () {}; // on start, one time
369 | this.extend = function () {}; // each time configure triggered
370 | this.init = function () {}; // each time configure triggered
371 | this.change = function (v) {}; // on change
372 | this.val = function (v) {}; // on release
373 | this.xy2val = function (x, y) {}; //
374 | this.draw = function () {}; // on change / on release
375 | this.clear = function () { this._clear(); };
376 |
377 | // Utils
378 | this.h2rgba = function (h, a) {
379 | var rgb;
380 | h = h.substring(1,7)
381 | rgb = [parseInt(h.substring(0,2),16)
382 | ,parseInt(h.substring(2,4),16)
383 | ,parseInt(h.substring(4,6),16)];
384 | return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")";
385 | };
386 |
387 | this.copy = function (f, t) {
388 | for (var i in f) { t[i] = f[i]; }
389 | };
390 | };
391 |
392 |
393 | /**
394 | * k.Dial
395 | */
396 | k.Dial = function () {
397 | k.o.call(this);
398 |
399 | this.startAngle = null;
400 | this.xy = null;
401 | this.radius = null;
402 | this.lineWidth = null;
403 | this.cursorExt = null;
404 | this.w2 = null;
405 | this.PI2 = 2*Math.PI;
406 |
407 | this.extend = function () {
408 | this.o = $.extend(
409 | {
410 | bgColor : this.$.data('bgcolor') || '#EEEEEE',
411 | angleOffset : this.$.data('angleoffset') || 0,
412 | angleArc : this.$.data('anglearc') || 360,
413 | inline : true
414 | }, this.o
415 | );
416 | };
417 |
418 | this.val = function (v) {
419 | if (null != v) {
420 | this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;
421 | this.v = this.cv;
422 | this.$.val(this.v);
423 | this._draw();
424 | } else {
425 | return this.v;
426 | }
427 | };
428 |
429 | this.xy2val = function (x, y) {
430 | var a, ret;
431 |
432 | a = Math.atan2(
433 | x - (this.x + this.w2)
434 | , - (y - this.y - this.w2)
435 | ) - this.angleOffset;
436 |
437 | if(this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {
438 | // if isset angleArc option, set to min if .5 under min
439 | a = 0;
440 | } else if (a < 0) {
441 | a += this.PI2;
442 | }
443 |
444 | ret = ~~ (0.5 + (a * (this.o.max - this.o.min) / this.angleArc))
445 | + this.o.min;
446 |
447 | this.o.stopper
448 | && (ret = max(min(ret, this.o.max), this.o.min));
449 |
450 | return ret;
451 | };
452 |
453 | this.listen = function () {
454 | // bind MouseWheel
455 | var s = this,
456 | mw = function (e) {
457 | e.preventDefault();
458 | var ori = e.originalEvent
459 | ,deltaX = ori.detail || ori.wheelDeltaX
460 | ,deltaY = ori.detail || ori.wheelDeltaY
461 | ,v = parseInt(s.$.val()) + (deltaX>0 || deltaY>0 ? s.o.step : deltaX<0 || deltaY<0 ? -s.o.step : 0);
462 |
463 | if (
464 | s.cH
465 | && (s.cH(v) === false)
466 | ) return;
467 |
468 | s.val(v);
469 | }
470 | , kval, to, m = 1, kv = {37:-s.o.step, 38:s.o.step, 39:s.o.step, 40:-s.o.step};
471 |
472 | this.$
473 | .bind(
474 | "keydown"
475 | ,function (e) {
476 | var kc = e.keyCode;
477 |
478 | // numpad support
479 | if(kc >= 96 && kc <= 105) {
480 | kc = e.keyCode = kc - 48;
481 | }
482 |
483 | kval = parseInt(String.fromCharCode(kc));
484 |
485 | if (isNaN(kval)) {
486 |
487 | (kc !== 13) // enter
488 | && (kc !== 8) // bs
489 | && (kc !== 9) // tab
490 | && (kc !== 189) // -
491 | && e.preventDefault();
492 |
493 | // arrows
494 | if ($.inArray(kc,[37,38,39,40]) > -1) {
495 | e.preventDefault();
496 |
497 | var v = parseInt(s.$.val()) + kv[kc] * m;
498 |
499 | s.o.stopper
500 | && (v = max(min(v, s.o.max), s.o.min));
501 |
502 | s.change(v);
503 | s._draw();
504 |
505 | // long time keydown speed-up
506 | to = window.setTimeout(
507 | function () { m*=2; }
508 | ,30
509 | );
510 | }
511 | }
512 | }
513 | )
514 | .bind(
515 | "keyup"
516 | ,function (e) {
517 | if (isNaN(kval)) {
518 | if (to) {
519 | window.clearTimeout(to);
520 | to = null;
521 | m = 1;
522 | s.val(s.$.val());
523 | }
524 | } else {
525 | // kval postcond
526 | (s.$.val() > s.o.max && s.$.val(s.o.max))
527 | || (s.$.val() < s.o.min && s.$.val(s.o.min));
528 | }
529 |
530 | }
531 | );
532 |
533 | this.$c.bind("mousewheel DOMMouseScroll", mw);
534 | this.$.bind("mousewheel DOMMouseScroll", mw)
535 | };
536 |
537 | this.init = function () {
538 |
539 | if (
540 | this.v < this.o.min
541 | || this.v > this.o.max
542 | ) this.v = this.o.min;
543 |
544 | this.$.val(this.v);
545 | this.w2 = this.o.width / 2;
546 | this.cursorExt = this.o.cursor / 100;
547 | this.xy = this.w2;
548 | this.lineWidth = this.xy * this.o.thickness;
549 | this.lineCap = this.o.lineCap;
550 | this.radius = this.xy - this.lineWidth / 2;
551 |
552 | this.o.angleOffset
553 | && (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);
554 |
555 | this.o.angleArc
556 | && (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);
557 |
558 | // deg to rad
559 | this.angleOffset = this.o.angleOffset * Math.PI / 180;
560 | this.angleArc = this.o.angleArc * Math.PI / 180;
561 |
562 | // compute start and end angles
563 | this.startAngle = 1.5 * Math.PI + this.angleOffset;
564 | this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;
565 |
566 | var s = max(
567 | String(Math.abs(this.o.max)).length
568 | , String(Math.abs(this.o.min)).length
569 | , 2
570 | ) + 2;
571 |
572 | this.o.displayInput
573 | && this.i.css({
574 | 'width' : ((this.o.width / 2 + 4) >> 0) + 'px'
575 | ,'height' : ((this.o.width / 3) >> 0) + 'px'
576 | ,'position' : 'absolute'
577 | ,'vertical-align' : 'middle'
578 | ,'margin-top' : ((this.o.width / 3) >> 0) + 'px'
579 | ,'margin-left' : '-' + ((this.o.width * 3 / 4 + 2) >> 0) + 'px'
580 | ,'border' : 0
581 | ,'background' : 'none'
582 | ,'font' : 'bold ' + ((this.o.width / s) >> 0) + 'px Arial'
583 | ,'text-align' : 'center'
584 | ,'color' : this.o.inputColor || this.o.fgColor
585 | ,'padding' : '0px'
586 | ,'-webkit-appearance': 'none'
587 | })
588 | || this.i.css({
589 | 'width' : '0px'
590 | ,'visibility' : 'hidden'
591 | });
592 | };
593 |
594 | this.change = function (v) {
595 | this.cv = v;
596 | this.$.val(v);
597 | };
598 |
599 | this.angle = function (v) {
600 | return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);
601 | };
602 |
603 | this.draw = function () {
604 |
605 | var c = this.g, // context
606 | a = this.angle(this.cv) // Angle
607 | , sat = this.startAngle // Start angle
608 | , eat = sat + a // End angle
609 | , sa, ea // Previous angles
610 | , r = 1;
611 |
612 | c.lineWidth = this.lineWidth;
613 |
614 | c.lineCap = this.lineCap;
615 |
616 | this.o.cursor
617 | && (sat = eat - this.cursorExt)
618 | && (eat = eat + this.cursorExt);
619 |
620 | c.beginPath();
621 | c.strokeStyle = this.o.bgColor;
622 | c.arc(this.xy, this.xy, this.radius, this.endAngle - 0.00001, this.startAngle + 0.00001, true);
623 | c.stroke();
624 |
625 | if (this.o.displayPrevious) {
626 | ea = this.startAngle + this.angle(this.v);
627 | sa = this.startAngle;
628 | this.o.cursor
629 | && (sa = ea - this.cursorExt)
630 | && (ea = ea + this.cursorExt);
631 |
632 | c.beginPath();
633 | c.strokeStyle = this.pColor;
634 | c.arc(this.xy, this.xy, this.radius, sa, ea, false);
635 | c.stroke();
636 | r = (this.cv == this.v);
637 | }
638 |
639 | c.beginPath();
640 | c.strokeStyle = r ? this.o.fgColor : this.fgColor ;
641 | c.arc(this.xy, this.xy, this.radius, sat, eat, false);
642 | c.stroke();
643 | };
644 |
645 | this.cancel = function () {
646 | this.val(this.v);
647 | };
648 | };
649 |
650 | $.fn.dial = $.fn.knob = function (o) {
651 | return this.each(
652 | function () {
653 | var d = new k.Dial();
654 | d.o = o;
655 | d.$ = $(this);
656 | d.run();
657 | }
658 | ).parent();
659 | };
660 |
661 | })(jQuery);
--------------------------------------------------------------------------------
/libs/js/bootstrap.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Bootstrap v3.0.3 (http://getbootstrap.com)
3 | * Copyright 2013 Twitter, Inc.
4 | * Licensed under http://www.apache.org/licenses/LICENSE-2.0
5 | */
6 |
7 | if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a('').insertAfter(a(this)).on("click",b),f.trigger(d=a.Event("show.bs.dropdown")),d.isDefaultPrevented())return;f.toggleClass("open").trigger("shown.bs.dropdown"),e.focus()}return!1}},f.prototype.keydown=function(b){if(/(38|40|27)/.test(b.keyCode)){var d=a(this);if(b.preventDefault(),b.stopPropagation(),!d.is(".disabled, :disabled")){var f=c(d),g=f.hasClass("open");if(!g||g&&27==b.keyCode)return 27==b.which&&f.find(e).focus(),d.click();var h=a("[role=menu] li:not(.divider):visible a",f);if(h.length){var i=h.index(h.filter(":focus"));38==b.keyCode&&i>0&&i--,40==b.keyCode&&i