Total Complexity | 258 |
Complexity/F | 4.37 |
Lines of Code | 998 |
Function Count | 59 |
Duplicated Lines | 998 |
Ratio | 100 % |
Changes | 0 |
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:
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 | /** |
||
13 | View Code Duplication | var jscolor = { |
|
|
|||
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'); |
||
55 | for(var i=0; i<e.length; i+=1) { |
||
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] + ')'))(); |
||
83 | } catch(eInvalidProp) {} |
||
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(); |
||
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 = [ |
||
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)) { |
||
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 { |
||
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 | } |
||
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; |
||
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+"')"; |
||
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; |
||
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)); |
||
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 ]; |
||
845 | var i = Math.floor(THIS.hsv[0]); |
||
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) { |
||
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)+'%,'+ |
||
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; |
||
875 | } |
||
876 | var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.sld[1]-1)); |
||
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); |
||
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 |