1/** 2 * jscolor - JavaScript Color Picker 3 * 4 * @link http://jscolor.com 5 * @license For open source use: GPLv3 6 * For commercial use: JSColor Commercial License 7 * @author Jan Odvarko 8 * @version 2.0.4 9 * 10 * See usage examples at http://jscolor.com/examples/ 11 */ 12 13 14"use strict"; 15 16 17if (!window.jscolor) { window.jscolor = (function () { 18 19 20var jsc = { 21 22 23 register : function () { 24 jsc.attachDOMReadyEvent(jsc.init); 25 jsc.attachEvent(document, 'mousedown', jsc.onDocumentMouseDown); 26 jsc.attachEvent(document, 'touchstart', jsc.onDocumentTouchStart); 27 jsc.attachEvent(window, 'resize', jsc.onWindowResize); 28 }, 29 30 31 init : function () { 32 if (jsc.jscolor.lookupClass) { 33 jsc.jscolor.installByClassName(jsc.jscolor.lookupClass); 34 } 35 }, 36 37 38 tryInstallOnElements : function (elms, className) { 39 var matchClass = new RegExp('(^|\\s)(' + className + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i'); 40 41 for (var i = 0; i < elms.length; i += 1) { 42 if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color') { 43 if (jsc.isColorAttrSupported) { 44 // skip inputs of type 'color' if supported by the browser 45 continue; 46 } 47 } 48 var m; 49 if (!elms[i].jscolor && elms[i].className && (m = elms[i].className.match(matchClass))) { 50 var targetElm = elms[i]; 51 var optsStr = null; 52 53 var dataOptions = jsc.getDataAttr(targetElm, 'jscolor'); 54 if (dataOptions !== null) { 55 optsStr = dataOptions; 56 } else if (m[4]) { 57 optsStr = m[4]; 58 } 59 60 var opts = {}; 61 if (optsStr) { 62 try { 63 opts = (new Function ('return (' + optsStr + ')'))(); 64 } catch(eParseError) { 65 jsc.warn('Error parsing jscolor options: ' + eParseError + ':\n' + optsStr); 66 } 67 } 68 targetElm.jscolor = new jsc.jscolor(targetElm, opts); 69 } 70 } 71 }, 72 73 74 isColorAttrSupported : (function () { 75 var elm = document.createElement('input'); 76 if (elm.setAttribute) { 77 elm.setAttribute('type', 'color'); 78 if (elm.type.toLowerCase() == 'color') { 79 return true; 80 } 81 } 82 return false; 83 })(), 84 85 86 isCanvasSupported : (function () { 87 var elm = document.createElement('canvas'); 88 return !!(elm.getContext && elm.getContext('2d')); 89 })(), 90 91 92 fetchElement : function (mixed) { 93 return typeof mixed === 'string' ? document.getElementById(mixed) : mixed; 94 }, 95 96 97 isElementType : function (elm, type) { 98 return elm.nodeName.toLowerCase() === type.toLowerCase(); 99 }, 100 101 102 getDataAttr : function (el, name) { 103 var attrName = 'data-' + name; 104 var attrValue = el.getAttribute(attrName); 105 if (attrValue !== null) { 106 return attrValue; 107 } 108 return null; 109 }, 110 111 112 attachEvent : function (el, evnt, func) { 113 if (el.addEventListener) { 114 el.addEventListener(evnt, func, false); 115 } else if (el.attachEvent) { 116 el.attachEvent('on' + evnt, func); 117 } 118 }, 119 120 121 detachEvent : function (el, evnt, func) { 122 if (el.removeEventListener) { 123 el.removeEventListener(evnt, func, false); 124 } else if (el.detachEvent) { 125 el.detachEvent('on' + evnt, func); 126 } 127 }, 128 129 130 _attachedGroupEvents : {}, 131 132 133 attachGroupEvent : function (groupName, el, evnt, func) { 134 if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { 135 jsc._attachedGroupEvents[groupName] = []; 136 } 137 jsc._attachedGroupEvents[groupName].push([el, evnt, func]); 138 jsc.attachEvent(el, evnt, func); 139 }, 140 141 142 detachGroupEvents : function (groupName) { 143 if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { 144 for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { 145 var evt = jsc._attachedGroupEvents[groupName][i]; 146 jsc.detachEvent(evt[0], evt[1], evt[2]); 147 } 148 delete jsc._attachedGroupEvents[groupName]; 149 } 150 }, 151 152 153 attachDOMReadyEvent : function (func) { 154 var fired = false; 155 var fireOnce = function () { 156 if (!fired) { 157 fired = true; 158 func(); 159 } 160 }; 161 162 if (document.readyState === 'complete') { 163 setTimeout(fireOnce, 1); // async 164 return; 165 } 166 167 if (document.addEventListener) { 168 document.addEventListener('DOMContentLoaded', fireOnce, false); 169 170 // Fallback 171 window.addEventListener('load', fireOnce, false); 172 173 } else if (document.attachEvent) { 174 // IE 175 document.attachEvent('onreadystatechange', function () { 176 if (document.readyState === 'complete') { 177 document.detachEvent('onreadystatechange', arguments.callee);
178 fireOnce(); 179 } 180 }) 181 182 // Fallback 183 window.attachEvent('onload', fireOnce); 184 185 // IE7/8 186 if (document.documentElement.doScroll && window == window.top) { 187 var tryScroll = function () { 188 if (!document.body) { return; } 189 try { 190 document.documentElement.doScroll('left'); 191 fireOnce(); 192 } catch (e) { 193 setTimeout(tryScroll, 1); 194 } 195 }; 196 tryScroll(); 197 } 198 } 199 }, 200 201 202 warn : function (msg) { 203 if (window.console && window.console.warn) { 204 window.console.warn(msg); 205 } 206 }, 207 208 209 preventDefault : function (e) { 210 if (e.preventDefault) { e.preventDefault(); } 211 e.returnValue = false; 212 }, 213 214 215 captureTarget : function (target) { 216 // IE 217 if (target.setCapture) { 218 jsc._capturedTarget = target; 219 jsc._capturedTarget.setCapture(); 220 } 221 }, 222 223 224 releaseTarget : function () { 225 // IE 226 if (jsc._capturedTarget) { 227 jsc._capturedTarget.releaseCapture(); 228 jsc._capturedTarget = null; 229 } 230 }, 231 232 233 fireEvent : function (el, evnt) { 234 if (!el) { 235 return; 236 } 237 if (document.createEvent) { 238 var ev = document.createEvent('HTMLEvents'); 239 ev.initEvent(evnt, true, true); 240 el.dispatchEvent(ev); 241 } else if (document.createEventObject) { 242 var ev = document.createEventObject(); 243 el.fireEvent('on' + evnt, ev); 244 } else if (el['on' + evnt]) { // alternatively use the traditional event model 245 el['on' + evnt](); 246 } 247 }, 248 249 250 classNameToList : function (className) { 251 return className.replace(/^\s+|\s+$/g, '').split(/\s+/); 252 }, 253 254 255 // The className parameter (str) can only contain a single class name 256 hasClass : function (elm, className) { 257 if (!className) { 258 return false; 259 } 260 return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); 261 }, 262 263 264 // The className parameter (str) can contain multiple class names separated by whitespace 265 setClass : function (elm, className) { 266 var classList = jsc.classNameToList(className); 267 for (var i = 0; i < classList.length; i += 1) { 268 if (!jsc.hasClass(elm, classList[i])) { 269 elm.className += (elm.className ? ' ' : '') + classList[i]; 270 } 271 } 272 }, 273 274 275 // The className parameter (str) can contain multiple class names separated by whitespace 276 unsetClass : function (elm, className) { 277 var classList = jsc.classNameToList(className); 278 for (var i = 0; i < classList.length; i += 1) { 279 var repl = new RegExp( 280 '^\\s*' + classList[i] + '\\s*|' + 281 '\\s*' + classList[i] + '\\s*$|' + 282 '\\s+' + classList[i] + '(\\s+)', 283 'g' 284 ); 285 elm.className = elm.className.replace(repl, '$1'); 286 } 287 }, 288 289 290 getStyle : function (elm) { 291 return window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; 292 }, 293 294 295 setStyle : (function () { 296 var helper = document.createElement('div'); 297 var getSupportedProp = function (names) { 298 for (var i = 0; i < names.length; i += 1) { 299 if (names[i] in helper.style) { 300 return names[i]; 301 } 302 } 303 }; 304 var props = { 305 borderRadius: getSupportedProp(['borderRadius', 'MozBorderRadius', 'webkitBorderRadius']), 306 boxShadow: getSupportedProp(['boxShadow', 'MozBoxShadow', 'webkitBoxShadow']) 307 }; 308 return function (elm, prop, value) { 309 switch (prop.toLowerCase()) { 310 case 'opacity': 311 var alphaOpacity = Math.round(parseFloat(value) * 100); 312 elm.style.opacity = value; 313 elm.style.filter = 'alpha(opacity=' + alphaOpacity + ')'; 314 break; 315 default: 316 elm.style[props[prop]] = value; 317 break; 318 } 319 }; 320 })(), 321 322 323 setBorderRadius : function (elm, value) { 324 jsc.setStyle(elm, 'borderRadius', value || '0'); 325 }, 326 327 328 setBoxShadow : function (elm, value) { 329 jsc.setStyle(elm, 'boxShadow', value || 'none'); 330 }, 331 332 333 getElementPos : function (e, relativeToViewport) { 334 var x=0, y=0; 335 var rect = e.getBoundingClientRect(); 336 x = rect.left; 337 y = rect.top; 338 if (!relativeToViewport) { 339 var viewPos = jsc.getViewPos(); 340 x += viewPos[0]; 341 y += viewPos[1]; 342 } 343 return [x, y]; 344 }, 345 346 347 getElementSize : function (e) { 348 return [e.offsetWidth, e.offsetHeight]; 349 }, 350 351 352 // get pointer's X/Y coordinates relative to viewport 353 getAbsPointerPos : function (e) { 354 if (!e) { e = window.event; } 355 var x = 0, y = 0; 356 if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { 357 // touch devices 358 x = e.changedTouches[0].clientX; 359 y = e.changedTouches[0].clientY; 360 } else if (typeof e.clientX === 'number') { 361 x = e.clientX; 362 y = e.clientY; 363 } 364 return { x: x, y: y }; 365 }, 366 367 368 // get pointer's X/Y coordinates relative to target element 369 getRelPointerPos : function (e) { 370 if (!e) { e = window.event; } 371 var target = e.target || e.srcElement; 372 var targetRect = target.getBoundingClientRect(); 373 374 var x = 0, y = 0; 375 376 var clientX = 0, clientY = 0; 377 if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { 378 // touch devices 379 clientX = e.changedTouches[0].clientX; 380 clientY = e.changedTouches[0].clientY; 381 } else if (typeof e.clientX === 'number') { 382 clientX = e.clientX; 383 clientY = e.clientY; 384 } 385 386 x = clientX - targetRect.left; 387 y = clientY - targetRect.top; 388 return { x: x, y: y }; 389 }, 390 391 392 getViewPos : function () { 393 var doc = document.documentElement; 394 return [ 395 (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), 396 (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) 397 ]; 398 }, 399 400 401 getViewSize : function () { 402 var doc = document.documentElement; 403 return [ 404 (window.innerWidth || doc.clientWidth), 405 (window.innerHeight || doc.clientHeight), 406 ]; 407 }, 408 409 410 redrawPosition : function () { 411 412 if (jsc.picker && jsc.picker.owner) { 413 var thisObj = jsc.picker.owner; 414 415 var tp, vp; 416 417 if (thisObj.fixed) { 418 // Fixed elements are positioned relative to viewport, 419 // therefore we can ignore the scroll offset 420 tp = jsc.getElementPos(thisObj.targetElement, true); // target pos 421 vp = [0, 0]; // view pos 422 } else { 423 tp = jsc.getElementPos(thisObj.targetElement); // target pos 424 vp = jsc.getViewPos(); // view pos 425 } 426 427 var ts = jsc.getElementSize(thisObj.targetElement); // target size 428 var vs = jsc.getViewSize(); // view size 429 var ps = jsc.getPickerOuterDims(thisObj); // picker size 430 var a, b, c; 431 switch (thisObj.position.toLowerCase()) { 432 case 'left': a=1; b=0; c=-1; break; 433 case 'right':a=1; b=0; c=1; break; 434 case 'top': a=0; b=1; c=-1; break; 435 default: a=0; b=1; c=1; break; 436 } 437 var l = (ts[b]+ps[b])/2; 438 439 // compute picker position 440 if (!thisObj.smartPosition) { 441 var pp = [ 442 tp[a], 443 tp[b]+ts[b]-l+l*c 444 ]; 445 } else { 446 var pp = [ 447 -vp[a]+tp[a]+ps[a] > vs[a] ? 448 (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >
448= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : 449 tp[a], 450 -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? 451 (-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) : 452 (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) 453 ]; 454 } 455 456 var x = pp[a]; 457 var y = pp[b]; 458 var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; 459 var contractShadow = 460 (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && 461 (pp[1] + ps[1] < tp[1] + ts[1]); 462 463 jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); 464 } 465 }, 466 467 468 _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { 469 var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px 470 471 jsc.picker.wrap.style.position = positionValue; 472 jsc.picker.wrap.style.left = x + 'px'; 473 jsc.picker.wrap.style.top = y + 'px'; 474 475 jsc.setBoxShadow( 476 jsc.picker.boxS, 477 thisObj.shadow ? 478 new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : 479 null); 480 }, 481 482 483 getPickerDims : function (thisObj) { 484 var displaySlider = !!jsc.getSliderComponent(thisObj); 485 var dims = [ 486 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.width + 487 (displaySlider ? 2 * thisObj.insetWidth + jsc.getPadToSliderPadding(thisObj) + thisObj.sliderSize : 0), 488 2 * thisObj.insetWidth + 2 * thisObj.padding + thisObj.height + 489 (thisObj.closable ? 2 * thisObj.insetWidth + thisObj.padding + thisObj.buttonHeight : 0) 490 ]; 491 return dims; 492 }, 493 494 495 getPickerOuterDims : function (thisObj) { 496 var dims = jsc.getPickerDims(thisObj); 497 return [ 498 dims[0] + 2 * thisObj.borderWidth, 499 dims[1] + 2 * thisObj.borderWidth 500 ]; 501 }, 502 503 504 getPadToSliderPadding : function (thisObj) { 505 return Math.max(thisObj.padding, 1.5 * (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness)); 506 }, 507 508 509 getPadYComponent : function (thisObj) { 510 switch (thisObj.mode.charAt(1).toLowerCase()) { 511 case 'v': return 'v'; break; 512 } 513 return 's'; 514 }, 515 516 517 getSliderComponent : function (thisObj) { 518 if (thisObj.mode.length > 2) { 519 switch (thisObj.mode.charAt(2).toLowerCase()) { 520 case 's': return 's'; break; 521 case 'v': return 'v'; break; 522 } 523 } 524 return null; 525 }, 526 527 528 onDocumentMouseDown : function (e) { 529 if (!e) { e = window.event; } 530 var target = e.target || e.srcElement; 531 532 if (target._jscLinkedInstance) { 533 if (target._jscLinkedInstance.showOnClick) { 534 target._jscLinkedInstance.show(); 535 } 536 } else if (target._jscControlName) { 537 jsc.onControlPointerStart(e, target, target._jscControlName, 'mouse'); 538 } else { 539 // Mouse is outside the picker controls -> hide the color picker! 540 if (jsc.picker && jsc.picker.owner) { 541 jsc.picker.owner.hide(); 542 } 543 } 544 }, 545 546 547 onDocumentTouchStart : function (e) { 548 if (!e) { e = window.event; } 549 var target = e.target || e.srcElement; 550 551 if (target._jscLinkedInstance) { 552 if (target._jscLinkedInstance.showOnClick) { 553 target._jscLinkedInstance.show(); 554 } 555 } else if (target._jscControlName) { 556 jsc.onControlPointerStart(e, target, target._jscControlName, 'touch'); 557 } else { 558 if (jsc.picker && jsc.picker.owner) { 559 jsc.picker.owner.hide(); 560 } 561 } 562 }, 563 564 565 onWindowResize : function (e) { 566 jsc.redrawPosition(); 567 }, 568 569 570 onParentScroll : function (e) { 571 // hide the picker when one of the parent elements is scrolled 572 if (jsc.picker && jsc.picker.owner) { 573 jsc.picker.owner.hide(); 574 } 575 }, 576 577 578 _pointerMoveEvent : { 579 mouse: 'mousemove', 580 touch: 'touchmove' 581 }, 582 _pointerEndEvent : { 583 mouse: 'mouseup', 584 touch: 'touchend' 585 }, 586 587 588 _pointerOrigin : null, 589 _capturedTarget : null, 590 591 592 onControlPointerStart : function (e, target, controlName, pointerType) { 593 var thisObj = target._jscInstance; 594 595 jsc.preventDefault(e); 596 jsc.captureTarget(target); 597 598 var registerDragEvents = function (doc, offset) { 599 jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], 600 jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); 601 jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], 602 jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); 603 }; 604 605 registerDragEvents(document, [0, 0]); 606 607 if (window.parent && window.frameElement) { 608 var rect = window.frameElement.getBoundingClientRect(); 609 var ofs = [-rect.left, -rect.top]; 610 registerDragEvents(window.parent.window.document, ofs); 611 } 612 613 var abs = jsc.getAbsPointerPos(e); 614 var rel = jsc.getRelPointerPos(e); 615 jsc._pointerOrigin = { 616 x: abs.x - rel.x, 617 y: abs.y - rel.y 618 }; 619 620 switch (controlName) { 621 case 'pad': 622 // if the slider is at the bottom, move it up 623 switch (jsc.getSliderComponent(thisObj)) { 624 case 's': if (thisObj.hsv[1] === 0) { thisObj.fromHSV(null, 100, null); }; break; 625 case 'v': if (thisObj.hsv[2] === 0) { thisObj.fromHSV(null, null, 100); }; break; 626 }
627 jsc.setPad(thisObj, e, 0, 0); 628 break; 629 630 case 'sld': 631 jsc.setSld(thisObj, e, 0); 632 break; 633 } 634 635 jsc.dispatchFineChange(thisObj); 636 }, 637 638 639 onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { 640 return function (e) { 641 var thisObj = target._jscInstance; 642 switch (controlName) { 643 case 'pad': 644 if (!e) { e = window.event; } 645 jsc.setPad(thisObj, e, offset[0], offset[1]); 646 jsc.dispatchFineChange(thisObj); 647 break; 648 649 case 'sld': 650 if (!e) { e = window.event; } 651 jsc.setSld(thisObj, e, offset[1]); 652 jsc.dispatchFineChange(thisObj); 653 break; 654 } 655 } 656 }, 657 658 659 onDocumentPointerEnd : function (e, target, controlName, pointerType) { 660 return function (e) { 661 var thisObj = target._jscInstance; 662 jsc.detachGroupEvents('drag'); 663 jsc.releaseTarget(); 664 // Always dispatch changes after detaching outstanding mouse handlers, 665 // in case some user interaction will occur in user's onchange callback 666 // that would intrude with current mouse events 667 jsc.dispatchChange(thisObj); 668 }; 669 }, 670 671 672 dispatchChange : function (thisObj) { 673 if (thisObj.valueElement) { 674 if (jsc.isElementType(thisObj.valueElement, 'input')) { 675 jsc.fireEvent(thisObj.valueElement, 'change'); 676 } 677 } 678 }, 679 680 681 dispatchFineChange : function (thisObj) { 682 if (thisObj.onFineChange) { 683 var callback; 684 if (typeof thisObj.onFineChange === 'string') { 685 callback = new Function (thisObj.onFineChange); 686 } else { 687 callback = thisObj.onFineChange; 688 } 689 callback.call(thisObj); 690 } 691 }, 692 693 694 setPad : function (thisObj, e, ofsX, ofsY) { 695 var pointerAbs = jsc.getAbsPointerPos(e); 696 var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.insetWidth; 697 var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; 698 699 var xVal = x * (360 / (thisObj.width - 1)); 700 var yVal = 100 - (y * (100 / (thisObj.height - 1))); 701 702 switch (jsc.getPadYComponent(thisObj)) { 703 case 's': thisObj.fromHSV(xVal, yVal, null, jsc.leaveSld); break; 704 case 'v': thisObj.fromHSV(xVal, null, yVal, jsc.leaveSld); break; 705 } 706 }, 707 708 709 setSld : function (thisObj, e, ofsY) { 710 var pointerAbs = jsc.getAbsPointerPos(e); 711 var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.insetWidth; 712 713 var yVal = 100 - (y * (100 / (thisObj.height - 1))); 714 715 switch (jsc.getSliderComponent(thisObj)) { 716 case 's': thisObj.fromHSV(null, yVal, null, jsc.leavePad); break; 717 case 'v': thisObj.fromHSV(null, null, yVal, jsc.leavePad); break; 718 } 719 }, 720 721 722 _vmlNS : 'jsc_vml_', 723 _vmlCSS : 'jsc_vml_css_', 724 _vmlReady : false, 725 726 727 initVML : function () { 728 if (!jsc._vmlReady) { 729 // init VML namespace 730 var doc = document; 731 if (!doc.namespaces[jsc._vmlNS]) { 732 doc.namespaces.add(jsc._vmlNS, 'urn:schemas-microsoft-com:vml'); 733 } 734 if (!doc.styleSheets[jsc._vmlCSS]) { 735 var tags = ['shape', 'shapetype', 'group', 'background', 'path', 'formulas', 'handles', 'fill', 'stroke', 'shadow', 'textbox', 'textpath', 'imagedata', 'line', 'polyline', 'curve', 'rect', 'roundrect', 'oval', 'arc', 'image']; 736 var ss = doc.createStyleSheet(); 737 ss.owningElement.id = jsc._vmlCSS; 738 for (var i = 0; i < tags.length; i += 1) { 739 ss.addRule(jsc._vmlNS + '\\:' + tags[i], 'behavior:url(#default#VML);'); 740 } 741 } 742 jsc._vmlReady = true; 743 } 744 }, 745 746 747 createPalette : function () { 748 749 var paletteObj = { 750 elm: null, 751 draw: null 752 }; 753 754 if (jsc.isCanvasSupported) { 755 // Canvas implementation for modern browsers 756 757 var canvas = document.createElement('canvas'); 758 var ctx = canvas.getContext('2d'); 759 760 var drawFunc = function (width, height, type) { 761 canvas.width = width; 762 canvas.height = height; 763 764 ctx.clearRect(0, 0, canvas.width, canvas.height); 765 766 var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); 767 hGrad.addColorStop(0 / 6, '#F00'); 768 hGrad.addColorStop(1 / 6, '#FF0'); 769 hGrad.addColorStop(2 / 6, '#0F0'); 770 hGrad.addColorStop(3 / 6, '#0FF'); 771 hGrad.addColorStop(4 / 6, '#00F'); 772 hGrad.addColorStop(5 / 6, '#F0F'); 773 hGrad.addColorStop(6 / 6, '#F00'); 774 775 ctx.fillStyle = hGrad; 776 ctx.fillRect(0, 0, canvas.width, canvas.height); 777 778 var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); 779 switch (type.toLowerCase()) { 780 case 's': 781 vGrad.addColorStop(0, 'rgba(255,255,255,0)'); 782 vGrad.addColorStop(1, 'rgba(255,255,255,1)'); 783 break; 784 case 'v': 785 vGrad.addColorStop(0, 'rgba(0,0,0,0)'); 786 vGrad.addColorStop(1, 'rgba(0,0,0,1)'); 787 break; 788 } 789 ctx.fillStyle = vGrad; 790 ctx.fillRect(0, 0, canvas.width, canvas.height); 791 }; 792 793 paletteObj.elm = canvas; 794 paletteObj.draw = drawFunc; 795 796 } else { 797 // VML fallback for IE 7 and 8 798 799 jsc.initVML(); 800 801 var vmlContainer = document.createElement('div'); 802 vmlContainer.style.position = 'relative'; 803 vmlContainer.style.overflow = 'hidden'; 804 805 var hGrad = document.createElement(jsc._vmlNS + ':fill'); 806 hGrad.type = 'gradient'; 807 hGrad.method = 'linear'; 808 hGrad.angle = '90'; 809 hGrad.colors = '16.67% #F0F, 33.33% #00F, 50% #0FF, 66.67% #0F0, 83.33% #FF0' 810 811 var hRect = document.createElement(jsc._vmlNS + ':rect'); 812 hRect.style.position = 'absolute'; 813 hRect.style.left = -1 + 'px'; 814 hRect.style.top = -1 + 'px'; 815 hRect.stroked = false; 816 hRect.appendChild(hGrad); 817 vmlContainer.appendChild(hRect); 818 819 var vGrad = document.createElement(jsc._vmlNS + ':fill'); 820 vGrad.type = 'gradient'; 821 vGrad.method = 'linear'; 822 vGrad.angle = '180'; 823 vGrad.opacity = '0'; 824 825 var vRect = document.createElement(jsc._vmlNS + ':rect'); 826 vRect.style.position = 'absolute'; 827 vRect.style.left = -1 + 'px'; 828 vRect.style.top = -1 + 'px'; 829 vRect.stroked = false; 830 vRect.appendChild(vGrad); 831 vmlContainer.appendChild(vRect); 832 833 var drawFunc = function (width, height, type) { 834 vmlContainer.style.width = width + 'px'; 835 vmlContainer.style.height = height + 'px'; 836 837 hRect.style.width = 838 vRect.style.width = 839 (width + 1) + 'px'; 840 hRect.style.height = 841 vRect.style.height = 842 (height + 1) + 'px'; 843 844 // Colors must be specified during every redraw, otherwise IE won't display 845 // a full gradient during a subsequential redraw 846 hGrad.color = '#F00'; 847 hGrad.color2 = '#F00'; 848 849 switch (type.toLowerCase()) { 850 case 's': 851 vGrad.color = vGrad.color2 = '#FFF'; 852 break; 853 case 'v': 854 vGrad.color = vGrad.color2 = '#000'; 855 break; 856 } 857 }; 858 859 paletteObj.elm = vmlContainer; 860 paletteObj.draw = drawFunc; 861 } 862 863 return paletteObj; 864 }, 865 866 867 createSliderGradient : function () { 868 869 var sliderObj = { 870 elm: null, 871 draw: null 872 }; 873 874 if (jsc.isCanvasSupported) { 875 // Canvas implementation for modern browsers 876 877 var canvas = document.createElement('canvas'); 878 var ctx = canvas.getContext('2d'); 879 880 var drawFunc = function (width, height, color1, color2) { 881 canvas.width = width; 882 canvas.height = height; 883 884 ctx.clearRect(0, 0, canvas.width, canvas.height); 885 886 var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); 887 grad.addColorStop(0, color1); 888 grad.addColorStop(1, color2); 889 890 ctx.fillStyle = grad; 891 ctx.fillRect(0, 0, canvas.width, canvas.height); 892 }; 893 894 sliderObj.elm = canvas; 895 sliderObj.draw = drawFunc; 896 897 } else { 898 // VML fallback for IE 7 and 8 899 900 jsc.initVML(); 901 902 var vmlContainer = document.createElement('div'); 903 vmlContainer.style.position = 'relative'; 904 vmlContainer.style.overflow = 'hidden'; 905 906 var grad = document.createElement(jsc._vmlNS + ':fill'); 907 grad.type = 'gradient'; 908 grad.method = 'linear'; 909 grad.angle = '180'; 910 911 var rect = document.createElement(jsc._vmlNS + ':rect'); 912 rect.style.position = 'absolute'; 913 rect.style.left = -1 + 'px'; 914 rect.style.top = -1 + 'px'; 915 rect.stroked = false; 916 rect.appendChild(grad); 917 vmlContainer.appendChild(rect); 918 919 var drawFunc = function (width, height, color1, color2) { 920 vmlContainer.style.width = width + 'px'; 921 vmlContainer.style.height = height + 'px'; 922 923 rect.style.width = (width + 1) + 'px'; 924 rect.style.height = (height + 1) + 'px'; 925 926 grad.color = color1; 927 grad.color2 = color2; 928 }; 929 930 sliderObj.elm = vmlContainer; 931 sliderObj.draw = drawFunc; 932 } 933 934 return sliderObj; 935 }, 936 937 938 leaveValue : 1<<0, 939 leaveStyle : 1<<1, 940 leavePad : 1<<2, 941 leaveSld : 1<<3, 942 943 944 BoxShadow : (function () { 945 var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) {
946 this.hShadow = hShadow; 947 this.vShadow = vShadow; 948 this.blur = blur; 949 this.spread = spread; 950 this.color = color; 951 this.inset = !!inset; 952 }; 953 954 BoxShadow.prototype.toString = function () { 955 var vals = [ 956 Math.round(this.hShadow) + 'px', 957 Math.round(this.vShadow) + 'px', 958 Math.round(this.blur) + 'px', 959 Math.round(this.spread) + 'px', 960 this.color 961 ]; 962 if (this.inset) { 963 vals.push('inset'); 964 } 965 return vals.join(' '); 966 }; 967 968 return BoxShadow; 969 })(), 970 971 972 // 973 // Usage: 974 // var myColor = new jscolor(<targetElement> [, <options>]) 975 // 976 977 jscolor : function (targetElement, options) { 978 979 // General options 980 // 981 this.value = null; // initial HEX color. To change it later, use methods fromString(), fromHSV() and fromRGB() 982 this.valueElement = targetElement; // element that will be used to display and input the color code 983 this.styleElement = targetElement; // element that will preview the picked color using CSS backgroundColor 984 this.required = true; // whether the associated text <input> can be left empty 985 this.refine = true; // whether to refine the entered color code (e.g. uppercase it and remove whitespace) 986 this.hash = false; // whether to prefix the HEX color code with # symbol 987 this.uppercase = true; // whether to uppercase the color code 988 this.onFineChange = null; // called instantly every time the color changes (value can be either a function or a string with javascript code) 989 this.activeClass = 'jscolor-active'; // class to be set to the target element when a picker window is open on it 990 this.minS = 0; // min allowed saturation (0 - 100) 991 this.maxS = 100; // max allowed saturation (0 - 100) 992 this.minV = 0; // min allowed value (brightness) (0 - 100) 993 this.maxV = 100; // max allowed value (brightness) (0 - 100) 994 995 // Accessing the picked color 996 // 997 this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100] 998 this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255] 999 1000 // Color Picker options 1001 // 1002 this.width = 181; // width of color palette (in px) 1003 this.height = 101; // height of color palette (in px) 1004 this.showOnClick = true; // whether to display the color picker when user clicks on its target element 1005 this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls 1006 this.position = 'bottom'; // left | right | top | bottom - position relative to the target element 1007 this.smartPosition = true; // automatically change picker position when there is not enough space for it 1008 this.sliderSize = 16; // px 1009 this.crossSize = 8; // px 1010 this.closable = false; // whether to display the Close button 1011 this.closeText = 'Close'; 1012 this.buttonColor = '#000000'; // CSS color 1013 this.buttonHeight = 18; // px 1014 this.padding = 12; // px 1015 this.backgroundColor = '#FFFFFF'; // CSS color 1016 this.borderWidth = 1; // px 1017 this.borderColor = '#BBBBBB'; // CSS color 1018 this.borderRadius = 8; // px 1019 this.insetWidth = 1; // px 1020 this.insetColor = '#BBBBBB'; // CSS color 1021 this.shadow = true; // whether to display shadow 1022 this.shadowBlur = 15; // px 1023 this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color 1024 this.pointerColor = '#4C4C4C'; // px 1025 this.pointerBorderColor = '#FFFFFF'; // px 1026 this.pointerBorderWidth = 1; // px 1027 this.pointerThickness = 2; // px 1028 this.zIndex = 1000; 1029 this.container = null; // where to append the color picker (BODY element by default) 1030 1031 1032 for (var opt in options) { 1033 if (options.hasOwnProperty(opt)) { 1034 this[opt] = options[opt]; 1035 } 1036 } 1037 1038 1039 this.hide = function () { 1040 if (isPickerOwner()) { 1041 detachPicker(); 1042 } 1043 }; 1044 1045 1046 this.show = function () { 1047 drawPicker(); 1048 }; 1049 1050 1051 this.redraw = function () { 1052 if (isPickerOwner()) { 1053 drawPicker(); 1054 } 1055 }; 1056 1057 1058 this.importColor = function () { 1059 if (!this.valueElement) { 1060 this.exportColor(); 1061 } else { 1062 if (jsc.isElementType(this.valueElement, 'input')) { 1063 if (!this.refine) { 1064 if (!this.fromString(this.valueElement.value, jsc.leaveValue)) { 1065 if (this.styleElement) { 1066 this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1067 this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1068 this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1069 } 1070 this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1071 } 1072 } else if (!this.required && /^\s*$/.test(this.valueElement.value)) { 1073 this.valueElement.value = ''; 1074 if (this.styleElement) { 1075 this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1076 this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1077 this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1078 } 1079 this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1080 1081 } else if (this.fromString(this.valueElement.value)) { 1082 // managed to import color successfully from the value -> OK, don't do anything 1083 } else { 1084 this.exportColor(); 1085 } 1086 } else { 1087 // not an input element -> doesn't have any value 1088 this.exportColor(); 1089 } 1090 } 1091 }; 1092 1093 1094 this.exportColor = function (flags) { 1095 if (!(flags & jsc.leaveValue) && this.valueElement) { 1096 var value = this.toString(); 1097 if (this.uppercase) { value = value.toUpperCase(); } 1098 if (this.hash) { value = '#' + value; } 1099 1100 if (jsc.isElementType(this.valueElement, 'input')) { 1101 this.valueElement.value = value; 1102 } else { 1103 this.valueElement.innerHTML = value; 1104 } 1105 } 1106 if (!(flags & jsc.leaveStyle)) { 1107 if (this.styleElement) { 1108 this.styleElement.style.backgroundImage = 'none'; 1109 this.styleElement.style.backgroundColor = '#' + this.toString(); 1110 this.styleElement.style.color = this.isLight() ? '#000' : '#FFF'; 1111 } 1112 } 1113 if (!(flags & jsc.leavePad) && isPickerOwner()) { 1114 redrawPad(); 1115 } 1116 if (!(flags & jsc.leaveSld) && isPickerOwner()) { 1117 redrawSld(); 1118 } 1119 }; 1120 1121 1122 // h: 0-360 1123 // s: 0-100 1124 // v: 0-100 1125 // 1126 this.fromHSV = function (h, s, v, flags) { // null = don't change 1127 if (h !== null) { 1128 if (isNaN(h)) { return false; } 1129 h = Math.max(0, Math.min(360, h)); 1130 } 1131 if (s !== null) { 1132 if (isNaN(s)) { return false; } 1133 s = Math.max(0, Math.min(100, this.maxS, s), this.minS); 1134 } 1135 if (v !== null) { 1136 if (isNaN(v)) { return false; } 1137 v = Math.max(0, Math.min(100, this.maxV, v), this.minV); 1138 } 1139 1140 this.rgb = HSV_RGB( 1141 h===null ? this.hsv[0] : (this.hsv[0]=h), 1142 s===null ? this.hsv[1] : (this.hsv[1]=s), 1143 v===null ? this.hsv[2] : (this.hsv[2]=v) 1144 ); 1145 1146 this.exportColor(flags); 1147 }; 1148 1149 1150 // r: 0-255 1151 // g: 0-255 1152 // b: 0-255 1153 // 1154 this.fromRGB = function (r, g, b, flags) { // null = don't change 1155 if (r !== null) { 1156 if (isNaN(r)) { return false; } 1157 r = Math.max(0, Math.min(255, r)); 1158 } 1159 if (g !== null) { 1160 if (isNaN(g)) { return false; } 1161 g = Math.max(0, Math.min(255, g)); 1162 } 1163 if (b !== null) { 1164 if (isNaN(b)) { return false; } 1165 b = Math.max(0, Math.min(255, b)); 1166 } 1167 1168 var hsv = RGB_HSV( 1169 r===null ? this.rgb[0] : r, 1170 g===null ? this.rgb[1] : g, 1171 b===null ? this.rgb[2] : b 1172 ); 1173 if (hsv[0] !== null) { 1174 this.hsv[0] = Math.max(0, Math.min(360, hsv[0])); 1175 } 1176 if (hsv[2] !== 0) { 1177 this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); 1178 } 1179 this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); 1180 1181 // update RGB according to final HSV, as some values might be trimmed 1182 var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); 1183 this.rgb[0] = rgb[0]; 1184 this.rgb[1] = rgb[1]; 1185 this.rgb[2] = rgb[2]; 1186 1187 this.exportColor(flags); 1188 }; 1189 1190 1191 this.fromString = function (str, flags) { 1192 var m; 1193 if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) { 1194 // HEX notation 1195 // 1196 1197 if (m[1].length === 6) { 1198 // 6-char notation 1199 this.fromRGB( 1200 parseInt(m[1].substr(0,2),16), 1201 parseInt(m[1].substr(2,2),16), 1202 parseInt(m[1].substr(4,2),16), 1203 flags 1204 ); 1205 } else { 1206 // 3-char notation 1207 this.fromRGB( 1208 parseInt(m[1].charAt(0) + m[1].charAt(0),16), 1209 parseInt(m[1].charAt(1) + m[1].charAt(1),16), 1210 parseInt(m[1].charAt(2) + m[1].charAt(2),16), 1211 flags 1212 ); 1213 } 1214 return true; 1215 1216 } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { 1217 var params = m[1].split(','); 1218 var re = /^\s*(\d*)(\.\d+)?\s*$/; 1219 var mR, mG, mB; 1220 if ( 1221 params.length >= 3 && 1222 (mR = params[0].match(re)) && 1223 (mG = params[1].match(re)) && 1224 (mB = params[2].match(re)) 1225 ) { 1226 var r = parseFloat((mR[1] || '0') + (mR[2] || '')); 1227 var g = parseFloat((mG[1] || '0') + (mG[2] || '')); 1228 var b = parseFloat((mB[1] || '0') + (mB[2] || '')); 1229 this.fromRGB(r, g, b, flags); 1230 return true; 1231 } 1232 } 1233 return false; 1234 }; 1235 1236 1237 this.toString = function () { 1238 return ( 1239 (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) + 1240 (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) + 1241 (0x100 | Math.round(this.rgb[2])).toString(16).substr(1) 1242 ); 1243 }; 1244 1245 1246 this.toHEXString = function () { 1247 return '#' + this.toString().toUpperCase(); 1248 }; 1249 1250 1251 this.toRGBString = function () { 1252 return ('rgb(' + 1253 Math.round(this.rgb[0]) + ',' + 1254 Math.round(this.rgb[1]) + ',' + 1255 Math.round(this.rgb[2]) + ')' 1256 ); 1257 }; 1258 1259 1260 this.isLight = function () { 1261 return ( 1262 0.213 * this.rgb[0] + 1263 0.715 * this.rgb[1] + 1264 0.072 * this.rgb[2] > 1265 255 / 2 1266 ); 1267 }; 1268 1269 1270 this._processParentElementsInDOM = function () { 1271 if (this._linkedElementsProcessed) { return; } 1272 this._linkedElementsProcessed = true; 1273 1274 var elm = this.targetElement; 1275 do { 1276 // If the target element or one of its parent nodes has fixed position, 1277 // then use fixed positioning instead 1278 // 1279 // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1280 // that's why we need to check if the returned style object is non-empty 1281 var currStyle = jsc.getStyle(elm); 1282 if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1283 this.fixed = true; 1284 } 1285 1286 if (elm !== this.targetElement) { 1287 // Ensure to attach onParentScroll only once to each parent element 1288 // (multiple targetElements can share the same parent nodes) 1289 // 1290 // Note: It's not just offsetParents that can be scrollable, 1291 // that's why we loop through all parent nodes 1292 if (!elm._jscEventsAttached) { 1293 jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1294 elm._jscEventsAttached = true; 1295 } 1296 } 1297 } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body')); 1298 }; 1299 1300 1301 // r: 0-255 1302 // g: 0-255 1303 // b: 0-255 1304 // 1305 // returns: [ 0-360, 0-100, 0-100 ] 1306 // 1307 function RGB_HSV (r, g, b) { 1308 r /= 255; 1309 g /= 255; 1310 b /= 255; 1311 var n = Math.min(Math.min(r,g),b); 1312 var v = Math.max(Math.max(r,g),b); 1313 var m = v - n; 1314 if (m === 0) { return [ null, 0, 100 * v ]; } 1315 var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 1316 return [ 1317 60 * (h===6?0:h), 1318 100 * (m/v), 1319 100 * v 1320 ]; 1321 } 1322 1323 1324 // h: 0-360 1325 // s: 0-100 1326 // v: 0-100 1327 // 1328 // returns: [ 0-255, 0-255, 0-255 ] 1329 // 1330 function HSV_RGB (h, s, v) { 1331 var u = 255 * (v / 100); 1332 1333 if (h === null) { 1334 return [ u, u, u ]; 1335 } 1336 1337 h /= 60; 1338 s /= 100; 1339 1340 var i = Math.floor(h); 1341 var f = i%2 ? h-i : 1-(h-i); 1342 var m = u * (1 - s); 1343 var n = u * (1 - s * f); 1344 switch (i) { 1345 case 6: 1346 case 0: return [u,n,m]; 1347 case 1: return [n,u,m]; 1348 case 2: return [m,u,n]; 1349 case 3: return [m,n,u]; 1350 case 4: return [n,m,u]; 1351 case 5: return [u,m,n]; 1352 } 1353 } 1354 1355 1356 function detachPicker () { 1357 jsc.unsetClass(THIS.targetElement, THIS.activeClass); 1358 jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); 1359 delete jsc.picker.owner; 1360 } 1361 1362 1363 function drawPicker () { 1364
1365 // At this point, when drawing the picker, we know what the parent elements are 1366 // and we can do all related DOM operations, such as registering events on them 1367 // or checking their positioning 1368 THIS._processParentElementsInDOM(); 1369 1370 if (!jsc.picker) { 1371 jsc.picker = { 1372 owner: null, 1373 wrap : document.createElement('div'), 1374 box : document.createElement('div'), 1375 boxS : document.createElement('div'), // shadow area 1376 boxB : document.createElement('div'), // border 1377 pad : document.createElement('div'), 1378 padB : document.createElement('div'), // border 1379 padM : document.createElement('div'), // mouse/touch area 1380 padPal : jsc.createPalette(), 1381 cross : document.createElement('div'), 1382 crossBY : document.createElement('div'), // border Y 1383 crossBX : document.createElement('div'), // border X 1384 crossLY : document.createElement('div'), // line Y 1385 crossLX : document.createElement('div'), // line X 1386 sld : document.createElement('div'), 1387 sldB : document.createElement('div'), // border 1388 sldM : document.createElement('div'), // mouse/touch area 1389 sldGrad : jsc.createSliderGradient(), 1390 sldPtrS : document.createElement('div'), // slider pointer spacer 1391 sldPtrIB : document.createElement('div'), // slider pointer inner border 1392 sldPtrMB : document.createElement('div'), // slider pointer middle border 1393 sldPtrOB : document.createElement('div'), // slider pointer outer border 1394 btn : document.createElement('div'), 1395 btnT : document.createElement('span') // text 1396 }; 1397 1398 jsc.picker.pad.appendChild(jsc.picker.padPal.elm); 1399 jsc.picker.padB.appendChild(jsc.picker.pad); 1400 jsc.picker.cross.appendChild(jsc.picker.crossBY); 1401 jsc.picker.cross.appendChild(jsc.picker.crossBX); 1402 jsc.picker.cross.appendChild(jsc.picker.crossLY); 1403 jsc.picker.cross.appendChild(jsc.picker.crossLX); 1404 jsc.picker.padB.appendChild(jsc.picker.cross); 1405 jsc.picker.box.appendChild(jsc.picker.padB); 1406 jsc.picker.box.appendChild(jsc.picker.padM); 1407 1408 jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); 1409 jsc.picker.sldB.appendChild(jsc.picker.sld); 1410 jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); 1411 jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); 1412 jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); 1413 jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); 1414 jsc.picker.box.appendChild(jsc.picker.sldB); 1415 jsc.picker.box.appendChild(jsc.picker.sldM); 1416 1417 jsc.picker.btn.appendChild(jsc.picker.btnT); 1418 jsc.picker.box.appendChild(jsc.picker.btn); 1419 1420 jsc.picker.boxB.appendChild(jsc.picker.box); 1421 jsc.picker.wrap.appendChild(jsc.picker.boxS); 1422 jsc.picker.wrap.appendChild(jsc.picker.boxB); 1423 } 1424 1425 var p = jsc.picker; 1426 1427 var displaySlider = !!jsc.getSliderComponent(THIS); 1428 var dims = jsc.getPickerDims(THIS); 1429 var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1430 var padToSliderPadding = jsc.getPadToSliderPadding(THIS); 1431 var borderRadius = Math.min( 1432 THIS.borderRadius, 1433 Math.round(THIS.padding * Math.PI)); // px 1434 var padCursor = 'crosshair'; 1435 1436 // wrap 1437 p.wrap.style.clear = 'both'; 1438 p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px'; 1439 p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px'; 1440 p.wrap.style.zIndex = THIS.zIndex; 1441 1442 // picker 1443 p.box.style.width = dims[0] + 'px'; 1444 p.box.style.height = dims[1] + 'px'; 1445 1446 p.boxS.style.position = 'absolute'; 1447 p.boxS.style.left = '0'; 1448 p.boxS.style.top = '0'; 1449 p.boxS.style.width = '100%'; 1450 p.boxS.style.height = '100%'; 1451 jsc.setBorderRadius(p.boxS, borderRadius + 'px'); 1452 1453 // picker border 1454 p.boxB.style.position = 'relative'; 1455 p.boxB.style.border = THIS.borderWidth + 'px solid'; 1456 p.boxB.style.borderColor = THIS.borderColor; 1457 p.boxB.style.background = THIS.backgroundColor; 1458 jsc.setBorderRadius(p.boxB, borderRadius + 'px'); 1459 1460 // IE hack: 1461 // If the element is transparent, IE will trigger the event on the elements under it, 1462 // e.g. on Canvas or on elements with border 1463 p.padM.style.background = 1464 p.sldM.style.background = 1465 '#FFF'; 1466 jsc.setStyle(p.padM, 'opacity', '0'); 1467 jsc.setStyle(p.sldM, 'opacity', '0'); 1468 1469 // pad 1470 p.pad.style.position = 'relative'; 1471 p.pad.style.width = THIS.width + 'px'; 1472 p.pad.style.height = THIS.height + 'px'; 1473 1474 // pad palettes (HSV and HVS) 1475 p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS)); 1476 1477 // pad border 1478 p.padB.style.position = 'absolute'; 1479 p.padB.style.left = THIS.padding + 'px'; 1480 p.padB.style.top = THIS.padding + 'px'; 1481 p.padB.style.border = THIS.insetWidth + 'px solid'; 1482 p.padB.style.borderColor = THIS.insetColor; 1483 1484 // pad mouse area 1485 p.padM._jscInstance = THIS; 1486 p.padM._jscControlName = 'pad'; 1487 p.padM.style.position = 'absolute'; 1488 p.padM.style.left = '0'; 1489 p.padM.style.top = '0'; 1490 p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px'; 1491 p.padM.style.height = dims[1] + 'px'; 1492 p.padM.style.cursor = padCursor; 1493 1494 // pad cross 1495 p.cross.style.position = 'absolute'; 1496 p.cross.style.left = 1497 p.cross.style.top = 1498 '0'; 1499 p.cross.style.width = 1500 p.cross.style.height = 1501 crossOuterSize + 'px'; 1502 1503 // pad cross border Y and X 1504 p.crossBY.style.position = 1505 p.crossBX.style.position = 1506 'absolute'; 1507 p.crossBY.style.background = 1508 p.crossBX.style.background = 1509 THIS.pointerBorderColor; 1510 p.crossBY.style.width = 1511 p.crossBX.style.height = 1512 (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1513 p.crossBY.style.height = 1514 p.crossBX.style.width = 1515 crossOuterSize + 'px'; 1516 p.crossBY.style.left = 1517 p.crossBX.style.top = 1518 (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; 1519 p.crossBY.style.top = 1520 p.crossBX.style.left = 1521 '0'; 1522 1523 // pad cross line Y and X 1524 p.crossLY.style.position = 1525 p.crossLX.style.position = 1526 'absolute'; 1527 p.crossLY.style.background = 1528 p.crossLX.style.background = 1529 THIS.pointerColor; 1530 p.crossLY.style.height = 1531 p.crossLX.style.width = 1532 (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; 1533 p.crossLY.style.width = 1534 p.crossLX.style.height = 1535 THIS.pointerThickness + 'px'; 1536 p.crossLY.style.left = 1537 p.crossLX.style.top = 1538 (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; 1539 p.crossLY.style.top = 1540 p.crossLX.style.left = 1541 THIS.pointerBorderWidth + 'px'; 1542 1543 // slider 1544 p.sld.style.overflow = 'hidden'; 1545 p.sld.style.width = THIS.sliderSize + 'px'; 1546 p.sld.style.height = THIS.height + 'px'; 1547 1548 // slider gradient 1549 p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); 1550 1551 // slider border 1552 p.sldB.style.display = displaySlider ? 'block' : 'none'; 1553 p.sldB.style.position = 'absolute'; 1554 p.sldB.style.right = THIS.padding + 'px'; 1555 p.sldB.style.top = THIS.padding + 'px'; 1556 p.sldB.style.border = THIS.insetWidth + 'px solid'; 1557 p.sldB.style.borderColor = THIS.insetColor; 1558 1559 // slider mouse area 1560 p.sldM._jscInstance = THIS; 1561 p.sldM._jscControlName = 'sld'; 1562 p.sldM.style.display = displaySlider ? 'block' : 'none'; 1563 p.sldM.style.position = 'absolute'; 1564 p.sldM.style.right = '0'; 1565 p.sldM.style.top = '0'; 1566 p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px'; 1567 p.sldM.style.height = dims[1] + 'px'; 1568 p.sldM.style.cursor = 'default'; 1569 1570 // slider pointer inner and outer border 1571 p.sldPtrIB.style.border = 1572 p.sldPtrOB.style.border = 1573 THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; 1574 1575 // slider pointer outer border 1576 p.sldPtrOB.style.position = 'absolute'; 1577 p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1578 p.sldPtrOB.style.top = '0'; 1579 1580 // slider pointer middle border 1581 p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; 1582 1583 // slider pointer spacer
1584 p.sldPtrS.style.width = THIS.sliderSize + 'px'; 1585 p.sldPtrS.style.height = sliderPtrSpace + 'px'; 1586 1587 // the Close button 1588 function setBtnBorder () { 1589 var insetColors = THIS.insetColor.split(/\s+/); 1590 var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 1591 p.btn.style.borderColor = outsetColor; 1592 } 1593 p.btn.style.display = THIS.closable ? 'block' : 'none'; 1594 p.btn.style.position = 'absolute'; 1595 p.btn.style.left = THIS.padding + 'px'; 1596 p.btn.style.bottom = THIS.padding + 'px'; 1597 p.btn.style.padding = '0 15px'; 1598 p.btn.style.height = THIS.buttonHeight + 'px'; 1599 p.btn.style.border = THIS.insetWidth + 'px solid'; 1600 setBtnBorder(); 1601 p.btn.style.color = THIS.buttonColor; 1602 p.btn.style.font = '12px sans-serif'; 1603 p.btn.style.textAlign = 'center'; 1604 try { 1605 p.btn.style.cursor = 'pointer'; 1606 } catch(eOldIE) { 1607 p.btn.style.cursor = 'hand'; 1608 } 1609 p.btn.onmousedown = function () { 1610 THIS.hide(); 1611 }; 1612 p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; 1613 p.btnT.innerHTML = ''; 1614 p.btnT.appendChild(document.createTextNode(THIS.closeText)); 1615 1616 // place pointers 1617 redrawPad(); 1618 redrawSld(); 1619 1620 // If we are changing the owner without first closing the picker, 1621 // make sure to first deal with the old owner 1622 if (jsc.picker.owner && jsc.picker.owner !== THIS) { 1623 jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass); 1624 } 1625 1626 // Set the new picker owner 1627 jsc.picker.owner = THIS; 1628 1629 // The redrawPosition() method needs picker.owner to be set, that's why we call it here, 1630 // after setting the owner 1631 if (jsc.isElementType(container, 'body')) { 1632 jsc.redrawPosition(); 1633 } else { 1634 jsc._drawPosition(THIS, 0, 0, 'relative', false); 1635 } 1636 1637 if (p.wrap.parentNode != container) { 1638 container.appendChild(p.wrap); 1639 } 1640 1641 jsc.setClass(THIS.targetElement, THIS.activeClass); 1642 } 1643 1644 1645 function redrawPad () { 1646 // redraw the pad pointer 1647 switch (jsc.getPadYComponent(THIS)) { 1648 case 's': var yComponent = 1; break; 1649 case 'v': var yComponent = 2; break; 1650 } 1651 var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1)); 1652 var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1653 var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1654 var ofs = -Math.floor(crossOuterSize / 2); 1655 jsc.picker.cross.style.left = (x + ofs) + 'px'; 1656 jsc.picker.cross.style.top = (y + ofs) + 'px'; 1657 1658 // redraw the slider 1659 switch (jsc.getSliderComponent(THIS)) { 1660 case 's': 1661 var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]); 1662 var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]); 1663 var color1 = 'rgb(' + 1664 Math.round(rgb1[0]) + ',' + 1665 Math.round(rgb1[1]) + ',' + 1666 Math.round(rgb1[2]) + ')'; 1667 var color2 = 'rgb(' + 1668 Math.round(rgb2[0]) + ',' + 1669 Math.round(rgb2[1]) + ',' + 1670 Math.round(rgb2[2]) + ')'; 1671 jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1672 break; 1673 case 'v': 1674 var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100); 1675 var color1 = 'rgb(' + 1676 Math.round(rgb[0]) + ',' + 1677 Math.round(rgb[1]) + ',' + 1678 Math.round(rgb[2]) + ')'; 1679 var color2 = '#000'; 1680 jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1681 break; 1682 } 1683 } 1684 1685 1686 function redrawSld () { 1687 var sldComponent = jsc.getSliderComponent(THIS); 1688 if (sldComponent) { 1689 // redraw the slider pointer 1690 switch (sldComponent) { 1691 case 's': var yComponent = 1; break; 1692 case 'v': var yComponent = 2; break; 1693 } 1694 var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1695 jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px'; 1696 } 1697 } 1698 1699 1700 function isPickerOwner () { 1701 return jsc.picker && jsc.picker.owner === THIS; 1702 } 1703 1704 1705 function blurValue () { 1706 THIS.importColor(); 1707 } 1708 1709 1710 // Find the target element 1711 if (typeof targetElement === 'string') { 1712 var id = targetElement; 1713 var elm = document.getElementById(id); 1714 if (elm) { 1715 this.targetElement = elm; 1716 } else { 1717 jsc.warn('Could not find target element with ID \'' + id + '\''); 1718 } 1719 } else if (targetElement) { 1720 this.targetElement = targetElement; 1721 } else { 1722 jsc.warn('Invalid target element: \'' + targetElement + '\''); 1723 } 1724 1725 if (this.targetElement._jscLinkedInstance) { 1726 jsc.warn('Cannot link jscolor twice to the same element. Skipping.'); 1727 return; 1728 } 1729 this.targetElement._jscLinkedInstance = this; 1730 1731 // Find the value element 1732 this.valueElement = jsc.fetchElement(this.valueElement); 1733 // Find the style element 1734 this.styleElement = jsc.fetchElement(this.styleElement); 1735 1736 var THIS = this; 1737 var container = 1738 this.container ? 1739 jsc.fetchElement(this.container) : 1740 document.getElementsByTagName('body')[0]; 1741 var sliderPtrSpace = 3; // px 1742 1743 // For BUTTON elements it's important to stop them from sending the form when clicked 1744 // (e.g. in Safari) 1745 if (jsc.isElementType(this.targetElement, 'button')) { 1746 if (this.targetElement.onclick) { 1747 var origCallback = this.targetElement.onclick; 1748 this.targetElement.onclick = function (evt) { 1749 origCallback.call(this, evt); 1750 return false; 1751 }; 1752 } else { 1753 this.targetElement.onclick = function () { return false; }; 1754 } 1755 } 1756 1757 /* 1758 var elm = this.targetElement; 1759 do { 1760 // If the target element or one of its offsetParents has fixed position, 1761 // then use fixed positioning instead 1762 // 1763 // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1764 // that's why we need to check if the returned style object is non-empty 1765 var currStyle = jsc.getStyle(elm); 1766 if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1767 this.fixed = true; 1768 } 1769 1770 if (elm !== this.targetElement) { 1771 // attach onParentScroll so that we can recompute the picker position 1772 // when one of the offsetParents is scrolled 1773 if (!elm._jscEventsAttached) { 1774 jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1775 elm._jscEventsAttached = true; 1776 } 1777 } 1778 } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body')); 1779 */ 1780 1781 // valueElement 1782 if (this.valueElement) { 1783 if (jsc.isElementType(this.valueElement, 'input')) { 1784 var updateField = function () { 1785 THIS.fromString(THIS.valueElement.value, jsc.leaveValue); 1786 jsc.dispatchFineChange(THIS); 1787 }; 1788 jsc.attachEvent(this.valueElement, 'keyup', updateField); 1789 jsc.attachEvent(this.valueElement, 'input', updateField); 1790 jsc.attachEvent(this.valueElement, 'blur', blurValue); 1791 this.valueElement.setAttribute('autocomplete', 'off'); 1792 } 1793 } 1794 1795 // styleElement 1796 if (this.styleElement) { 1797 this.styleElement._jscOrigStyle = { 1798 backgroundImage : this.styleElement.style.backgroundImage, 1799 backgroundColor : this.styleElement.style.backgroundColor, 1800 color : this.styleElement.style.color 1801 }; 1802 } 1803 1804 if (this.value) { 1805 // Try to set the color from the .value option and if unsuccessful, 1806 // export the current color 1807 this.fromString(this.value) || this.exportColor(); 1808 } else { 1809 this.importColor(); 1810 } 1811 } 1812 1813}; 1814 1815 1816//================================ 1817// Public properties and methods 1818//================================ 1819 1820 1821// By default, search for all elements with class="jscolor" and install a color picker on them. 1822// 1823// You can change what class name will be looked for by setting the property jscolor.lookupClass 1824// anywhere in your HTML document. To completely disable the automatic lookup, set it to null. 1825// 1826jsc.jscolor.lookupClass = 'jscolor'; 1827 1828 1829jsc.jscolor.installByClassName = function (className) { 1830 var inputElms = document.getElementsByTagName('input'); 1831 var buttonElms = document.getElementsByTagName('button'); 1832 1833 jsc.tryInstallOnElements(inputElms, className); 1834 jsc.tryInstallOnElements(buttonElms, className); 1835}; 1836 1837 1838jsc.register(); 1839 1840 1841return jsc.jscolor; 1842 1843 1844})(); }
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.