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