1/** 2 * jscolor, JavaScript Color Picker 3 * 4 * @version 1.4.5 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 2015-09-19 9 * @link http://jscolor.com 10 */ 11 12 13var 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.pickerFixedPosition = false; // set to true to stop picker from moving on scroll 355 this.pickerButtonHeight = 20; // px 356 this.pickerClosable = false; 357 this.pickerCloseText = 'Close'; 358 this.pickerButtonColor = 'ButtonText'; // px 359 this.pickerFace = 10; // px 360 this.pickerFaceColor = 'ThreeDFace'; // CSS color 361 this.pickerBorder = 1; // px 362 this.pickerBorderColor = 'ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight'; // CSS color 363 this.pickerInset = 1; // px 364 this.pickerInsetColor = 'ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow'; // CSS color 365 this.pickerZIndex = 10000; 366 367 368 for(var p in prop) { 369 if(prop.hasOwnProperty(p)) { 370 this[p] = prop[p]; 371 } 372 } 373 374 375 this.hidePicker = function() { 376 if(isPickerOwner()) { 377 removePicker(); 378 } 379 }; 380 381 382 this.showPicker = function() { 383 if(!isPickerOwner()) { 384 var tp = jscolor.getElementPos(target); // target pos 385 var ts = jscolor.getElementSize(target); // target size 386 var vp = jscolor.getViewPos(); // view pos 387 var vs = jscolor.getViewSize(); // view size 388 var ps = getPickerDims(this); // picker size 389 var a, b, c; 390 switch(this.pickerPosition.toLowerCase()) { 391 case 'left': a=1; b=0; c=-1; break; 392 case 'right':a=1; b=0; c=1; break; 393 case 'top': a=0; b=1; c=-1; break; 394 default: a=0; b=1; c=1; break; 395 } 396 var l = (ts[b]+ps[b])/2; 397 398 // picker pos 399 if (!this.pickerSmartPosition) { 400 var pp = [ 401 tp[a], 402 tp[b]+ts[b]-l+l*c 403 ]; 404 } else { 405 var pp = [ 406 -vp[a]+tp[a]+ps[a] > vs[a] ? 407 (-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]) : 408 tp[a], 409 -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? 410 (-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) : 411 (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) 412 ]; 413 } 414 drawPicker(pp[a], pp[b]); 415 } 416 }; 417 418 419 this.importColor = function() { 420 if(!valueElement) { 421 this.exportColor(); 422 } else { 423 if(!this.adjust) { 424 if(!this.fromString(valueElement.value, leaveValue)) { 425 styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; 426 styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; 427 styleElement.style.color = styleElement.jscStyle.color; 428 this.exportColor(leaveValue | leaveStyle); 429 } 430 } else if(!this.required && /^\s*$/.test(valueElement.value)) { 431 valueElement.value = ''; 432 styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; 433 styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; 434 styleElement.style.color = styleElement.jscStyle.color; 435 this.exportColor(leaveValue | leaveStyle); 436 437 } else if(this.fromString(valueElement.value)) { 438 // OK 439 } else { 440 this.exportColor(); 441 } 442 } 443 }; 444 445 446 this.exportColor = function(flags) { 447 if(!(flags & leaveValue) && valueElement) { 448 var value = this.toString(); 449 if(this.caps) { value = value.toUpperCase(); } 450 if(this.hash) { value = '#'+value; } 451 valueElement.value = value; 452 } 453 if(!(flags & leaveStyle) && styleElement) { 454 styleElement.style.backgroundImage = "none"; 455 styleElement.style.backgroundColor = 456 '#'+this.toString(); 457 styleElement.style.color = 458 0.213 * this.rgb[0] + 459 0.715 * this.rgb[1] + 460 0.072 * this.rgb[2] 461 < 0.5 ? '#FFF' : '#000'; 462 } 463 if(!(flags & leavePad) && isPickerOwner()) { 464 redrawPad(); 465 } 466 if(!(flags & leaveSld) && isPickerOwner()) { 467 redrawSld(); 468 } 469 }; 470 471 472 this.fromHSV = function(h, s, v, flags) { // null = don't change 473 if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); } 474 if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); } 475 if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); } 476 477 this.rgb = HSV_RGB( 478 h===null ? this.hsv[0] : (this.hsv[0]=h), 479 s===null ? this.hsv[1] : (this.hsv[1]=s), 480 v===null ? this.hsv[2] : (this.hsv[2]=v) 481 ); 482 483 this.exportColor(flags); 484 }; 485 486 487 this.fromRGB = function(r, g, b, flags) { // null = don't change 488 if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); } 489 if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); } 490 if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); } 491 492 var hsv = RGB_HSV( 493 r===null ? this.rgb[0] : r, 494 g===null ? this.rgb[1] : g, 495 b===null ? this.rgb[2] : b 496 ); 497 if(hsv[0] !== null) { 498 this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0])); 499 } 500 if(hsv[2] !== 0) { 501 this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1])); 502 } 503 this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2])); 504 505 // update RGB according to final HSV, as some values might be trimmed 506 var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); 507 this.rgb[0] = rgb[0]; 508 this.rgb[1] = rgb[1]; 509 this.rgb[2] = rgb[2]; 510 511 this.exportColor(flags); 512 }; 513 514 515 this.fromString = function(hex, flags) { 516 var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); 517 if(!m) { 518 return false; 519 } else { 520 if(m[1].length === 6) { // 6-char notation 521 this.fromRGB( 522 parseInt(m[1].substr(0,2),16) / 255, 523 parseInt(m[1].substr(2,2),16) / 255, 524 parseInt(m[1].substr(4,2),16) / 255, 525 flags 526 ); 527 } else { // 3-char notation 528 this.fromRGB( 529 parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, 530 parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, 531 parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, 532 flags 533 ); 534 } 535 return true; 536 } 537 }; 538 539 540 this.toString = function() { 541 return ( 542 (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + 543 (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + 544 (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) 545 ); 546 }; 547 548 549 function RGB_HSV(r, g, b) { 550 var n = Math.min(Math.min(r,g),b); 551 var v = Math.max(Math.max(r,g),b); 552 var m = v - n; 553 if(m === 0) { return [ null, 0, v ]; } 554 var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 555 return [ h===6?0:h, m/v, v ]; 556 } 557 558 559 function HSV_RGB(h, s, v) { 560 if(h === null) { return [ v, v, v ]; } 561 var i = Math.floor(h); 562 var f = i%2 ? h-i : 1-(h-i); 563 var m = v * (1 - s); 564 var n = v * (1 - s*f); 565 switch(i) { 566 case 6: 567 case 0: return [v,n,m]; 568 case 1: return [n,v,m]; 569 case 2: return [m,v,n]; 570 case 3: return [m,n,v]; 571 case 4: return [n,m,v]; 572 case 5: return [v,m,n]; 573 } 574 } 575 576 577 function removePicker() { 578 delete jscolor.picker.owner; 579 document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); 580 } 581 582 583 function drawPicker(x, y) { 584 if(!jscolor.picker) { 585 jscolor.picker = { 586 box : document.createElement('div'), 587 boxB : document.createElement('div'), 588 pad : document.createElement('div'), 589 padB : document.createElement('div'), 590 padM : document.createElement('div'), 591 sld : document.createElement('div'), 592 sldB : document.createElement('div'), 593 sldM : document.createElement('div'), 594 btn : document.createElement('div'), 595 btnS : document.createElement('span'), 596 btnT : document.createTextNode(THIS.pickerCloseText) 597 }; 598 for(var i=0,segSize=4; i<jscolor.images.sld[1]; i+=segSize) { 599 var seg = document.createElement('div'); 600 seg.style.height = segSize+'px'; 601 seg.style.fontSize = '1px'; 602 seg.style.lineHeight = '0'; 603 jscolor.picker.sld.appendChild(seg); 604 } 605 jscolor.picker.sldB.appendChild(jscolor.picker.sld); 606 jscolor.picker.box.appendChild(jscolor.picker.sldB); 607 jscolor.picker.box.appendChild(jscolor.picker.sldM); 608 jscolor.picker.padB.appendChild(jscolor.picker.pad); 609 jscolor.picker.box.appendChild(jscolor.picker.padB); 610 jscolor.picker.box.appendChild(jscolor.picker.padM); 611 jscolor.picker.btnS.appendChild(jscolor.picker.btnT); 612 jscolor.picker.btn.appendChild(jscolor.picker.btnS); 613 jscolor.picker.box.appendChild(jscolor.picker.btn); 614 jscolor.picker.boxB.appendChild(jscolor.picker.box); 615 } 616 617 var p = jscolor.picker; 618 619 // controls interaction 620 p.box.onmouseup = 621 p.box.onmouseout = function() { target.focus(); }; 622 p.box.onmousedown = function() { abortBlur=true; }; 623 p.box.onmousemove = function(e) { 624 if (holdPad || holdSld) { 625 holdPad && setPad(e); 626 holdSld && setSld(e); 627 if (document.selection) { 628 document.selection.empty(); 629 } else if (window.getSelection) { 630 window.getSelection().removeAllRanges(); 631 } 632 dispatchImmediateChange(); 633 } 634 }; 635 if('ontouchstart' in window) { // if touch device 636 var handle_touchmove = function(e) { 637 var event={ 638 'offsetX': e.touches[0].pageX-touchOffset.X, 639 'offsetY': e.touches[0].pageY-touchOffset.Y 640 }; 641 if (holdPad || holdSld) { 642 holdPad && setPad(event); 643 holdSld && setSld(event); 644 dispatchImmediateChange(); 645 } 646 e.stopPropagation(); // prevent move "view" on broswer 647 e.preventDefault(); // prevent Default - Android Fix (else android generated only 1-2 touchmove events) 648 }; 649 p.box.removeEventListener('touchmove', handle_touchmove, false) 650 p.box.addEventListener('touchmove', handle_touchmove, false) 651 } 652 p.padM.onmouseup = 653 p.padM.onmouseout = function() { if(holdPad) { holdPad=false; jscolor.fireEvent(valueElement,'change'); } }; 654 p.padM.onmousedown = function(e) { 655 // if the slider is at the bottom, move it up 656 switch(modeID) { 657 case 0: if (THIS.hsv[2] === 0) { THIS.fromHSV(null, null, 1.0); }; break; 658 case 1: if (THIS.hsv[1] === 0) { THIS.fromHSV(null, 1.0, null); }; break; 659 } 660 holdSld=false; 661 holdPad=true; 662 setPad(e); 663 dispatchImmediateChange(); 664 }; 665 if('ontouchstart' in window) { 666 p.padM.addEventListener('touchstart', function(e) { 667 touchOffset={ 668 'X': e.target.offsetParent.offsetLeft, 669 'Y': e.target.offsetParent.offsetTop 670 }; 671 this.onmousedown({ 672 'offsetX':e.touches[0].pageX-touchOffset.X, 673 'offsetY':e.touches[0].pageY-touchOffset.Y 674 }); 675 }); 676 } 677 p.sldM.onmouseup = 678 p.sldM.onmouseout = function() { if(holdSld) { holdSld=false; jscolor.fireEvent(valueElement,'change'); } }; 679 p.sldM.onmousedown = function(e) { 680 holdPad=false; 681 holdSld=true; 682 setSld(e); 683 dispatchImmediateChange(); 684 }; 685 if('ontouchstart' in window) { 686 p.sldM.addEventListener('touchstart', function(e) { 687 touchOffset={ 688 'X': e.target.offsetParent.offsetLeft, 689 'Y': e.target.offsetParent.offsetTop 690 }; 691 this.onmousedown({ 692 'offsetX':e.touches[0].pageX-touchOffset.X, 693 'offsetY':e.touches[0].pageY-touchOffset.Y 694 }); 695 }); 696 } 697 698 // picker 699 var dims = getPickerDims(THIS); 700 p.box.style.width = dims[0] + 'px'; 701 p.box.style.height = dims[1] + 'px'; 702 703 // picker border 704 p.boxB.style.position = THIS.pickerFixedPosition ? 'fixed' : 'absolute'; 705 p.boxB.style.clear = 'both'; 706 p.boxB.style.left = x+'px'; 707 p.boxB.style.top = y+'px'; 708 p.boxB.style.zIndex = THIS.pickerZIndex; 709 p.boxB.style.border = THIS.pickerBorder+'px solid'; 710 p.boxB.style.borderColor = THIS.pickerBorderColor; 711 p.boxB.style.background = THIS.pickerFaceColor; 712 713 // pad image 714 p.pad.style.width = jscolor.images.pad[0]+'px'; 715 p.pad.style.height = jscolor.images.pad[1]+'px'; 716 717 // pad border 718 p.padB.style.position = 'absolute'; 719 p.padB.style.left = THIS.pickerFace+'px'; 720 p.padB.style.top = THIS.pickerFace+'px'; 721 p.padB.style.border = THIS.pickerInset+'px solid'; 722 p.padB.style.borderColor = THIS.pickerInsetColor; 723 724 // pad mouse area 725 p.padM.style.position = 'absolute'; 726 p.padM.style.left = '0'; 727 p.padM.style.top = '0'; 728 p.padM.style.width = THIS.pickerFace + 2*THIS.pickerInset + jscolor.images.pad[0] + jscolor.images.arrow[0] + 'px'; 729 p.padM.style.height = p.box.style.height; 730 p.padM.style.cursor = 'crosshair'; 731 732 // slider image 733 p.sld.style.overflow = 'hidden'; 734 p.sld.style.width = jscolor.images.sld[0]+'px'; 735 p.sld.style.height = jscolor.images.sld[1]+'px'; 736 737 // slider border 738 p.sldB.style.display = THIS.slider ? 'block' : 'none'; 739 p.sldB.style.position = 'absolute'; 740 p.sldB.style.right = THIS.pickerFace+'px'; 741 p.sldB.style.top = THIS.pickerFace+'px'; 742 p.sldB.style.border = THIS.pickerInset+'px solid'; 743 p.sldB.style.borderColor = THIS.pickerInsetColor; 744 745 // slider mouse area 746 p.sldM.style.display = THIS.slider ? 'block' : 'none'; 747 p.sldM.style.position = 'absolute'; 748 p.sldM.style.right = '0'; 749 p.sldM.style.top = '0'; 750 p.sldM.style.width = jscolor.images.sld[0] + jscolor.images.arrow[0] + THIS.pickerFace + 2*THIS.pickerInset + 'px'; 751 p.sldM.style.height = p.box.style.height; 752 try { 753 p.sldM.style.cursor = 'pointer'; 754 } catch(eOldIE) { 755 p.sldM.style.cursor = 'hand'; 756 } 757 758 // "close" button 759 function setBtnBorder() { 760 var insetColors = THIS.pickerInsetColor.split(/\s+/); 761 var pickerOutsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 762 p.btn.style.borderColor = pickerOutsetColor; 763 } 764 p.btn.style.display = THIS.pickerClosable ? 'block' : 'none'; 765 p.btn.style.position = 'absolute'; 766 p.btn.style.left = THIS.pickerFace + 'px'; 767 p.btn.style.bottom = THIS.pickerFace + 'px'; 768 p.btn.style.padding = '0 15px'; 769 p.btn.style.height = '18px'; 770 p.btn.style.border = THIS.pickerInset + 'px solid'; 771 setBtnBorder(); 772 p.btn.style.color = THIS.pickerButtonColor; 773 p.btn.style.font = '12px sans-serif'; 774 p.btn.style.textAlign = 'center'; 775 try { 776 p.btn.style.cursor = 'pointer'; 777 } catch(eOldIE) { 778 p.btn.style.cursor = 'hand'; 779 } 780 p.btn.onmousedown = function () { 781 THIS.hidePicker(); 782 }; 783 p.btnS.style.lineHeight = p.btn.style.height; 784 785 // load images in optimal order 786 switch(modeID) { 787 case 0: var padImg = 'hs.png'; break; 788 case 1: var padImg = 'hv.png'; break; 789 } 790 p.padM.style.backgroundImage = "url('"+jscolor.getDir()+"cross.gif')"; 791 p.padM.style.backgroundRepeat = "no-repeat"; 792 p.sldM.style.backgroundImage = "url('"+jscolor.getDir()+"arrow.gif')"; 793 p.sldM.style.backgroundRepeat = "no-repeat"; 794 p.pad.style.backgroundImage = "url('"+jscolor.getDir()+padImg+"')"; 795 p.pad.style.backgroundRepeat = "no-repeat"; 796 p.pad.style.backgroundPosition = "0 0"; 797 798 // place pointers 799 redrawPad(); 800 redrawSld(); 801 802 jscolor.picker.owner = THIS; 803 document.getElementsByTagName('body')[0].appendChild(p.boxB); 804 } 805 806 807 function getPickerDims(o) { 808 var dims = [ 809 2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[0] + 810 (o.slider ? 2*o.pickerInset + 2*jscolor.images.arrow[0] + jscolor.images.sld[0] : 0), 811 o.pickerClosable ? 812 4*o.pickerInset + 3*o.pickerFace + jscolor.images.pad[1] + o.pickerButtonHeight : 813 2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[1] 814 ]; 815 return dims; 816 } 817 818 819 function redrawPad() { 820 // redraw the pad pointer 821 switch(modeID) { 822 case 0: var yComponent = 1; break; 823 case 1: var yComponent = 2; break; 824 } 825 var x = Math.round((THIS.hsv[0]/6) * (jscolor.images.pad[0]-1)); 826 var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.pad[1]-1)); 827 jscolor.picker.padM.style.backgroundPosition = 828 (THIS.pickerFace+THIS.pickerInset+x - Math.floor(jscolor.images.cross[0]/2)) + 'px ' + 829 (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.cross[1]/2)) + 'px'; 830 831 // redraw the slider image 832 var seg = jscolor.picker.sld.childNodes; 833 834 switch(modeID) { 835 case 0: 836 var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1); 837 for(var i=0; i<seg.length; i+=1) { 838 seg[i].style.backgroundColor = 'rgb('+ 839 (rgb[0]*(1-i/seg.length)*100)+'%,'+ 840 (rgb[1]*(1-i/seg.length)*100)+'%,'+ 841 (rgb[2]*(1-i/seg.length)*100)+'%)'; 842 } 843 break; 844 case 1: 845 var rgb, s, c = [ THIS.hsv[2], 0, 0 ]; 846 var i = Math.floor(THIS.hsv[0]); 847 var f = i%2 ? THIS.hsv[0]-i : 1-(THIS.hsv[0]-i); 848 switch(i) { 849 case 6: 850 case 0: rgb=[0,1,2]; break; 851 case 1: rgb=[1,0,2]; break; 852 case 2: rgb=[2,0,1]; break; 853 case 3: rgb=[2,1,0]; break; 854 case 4: rgb=[1,2,0]; break; 855 case 5: rgb=[0,2,1]; break; 856 } 857 for(var i=0; i<seg.length; i+=1) { 858 s = 1 - 1/(seg.length-1)*i; 859 c[1] = c[0] * (1 - s*f); 860 c[2] = c[0] * (1 - s); 861 seg[i].style.backgroundColor = 'rgb('+ 862 (c[rgb[0]]*100)+'%,'+ 863 (c[rgb[1]]*100)+'%,'+ 864 (c[rgb[2]]*100)+'%)'; 865 } 866 break; 867 } 868 } 869 870 871 function redrawSld() { 872 // redraw the slider pointer 873 switch(modeID) { 874 case 0: var yComponent = 2; break; 875 case 1: var yComponent = 1; break; 876 } 877 var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.sld[1]-1)); 878 jscolor.picker.sldM.style.backgroundPosition = 879 '0 ' + (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.arrow[1]/2)) + 'px'; 880 } 881 882 883 function isPickerOwner() { 884 return jscolor.picker && jscolor.picker.owner === THIS; 885 } 886 887 888 function blurTarget() { 889 if(valueElement === target) { 890 THIS.importColor(); 891 } 892 if(THIS.pickerOnfocus) { 893 THIS.hidePicker(); 894 } 895 } 896 897 898 function blurValue() { 899 if(valueElement !== target) { 900 THIS.importColor(); 901 } 902 } 903 904 905 function setPad(e) { 906 var mpos = jscolor.getRelMousePos(e); 907 var x = mpos.x - THIS.pickerFace - THIS.pickerInset; 908 var y = mpos.y - THIS.pickerFace - THIS.pickerInset; 909 switch(modeID) { 910 case 0: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), 1 - y/(jscolor.images.pad[1]-1), null, leaveSld); break; 911 case 1: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), null, 1 - y/(jscolor.images.pad[1]-1), leaveSld); break; 912 } 913 } 914 915 916 function setSld(e) { 917 var mpos = jscolor.getRelMousePos(e); 918 var y = mpos.y - THIS.pickerFace - THIS.pickerInset; 919 switch(modeID) { 920 case 0: THIS.fromHSV(null, null, 1 - y/(jscolor.images.sld[1]-1), leavePad); break; 921 case 1: THIS.fromHSV(null, 1 - y/(jscolor.images.sld[1]-1), null, leavePad); break; 922 } 923 } 924 925 926 function dispatchImmediateChange() { 927 if (THIS.onImmediateChange) { 928 var callback; 929 if (typeof THIS.onImmediateChange === 'string') { 930 callback = new Function (THIS.onImmediateChange); 931 } else { 932 callback = THIS.onImmediateChange; 933 } 934 callback.call(THIS); 935 } 936 } 937 938 939 var THIS = this; 940 var modeID = this.pickerMode.toLowerCase()==='hvs' ? 1 : 0; 941 var abortBlur = false; 942 var 943 valueElement = jscolor.fetchElement(this.valueElement), 944 styleElement = jscolor.fetchElement(this.styleElement); 945 var 946 holdPad = false, 947 holdSld = false, 948 touchOffset = {}; 949 var 950 leaveValue = 1<<0, 951 leaveStyle = 1<<1, 952 leavePad = 1<<2, 953 leaveSld = 1<<3; 954 955 jscolor.isColorAttrSupported = false; 956 var el = document.createElement('input'); 957 if(el.setAttribute) { 958 el.setAttribute('type', 'color'); 959 if(el.type.toLowerCase() == 'color') { 960 jscolor.isColorAttrSupported = true; 961 } 962 } 963 964 // target 965 jscolor.addEvent(target, 'focus', function() { 966 if(THIS.pickerOnfocus) { THIS.showPicker(); } 967 }); 968 jscolor.addEvent(target, 'blur', function() { 969 if(!abortBlur) { 970 window.setTimeout(function(){ abortBlur || blurTarget(); abortBlur=false; }, 0); 971 } else { 972 abortBlur = false; 973 } 974 }); 975 976 // valueElement 977 if(valueElement) { 978 var updateField = function() { 979 THIS.fromString(valueElement.value, leaveValue); 980 dispatchImmediateChange(); 981 }; 982 jscolor.addEvent(valueElement, 'keyup', updateField); 983 jscolor.addEvent(valueElement, 'input', updateField); 984 jscolor.addEvent(valueElement, 'blur', blurValue); 985 valueElement.setAttribute('autocomplete', 'off'); 986 } 987 988 // styleElement 989 if(styleElement) { 990 styleElement.jscStyle = { 991 backgroundImage : styleElement.style.backgroundImage, 992 backgroundColor : styleElement.style.backgroundColor, 993 color : styleElement.style.color 994 }; 995 } 996 997 // require images 998 switch(modeID) { 999 case 0: jscolor.requireImage('hs.png'); break; 1000 case 1: jscolor.requireImage('hv.png'); break; 1001 } 1002 jscolor.requireImage('cross.gif'); 1003 jscolor.requireImage('arrow.gif'); 1004 1005 this.importColor(); 1006 } 1007 1008}; 1009 1010 1011jscolor.install(); 1012