config/js/jscolor.js   F
last analyzed

Complexity

Total Complexity 258
Complexity/F 4.37

Size

Lines of Code 998
Function Count 59

Duplication

Duplicated Lines 998
Ratio 100 %

Importance

Changes 0
Metric Value
cc 0
eloc 682
nc 1
dl 998
loc 998
rs 1.918
c 0
b 0
f 0
wmc 258
mnd 5
bc 186
fnc 59
bpm 3.1525
cpm 4.3728
noi 20

18 Functions

Rating   Name   Duplication   Size   Complexity  
A jscolor.install 3 3 1
A jscolor.init 8 8 3
A jscolor.loadImage 6 6 2
D jscolor.color 676 676 10
A jscolor.getElementSize 3 3 1
B jscolor.bind 20 20 8
A jscolor.getRelMousePos 12 12 4
A jscolor.preload 7 7 3
B jscolor.getViewSize 11 11 8
B jscolor.URI 105 105 2
A jscolor.fireEvent 15 15 5
A jscolor.addEvent 7 7 3
B jscolor.detectDir 21 21 6
A jscolor.getElementPos 15 15 5
B jscolor.getViewPos 11 11 8
A jscolor.fetchElement 3 3 2
A jscolor.requireImage 3 3 1
A jscolor.getDir 7 7 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like config/js/jscolor.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * jscolor, JavaScript Color Picker
3
 *
4
 * @version 1.4.4
5
 * @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html
6
 * @author  Jan Odvarko, http://odvarko.cz
7
 * @created 2008-06-15
8
 * @updated 2014-12-09
9
 * @link    http://jscolor.com
10
 */
11
12
13 View Code Duplication
var jscolor = {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
14
15
16
	dir : '', // location of jscolor directory (leave empty to autodetect)
17
	bindClass : 'color', // class name
18
	binding : true, // automatic binding via <input class="...">
19
	preloading : true, // use image preloading?
20
21
22
	install : function() {
23
		jscolor.addEvent(window, 'load', jscolor.init);
24
	},
25
26
27
	init : function() {
28
		if(jscolor.binding) {
29
			jscolor.bind();
30
		}
31
		if(jscolor.preloading) {
32
			jscolor.preload();
33
		}
34
	},
35
36
37
	getDir : function() {
38
		if(!jscolor.dir) {
39
			var detected = jscolor.detectDir();
40
			jscolor.dir = detected!==false ? detected : 'jscolor/';
41
		}
42
		return jscolor.dir;
43
	},
44
45
46
	detectDir : function() {
47
		var base = location.href;
48
49
		var e = document.getElementsByTagName('base');
50
		for(var i=0; i<e.length; i+=1) {
51
			if(e[i].href) { base = e[i].href; }
52
		}
53
54
		var e = document.getElementsByTagName('script');
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable e already seems to be declared on line 49. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
55
		for(var i=0; i<e.length; i+=1) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 50. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
56
			if(e[i].src && /(^|\/)jscolor\.js([?#].*)?$/i.test(e[i].src)) {
57
				var src = new jscolor.URI(e[i].src);
58
				var srcAbs = src.toAbsolute(base);
59
				srcAbs.path = srcAbs.path.replace(/[^\/]+$/, ''); // remove filename
60
				srcAbs.query = null;
61
				srcAbs.fragment = null;
62
				return srcAbs.toString();
63
			}
64
		}
65
		return false;
66
	},
67
68
69
	bind : function() {
70
		var matchClass = new RegExp('(^|\\s)('+jscolor.bindClass+')(\\s*(\\{[^}]*\\})|\\s|$)', 'i');
71
		var e = document.getElementsByTagName('input');
72
		for(var i=0; i<e.length; i+=1) {
73
			if(jscolor.isColorAttrSupported && e[i].type.toLowerCase() == 'color') {
74
				// skip inputs of type 'color' if the browser supports this feature
75
				continue;
76
			}
77
			var m;
78
			if(!e[i].color && e[i].className && (m = e[i].className.match(matchClass))) {
79
				var prop = {};
80
				if(m[4]) {
81
					try {
82
						prop = (new Function ('return (' + m[4] + ')'))();
0 ignored issues
show
Performance Best Practice introduced by
Using new Function() to create a function is slow and difficult to debug. Such functions do not create a closure. Consider using another way to define your function.
Loading history...
83
					} catch(eInvalidProp) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
84
				}
85
				e[i].color = new jscolor.color(e[i], prop);
86
			}
87
		}
88
	},
89
90
91
	preload : function() {
92
		for(var fn in jscolor.imgRequire) {
93
			if(jscolor.imgRequire.hasOwnProperty(fn)) {
94
				jscolor.loadImage(fn);
95
			}
96
		}
97
	},
98
99
100
	images : {
101
		pad : [ 181, 101 ],
102
		sld : [ 16, 101 ],
103
		cross : [ 15, 15 ],
104
		arrow : [ 7, 11 ]
105
	},
106
107
108
	imgRequire : {},
109
	imgLoaded : {},
110
111
112
	requireImage : function(filename) {
113
		jscolor.imgRequire[filename] = true;
114
	},
115
116
117
	loadImage : function(filename) {
118
		if(!jscolor.imgLoaded[filename]) {
119
			jscolor.imgLoaded[filename] = new Image();
120
			jscolor.imgLoaded[filename].src = jscolor.getDir()+filename;
121
		}
122
	},
123
124
125
	fetchElement : function(mixed) {
126
		return typeof mixed === 'string' ? document.getElementById(mixed) : mixed;
127
	},
128
129
130
	addEvent : function(el, evnt, func) {
131
		if(el.addEventListener) {
132
			el.addEventListener(evnt, func, false);
133
		} else if(el.attachEvent) {
134
			el.attachEvent('on'+evnt, func);
135
		}
136
	},
137
138
139
	fireEvent : function(el, evnt) {
140
		if(!el) {
141
			return;
142
		}
143
		if(document.createEvent) {
144
			var ev = document.createEvent('HTMLEvents');
145
			ev.initEvent(evnt, true, true);
146
			el.dispatchEvent(ev);
147
		} else if(document.createEventObject) {
148
			var ev = document.createEventObject();
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable ev already seems to be declared on line 144. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
149
			el.fireEvent('on'+evnt, ev);
150
		} else if(el['on'+evnt]) { // alternatively use the traditional event model (IE5)
151
			el['on'+evnt]();
152
		}
153
	},
154
155
156
	getElementPos : function(e) {
157
		var e1=e, e2=e;
158
		var x=0, y=0;
159
		if(e1.offsetParent) {
160
			do {
161
				x += e1.offsetLeft;
162
				y += e1.offsetTop;
163
			} while(e1 = e1.offsetParent);
164
		}
165
		while((e2 = e2.parentNode) && e2.nodeName.toUpperCase() !== 'BODY') {
166
			x -= e2.scrollLeft;
167
			y -= e2.scrollTop;
168
		}
169
		return [x, y];
170
	},
171
172
173
	getElementSize : function(e) {
174
		return [e.offsetWidth, e.offsetHeight];
175
	},
176
177
178
	getRelMousePos : function(e) {
179
		var x = 0, y = 0;
180
		if (!e) { e = window.event; }
181
		if (typeof e.offsetX === 'number') {
182
			x = e.offsetX;
183
			y = e.offsetY;
184
		} else if (typeof e.layerX === 'number') {
185
			x = e.layerX;
186
			y = e.layerY;
187
		}
188
		return { x: x, y: y };
189
	},
190
191
192
	getViewPos : function() {
193
		if(typeof window.pageYOffset === 'number') {
194
			return [window.pageXOffset, window.pageYOffset];
195
		} else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) {
196
			return [document.body.scrollLeft, document.body.scrollTop];
197
		} else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
198
			return [document.documentElement.scrollLeft, document.documentElement.scrollTop];
199
		} else {
200
			return [0, 0];
201
		}
202
	},
203
204
205
	getViewSize : function() {
206
		if(typeof window.innerWidth === 'number') {
207
			return [window.innerWidth, window.innerHeight];
208
		} else if(document.body && (document.body.clientWidth || document.body.clientHeight)) {
209
			return [document.body.clientWidth, document.body.clientHeight];
210
		} else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
211
			return [document.documentElement.clientWidth, document.documentElement.clientHeight];
212
		} else {
213
			return [0, 0];
214
		}
215
	},
216
217
218
	URI : function(uri) { // See RFC3986
219
220
		this.scheme = null;
221
		this.authority = null;
222
		this.path = '';
223
		this.query = null;
224
		this.fragment = null;
225
226
		this.parse = function(uri) {
227
			var m = uri.match(/^(([A-Za-z][0-9A-Za-z+.-]*)(:))?((\/\/)([^\/?#]*))?([^?#]*)((\?)([^#]*))?((#)(.*))?/);
228
			this.scheme = m[3] ? m[2] : null;
229
			this.authority = m[5] ? m[6] : null;
230
			this.path = m[7];
231
			this.query = m[9] ? m[10] : null;
232
			this.fragment = m[12] ? m[13] : null;
233
			return this;
234
		};
235
236
		this.toString = function() {
237
			var result = '';
238
			if(this.scheme !== null) { result = result + this.scheme + ':'; }
239
			if(this.authority !== null) { result = result + '//' + this.authority; }
240
			if(this.path !== null) { result = result + this.path; }
241
			if(this.query !== null) { result = result + '?' + this.query; }
242
			if(this.fragment !== null) { result = result + '#' + this.fragment; }
243
			return result;
244
		};
245
246
		this.toAbsolute = function(base) {
247
			var base = new jscolor.URI(base);
248
			var r = this;
249
			var t = new jscolor.URI;
250
251
			if(base.scheme === null) { return false; }
252
253
			if(r.scheme !== null && r.scheme.toLowerCase() === base.scheme.toLowerCase()) {
254
				r.scheme = null;
255
			}
256
257
			if(r.scheme !== null) {
258
				t.scheme = r.scheme;
259
				t.authority = r.authority;
260
				t.path = removeDotSegments(r.path);
261
				t.query = r.query;
262
			} else {
263
				if(r.authority !== null) {
264
					t.authority = r.authority;
265
					t.path = removeDotSegments(r.path);
266
					t.query = r.query;
267
				} else {
268
					if(r.path === '') {
269
						t.path = base.path;
270
						if(r.query !== null) {
271
							t.query = r.query;
272
						} else {
273
							t.query = base.query;
274
						}
275
					} else {
276
						if(r.path.substr(0,1) === '/') {
277
							t.path = removeDotSegments(r.path);
278
						} else {
279
							if(base.authority !== null && base.path === '') {
280
								t.path = '/'+r.path;
281
							} else {
282
								t.path = base.path.replace(/[^\/]+$/,'')+r.path;
283
							}
284
							t.path = removeDotSegments(t.path);
285
						}
286
						t.query = r.query;
287
					}
288
					t.authority = base.authority;
289
				}
290
				t.scheme = base.scheme;
291
			}
292
			t.fragment = r.fragment;
293
294
			return t;
295
		};
296
297
		function removeDotSegments(path) {
298
			var out = '';
299
			while(path) {
300
				if(path.substr(0,3)==='../' || path.substr(0,2)==='./') {
301
					path = path.replace(/^\.+/,'').substr(1);
302
				} else if(path.substr(0,3)==='/./' || path==='/.') {
303
					path = '/'+path.substr(3);
304
				} else if(path.substr(0,4)==='/../' || path==='/..') {
305
					path = '/'+path.substr(4);
306
					out = out.replace(/\/?[^\/]*$/, '');
307
				} else if(path==='.' || path==='..') {
308
					path = '';
309
				} else {
310
					var rm = path.match(/^\/?[^\/]*/)[0];
311
					path = path.substr(rm.length);
312
					out = out + rm;
313
				}
314
			}
315
			return out;
316
		}
317
318
		if(uri) {
319
			this.parse(uri);
320
		}
321
322
	},
323
324
325
	//
326
	// Usage example:
327
	// var myColor = new jscolor.color(myInputElement)
328
	//
329
330
	color : function(target, prop) {
331
332
333
		this.required = true; // refuse empty values?
334
		this.adjust = true; // adjust value to uniform notation?
335
		this.hash = false; // prefix color with # symbol?
336
		this.caps = true; // uppercase?
337
		this.slider = true; // show the value/saturation slider?
338
		this.valueElement = target; // value holder
339
		this.styleElement = target; // where to reflect current color
340
		this.onImmediateChange = null; // onchange callback (can be either string or function)
341
		this.hsv = [0, 0, 1]; // read-only  0-6, 0-1, 0-1
342
		this.rgb = [1, 1, 1]; // read-only  0-1, 0-1, 0-1
343
		this.minH = 0; // read-only  0-6
344
		this.maxH = 6; // read-only  0-6
345
		this.minS = 0; // read-only  0-1
346
		this.maxS = 1; // read-only  0-1
347
		this.minV = 0; // read-only  0-1
348
		this.maxV = 1; // read-only  0-1
349
350
		this.pickerOnfocus = true; // display picker on focus?
351
		this.pickerMode = 'HSV'; // HSV | HVS
352
		this.pickerPosition = 'bottom'; // left | right | top | bottom
353
		this.pickerSmartPosition = true; // automatically adjust picker position when necessary
354
		this.pickerButtonHeight = 20; // px
355
		this.pickerClosable = false;
356
		this.pickerCloseText = 'Close';
357
		this.pickerButtonColor = 'ButtonText'; // px
358
		this.pickerFace = 10; // px
359
		this.pickerFaceColor = 'ThreeDFace'; // CSS color
360
		this.pickerBorder = 1; // px
361
		this.pickerBorderColor = 'ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight'; // CSS color
362
		this.pickerInset = 1; // px
363
		this.pickerInsetColor = 'ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow'; // CSS color
364
		this.pickerZIndex = 10000;
365
366
367
		for(var p in prop) {
368
			if(prop.hasOwnProperty(p)) {
369
				this[p] = prop[p];
370
			}
371
		}
372
373
374
		this.hidePicker = function() {
375
			if(isPickerOwner()) {
376
				removePicker();
377
			}
378
		};
379
380
381
		this.showPicker = function() {
382
			if(!isPickerOwner()) {
383
				var tp = jscolor.getElementPos(target); // target pos
384
				var ts = jscolor.getElementSize(target); // target size
385
				var vp = jscolor.getViewPos(); // view pos
386
				var vs = jscolor.getViewSize(); // view size
387
				var ps = getPickerDims(this); // picker size
388
				var a, b, c;
389
				switch(this.pickerPosition.toLowerCase()) {
390
					case 'left': a=1; b=0; c=-1; break;
391
					case 'right':a=1; b=0; c=1; break;
392
					case 'top':  a=0; b=1; c=-1; break;
393
					default:     a=0; b=1; c=1; break;
394
				}
395
				var l = (ts[b]+ps[b])/2;
396
397
				// picker pos
398
				if (!this.pickerSmartPosition) {
399
					var pp = [
400
						tp[a],
401
						tp[b]+ts[b]-l+l*c
402
					];
403
				} else {
404
					var pp = [
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable pp already seems to be declared on line 399. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
405
						-vp[a]+tp[a]+ps[a] > vs[a] ?
406
							(-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) :
407
							tp[a],
408
						-vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ?
409
							(-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) :
410
							(tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c)
411
					];
412
				}
413
				drawPicker(pp[a], pp[b]);
414
			}
415
		};
416
417
418
		this.importColor = function() {
419
			if(!valueElement) {
420
				this.exportColor();
421
			} else {
422
				if(!this.adjust) {
423
					if(!this.fromString(valueElement.value, leaveValue)) {
424
						styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage;
425
						styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor;
426
						styleElement.style.color = styleElement.jscStyle.color;
427
						this.exportColor(leaveValue | leaveStyle);
428
					}
429
				} else if(!this.required && /^\s*$/.test(valueElement.value)) {
430
					valueElement.value = '';
431
					styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage;
432
					styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor;
433
					styleElement.style.color = styleElement.jscStyle.color;
434
					this.exportColor(leaveValue | leaveStyle);
435
436
				} else if(this.fromString(valueElement.value)) {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
437
					// OK
438
				} else {
439
					this.exportColor();
440
				}
441
			}
442
		};
443
444
445
		this.exportColor = function(flags) {
446
			if(!(flags & leaveValue) && valueElement) {
447
				var value = this.toString();
448
				if(this.caps) { value = value.toUpperCase(); }
449
				if(this.hash) { value = '#'+value; }
450
				valueElement.value = value;
451
			}
452
			if(!(flags & leaveStyle) && styleElement) {
453
				styleElement.style.backgroundImage = "none";
454
				styleElement.style.backgroundColor =
455
					'#'+this.toString();
456
				styleElement.style.color =
457
					0.213 * this.rgb[0] +
458
					0.715 * this.rgb[1] +
459
					0.072 * this.rgb[2]
460
					< 0.5 ? '#FFF' : '#000';
461
			}
462
			if(!(flags & leavePad) && isPickerOwner()) {
463
				redrawPad();
464
			}
465
			if(!(flags & leaveSld) && isPickerOwner()) {
466
				redrawSld();
467
			}
468
		};
469
470
471
		this.fromHSV = function(h, s, v, flags) { // null = don't change
472
			if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); }
473
			if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); }
474
			if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); }
475
476
			this.rgb = HSV_RGB(
477
				h===null ? this.hsv[0] : (this.hsv[0]=h),
478
				s===null ? this.hsv[1] : (this.hsv[1]=s),
479
				v===null ? this.hsv[2] : (this.hsv[2]=v)
480
			);
481
482
			this.exportColor(flags);
483
		};
484
485
486
		this.fromRGB = function(r, g, b, flags) { // null = don't change
487
			if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); }
488
			if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); }
489
			if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); }
490
491
			var hsv = RGB_HSV(
492
				r===null ? this.rgb[0] : r,
493
				g===null ? this.rgb[1] : g,
494
				b===null ? this.rgb[2] : b
495
			);
496
			if(hsv[0] !== null) {
497
				this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0]));
498
			}
499
			if(hsv[2] !== 0) {
500
				this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1]));
501
			}
502
			this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2]));
503
504
			// update RGB according to final HSV, as some values might be trimmed
505
			var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]);
506
			this.rgb[0] = rgb[0];
507
			this.rgb[1] = rgb[1];
508
			this.rgb[2] = rgb[2];
509
510
			this.exportColor(flags);
511
		};
512
513
514
		this.fromString = function(hex, flags) {
515
			var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i);
516
			if(!m) {
517
				return false;
518
			} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
519
				if(m[1].length === 6) { // 6-char notation
520
					this.fromRGB(
521
						parseInt(m[1].substr(0,2),16) / 255,
522
						parseInt(m[1].substr(2,2),16) / 255,
523
						parseInt(m[1].substr(4,2),16) / 255,
524
						flags
525
					);
526
				} else { // 3-char notation
527
					this.fromRGB(
528
						parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255,
529
						parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255,
530
						parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255,
531
						flags
532
					);
533
				}
534
				return true;
535
			}
536
		};
537
538
539
		this.toString = function() {
540
			return (
541
				(0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) +
542
				(0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) +
543
				(0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1)
544
			);
545
		};
546
547
548
		function RGB_HSV(r, g, b) {
549
			var n = Math.min(Math.min(r,g),b);
550
			var v = Math.max(Math.max(r,g),b);
551
			var m = v - n;
552
			if(m === 0) { return [ null, 0, v ]; }
553
			var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m);
554
			return [ h===6?0:h, m/v, v ];
555
		}
556
557
558
		function HSV_RGB(h, s, v) {
559
			if(h === null) { return [ v, v, v ]; }
560
			var i = Math.floor(h);
561
			var f = i%2 ? h-i : 1-(h-i);
562
			var m = v * (1 - s);
563
			var n = v * (1 - s*f);
564
			switch(i) {
565
				case 6:
566
				case 0: return [v,n,m];
567
				case 1: return [n,v,m];
568
				case 2: return [m,v,n];
569
				case 3: return [m,n,v];
570
				case 4: return [n,m,v];
571
				case 5: return [v,m,n];
572
			}
0 ignored issues
show
Comprehensibility introduced by
There is no default case in this switch, so nothing gets returned when all cases fail. You might want to consider adding a default or return undefined explicitly.
Loading history...
573
		}
574
575
576
		function removePicker() {
577
			delete jscolor.picker.owner;
578
			document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB);
579
		}
580
581
582
		function drawPicker(x, y) {
583
			if(!jscolor.picker) {
584
				jscolor.picker = {
585
					box : document.createElement('div'),
586
					boxB : document.createElement('div'),
587
					pad : document.createElement('div'),
588
					padB : document.createElement('div'),
589
					padM : document.createElement('div'),
590
					sld : document.createElement('div'),
591
					sldB : document.createElement('div'),
592
					sldM : document.createElement('div'),
593
					btn : document.createElement('div'),
594
					btnS : document.createElement('span'),
595
					btnT : document.createTextNode(THIS.pickerCloseText)
596
				};
597
				for(var i=0,segSize=4; i<jscolor.images.sld[1]; i+=segSize) {
598
					var seg = document.createElement('div');
599
					seg.style.height = segSize+'px';
600
					seg.style.fontSize = '1px';
601
					seg.style.lineHeight = '0';
602
					jscolor.picker.sld.appendChild(seg);
603
				}
604
				jscolor.picker.sldB.appendChild(jscolor.picker.sld);
605
				jscolor.picker.box.appendChild(jscolor.picker.sldB);
606
				jscolor.picker.box.appendChild(jscolor.picker.sldM);
607
				jscolor.picker.padB.appendChild(jscolor.picker.pad);
608
				jscolor.picker.box.appendChild(jscolor.picker.padB);
609
				jscolor.picker.box.appendChild(jscolor.picker.padM);
610
				jscolor.picker.btnS.appendChild(jscolor.picker.btnT);
611
				jscolor.picker.btn.appendChild(jscolor.picker.btnS);
612
				jscolor.picker.box.appendChild(jscolor.picker.btn);
613
				jscolor.picker.boxB.appendChild(jscolor.picker.box);
614
			}
615
616
			var p = jscolor.picker;
617
618
			// controls interaction
619
			p.box.onmouseup =
620
			p.box.onmouseout = function() { target.focus(); };
621
			p.box.onmousedown = function() { abortBlur=true; };
622
			p.box.onmousemove = function(e) {
623
				if (holdPad || holdSld) {
624
					holdPad && setPad(e);
625
					holdSld && setSld(e);
626
					if (document.selection) {
627
						document.selection.empty();
628
					} else if (window.getSelection) {
629
						window.getSelection().removeAllRanges();
630
					}
631
					dispatchImmediateChange();
632
				}
633
			};
634
			if('ontouchstart' in window) { // if touch device
635
				var handle_touchmove = function(e) {
636
					var event={
637
						'offsetX': e.touches[0].pageX-touchOffset.X,
638
						'offsetY': e.touches[0].pageY-touchOffset.Y
639
					};
640
					if (holdPad || holdSld) {
641
						holdPad && setPad(event);
642
						holdSld && setSld(event);
643
						dispatchImmediateChange();
644
					}
645
					e.stopPropagation(); // prevent move "view" on broswer
646
					e.preventDefault(); // prevent Default - Android Fix (else android generated only 1-2 touchmove events)
647
				};
648
				p.box.removeEventListener('touchmove', handle_touchmove, false)
649
				p.box.addEventListener('touchmove', handle_touchmove, false)
650
			}
651
			p.padM.onmouseup =
652
			p.padM.onmouseout = function() { if(holdPad) { holdPad=false; jscolor.fireEvent(valueElement,'change'); } };
653
			p.padM.onmousedown = function(e) {
654
				// if the slider is at the bottom, move it up
655
				switch(modeID) {
656
					case 0: if (THIS.hsv[2] === 0) { THIS.fromHSV(null, null, 1.0); }; break;
657
					case 1: if (THIS.hsv[1] === 0) { THIS.fromHSV(null, 1.0, null); }; break;
658
				}
659
				holdSld=false;
660
				holdPad=true;
661
				setPad(e);
662
				dispatchImmediateChange();
663
			};
664
			if('ontouchstart' in window) {
665
				p.padM.addEventListener('touchstart', function(e) {
666
					touchOffset={
667
						'X': e.target.offsetParent.offsetLeft,
668
						'Y': e.target.offsetParent.offsetTop
669
					};
670
					this.onmousedown({
671
						'offsetX':e.touches[0].pageX-touchOffset.X,
672
						'offsetY':e.touches[0].pageY-touchOffset.Y
673
					});
674
				});
675
			}
676
			p.sldM.onmouseup =
677
			p.sldM.onmouseout = function() { if(holdSld) { holdSld=false; jscolor.fireEvent(valueElement,'change'); } };
678
			p.sldM.onmousedown = function(e) {
679
				holdPad=false;
680
				holdSld=true;
681
				setSld(e);
682
				dispatchImmediateChange();
683
			};
684
			if('ontouchstart' in window) {
685
				p.sldM.addEventListener('touchstart', function(e) {
686
					touchOffset={
687
						'X': e.target.offsetParent.offsetLeft,
688
						'Y': e.target.offsetParent.offsetTop
689
					};
690
					this.onmousedown({
691
						'offsetX':e.touches[0].pageX-touchOffset.X,
692
						'offsetY':e.touches[0].pageY-touchOffset.Y
693
					});
694
				});
695
			}
696
697
			// picker
698
			var dims = getPickerDims(THIS);
699
			p.box.style.width = dims[0] + 'px';
700
			p.box.style.height = dims[1] + 'px';
701
702
			// picker border
703
			p.boxB.style.position = 'absolute';
704
			p.boxB.style.clear = 'both';
705
			p.boxB.style.left = x+'px';
706
			p.boxB.style.top = y+'px';
707
			p.boxB.style.zIndex = THIS.pickerZIndex;
708
			p.boxB.style.border = THIS.pickerBorder+'px solid';
709
			p.boxB.style.borderColor = THIS.pickerBorderColor;
710
			p.boxB.style.background = THIS.pickerFaceColor;
711
712
			// pad image
713
			p.pad.style.width = jscolor.images.pad[0]+'px';
714
			p.pad.style.height = jscolor.images.pad[1]+'px';
715
716
			// pad border
717
			p.padB.style.position = 'absolute';
718
			p.padB.style.left = THIS.pickerFace+'px';
719
			p.padB.style.top = THIS.pickerFace+'px';
720
			p.padB.style.border = THIS.pickerInset+'px solid';
721
			p.padB.style.borderColor = THIS.pickerInsetColor;
722
723
			// pad mouse area
724
			p.padM.style.position = 'absolute';
725
			p.padM.style.left = '0';
726
			p.padM.style.top = '0';
727
			p.padM.style.width = THIS.pickerFace + 2*THIS.pickerInset + jscolor.images.pad[0] + jscolor.images.arrow[0] + 'px';
728
			p.padM.style.height = p.box.style.height;
729
			p.padM.style.cursor = 'crosshair';
730
731
			// slider image
732
			p.sld.style.overflow = 'hidden';
733
			p.sld.style.width = jscolor.images.sld[0]+'px';
734
			p.sld.style.height = jscolor.images.sld[1]+'px';
735
736
			// slider border
737
			p.sldB.style.display = THIS.slider ? 'block' : 'none';
738
			p.sldB.style.position = 'absolute';
739
			p.sldB.style.right = THIS.pickerFace+'px';
740
			p.sldB.style.top = THIS.pickerFace+'px';
741
			p.sldB.style.border = THIS.pickerInset+'px solid';
742
			p.sldB.style.borderColor = THIS.pickerInsetColor;
743
744
			// slider mouse area
745
			p.sldM.style.display = THIS.slider ? 'block' : 'none';
746
			p.sldM.style.position = 'absolute';
747
			p.sldM.style.right = '0';
748
			p.sldM.style.top = '0';
749
			p.sldM.style.width = jscolor.images.sld[0] + jscolor.images.arrow[0] + THIS.pickerFace + 2*THIS.pickerInset + 'px';
750
			p.sldM.style.height = p.box.style.height;
751
			try {
752
				p.sldM.style.cursor = 'pointer';
753
			} catch(eOldIE) {
754
				p.sldM.style.cursor = 'hand';
755
			}
756
757
			// "close" button
758
			function setBtnBorder() {
759
				var insetColors = THIS.pickerInsetColor.split(/\s+/);
760
				var pickerOutsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1];
761
				p.btn.style.borderColor = pickerOutsetColor;
762
			}
763
			p.btn.style.display = THIS.pickerClosable ? 'block' : 'none';
764
			p.btn.style.position = 'absolute';
765
			p.btn.style.left = THIS.pickerFace + 'px';
766
			p.btn.style.bottom = THIS.pickerFace + 'px';
767
			p.btn.style.padding = '0 15px';
768
			p.btn.style.height = '18px';
769
			p.btn.style.border = THIS.pickerInset + 'px solid';
770
			setBtnBorder();
771
			p.btn.style.color = THIS.pickerButtonColor;
772
			p.btn.style.font = '12px sans-serif';
773
			p.btn.style.textAlign = 'center';
774
			try {
775
				p.btn.style.cursor = 'pointer';
776
			} catch(eOldIE) {
777
				p.btn.style.cursor = 'hand';
778
			}
779
			p.btn.onmousedown = function () {
780
				THIS.hidePicker();
781
			};
782
			p.btnS.style.lineHeight = p.btn.style.height;
783
784
			// load images in optimal order
785
			switch(modeID) {
786
				case 0: var padImg = 'hs.png'; break;
787
				case 1: var padImg = 'hv.png'; break;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable padImg already seems to be declared on line 786. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
788
			}
789
			p.padM.style.backgroundImage = "url('"+jscolor.getDir()+"cross.gif')";
790
			p.padM.style.backgroundRepeat = "no-repeat";
791
			p.sldM.style.backgroundImage = "url('"+jscolor.getDir()+"arrow.gif')";
792
			p.sldM.style.backgroundRepeat = "no-repeat";
793
			p.pad.style.backgroundImage = "url('"+jscolor.getDir()+padImg+"')";
0 ignored issues
show
Bug introduced by
The variable padImg seems to not be initialized for all possible execution paths.
Loading history...
794
			p.pad.style.backgroundRepeat = "no-repeat";
795
			p.pad.style.backgroundPosition = "0 0";
796
797
			// place pointers
798
			redrawPad();
799
			redrawSld();
800
801
			jscolor.picker.owner = THIS;
802
			document.getElementsByTagName('body')[0].appendChild(p.boxB);
803
		}
804
805
806
		function getPickerDims(o) {
807
			var dims = [
808
				2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[0] +
809
					(o.slider ? 2*o.pickerInset + 2*jscolor.images.arrow[0] + jscolor.images.sld[0] : 0),
810
				o.pickerClosable ?
811
					4*o.pickerInset + 3*o.pickerFace + jscolor.images.pad[1] + o.pickerButtonHeight :
812
					2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[1]
813
			];
814
			return dims;
815
		}
816
817
818
		function redrawPad() {
819
			// redraw the pad pointer
820
			switch(modeID) {
821
				case 0: var yComponent = 1; break;
822
				case 1: var yComponent = 2; break;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable yComponent already seems to be declared on line 821. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
823
			}
824
			var x = Math.round((THIS.hsv[0]/6) * (jscolor.images.pad[0]-1));
825
			var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.pad[1]-1));
0 ignored issues
show
Bug introduced by
The variable yComponent seems to not be initialized for all possible execution paths.
Loading history...
826
			jscolor.picker.padM.style.backgroundPosition =
827
				(THIS.pickerFace+THIS.pickerInset+x - Math.floor(jscolor.images.cross[0]/2)) + 'px ' +
828
				(THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.cross[1]/2)) + 'px';
829
830
			// redraw the slider image
831
			var seg = jscolor.picker.sld.childNodes;
832
833
			switch(modeID) {
834
				case 0:
835
					var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1);
836
					for(var i=0; i<seg.length; i+=1) {
837
						seg[i].style.backgroundColor = 'rgb('+
838
							(rgb[0]*(1-i/seg.length)*100)+'%,'+
839
							(rgb[1]*(1-i/seg.length)*100)+'%,'+
840
							(rgb[2]*(1-i/seg.length)*100)+'%)';
841
					}
842
					break;
843
				case 1:
844
					var rgb, s, c = [ THIS.hsv[2], 0, 0 ];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable rgb already seems to be declared on line 835. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
845
					var i = Math.floor(THIS.hsv[0]);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 836. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
846
					var f = i%2 ? THIS.hsv[0]-i : 1-(THIS.hsv[0]-i);
847
					switch(i) {
848
						case 6:
849
						case 0: rgb=[0,1,2]; break;
850
						case 1: rgb=[1,0,2]; break;
851
						case 2: rgb=[2,0,1]; break;
852
						case 3: rgb=[2,1,0]; break;
853
						case 4: rgb=[1,2,0]; break;
854
						case 5: rgb=[0,2,1]; break;
855
					}
856
					for(var i=0; i<seg.length; i+=1) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 836. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
857
						s = 1 - 1/(seg.length-1)*i;
858
						c[1] = c[0] * (1 - s*f);
859
						c[2] = c[0] * (1 - s);
860
						seg[i].style.backgroundColor = 'rgb('+
861
							(c[rgb[0]]*100)+'%,'+
0 ignored issues
show
Bug introduced by
The variable rgb seems to not be initialized for all possible execution paths.
Loading history...
862
							(c[rgb[1]]*100)+'%,'+
863
							(c[rgb[2]]*100)+'%)';
864
					}
865
					break;
866
			}
867
		}
868
869
870
		function redrawSld() {
871
			// redraw the slider pointer
872
			switch(modeID) {
873
				case 0: var yComponent = 2; break;
874
				case 1: var yComponent = 1; break;
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable yComponent already seems to be declared on line 873. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
875
			}
876
			var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.sld[1]-1));
0 ignored issues
show
Bug introduced by
The variable yComponent seems to not be initialized for all possible execution paths.
Loading history...
877
			jscolor.picker.sldM.style.backgroundPosition =
878
				'0 ' + (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.arrow[1]/2)) + 'px';
879
		}
880
881
882
		function isPickerOwner() {
883
			return jscolor.picker && jscolor.picker.owner === THIS;
884
		}
885
886
887
		function blurTarget() {
888
			if(valueElement === target) {
889
				THIS.importColor();
890
			}
891
			if(THIS.pickerOnfocus) {
892
				THIS.hidePicker();
893
			}
894
		}
895
896
897
		function blurValue() {
898
			if(valueElement !== target) {
899
				THIS.importColor();
900
			}
901
		}
902
903
904
		function setPad(e) {
905
			var mpos = jscolor.getRelMousePos(e);
906
			var x = mpos.x - THIS.pickerFace - THIS.pickerInset;
907
			var y = mpos.y - THIS.pickerFace - THIS.pickerInset;
908
			switch(modeID) {
909
				case 0: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), 1 - y/(jscolor.images.pad[1]-1), null, leaveSld); break;
910
				case 1: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), null, 1 - y/(jscolor.images.pad[1]-1), leaveSld); break;
911
			}
912
		}
913
914
915
		function setSld(e) {
916
			var mpos = jscolor.getRelMousePos(e);
917
			var y = mpos.y - THIS.pickerFace - THIS.pickerInset;
918
			switch(modeID) {
919
				case 0: THIS.fromHSV(null, null, 1 - y/(jscolor.images.sld[1]-1), leavePad); break;
920
				case 1: THIS.fromHSV(null, 1 - y/(jscolor.images.sld[1]-1), null, leavePad); break;
921
			}
922
		}
923
924
925
		function dispatchImmediateChange() {
926
			if (THIS.onImmediateChange) {
927
				var callback;
928
				if (typeof THIS.onImmediateChange === 'string') {
929
					callback = new Function (THIS.onImmediateChange);
0 ignored issues
show
Performance Best Practice introduced by
Using new Function() to create a function is slow and difficult to debug. Such functions do not create a closure. Consider using another way to define your function.
Loading history...
930
				} else {
931
					callback = THIS.onImmediateChange;
932
				}
933
				callback.call(THIS);
934
			}
935
		}
936
937
938
		var THIS = this;
939
		var modeID = this.pickerMode.toLowerCase()==='hvs' ? 1 : 0;
940
		var abortBlur = false;
941
		var
942
			valueElement = jscolor.fetchElement(this.valueElement),
943
			styleElement = jscolor.fetchElement(this.styleElement);
944
		var
945
			holdPad = false,
946
			holdSld = false,
947
			touchOffset = {};
948
		var
949
			leaveValue = 1<<0,
950
			leaveStyle = 1<<1,
951
			leavePad = 1<<2,
952
			leaveSld = 1<<3;
953
954
		jscolor.isColorAttrSupported = false;
955
		var el = document.createElement('input');
956
		if(el.setAttribute) {
957
			el.setAttribute('type', 'color');
958
			if(el.type.toLowerCase() == 'color') {
959
				jscolor.isColorAttrSupported = true;
960
			}
961
		}
962
963
		// target
964
		jscolor.addEvent(target, 'focus', function() {
965
			if(THIS.pickerOnfocus) { THIS.showPicker(); }
966
		});
967
		jscolor.addEvent(target, 'blur', function() {
968
			if(!abortBlur) {
969
				window.setTimeout(function(){ abortBlur || blurTarget(); abortBlur=false; }, 0);
970
			} else {
971
				abortBlur = false;
972
			}
973
		});
974
975
		// valueElement
976
		if(valueElement) {
977
			var updateField = function() {
978
				THIS.fromString(valueElement.value, leaveValue);
979
				dispatchImmediateChange();
980
			};
981
			jscolor.addEvent(valueElement, 'keyup', updateField);
982
			jscolor.addEvent(valueElement, 'input', updateField);
983
			jscolor.addEvent(valueElement, 'blur', blurValue);
984
			valueElement.setAttribute('autocomplete', 'off');
985
		}
986
987
		// styleElement
988
		if(styleElement) {
989
			styleElement.jscStyle = {
990
				backgroundImage : styleElement.style.backgroundImage,
991
				backgroundColor : styleElement.style.backgroundColor,
992
				color : styleElement.style.color
993
			};
994
		}
995
996
		// require images
997
		switch(modeID) {
998
			case 0: jscolor.requireImage('hs.png'); break;
999
			case 1: jscolor.requireImage('hv.png'); break;
1000
		}
1001
		jscolor.requireImage('cross.gif');
1002
		jscolor.requireImage('arrow.gif');
1003
1004
		this.importColor();
1005
	}
1006
1007
};
1008
1009
1010
jscolor.install();
1011