1/** 2 * jscolor, JavaScript Color Picker 3 * 4 * @version 1.4.2 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 2013-11-25 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*(\\{[^}]*\\})?', 'i'); 71 var e = document.getElementsByTagName('input'); 72 for(var i=0; i<e.length; i+=1) { 73 var m; 74 if(!e[i].color && e[i].className && (m = e[i].className.match(matchClass))) { 75 var prop = {}; 76 if(m[3]) { 77 try { 78 prop = (new Function ('return (' + m[3] + ')'))(); 79 } catch(eInvalidProp) {} 80 } 81 e[i].color = new jscolor.color(e[i], prop); 82 } 83 } 84 }, 85 86 87 preload : function() { 88 for(var fn in jscolor.imgRequire) { 89 if(jscolor.imgRequire.hasOwnProperty(fn)) { 90 jscolor.loadImage(fn); 91 } 92 } 93 }, 94 95 96 images : { 97 pad : [ 181, 101 ], 98 sld : [ 16, 101 ], 99 cross : [ 15, 15 ], 100 arrow : [ 7, 11 ] 101 }, 102 103 104 imgRequire : {}, 105 imgLoaded : {}, 106 107 108 requireImage : function(filename) { 109 jscolor.imgRequire[filename] = true; 110 }, 111 112 113 loadImage : function(filename) { 114 if(!jscolor.imgLoaded[filename]) { 115 jscolor.imgLoaded[filename] = new Image(); 116 jscolor.imgLoaded[filename].src = jscolor.getDir()+filename; 117 } 118 }, 119 120 121 fetchElement : function(mixed) { 122 return typeof mixed === 'string' ? document.getElementById(mixed) : mixed; 123 }, 124 125 126 addEvent : function(el, evnt, func) { 127 if(el.addEventListener) { 128 el.addEventListener(evnt, func, false); 129 } else if(el.attachEvent) { 130 el.attachEvent('on'+evnt, func); 131 } 132 }, 133 134 135 fireEvent : function(el, evnt) { 136 if(!el) { 137 return; 138 } 139 if(document.createEvent) { 140 var ev = document.createEvent('HTMLEvents'); 141 ev.initEvent(evnt, true, true); 142 el.dispatchEvent(ev); 143 } else if(document.createEventObject) { 144 var ev = document.createEventObject(); 145 el.fireEvent('on'+evnt, ev); 146 } else if(el['on'+evnt]) { // alternatively use the traditional event model (IE5) 147 el['on'+evnt](); 148 } 149 }, 150 151 152 getElementPos : function(e) { 153 var e1=e, e2=e; 154 var x=0, y=0; 155 if(e1.offsetParent) { 156 do { 157 x += e1.offsetLeft; 158 y += e1.offsetTop; 159 } while(e1 = e1.offsetParent); 160 } 161 while((e2 = e2.parentNode) && e2.nodeName.toUpperCase() !== 'BODY') { 162 x -= e2.scrollLeft; 163 y -= e2.scrollTop; 164 } 165 return [x, y]; 166 }, 167 168 169 getElementSize : function(e) { 170 return [e.offsetWidth, e.offsetHeight]; 171 }, 172 173 174 getRelMousePos : function(e) { 175 var x = 0, y = 0; 176 if (!e) { e = window.event; } 177 if (typeof e.offsetX === 'number') { 178 x = e.offsetX; 179 y = e.offsetY; 180 } else if (typeof e.layerX === 'number') { 181 x = e.layerX; 182 y = e.layerY; 183 } 184 return { x: x, y: y }; 185 }, 186 187 188 getViewPos : function() { 189 if(typeof window.pageYOffset === 'number') { 190 return [window.pageXOffset, window.pageYOffset]; 191 } else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) { 192 return [document.body.scrollLeft, document.body.scrollTop]; 193 } else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { 194 return [document.documentElement.scrollLeft, document.documentElement.scrollTop]; 195 } else { 196 return [0, 0]; 197 } 198 }, 199 200 201 getViewSize : function() { 202 if(typeof window.innerWidth === 'number') { 203 return [window.innerWidth, window.innerHeight]; 204 } else if(document.body && (document.body.clientWidth || document.body.clientHeight)) { 205 return [document.body.clientWidth, document.body.clientHeight]; 206 } else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) { 207 return [document.documentElement.clientWidth, document.documentElement.clientHeight]; 208 } else { 209 return [0, 0]; 210 } 211 }, 212 213 214 URI : function(uri) { // See RFC3986 215 216 this.scheme = null; 217 this.authority = null; 218 this.path = ''; 219 this.query = null; 220 this.fragment = null; 221 222 this.parse = function(uri) { 223 var m = uri.match(/^(([A-Za-z][0-9A-Za-z+.-]*)(:))?((\/\/)([^\/?#]*))?([^?#]*)((\?)([^#]*))?((#)(.*))?/); 224 this.scheme = m[3] ? m[2] : null; 225 this.authority = m[5] ? m[6] : null; 226 this.path = m[7]; 227 this.query = m[9] ? m[10] : null; 228 this.fragment = m[12] ? m[13] : null; 229 return this; 230 }; 231 232 this.toString = function() { 233 var result = ''; 234 if(this.scheme !== null) { result = result + this.scheme + ':'; } 235 if(this.authority !== null) { result = result + '//' + this.authority; } 236 if(this.path !== null) { result = result + this.path; } 237 if(this.query !== null) { result = result + '?' + this.query; } 238 if(this.fragment !== null) { result = result + '#' + this.fragment; } 239 return result; 240 }; 241 242 this.toAbsolute = function(base) { 243 var base = new jscolor.URI(base); 244 var r = this; 245 var t = new jscolor.URI; 246 247 if(base.scheme === null) { return false; } 248 249 if(r.scheme !== null && r.scheme.toLowerCase() === base.scheme.toLowerCase()) { 250 r.scheme = null; 251 } 252 253 if(r.scheme !== null) { 254 t.scheme = r.scheme; 255 t.authority = r.authority; 256 t.path = removeDotSegments(r.path); 257 t.query = r.query; 258 } else { 259 if(r.authority !== null) { 260 t.authority = r.authority; 261 t.path = removeDotSegments(r.path); 262 t.query = r.query; 263 } else { 264 if(r.path === '') { 265 t.path = base.path; 266 if(r.query !== null) { 267 t.query = r.query; 268 } else { 269 t.query = base.query; 270 } 271 } else { 272 if(r.path.substr(0,1) === '/') { 273 t.path = removeDotSegments(r.path); 274 } else { 275 if(base.authority !== null && base.path === '') { 276 t.path = '/'+r.path; 277 } else { 278 t.path = base.path.replace(/[^\/]+$/,'')+r.path; 279 } 280 t.path = removeDotSegments(t.path); 281 } 282 t.query = r.query; 283 } 284 t.authority = base.authority; 285 } 286 t.scheme = base.scheme; 287 } 288 t.fragment = r.fragment; 289 290 return t; 291 }; 292 293 function removeDotSegments(path) { 294 var out = ''; 295 while(path) { 296 if(path.substr(0,3)==='../' || path.substr(0,2)==='./') { 297 path = path.replace(/^\.+/,'').substr(1); 298 } else if(path.substr(0,3)==='/./' || path==='/.') { 299 path = '/'+path.substr(3); 300 } else if(path.substr(0,4)==='/../' || path==='/..') { 301 path = '/'+path.substr(4); 302 out = out.replace(/\/?[^\/]*$/, ''); 303 } else if(path==='.' || path==='..') { 304 path = ''; 305 } else { 306 var rm = path.match(/^\/?[^\/]*/)[0]; 307 path = path.substr(rm.length); 308 out = out + rm; 309 } 310 } 311 return out; 312 } 313 314 if(uri) { 315 this.parse(uri); 316 } 317 318 }, 319 320 321 // 322 // Usage example: 323 // var myColor = new jscolor.color(myInputElement) 324 // 325 326 color : function(target, prop) { 327 328 329 this.required = true; // refuse empty values? 330 this.adjust = true; // adjust value to uniform notation? 331 this.hash = false; // prefix color with # symbol? 332 this.caps = true; // uppercase? 333 this.slider = true; // show the value/saturation slider? 334 this.valueElement = target; // value holder 335 this.styleElement = target; // where to reflect current color 336 this.onImmediateChange = null;
336 // onchange callback (can be either string or function) 337 this.hsv = [0, 0, 1]; // read-only 0-6, 0-1, 0-1 338 this.rgb = [1, 1, 1]; // read-only 0-1, 0-1, 0-1 339 this.minH = 0; // read-only 0-6 340 this.maxH = 6; // read-only 0-6 341 this.minS = 0; // read-only 0-1 342 this.maxS = 1; // read-only 0-1 343 this.minV = 0; // read-only 0-1 344 this.maxV = 1; // read-only 0-1 345 346 this.pickerOnfocus = true; // display picker on focus? 347 this.pickerMode = 'HSV'; // HSV | HVS 348 this.pickerPosition = 'bottom'; // left | right | top | bottom 349 this.pickerSmartPosition = true; // automatically adjust picker position when necessary 350 this.pickerButtonHeight = 20; // px 351 this.pickerClosable = false; 352 this.pickerCloseText = 'Close'; 353 this.pickerButtonColor = 'ButtonText'; // px 354 this.pickerFace = 10; // px 355 this.pickerFaceColor = 'ThreeDFace'; // CSS color 356 this.pickerBorder = 1; // px 357 this.pickerBorderColor = 'ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight'; // CSS color 358 this.pickerInset = 1; // px 359 this.pickerInsetColor = 'ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow'; // CSS color 360 this.pickerZIndex = 10000; 361 362 363 for(var p in prop) { 364 if(prop.hasOwnProperty(p)) { 365 this[p] = prop[p]; 366 } 367 } 368 369 370 this.hidePicker = function() { 371 if(isPickerOwner()) { 372 removePicker(); 373 } 374 }; 375 376 377 this.showPicker = function() { 378 if(!isPickerOwner()) { 379 var tp = jscolor.getElementPos(target); // target pos 380 var ts = jscolor.getElementSize(target); // target size 381 var vp = jscolor.getViewPos(); // view pos 382 var vs = jscolor.getViewSize(); // view size 383 var ps = getPickerDims(this); // picker size 384 var a, b, c; 385 switch(this.pickerPosition.toLowerCase()) { 386 case 'left': a=1; b=0; c=-1; break; 387 case 'right':a=1; b=0; c=1; break; 388 case 'top': a=0; b=1; c=-1; break; 389 default: a=0; b=1; c=1; break; 390 } 391 var l = (ts[b]+ps[b])/2; 392 393 // picker pos 394 if (!this.pickerSmartPosition) { 395 var pp = [ 396 tp[a], 397 tp[b]+ts[b]-l+l*c 398 ]; 399 } else { 400 var pp = [ 401 -vp[a]+tp[a]+ps[a] > vs[a] ? 402 (-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]) : 403 tp[a], 404 -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? 405 (-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) : 406 (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) 407 ]; 408 } 409 drawPicker(pp[a], pp[b]); 410 } 411 }; 412 413 414 this.importColor = function() { 415 if(!valueElement) { 416 this.exportColor(); 417 } else { 418 if(!this.adjust) { 419 if(!this.fromString(valueElement.value, leaveValue)) { 420 styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; 421 styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; 422 styleElement.style.color = styleElement.jscStyle.color; 423 this.exportColor(leaveValue | leaveStyle); 424 } 425 } else if(!this.required && /^\s*$/.test(valueElement.value)) { 426 valueElement.value = ''; 427 styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; 428 styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; 429 styleElement.style.color = styleElement.jscStyle.color; 430 this.exportColor(leaveValue | leaveStyle); 431 432 } else if(this.fromString(valueElement.value)) { 433 // OK 434 } else { 435 this.exportColor(); 436 } 437 } 438 }; 439 440 441 this.exportColor = function(flags) { 442 if(!(flags & leaveValue) && valueElement) { 443 var value = this.toString(); 444 if(this.caps) { value = value.toUpperCase(); } 445 if(this.hash) { value = '#'+value; } 446 valueElement.value = value; 447 } 448 if(!(flags & leaveStyle) && styleElement) { 449 styleElement.style.backgroundImage = "none"; 450 styleElement.style.backgroundColor = 451 '#'+this.toString(); 452 styleElement.style.color = 453 0.213 * this.rgb[0] + 454 0.715 * this.rgb[1] + 455 0.072 * this.rgb[2] 456 < 0.5 ? '#FFF' : '#000'; 457 } 458 if(!(flags & leavePad) && isPickerOwner()) { 459 redrawPad(); 460 } 461 if(!(flags & leaveSld) && isPickerOwner()) { 462 redrawSld(); 463 } 464 }; 465 466 467 this.fromHSV = function(h, s, v, flags) { // null = don't change 468 if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); } 469 if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); } 470 if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); } 471 472 this.rgb = HSV_RGB( 473 h===null ? this.hsv[0] : (this.hsv[0]=h), 474 s===null ? this.hsv[1] : (this.hsv[1]=s), 475 v===null ? this.hsv[2] : (this.hsv[2]=v) 476 ); 477 478 this.exportColor(flags); 479 }; 480 481 482 this.fromRGB = function(r, g, b, flags) { // null = don't change 483 if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); } 484 if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); } 485 if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); } 486 487 var hsv = RGB_HSV( 488 r===null ? this.rgb[0] : r, 489 g===null ? this.rgb[1] : g, 490 b===null ? this.rgb[2] : b 491 ); 492 if(hsv[0] !== null) { 493 this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0])); 494 } 495 if(hsv[2] !== 0) { 496 this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1])); 497 } 498 this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2])); 499 500 // update RGB according to final HSV, as some values might be trimmed 501 var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); 502 this.rgb[0] = rgb[0]; 503 this.rgb[1] = rgb[1]; 504 this.rgb[2] = rgb[2]; 505 506 this.exportColor(flags); 507 }; 508 509 510 this.fromString = function(hex, flags) { 511 var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); 512 if(!m) { 513 return false; 514 } else { 515 if(m[1].length === 6) { // 6-char notation 516 this.fromRGB(
517 parseInt(m[1].substr(0,2),16) / 255, 518 parseInt(m[1].substr(2,2),16) / 255, 519 parseInt(m[1].substr(4,2),16) / 255, 520 flags 521 ); 522 } else { // 3-char notation 523 this.fromRGB( 524 parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, 525 parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, 526 parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, 527 flags 528 ); 529 } 530 return true; 531 } 532 }; 533 534 535 this.toString = function() { 536 return ( 537 (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + 538 (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + 539 (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) 540 ); 541 }; 542 543 544 function RGB_HSV(r, g, b) { 545 var n = Math.min(Math.min(r,g),b); 546 var v = Math.max(Math.max(r,g),b); 547 var m = v - n; 548 if(m === 0) { return [ null, 0, v ]; } 549 var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 550 return [ h===6?0:h, m/v, v ]; 551 } 552 553 554 function HSV_RGB(h, s, v) { 555 if(h === null) { return [ v, v, v ]; } 556 var i = Math.floor(h); 557 var f = i%2 ? h-i : 1-(h-i); 558 var m = v * (1 - s); 559 var n = v * (1 - s*f); 560 switch(i) { 561 case 6: 562 case 0: return [v,n,m]; 563 case 1: return [n,v,m]; 564 case 2: return [m,v,n]; 565 case 3: return [m,n,v]; 566 case 4: return [n,m,v]; 567 case 5: return [v,m,n]; 568 } 569 } 570 571 572 function removePicker() { 573 delete jscolor.picker.owner; 574 document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); 575 } 576 577 578 function drawPicker(x, y) { 579 if(!jscolor.picker) { 580 jscolor.picker = { 581 box : document.createElement('div'), 582 boxB : document.createElement('div'), 583 pad : document.createElement('div'), 584 padB : document.createElement('div'), 585 padM : document.createElement('div'), 586 sld : document.createElement('div'), 587 sldB : document.createElement('div'), 588 sldM : document.createElement('div'), 589 btn : document.createElement('div'), 590 btnS : document.createElement('span'), 591 btnT : document.createTextNode(THIS.pickerCloseText) 592 }; 593 for(var i=0,segSize=4; i<jscolor.images.sld[1]; i+=segSize) { 594 var seg = document.createElement('div'); 595 seg.style.height = segSize+'px'; 596 seg.style.fontSize = '1px'; 597 seg.style.lineHeight = '0'; 598 jscolor.picker.sld.appendChild(seg); 599 } 600 jscolor.picker.sldB.appendChild(jscolor.picker.sld); 601 jscolor.picker.box.appendChild(jscolor.picker.sldB); 602 jscolor.picker.box.appendChild(jscolor.picker.sldM); 603 jscolor.picker.padB.appendChild(jscolor.picker.pad); 604 jscolor.picker.box.appendChild(jscolor.picker.padB); 605 jscolor.picker.box.appendChild(jscolor.picker.padM); 606 jscolor.picker.btnS.appendChild(jscolor.picker.btnT); 607 jscolor.picker.btn.appendChild(jscolor.picker.btnS); 608 jscolor.picker.box.appendChild(jscolor.picker.btn); 609 jscolor.picker.boxB.appendChild(jscolor.picker.box); 610 } 611 612 var p = jscolor.picker; 613 614 // controls interaction 615 p.box.onmouseup = 616 p.box.onmouseout = function() { target.focus(); }; 617 p.box.onmousedown = function() { abortBlur=true; }; 618 p.box.onmousemove = function(e) { 619 if (holdPad || holdSld) { 620 holdPad && setPad(e); 621 holdSld && setSld(e); 622 if (document.selection) { 623 document.selection.empty(); 624 } else if (window.getSelection) { 625 window.getSelection().removeAllRanges(); 626 } 627 dispatchImmediateChange(); 628 } 629 }; 630 if('ontouchstart' in window) { // if touch device 631 var handle_touchmove = function(e) { 632 var event={ 633 'offsetX': e.touches[0].pageX-touchOffset.X, 634 'offsetY': e.touches[0].pageY-touchOffset.Y 635 }; 636 if (holdPad || holdSld) { 637 holdPad && setPad(event); 638 holdSld && setSld(event); 639 dispatchImmediateChange(); 640 } 641 e.stopPropagation(); // prevent move "view" on broswer 642 e.preventDefault(); // prevent Default - Android Fix (else android generated only 1-2 touchmove events) 643 }; 644 p.box.removeEventListener('touchmove', handle_touchmove, false) 645 p.box.addEventListener('touchmove', handle_touchmove, false) 646 } 647 p.padM.onmouseup = 648 p.padM.onmouseout = function() { if(holdPad) { holdPad=false; jscolor.fireEvent(valueElement,'change'); } }; 649 p.padM.onmousedown = function(e) { 650 // if the slider is at the bottom, move it up 651 switch(modeID) { 652 case 0: if (THIS.hsv[2] === 0) { THIS.fromHSV(null, null, 1.0); }; break; 653 case 1: if (THIS.hsv[1] === 0) { THIS.fromHSV(null, 1.0, null); }; break; 654 } 655 holdSld=false; 656 holdPad=true; 657 setPad(e); 658 dispatchImmediateChange(); 659 }; 660 if('ontouchstart' in window) { 661 p.padM.addEventListener('touchstart', function(e) { 662 touchOffset={ 663 'X': e.target.offsetParent.offsetLeft, 664 'Y': e.target.offsetParent.offsetTop 665 }; 666 this.onmousedown({ 667 'offsetX':e.touches[0].pageX-touchOffset.X, 668 'offsetY':e.touches[0].pageY-touchOffset.Y 669 });
670 }); 671 } 672 p.sldM.onmouseup = 673 p.sldM.onmouseout = function() { if(holdSld) { holdSld=false; jscolor.fireEvent(valueElement,'change'); } }; 674 p.sldM.onmousedown = function(e) { 675 holdPad=false; 676 holdSld=true; 677 setSld(e); 678 dispatchImmediateChange(); 679 }; 680 if('ontouchstart' in window) { 681 p.sldM.addEventListener('touchstart', function(e) { 682 touchOffset={ 683 'X': e.target.offsetParent.offsetLeft, 684 'Y': e.target.offsetParent.offsetTop 685 }; 686 this.onmousedown({ 687 'offsetX':e.touches[0].pageX-touchOffset.X, 688 'offsetY':e.touches[0].pageY-touchOffset.Y 689 }); 690 }); 691 } 692 693 // picker 694 var dims = getPickerDims(THIS); 695 p.box.style.width = dims[0] + 'px'; 696 p.box.style.height = dims[1] + 'px'; 697 698 // picker border 699 p.boxB.style.position = 'absolute'; 700 p.boxB.style.clear = 'both'; 701 p.boxB.style.left = x+'px'; 702 p.boxB.style.top = y+'px'; 703 p.boxB.style.zIndex = THIS.pickerZIndex; 704 p.boxB.style.border = THIS.pickerBorder+'px solid'; 705 p.boxB.style.borderColor = THIS.pickerBorderColor; 706 p.boxB.style.background = THIS.pickerFaceColor; 707 708 // pad image 709 p.pad.style.width = jscolor.images.pad[0]+'px'; 710 p.pad.style.height = jscolor.images.pad[1]+'px'; 711 712 // pad border 713 p.padB.style.position = 'absolute'; 714 p.padB.style.left = THIS.pickerFace+'px'; 715 p.padB.style.top = THIS.pickerFace+'px'; 716 p.padB.style.border = THIS.pickerInset+'px solid'; 717 p.padB.style.borderColor = THIS.pickerInsetColor; 718 719 // pad mouse area 720 p.padM.style.position = 'absolute'; 721 p.padM.style.left = '0'; 722 p.padM.style.top = '0'; 723 p.padM.style.width = THIS.pickerFace + 2*THIS.pickerInset + jscolor.images.pad[0] + jscolor.images.arrow[0] + 'px'; 724 p.padM.style.height = p.box.style.height; 725 p.padM.style.cursor = 'crosshair'; 726 727 // slider image 728 p.sld.style.overflow = 'hidden'; 729 p.sld.style.width = jscolor.images.sld[0]+'px'; 730 p.sld.style.height = jscolor.images.sld[1]+'px'; 731 732 // slider border 733 p.sldB.style.display = THIS.slider ? 'block' : 'none'; 734 p.sldB.style.position = 'absolute'; 735 p.sldB.style.right = THIS.pickerFace+'px'; 736 p.sldB.style.top = THIS.pickerFace+'px'; 737 p.sldB.style.border = THIS.pickerInset+'px solid'; 738 p.sldB.style.borderColor = THIS.pickerInsetColor; 739 740 // slider mouse area 741 p.sldM.style.display = THIS.slider ? 'block' : 'none'; 742 p.sldM.style.position = 'absolute'; 743 p.sldM.style.right = '0'; 744 p.sldM.style.top = '0'; 745 p.sldM.style.width = jscolor.images.sld[0] + jscolor.images.arrow[0] + THIS.pickerFace + 2*THIS.pickerInset + 'px'; 746 p.sldM.style.height = p.box.style.height; 747 try { 748 p.sldM.style.cursor = 'pointer'; 749 } catch(eOldIE) { 750 p.sldM.style.cursor = 'hand'; 751 } 752 753 // "close" button 754 function setBtnBorder() { 755 var insetColors = THIS.pickerInsetColor.split(/\s+/); 756 var pickerOutsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 757 p.btn.style.borderColor = pickerOutsetColor; 758 } 759 p.btn.style.display = THIS.pickerClosable ? 'block' : 'none'; 760 p.btn.style.position = 'absolute'; 761 p.btn.style.left = THIS.pickerFace + 'px'; 762 p.btn.style.bottom = THIS.pickerFace + 'px'; 763 p.btn.style.padding = '0 15px'; 764 p.btn.style.height = '18px'; 765 p.btn.style.border = THIS.pickerInset + 'px solid'; 766 setBtnBorder(); 767 p.btn.style.color = THIS.pickerButtonColor; 768 p.btn.style.font = '12px sans-serif'; 769 p.btn.style.textAlign = 'center'; 770 try { 771 p.btn.style.cursor = 'pointer'; 772 } catch(eOldIE) { 773 p.btn.style.cursor = 'hand'; 774 } 775 p.btn.onmousedown = function () { 776 THIS.hidePicker(); 777 }; 778 p.btnS.style.lineHeight = p.btn.style.height; 779 780 // load images in optimal order 781 switch(modeID) { 782 case 0: var padImg = 'hs.png'; break; 783 case 1: var padImg = 'hv.png'; break; 784 } 785 p.padM.style.backgroundImage = "url('"+jscolor.getDir()+"cross.gif')"; 786 p.padM.style.backgroundRepeat = "no-repeat"; 787 p.sldM.style.backgroundImage = "url('"+jscolor.getDir()+"arrow.gif')"; 788 p.sldM.style.backgroundRepeat = "no-repeat"; 789 p.pad.style.backgroundImage = "url('"+jscolor.getDir()+padImg+"')"; 790 p.pad.style.backgroundRepeat = "no-repeat"; 791 p.pad.style.backgroundPosition = "0 0"; 792 793 // place pointers 794 redrawPad(); 795 redrawSld(); 796 797 jscolor.picker.owner = THIS; 798 document.getElementsByTagName('body')[0].appendChild(p.boxB); 799 } 800 801 802 function getPickerDims(o) { 803 var dims = [ 804 2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[0] + 805 (o.slider ? 2*o.pickerInset + 2*jscolor.images.arrow[0] + jscolor.images.sld[0] : 0), 806 o.pickerClosable ? 807 4*o.pickerInset + 3*o.pickerFace + jscolor.images.pad[1] + o.pickerButtonHeight : 808 2*o.pickerInset + 2*o.pickerFace + jscolor.images.pad[1] 809 ]; 810 return dims; 811 } 812 813 814 function redrawPad() { 815 // redraw the pad pointer 816 switch(modeID) { 817 case 0: var yComponent = 1; break; 818 case 1: var yComponent = 2; break; 819 } 820 var x = Math.round((THIS.hsv[0]/6) * (jscolor.images.pad[0]-1)); 821 var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.pad[1]-1)); 822 jscolor.picker.padM.style.backgroundPosition = 823 (THIS.pickerFace+THIS.pickerInset+x - Math.floor(jscolor.images.cross[0]/2)) + 'px ' + 824 (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.cross[1]/2)) + 'px'; 825 826 // redraw the slider image 827 var seg = jscolor.picker.sld.childNodes; 828 829 switch(modeID) { 830 case 0: 831 var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1); 832 for(var i=0; i<seg.length; i+=1) { 833 seg[i].style.backgroundColor = 'rgb('+ 834 (rgb[0]*(1-i/seg.length)*100)+'%,'+ 835 (rgb[1]*(1-i/seg.length)*100)+'%,'+ 836 (rgb[2]*(1-i/seg.length)*100)+'%)'; 837 } 838 break; 839 case 1: 840 var rgb, s, c = [ THIS.hsv[2], 0, 0 ]; 841 var i = Math.floor(THIS.hsv[0]); 842 var f = i%2 ? THIS.hsv[0]-i : 1-(THIS.hsv[0]-i); 843 switch(i) { 844 case 6: 845 case 0: rgb=[0,1,2]; break; 846 case 1: rgb=[1,0,2]; break; 847 case 2: rgb=[2,0,1]; break; 848 case 3: rgb=[2,1,0]; break; 849 case 4: rgb=[1,2,0]; break; 850 case 5: rgb=[0,2,1]; break; 851 } 852 for(var i=0; i<seg.length; i+=1) { 853 s = 1 - 1/(seg.length-1)*i; 854 c[1] = c[0] * (1 - s*f); 855 c[2] = c[0] * (1 - s); 856 seg[i].style.backgroundColor = 'rgb('+ 857 (c[rgb[0]]*100)+'%,'+ 858 (c[rgb[1]]*100)+'%,'+ 859 (c[rgb[2]]*100)+'%)'; 860 } 861 break; 862 } 863 } 864 865 866 function redrawSld() { 867 // redraw the slider pointer 868 switch(modeID) { 869 case 0: var yComponent = 2; break; 870 case 1: var yComponent = 1; break; 871 } 872 var y = Math.round((1-THIS.hsv[yComponent]) * (jscolor.images.sld[1]-1)); 873 jscolor.picker.sldM.style.backgroundPosition = 874 '0 ' + (THIS.pickerFace+THIS.pickerInset+y - Math.floor(jscolor.images.arrow[1]/2)) + 'px'; 875 } 876 877 878 function isPickerOwner() { 879 return jscolor.picker && jscolor.picker.owner === THIS; 880 } 881 882 883 function blurTarget() { 884 if(valueElement === target) { 885 THIS.importColor(); 886 } 887 if(THIS.pickerOnfocus) { 888 THIS.hidePicker(); 889 } 890 } 891 892 893 function blurValue() { 894 if(valueElement !== target) { 895 THIS.importColor(); 896 } 897 } 898 899 900 function setPad(e) { 901 var mpos = jscolor.getRelMousePos(e); 902 var x = mpos.x - THIS.pickerFace - THIS.pickerInset; 903 var y = mpos.y - THIS.pickerFace - THIS.pickerInset; 904 switch(modeID) { 905 case 0: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), 1 - y/(jscolor.images.pad[1]-1), null, leaveSld); break; 906 case 1: THIS.fromHSV(x*(6/(jscolor.images.pad[0]-1)), null, 1 - y/(jscolor.images.pad[1]-1), leaveSld); break; 907 } 908 } 909 910 911 function setSld(e) { 912 var mpos = jscolor.getRelMousePos(e); 913 var y = mpos.y - THIS.pickerFace - THIS.pickerInset; 914 switch(modeID) { 915 case 0: THIS.fromHSV(null, null, 1 - y/(jscolor.images.sld[1]-1), leavePad); break; 916 case 1: THIS.fromHSV(null, 1 - y/(jscolor.images.sld[1]-1), null, leavePad); break; 917 } 918 } 919 920 921 function dispatchImmediateChange() { 922 if (THIS.onImmediateChange) { 923 var callback; 924 if (typeof THIS.onImmediateChange === 'string') { 925 callback = new Function (THIS.onImmediateChange); 926 } else { 927 callback = THIS.onImmediateChange; 928 } 929 callback.call(THIS); 930 } 931 } 932 933 934 var THIS = this; 935 var modeID = this.pickerMode.toLowerCase()==='hvs' ? 1 : 0; 936 var abortBlur = false; 937 var 938 valueElement = jscolor.fetchElement(this.valueElement), 939 styleElement = jscolor.fetchElement(this.styleElement); 940 var 941 holdPad = false, 942 holdSld = false, 943 touchOffset = {}; 944 var 945 leaveValue = 1<<0, 946 leaveStyle = 1<<1, 947 leavePad = 1<<2, 948 leaveSld = 1<<3; 949 950 // target 951 jscolor.addEvent(target, 'focus', function() { 952 if(THIS.pickerOnfocus) { THIS.showPicker(); } 953 }); 954 jscolor.addEvent(target, 'blur', function() { 955 if(!abortBlur) { 956 window.setTimeout(function(){ abortBlur || blurTarget(); abortBlur=false; }, 0); 957 } else { 958 abortBlur = false; 959 } 960 }); 961 962 // valueElement 963 if(valueElement) { 964 var updateField = function() { 965 THIS.fromString(valueElement.value, leaveValue); 966 dispatchImmediateChange(); 967 }; 968 jscolor.addEvent(valueElement, 'keyup', updateField); 969 jscolor.addEvent(valueElement, 'input', updateField); 970 jscolor.addEvent(valueElement, 'blur', blurValue); 971 valueElement.setAttribute('autocomplete', 'off'); 972 } 973 974 // styleElement 975 if(styleElement) { 976 styleElement.jscStyle = { 977 backgroundImage : styleElement.style.backgroundImage, 978 backgroundColor : styleElement.style.backgroundColor, 979 color : styleElement.style.color 980 }; 981 } 982 983 // require images 984 switch(modeID) { 985 case 0: jscolor.requireImage('hs.png'); break; 986 case 1: jscolor.requireImage('hv.png'); break; 987 } 988 jscolor.requireImage('cross.gif'); 989 jscolor.requireImage('arrow.gif'); 990 991 this.importColor(); 992 } 993 994}; 995 996 997jscolor.install();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.