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.5 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 show the color code in upper case 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.overwriteImportant = false; // whether to overwrite colors of styleElement using !important 991 this.minS = 0; // min allowed saturation (0 - 100) 992 this.maxS = 100; // max allowed saturation (0 - 100) 993 this.minV = 0; // min allowed value (brightness) (0 - 100) 994 this.maxV = 100; // max allowed value (brightness) (0 - 100) 995 996 // Accessing the picked color 997 // 998 this.hsv = [0, 0, 100]; // read-only [0-360, 0-100, 0-100] 999 this.rgb = [255, 255, 255]; // read-only [0-255, 0-255, 0-255] 1000 1001 // Color Picker options 1002 // 1003 this.width = 181; // width of color palette (in px) 1004 this.height = 101; // height of color palette (in px) 1005 this.showOnClick = true; // whether to display the color picker when user clicks on its target element 1006 this.mode = 'HSV'; // HSV | HVS | HS | HV - layout of the color picker controls 1007 this.position = 'bottom'; // left | right | top | bottom - position relative to the target element 1008 this.smartPosition = true; // automatically change picker position when there is not enough space for it 1009 this.sliderSize = 16; // px 1010 this.crossSize = 8; // px 1011 this.closable = false; // whether to display the Close button 1012 this.closeText = 'Close'; 1013 this.buttonColor = '#000000'; // CSS color 1014 this.buttonHeight = 18; // px 1015 this.padding = 12; // px 1016 this.backgroundColor = '#FFFFFF'; // CSS color 1017 this.borderWidth = 1; // px 1018 this.borderColor = '#BBBBBB'; // CSS color 1019 this.borderRadius = 8; // px 1020 this.insetWidth = 1; // px 1021 this.insetColor = '#BBBBBB'; // CSS color 1022 this.shadow = true; // whether to display shadow 1023 this.shadowBlur = 15; // px 1024 this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color 1025 this.pointerColor = '#4C4C4C'; // px 1026 this.pointerBorderColor = '#FFFFFF'; // px 1027 this.pointerBorderWidth = 1; // px 1028 this.pointerThickness = 2; // px 1029 this.zIndex = 1000; 1030 this.container = null; // where to append the color picker (BODY element by default) 1031 1032 1033 for (var opt in options) { 1034 if (options.hasOwnProperty(opt)) { 1035 this[opt] = options[opt]; 1036 } 1037 } 1038 1039 1040 this.hide = function () { 1041 if (isPickerOwner()) { 1042 detachPicker(); 1043 } 1044 }; 1045 1046 1047 this.show = function () { 1048 drawPicker(); 1049 }; 1050 1051 1052 this.redraw = function () { 1053 if (isPickerOwner()) { 1054 drawPicker(); 1055 } 1056 }; 1057 1058 1059 this.importColor = function () { 1060 if (!this.valueElement) { 1061 this.exportColor(); 1062 } else { 1063 if (jsc.isElementType(this.valueElement, 'input')) { 1064 if (!this.refine) { 1065 if (!this.fromString(this.valueElement.value, jsc.leaveValue)) { 1066 if (this.styleElement) { 1067 this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1068 this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1069 this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1070 } 1071 this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1072 } 1073 } else if (!this.required && /^\s*$/.test(this.valueElement.value)) { 1074 this.valueElement.value = ''; 1075 if (this.styleElement) { 1076 this.styleElement.style.backgroundImage = this.styleElement._jscOrigStyle.backgroundImage; 1077 this.styleElement.style.backgroundColor = this.styleElement._jscOrigStyle.backgroundColor; 1078 this.styleElement.style.color = this.styleElement._jscOrigStyle.color; 1079 } 1080 this.exportColor(jsc.leaveValue | jsc.leaveStyle); 1081 1082 } else if (this.fromString(this.valueElement.value)) { 1083 // managed to import color successfully from the value -> OK, don't do anything 1084 } else { 1085 this.exportColor(); 1086 } 1087 } else { 1088 // not an input element -> doesn't have any value 1089 this.exportColor(); 1090 } 1091 } 1092 }; 1093 1094 1095 this.exportColor = function (flags) { 1096 if (!(flags & jsc.leaveValue) && this.valueElement) { 1097 var value = this.toString(); 1098 if (this.uppercase) { value = value.toUpperCase(); } 1099 if (this.hash) { value = '#' + value; } 1100 1101 if (jsc.isElementType(this.valueElement, 'input')) { 1102 this.valueElement.value = value; 1103 } else { 1104 this.valueElement.innerHTML = value; 1105 } 1106 } 1107 if (!(flags & jsc.leaveStyle)) { 1108 if (this.styleElement) { 1109 var bgColor = '#' + this.toString(); 1110 var fgColor = this.isLight() ? '#000' : '#FFF'; 1111 1112 this.styleElement.style.backgroundImage = 'none'; 1113 this.styleElement.style.backgroundColor = bgColor; 1114 this.styleElement.style.color = fgColor; 1115 1116 if (this.overwriteImportant) { 1117 this.styleElement.setAttribute('style', 1118 'background: ' + bgColor + ' !important; ' + 1119 'color: ' + fgColor + ' !important;' 1120 ); 1121 } 1122 } 1123 } 1124 if (!(flags & jsc.leavePad) && isPickerOwner()) { 1125 redrawPad(); 1126 } 1127 if (!(flags & jsc.leaveSld) && isPickerOwner()) { 1128 redrawSld(); 1129 } 1130 }; 1131 1132 1133 // h: 0-360 1134 // s: 0-100 1135 // v: 0-100 1136 // 1137 this.fromHSV = function (h, s, v, flags) { // null = don't change 1138 if (h !== null) { 1139 if (isNaN(h)) { return false; } 1140 h = Math.max(0, Math.min(360, h)); 1141 } 1142 if (s !== null) { 1143 if (isNaN(s)) { return false; } 1144 s = Math.max(0, Math.min(100, this.maxS, s), this.minS); 1145 } 1146 if (v !== null) { 1147 if (isNaN(v)) { return false; } 1148 v = Math.max(0, Math.min(100, this.maxV, v), this.minV); 1149 } 1150 1151 this.rgb = HSV_RGB( 1152 h===null ? this.hsv[0] : (this.hsv[0]=h), 1153 s===null ? this.hsv[1] : (this.hsv[1]=s), 1154 v===null ? this.hsv[2] : (this.hsv[2]=v) 1155 ); 1156 1157 this.exportColor(flags); 1158 }; 1159 1160 1161 // r: 0-255 1162 // g: 0-255 1163 // b: 0-255 1164 // 1165 this.fromRGB = function (r, g, b, flags) { // null = don't change 1166 if (r !== null) { 1167 if (isNaN(r)) { return false; } 1168 r = Math.max(0, Math.min(255, r)); 1169 } 1170 if (g !== null) { 1171 if (isNaN(g)) { return false; } 1172 g = Math.max(0, Math.min(255, g)); 1173 } 1174 if (b !== null) { 1175 if (isNaN(b)) { return false; } 1176 b = Math.max(0, Math.min(255, b)); 1177 } 1178 1179 var hsv = RGB_HSV( 1180 r===null ? this.rgb[0] : r, 1181 g===null ? this.rgb[1] : g, 1182 b===null ? this.rgb[2] : b 1183 ); 1184 if (hsv[0] !== null) { 1185 this.hsv[0] = Math.max(0, Math.min(360, hsv[0])); 1186 } 1187 if (hsv[2] !== 0) { 1188 this.hsv[1] = hsv[1]===null ? null : Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); 1189 } 1190 this.hsv[2] = hsv[2]===null ? null : Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); 1191 1192 // update RGB according to final HSV, as some values might be trimmed 1193 var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); 1194 this.rgb[0] = rgb[0]; 1195 this.rgb[1] = rgb[1]; 1196 this.rgb[2] = rgb[2]; 1197 1198 this.exportColor(flags); 1199 }; 1200 1201 1202 this.fromString = function (str, flags) { 1203 var m; 1204 if (m = str.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i)) { 1205 // HEX notation 1206 // 1207 1208 if (m[1].length === 6) { 1209 // 6-char notation 1210 this.fromRGB( 1211 parseInt(m[1].substr(0,2),16), 1212 parseInt(m[1].substr(2,2),16), 1213 parseInt(m[1].substr(4,2),16), 1214 flags 1215 ); 1216 } else { 1217 // 3-char notation 1218 this.fromRGB( 1219 parseInt(m[1].charAt(0) + m[1].charAt(0),16), 1220 parseInt(m[1].charAt(1) + m[1].charAt(1),16), 1221 parseInt(m[1].charAt(2) + m[1].charAt(2),16), 1222 flags 1223 ); 1224 } 1225 return true; 1226 1227 } else if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { 1228 var params = m[1].split(','); 1229 var re = /^\s*(\d*)(\.\d+)?\s*$/; 1230 var mR, mG, mB; 1231 if ( 1232 params.length >= 3 && 1233 (mR = params[0].match(re)) && 1234 (mG = params[1].match(re)) && 1235 (mB = params[2].match(re)) 1236 ) { 1237 var r = parseFloat((mR[1] || '0') + (mR[2] || '')); 1238 var g = parseFloat((mG[1] || '0') + (mG[2] || '')); 1239 var b = parseFloat((mB[1] || '0') + (mB[2] || '')); 1240 this.fromRGB(r, g, b, flags); 1241 return true; 1242 } 1243 } 1244 return false; 1245 }; 1246 1247 1248 this.toString = function () { 1249 return ( 1250 (0x100 | Math.round(this.rgb[0])).toString(16).substr(1) + 1251 (0x100 | Math.round(this.rgb[1])).toString(16).substr(1) + 1252 (0x100 | Math.round(this.rgb[2])).toString(16).substr(1) 1253 ); 1254 }; 1255 1256 1257 this.toHEXString = function () { 1258 return '#' + this.toString().toUpperCase(); 1259 }; 1260 1261 1262 this.toRGBString = function () { 1263 return ('rgb(' + 1264 Math.round(this.rgb[0]) + ',' + 1265 Math.round(this.rgb[1]) + ',' + 1266 Math.round(this.rgb[2]) + ')' 1267 ); 1268 }; 1269 1270 1271 this.isLight = function () { 1272 return ( 1273 0.213 * this.rgb[0] + 1274 0.715 * this.rgb[1] + 1275 0.072 * this.rgb[2] > 1276 255 / 2 1277 ); 1278 }; 1279 1280 1281 this._processParentElementsInDOM = function () { 1282 if (this._linkedElementsProcessed) { return; } 1283 this._linkedElementsProcessed = true; 1284 1285 var elm = this.targetElement; 1286 do { 1287 // If the target element or one of its parent nodes has fixed position, 1288 // then use fixed positioning instead 1289 // 1290 // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1291 // that's why we need to check if the returned style object is non-empty 1292 var currStyle = jsc.getStyle(elm); 1293 if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1294 this.fixed = true; 1295 } 1296 1297 if (elm !== this.targetElement) { 1298 // Ensure to attach onParentScroll only once to each parent element 1299 // (multiple targetElements can share the same parent nodes) 1300 // 1301 // Note: It's not just offsetParents that can be scrollable, 1302 // that's why we loop through all parent nodes 1303 if (!elm._jscEventsAttached) { 1304 jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1305 elm._jscEventsAttached = true; 1306 } 1307 } 1308 } while ((elm = elm.parentNode) && !jsc.isElementType(elm, 'body')); 1309 }; 1310 1311 1312 // r: 0-255 1313 // g: 0-255 1314 // b: 0-255 1315 // 1316 // returns: [ 0-360, 0-100, 0-100 ] 1317 // 1318 function RGB_HSV (r, g, b) { 1319 r /= 255; 1320 g /= 255; 1321 b /= 255; 1322 var n = Math.min(Math.min(r,g),b); 1323 var v = Math.max(Math.max(r,g),b); 1324 var m = v - n; 1325 if (m === 0) { return [ null, 0, 100 * v ]; } 1326 var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 1327 return [ 1328 60 * (h===6?0:h), 1329 100 * (m/v), 1330 100 * v 1331 ]; 1332 } 1333 1334 1335 // h: 0-360 1336 // s: 0-100 1337 // v: 0-100 1338 // 1339 // returns: [ 0-255, 0-255, 0-255 ] 1340 // 1341 function HSV_RGB (h, s, v) { 1342 var u = 255 * (v / 100); 1343 1344 if (h === null) { 1345 return [ u, u, u ]; 1346 } 1347 1348 h /= 60; 1349 s /= 100; 1350 1351 var i = Math.floor(h); 1352 var f = i%2 ? h-i : 1-(h-i); 1353 var m = u * (1 - s); 1354 var n = u * (1 - s * f); 1355 switch (i) { 1356 case 6: 1357 case 0: return [u,n,m]; 1358 case 1: return [n,u,m]; 1359 case 2: return [m,u,n]; 1360 case 3: return [m,n,u]; 1361 case 4: return [n,m,u]; 1362 case 5: return [u,m,n]; 1363 } 1364 } 1365 1366 1367 function detachPicker () { 1368 jsc.unsetClass(THIS.targetElement, THIS.activeClass); 1369 jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); 1370 delete jsc.picker.owner; 1371 } 1372 1373 1374 function drawPicker () { 1375
1376 // At this point, when drawing the picker, we know what the parent elements are 1377 // and we can do all related DOM operations, such as registering events on them 1378 // or checking their positioning 1379 THIS._processParentElementsInDOM(); 1380 1381 if (!jsc.picker) { 1382 jsc.picker = { 1383 owner: null, 1384 wrap : document.createElement('div'), 1385 box : document.createElement('div'), 1386 boxS : document.createElement('div'), // shadow area 1387 boxB : document.createElement('div'), // border 1388 pad : document.createElement('div'), 1389 padB : document.createElement('div'), // border 1390 padM : document.createElement('div'), // mouse/touch area 1391 padPal : jsc.createPalette(), 1392 cross : document.createElement('div'), 1393 crossBY : document.createElement('div'), // border Y 1394 crossBX : document.createElement('div'), // border X 1395 crossLY : document.createElement('div'), // line Y 1396 crossLX : document.createElement('div'), // line X 1397 sld : document.createElement('div'), 1398 sldB : document.createElement('div'), // border 1399 sldM : document.createElement('div'), // mouse/touch area 1400 sldGrad : jsc.createSliderGradient(), 1401 sldPtrS : document.createElement('div'), // slider pointer spacer 1402 sldPtrIB : document.createElement('div'), // slider pointer inner border 1403 sldPtrMB : document.createElement('div'), // slider pointer middle border 1404 sldPtrOB : document.createElement('div'), // slider pointer outer border 1405 btn : document.createElement('div'), 1406 btnT : document.createElement('span') // text 1407 }; 1408 1409 jsc.picker.pad.appendChild(jsc.picker.padPal.elm); 1410 jsc.picker.padB.appendChild(jsc.picker.pad); 1411 jsc.picker.cross.appendChild(jsc.picker.crossBY); 1412 jsc.picker.cross.appendChild(jsc.picker.crossBX); 1413 jsc.picker.cross.appendChild(jsc.picker.crossLY); 1414 jsc.picker.cross.appendChild(jsc.picker.crossLX); 1415 jsc.picker.padB.appendChild(jsc.picker.cross); 1416 jsc.picker.box.appendChild(jsc.picker.padB); 1417 jsc.picker.box.appendChild(jsc.picker.padM); 1418 1419 jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); 1420 jsc.picker.sldB.appendChild(jsc.picker.sld); 1421 jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); 1422 jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); 1423 jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); 1424 jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); 1425 jsc.picker.box.appendChild(jsc.picker.sldB); 1426 jsc.picker.box.appendChild(jsc.picker.sldM); 1427 1428 jsc.picker.btn.appendChild(jsc.picker.btnT); 1429 jsc.picker.box.appendChild(jsc.picker.btn); 1430 1431 jsc.picker.boxB.appendChild(jsc.picker.box); 1432 jsc.picker.wrap.appendChild(jsc.picker.boxS); 1433 jsc.picker.wrap.appendChild(jsc.picker.boxB); 1434 } 1435 1436 var p = jsc.picker; 1437 1438 var displaySlider = !!jsc.getSliderComponent(THIS); 1439 var dims = jsc.getPickerDims(THIS); 1440 var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1441 var padToSliderPadding = jsc.getPadToSliderPadding(THIS); 1442 var borderRadius = Math.min( 1443 THIS.borderRadius, 1444 Math.round(THIS.padding * Math.PI)); // px 1445 var padCursor = 'crosshair'; 1446 1447 // wrap 1448 p.wrap.style.clear = 'both'; 1449 p.wrap.style.width = (dims[0] + 2 * THIS.borderWidth) + 'px'; 1450 p.wrap.style.height = (dims[1] + 2 * THIS.borderWidth) + 'px'; 1451 p.wrap.style.zIndex = THIS.zIndex; 1452 1453 // picker 1454 p.box.style.width = dims[0] + 'px'; 1455 p.box.style.height = dims[1] + 'px'; 1456 1457 p.boxS.style.position = 'absolute'; 1458 p.boxS.style.left = '0'; 1459 p.boxS.style.top = '0'; 1460 p.boxS.style.width = '100%'; 1461 p.boxS.style.height = '100%'; 1462 jsc.setBorderRadius(p.boxS, borderRadius + 'px'); 1463 1464 // picker border 1465 p.boxB.style.position = 'relative'; 1466 p.boxB.style.border = THIS.borderWidth + 'px solid'; 1467 p.boxB.style.borderColor = THIS.borderColor; 1468 p.boxB.style.background = THIS.backgroundColor; 1469 jsc.setBorderRadius(p.boxB, borderRadius + 'px'); 1470 1471 // IE hack: 1472 // If the element is transparent, IE will trigger the event on the elements under it, 1473 // e.g. on Canvas or on elements with border 1474 p.padM.style.background = 1475 p.sldM.style.background = 1476 '#FFF'; 1477 jsc.setStyle(p.padM, 'opacity', '0'); 1478 jsc.setStyle(p.sldM, 'opacity', '0'); 1479 1480 // pad 1481 p.pad.style.position = 'relative'; 1482 p.pad.style.width = THIS.width + 'px'; 1483 p.pad.style.height = THIS.height + 'px'; 1484 1485 // pad palettes (HSV and HVS) 1486 p.padPal.draw(THIS.width, THIS.height, jsc.getPadYComponent(THIS)); 1487 1488 // pad border 1489 p.padB.style.position = 'absolute'; 1490 p.padB.style.left = THIS.padding + 'px'; 1491 p.padB.style.top = THIS.padding + 'px'; 1492 p.padB.style.border = THIS.insetWidth + 'px solid'; 1493 p.padB.style.borderColor = THIS.insetColor; 1494 1495 // pad mouse area 1496 p.padM._jscInstance = THIS; 1497 p.padM._jscControlName = 'pad'; 1498 p.padM.style.position = 'absolute'; 1499 p.padM.style.left = '0'; 1500 p.padM.style.top = '0'; 1501 p.padM.style.width = (THIS.padding + 2 * THIS.insetWidth + THIS.width + padToSliderPadding / 2) + 'px'; 1502 p.padM.style.height = dims[1] + 'px'; 1503 p.padM.style.cursor = padCursor; 1504 1505 // pad cross 1506 p.cross.style.position = 'absolute'; 1507 p.cross.style.left = 1508 p.cross.style.top = 1509 '0'; 1510 p.cross.style.width = 1511 p.cross.style.height = 1512 crossOuterSize + 'px'; 1513 1514 // pad cross border Y and X 1515 p.crossBY.style.position = 1516 p.crossBX.style.position = 1517 'absolute'; 1518 p.crossBY.style.background = 1519 p.crossBX.style.background = 1520 THIS.pointerBorderColor; 1521 p.crossBY.style.width = 1522 p.crossBX.style.height = 1523 (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1524 p.crossBY.style.height = 1525 p.crossBX.style.width = 1526 crossOuterSize + 'px'; 1527 p.crossBY.style.left = 1528 p.crossBX.style.top = 1529 (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; 1530 p.crossBY.style.top = 1531 p.crossBX.style.left = 1532 '0'; 1533 1534 // pad cross line Y and X 1535 p.crossLY.style.position = 1536 p.crossLX.style.position = 1537 'absolute'; 1538 p.crossLY.style.background = 1539 p.crossLX.style.background = 1540 THIS.pointerColor; 1541 p.crossLY.style.height = 1542 p.crossLX.style.width = 1543 (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; 1544 p.crossLY.style.width = 1545 p.crossLX.style.height = 1546 THIS.pointerThickness + 'px'; 1547 p.crossLY.style.left = 1548 p.crossLX.style.top = 1549 (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; 1550 p.crossLY.style.top = 1551 p.crossLX.style.left = 1552 THIS.pointerBorderWidth + 'px'; 1553 1554 // slider 1555 p.sld.style.overflow = 'hidden'; 1556 p.sld.style.width = THIS.sliderSize + 'px'; 1557 p.sld.style.height = THIS.height + 'px'; 1558 1559 // slider gradient 1560 p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); 1561 1562 // slider border 1563 p.sldB.style.display = displaySlider ? 'block' : 'none'; 1564 p.sldB.style.position = 'absolute'; 1565 p.sldB.style.right = THIS.padding + 'px'; 1566 p.sldB.style.top = THIS.padding + 'px'; 1567 p.sldB.style.border = THIS.insetWidth + 'px solid'; 1568 p.sldB.style.borderColor = THIS.insetColor; 1569 1570 // slider mouse area 1571 p.sldM._jscInstance = THIS; 1572 p.sldM._jscControlName = 'sld'; 1573 p.sldM.style.display = displaySlider ? 'block' : 'none'; 1574 p.sldM.style.position = 'absolute'; 1575 p.sldM.style.right = '0'; 1576 p.sldM.style.top = '0'; 1577 p.sldM.style.width = (THIS.sliderSize + padToSliderPadding / 2 + THIS.padding + 2 * THIS.insetWidth) + 'px'; 1578 p.sldM.style.height = dims[1] + 'px'; 1579 p.sldM.style.cursor = 'default'; 1580 1581 // slider pointer inner and outer border 1582 p.sldPtrIB.style.border = 1583 p.sldPtrOB.style.border = 1584 THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; 1585 1586 // slider pointer outer border 1587 p.sldPtrOB.style.position = 'absolute'; 1588 p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 1589 p.sldPtrOB.style.top = '0'; 1590 1591 // slider pointer middle border 1592 p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; 1593 1594 // slider pointer spacer
1595 p.sldPtrS.style.width = THIS.sliderSize + 'px'; 1596 p.sldPtrS.style.height = sliderPtrSpace + 'px'; 1597 1598 // the Close button 1599 function setBtnBorder () { 1600 var insetColors = THIS.insetColor.split(/\s+/); 1601 var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 1602 p.btn.style.borderColor = outsetColor; 1603 } 1604 p.btn.style.display = THIS.closable ? 'block' : 'none'; 1605 p.btn.style.position = 'absolute'; 1606 p.btn.style.left = THIS.padding + 'px'; 1607 p.btn.style.bottom = THIS.padding + 'px'; 1608 p.btn.style.padding = '0 15px'; 1609 p.btn.style.height = THIS.buttonHeight + 'px'; 1610 p.btn.style.border = THIS.insetWidth + 'px solid'; 1611 setBtnBorder(); 1612 p.btn.style.color = THIS.buttonColor; 1613 p.btn.style.font = '12px sans-serif'; 1614 p.btn.style.textAlign = 'center'; 1615 try { 1616 p.btn.style.cursor = 'pointer'; 1617 } catch(eOldIE) { 1618 p.btn.style.cursor = 'hand'; 1619 } 1620 p.btn.onmousedown = function () { 1621 THIS.hide(); 1622 }; 1623 p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; 1624 p.btnT.innerHTML = ''; 1625 p.btnT.appendChild(document.createTextNode(THIS.closeText)); 1626 1627 // place pointers 1628 redrawPad(); 1629 redrawSld(); 1630 1631 // If we are changing the owner without first closing the picker, 1632 // make sure to first deal with the old owner 1633 if (jsc.picker.owner && jsc.picker.owner !== THIS) { 1634 jsc.unsetClass(jsc.picker.owner.targetElement, THIS.activeClass); 1635 } 1636 1637 // Set the new picker owner 1638 jsc.picker.owner = THIS; 1639 1640 // The redrawPosition() method needs picker.owner to be set, that's why we call it here, 1641 // after setting the owner 1642 if (jsc.isElementType(container, 'body')) { 1643 jsc.redrawPosition(); 1644 } else { 1645 jsc._drawPosition(THIS, 0, 0, 'relative', false); 1646 } 1647 1648 if (p.wrap.parentNode != container) { 1649 container.appendChild(p.wrap); 1650 } 1651 1652 jsc.setClass(THIS.targetElement, THIS.activeClass); 1653 } 1654 1655 1656 function redrawPad () { 1657 // redraw the pad pointer 1658 switch (jsc.getPadYComponent(THIS)) { 1659 case 's': var yComponent = 1; break; 1660 case 'v': var yComponent = 2; break; 1661 } 1662 var x = Math.round((THIS.hsv[0] / 360) * (THIS.width - 1)); 1663 var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1664 var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 1665 var ofs = -Math.floor(crossOuterSize / 2); 1666 jsc.picker.cross.style.left = (x + ofs) + 'px'; 1667 jsc.picker.cross.style.top = (y + ofs) + 'px'; 1668 1669 // redraw the slider 1670 switch (jsc.getSliderComponent(THIS)) { 1671 case 's': 1672 var rgb1 = HSV_RGB(THIS.hsv[0], 100, THIS.hsv[2]); 1673 var rgb2 = HSV_RGB(THIS.hsv[0], 0, THIS.hsv[2]); 1674 var color1 = 'rgb(' + 1675 Math.round(rgb1[0]) + ',' + 1676 Math.round(rgb1[1]) + ',' + 1677 Math.round(rgb1[2]) + ')'; 1678 var color2 = 'rgb(' + 1679 Math.round(rgb2[0]) + ',' + 1680 Math.round(rgb2[1]) + ',' + 1681 Math.round(rgb2[2]) + ')'; 1682 jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1683 break; 1684 case 'v': 1685 var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 100); 1686 var color1 = 'rgb(' + 1687 Math.round(rgb[0]) + ',' + 1688 Math.round(rgb[1]) + ',' + 1689 Math.round(rgb[2]) + ')'; 1690 var color2 = '#000'; 1691 jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 1692 break; 1693 } 1694 } 1695 1696 1697 function redrawSld () { 1698 var sldComponent = jsc.getSliderComponent(THIS); 1699 if (sldComponent) { 1700 // redraw the slider pointer 1701 switch (sldComponent) { 1702 case 's': var yComponent = 1; break; 1703 case 'v': var yComponent = 2; break; 1704 } 1705 var y = Math.round((1 - THIS.hsv[yComponent] / 100) * (THIS.height - 1)); 1706 jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(sliderPtrSpace / 2)) + 'px'; 1707 } 1708 } 1709 1710 1711 function isPickerOwner () { 1712 return jsc.picker && jsc.picker.owner === THIS; 1713 } 1714 1715 1716 function blurValue () { 1717 THIS.importColor(); 1718 } 1719 1720 1721 // Find the target element 1722 if (typeof targetElement === 'string') { 1723 var id = targetElement; 1724 var elm = document.getElementById(id); 1725 if (elm) { 1726 this.targetElement = elm; 1727 } else { 1728 jsc.warn('Could not find target element with ID \'' + id + '\''); 1729 } 1730 } else if (targetElement) { 1731 this.targetElement = targetElement; 1732 } else { 1733 jsc.warn('Invalid target element: \'' + targetElement + '\''); 1734 } 1735 1736 if (this.targetElement._jscLinkedInstance) { 1737 jsc.warn('Cannot link jscolor twice to the same element. Skipping.'); 1738 return; 1739 } 1740 this.targetElement._jscLinkedInstance = this; 1741 1742 // Find the value element 1743 this.valueElement = jsc.fetchElement(this.valueElement); 1744 // Find the style element 1745 this.styleElement = jsc.fetchElement(this.styleElement); 1746 1747 var THIS = this; 1748 var container = 1749 this.container ? 1750 jsc.fetchElement(this.container) : 1751 document.getElementsByTagName('body')[0]; 1752 var sliderPtrSpace = 3; // px 1753 1754 // For BUTTON elements it's important to stop them from sending the form when clicked 1755 // (e.g. in Safari) 1756 if (jsc.isElementType(this.targetElement, 'button')) { 1757 if (this.targetElement.onclick) { 1758 var origCallback = this.targetElement.onclick; 1759 this.targetElement.onclick = function (evt) { 1760 origCallback.call(this, evt); 1761 return false; 1762 }; 1763 } else { 1764 this.targetElement.onclick = function () { return false; }; 1765 } 1766 } 1767 1768 /* 1769 var elm = this.targetElement; 1770 do { 1771 // If the target element or one of its offsetParents has fixed position, 1772 // then use fixed positioning instead 1773 // 1774 // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 1775 // that's why we need to check if the returned style object is non-empty 1776 var currStyle = jsc.getStyle(elm); 1777 if (currStyle && currStyle.position.toLowerCase() === 'fixed') { 1778 this.fixed = true; 1779 } 1780 1781 if (elm !== this.targetElement) { 1782 // attach onParentScroll so that we can recompute the picker position 1783 // when one of the offsetParents is scrolled 1784 if (!elm._jscEventsAttached) { 1785 jsc.attachEvent(elm, 'scroll', jsc.onParentScroll); 1786 elm._jscEventsAttached = true; 1787 } 1788 } 1789 } while ((elm = elm.offsetParent) && !jsc.isElementType(elm, 'body')); 1790 */ 1791 1792 // valueElement 1793 if (this.valueElement) { 1794 if (jsc.isElementType(this.valueElement, 'input')) { 1795 var updateField = function () { 1796 THIS.fromString(THIS.valueElement.value, jsc.leaveValue); 1797 jsc.dispatchFineChange(THIS); 1798 }; 1799 jsc.attachEvent(this.valueElement, 'keyup', updateField); 1800 jsc.attachEvent(this.valueElement, 'input', updateField); 1801 jsc.attachEvent(this.valueElement, 'blur', blurValue); 1802 this.valueElement.setAttribute('autocomplete', 'off'); 1803 } 1804 } 1805 1806 // styleElement 1807 if (this.styleElement) { 1808 this.styleElement._jscOrigStyle = { 1809 backgroundImage : this.styleElement.style.backgroundImage, 1810 backgroundColor : this.styleElement.style.backgroundColor, 1811 color : this.styleElement.style.color 1812 }; 1813 } 1814 1815 if (this.value) { 1816 // Try to set the color from the .value option and if unsuccessful, 1817 // export the current color 1818 this.fromString(this.value) || this.exportColor(); 1819 } else { 1820 this.importColor(); 1821 } 1822 } 1823 1824}; 1825 1826 1827//================================ 1828// Public properties and methods 1829//================================ 1830 1831 1832// By default, search for all elements with class="jscolor" and install a color picker on them. 1833// 1834// You can change what class name will be looked for by setting the property jscolor.lookupClass 1835// anywhere in your HTML document. To completely disable the automatic lookup, set it to null. 1836// 1837jsc.jscolor.lookupClass = 'jscolor'; 1838 1839 1840jsc.jscolor.installByClassName = function (className) { 1841 var inputElms = document.getElementsByTagName('input'); 1842 var buttonElms = document.getElementsByTagName('button'); 1843 1844 jsc.tryInstallOnElements(inputElms, className); 1845 jsc.tryInstallOnElements(buttonElms, className); 1846}; 1847 1848 1849jsc.register(); 1850 1851 1852return jsc.jscolor; 1853 1854 1855})(); }
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.