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 - East Desire 8 * @version 2.5.2 9 * 10 * See usage examples at http://jscolor.com/examples/ 11 */ 12 13 14(function (global, factory) { 15 16 'use strict'; 17 18 if (typeof module === 'object' && typeof module.exports === 'object') { 19 // Export jscolor as a module 20 module.exports = global.document ? 21 factory (global) : 22 function (win) { 23 if (!win.document) { 24 throw new Error('jscolor needs a window with document'); 25 } 26 return factory(win); 27 } 28 return; 29 } 30 31 // Default use (no module export) 32 factory(global); 33 34})(typeof window !== 'undefined' ? window : this, function (window) { // BEGIN factory 35 36// BEGIN jscolor code 37 38 39'use strict'; 40 41 42var jscolor = (function () { // BEGIN jscolor 43 44var jsc = { 45 46 47 initialized : false, 48 49 instances : [], // created instances of jscolor 50 51 readyQueue : [], // functions waiting to be called after init 52 53 54 register : function () { 55 if (typeof window !== 'undefined' && window.document) { 56 if (window.document.readyState !== 'loading') { 57 jsc.pub.init(); 58 } else { 59 window.document.addEventListener('DOMContentLoaded', jsc.pub.init, false); 60 } 61 } 62 }, 63 64 65 installBySelector : function (selector, rootNode) { 66 rootNode = rootNode ? jsc.node(rootNode) : window.document; 67 if (!rootNode) { 68 throw new Error('Missing root node'); 69 } 70 71 var elms = rootNode.querySelectorAll(selector); 72 73 // for backward compatibility with DEPRECATED installation/configuration using className 74 var matchClass = new RegExp('(^|\\s)(' + jsc.pub.lookupClass + ')(\\s*(\\{[^}]*\\})|\\s|$)', 'i'); 75 76 for (var i = 0; i < elms.length; i += 1) { 77 78 if (elms[i].jscolor && elms[i].jscolor instanceof jsc.pub) { 79 continue; // jscolor already installed on this element 80 } 81 82 if (elms[i].type !== undefined && elms[i].type.toLowerCase() == 'color' && jsc.isColorAttrSupported) { 83 continue; // skips inputs of type 'color' if supported by the browser 84 } 85 86 var dataOpts, m; 87 88 if ( 89 (dataOpts = jsc.getDataAttr(elms[i], 'jscolor')) !== null || 90 (elms[i].className && (m = elms[i].className.match(matchClass))) // installation using className (DEPRECATED) 91 ) { 92 var targetElm = elms[i]; 93 94 var optsStr = ''; 95 if (dataOpts !== null) { 96 optsStr = dataOpts; 97 98 } else if (m) { // installation using className (DEPRECATED) 99 console.warn('Installation using class name is DEPRECATED. Use data-jscolor="" attribute instead.' + jsc.docsRef); 100 if (m[4]) { 101 optsStr = m[4]; 102 } 103 } 104 105 var opts = null; 106 if (optsStr.trim()) { 107 try { 108 opts = jsc.parseOptionsStr(optsStr); 109 } catch (e) { 110 console.warn(e + '\n' + optsStr); 111 } 112 } 113 114 try { 115 new jsc.pub(targetElm, opts); 116 } catch (e) { 117 console.warn(e); 118 } 119 } 120 } 121 }, 122 123 124 parseOptionsStr : function (str) { 125 var opts = null; 126 127 try { 128 opts = JSON.parse(str); 129 130 } catch (eParse) { 131 if (!jsc.pub.looseJSON) { 132 throw new Error('Could not parse jscolor options as JSON: ' + eParse); 133 } else { 134 // loose JSON syntax is enabled -> try to evaluate the options string as JavaScript object 135 try { 136 opts = (new Function ('var opts = (' + str + '); return typeof opts === "object" ? opts : {};'))(); 137 } catch (eEval) { 138 throw new Error('Could not evaluate jscolor options: ' + eEval); 139 } 140 } 141 } 142 return opts; 143 }, 144 145 146 getInstances : function () { 147 var inst = []; 148 for (var i = 0; i < jsc.instances.length; i += 1) { 149 // if the targetElement still exists, the instance is considered "alive" 150 if (jsc.instances[i] && jsc.instances[i].targetElement) { 151 inst.push(jsc.instances[i]); 152 } 153 } 154 return inst; 155 }, 156 157 158 createEl : function (tagName) { 159 var el = window.document.createElement(tagName); 160 jsc.setData(el, 'gui', true); 161 return el; 162 }, 163 164 165 node : function (nodeOrSelector) { 166 if (!nodeOrSelector) { 167 return null; 168 } 169 170 if (typeof nodeOrSelector === 'string') { 171 // query selector 172 var sel = nodeOrSelector; 173 var el = null; 174 try { 175 el = window.document.querySelector(sel); 176 } catch (e) { 177 console.warn(e); 178 return null; 179 } 180 if (!el) { 181 console.warn('No element matches the selector: %s', sel); 182 } 183 return el; 184 } 185 186 if (jsc.isNode(nodeOrSelector)) { 187 // DOM node 188 return nodeOrSelector; 189 } 190 191 console.warn('Invalid node of type %s: %s', typeof nodeOrSelector, nodeOrSelector); 192 return null; 193 }, 194 195 196 // See https://stackoverflow.com/questions/384286/ 197 isNode : function (val) { 198 if (typeof Node === 'object') { 199 return val instanceof Node; 200 } 201 return val && typeof val === 'object' && typeof val.nodeType === 'number' && typeof val.nodeName === 'string'; 202 }, 203 204 205 nodeName : function (node) { 206 if (node && node.nodeName) { 207 return node.nodeName.toLowerCase(); 208 } 209 return false; 210 }, 211 212 213 removeChildren : function (node) { 214 while (node.firstChild) { 215 node.removeChild(node.firstChild); 216 } 217 }, 218 219 220 isTextInput : function (el) {
221 return el && jsc.nodeName(el) === 'input' && el.type.toLowerCase() === 'text'; 222 }, 223 224 225 isButton : function (el) { 226 if (!el) { 227 return false; 228 } 229 var n = jsc.nodeName(el); 230 return ( 231 (n === 'button') || 232 (n === 'input' && ['button', 'submit', 'reset'].indexOf(el.type.toLowerCase()) > -1) 233 ); 234 }, 235 236 237 isButtonEmpty : function (el) { 238 switch (jsc.nodeName(el)) { 239 case 'input': return (!el.value || el.value.trim() === ''); 240 case 'button': return (el.textContent.trim() === ''); 241 } 242 return null; // could not determine element's text 243 }, 244 245 246 // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md 247 isPassiveEventSupported : (function () { 248 var supported = false; 249 250 try { 251 var opts = Object.defineProperty({}, 'passive', { 252 get: function () { supported = true; } 253 }); 254 window.addEventListener('testPassive', null, opts); 255 window.removeEventListener('testPassive', null, opts); 256 } catch (e) {} 257 258 return supported; 259 })(), 260 261 262 isColorAttrSupported : (function () { 263 var elm = window.document.createElement('input'); 264 if (elm.setAttribute) { 265 elm.setAttribute('type', 'color'); 266 if (elm.type.toLowerCase() == 'color') { 267 return true; 268 } 269 } 270 return false; 271 })(), 272 273 274 dataProp : '_data_jscolor', 275 276 277 // usage: 278 // setData(obj, prop, value) 279 // setData(obj, {prop:value, ...}) 280 // 281 setData : function () { 282 var obj = arguments[0]; 283 284 if (arguments.length === 3) { 285 // setting a single property 286 var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : (obj[jsc.dataProp] = {}); 287 var prop = arguments[1]; 288 var value = arguments[2]; 289 290 data[prop] = value; 291 return true; 292 293 } else if (arguments.length === 2 && typeof arguments[1] === 'object') { 294 // setting multiple properties 295 var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : (obj[jsc.dataProp] = {}); 296 var map = arguments[1]; 297 298 for (var prop in map) { 299 if (map.hasOwnProperty(prop)) { 300 data[prop] = map[prop]; 301 } 302 } 303 return true; 304 } 305 306 throw new Error('Invalid arguments'); 307 }, 308 309 310 // usage: 311 // removeData(obj, prop, [prop...]) 312 // 313 removeData : function () { 314 var obj = arguments[0]; 315 if (!obj.hasOwnProperty(jsc.dataProp)) { 316 return true; // data object does not exist 317 } 318 for (var i = 1; i < arguments.length; i += 1) { 319 var prop = arguments[i]; 320 delete obj[jsc.dataProp][prop]; 321 } 322 return true; 323 }, 324 325 326 getData : function (obj, prop, setDefault) { 327 if (!obj.hasOwnProperty(jsc.dataProp)) { 328 // data object does not exist 329 if (setDefault !== undefined) { 330 obj[jsc.dataProp] = {}; // create data object 331 } else { 332 return undefined; // no value to return 333 } 334 } 335 var data = obj[jsc.dataProp]; 336 337 if (!data.hasOwnProperty(prop) && setDefault !== undefined) { 338 data[prop] = setDefault; 339 } 340 return data[prop]; 341 }, 342 343 344 getDataAttr : function (el, name) { 345 var attrName = 'data-' + name; 346 var attrValue = el.getAttribute(attrName); 347 return attrValue; 348 }, 349 350 351 setDataAttr : function (el, name, value) { 352 var attrName = 'data-' + name; 353 el.setAttribute(attrName, value); 354 }, 355 356 357 _attachedGroupEvents : {}, 358 359 360 attachGroupEvent : function (groupName, el, evnt, func) { 361 if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { 362 jsc._attachedGroupEvents[groupName] = []; 363 } 364 jsc._attachedGroupEvents[groupName].push([el, evnt, func]); 365 el.addEventListener(evnt, func, false); 366 }, 367 368 369 detachGroupEvents : function (groupName) { 370 if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { 371 for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { 372 var evt = jsc._attachedGroupEvents[groupName][i]; 373 evt[0].removeEventListener(evt[1], evt[2], false); 374 } 375 delete jsc._attachedGroupEvents[groupName]; 376 } 377 }, 378 379 380 preventDefault : function (e) { 381 if (e.preventDefault) { e.preventDefault(); } 382 e.returnValue = false; 383 }, 384 385 386 triggerEvent : function (el, eventName, bubbles, cancelable) { 387 if (!el) { 388 return; 389 } 390 391 var ev = null; 392 393 if (typeof Event === 'function') { 394 ev = new Event(eventName, { 395 bubbles: bubbles, 396 cancelable: cancelable 397 }); 398 } else { 399 // IE 400 ev = window.document.createEvent('Event');
401 ev.initEvent(eventName, bubbles, cancelable); 402 } 403 404 if (!ev) { 405 return false; 406 } 407 408 // so that we know that the event was triggered internally 409 jsc.setData(ev, 'internal', true); 410 411 el.dispatchEvent(ev); 412 return true; 413 }, 414 415 416 triggerInputEvent : function (el, eventName, bubbles, cancelable) { 417 if (!el) { 418 return; 419 } 420 if (jsc.isTextInput(el)) { 421 jsc.triggerEvent(el, eventName, bubbles, cancelable); 422 } 423 }, 424 425 426 eventKey : function (ev) { 427 var keys = { 428 9: 'Tab', 429 13: 'Enter', 430 27: 'Escape', 431 }; 432 if (typeof ev.code === 'string') { 433 return ev.code; 434 } else if (ev.keyCode !== undefined && keys.hasOwnProperty(ev.keyCode)) { 435 return keys[ev.keyCode]; 436 } 437 return null; 438 }, 439 440 441 strList : function (str) { 442 if (!str) { 443 return []; 444 } 445 return str.replace(/^\s+|\s+$/g, '').split(/\s+/); 446 }, 447 448 449 // The className parameter (str) can only contain a single class name 450 hasClass : function (elm, className) { 451 if (!className) { 452 return false; 453 } 454 if (elm.classList !== undefined) { 455 return elm.classList.contains(className); 456 } 457 // polyfill 458 return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); 459 }, 460 461 462 // The className parameter (str) can contain multiple class names separated by whitespace 463 addClass : function (elm, className) { 464 var classNames = jsc.strList(className); 465 466 if (elm.classList !== undefined) { 467 for (var i = 0; i < classNames.length; i += 1) { 468 elm.classList.add(classNames[i]); 469 } 470 return; 471 } 472 // polyfill 473 for (var i = 0; i < classNames.length; i += 1) { 474 if (!jsc.hasClass(elm, classNames[i])) { 475 elm.className += (elm.className ? ' ' : '') + classNames[i]; 476 } 477 } 478 }, 479 480 481 // The className parameter (str) can contain multiple class names separated by whitespace 482 removeClass : function (elm, className) { 483 var classNames = jsc.strList(className); 484 485 if (elm.classList !== undefined) { 486 for (var i = 0; i < classNames.length; i += 1) { 487 elm.classList.remove(classNames[i]); 488 } 489 return; 490 } 491 // polyfill 492 for (var i = 0; i < classNames.length; i += 1) { 493 var repl = new RegExp( 494 '^\\s*' + classNames[i] + '\\s*|' + 495 '\\s*' + classNames[i] + '\\s*$|' + 496 '\\s+' + classNames[i] + '(\\s+)', 497 'g' 498 ); 499 elm.className = elm.className.replace(repl, '$1'); 500 } 501 }, 502 503 504 getCompStyle : function (elm) { 505 var compStyle = window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; 506 507 // Note: In Firefox, getComputedStyle returns null in a hidden iframe, 508 // that's why we need to check if the returned value is non-empty 509 if (!compStyle) { 510 return {}; 511 } 512 return compStyle; 513 }, 514 515 516 // Note: 517 // Setting a property to NULL reverts it to the state before it was first set 518 // with the 'reversible' flag enabled 519 // 520 setStyle : function (elm, styles, important, reversible) { 521 // using '' for standard priority (IE10 apparently doesn't like value undefined) 522 var priority = important ? 'important' : ''; 523 var origStyle = null; 524 525 for (var prop in styles) { 526 if (styles.hasOwnProperty(prop)) { 527 var setVal = null; 528 529 if (styles[prop] === null) { 530 // reverting a property value 531 532 if (!origStyle) { 533 // get the original style object, but dont't try to create it if it doesn't exist 534 origStyle = jsc.getData(elm, 'origStyle'); 535 } 536 if (origStyle && origStyle.hasOwnProperty(prop)) { 537 // we have property's original value -> use it 538 setVal = origStyle[prop]; 539 } 540 541 } else { 542 // setting a property value 543 544 if (reversible) { 545 if (!origStyle) { 546 // get the original style object and if it doesn't exist, create it 547 origStyle = jsc.getData(elm, 'origStyle', {}); 548 } 549 if (!origStyle.hasOwnProperty(prop)) { 550 // original property value not yet stored -> store it 551 origStyle[prop] = elm.style[prop]; 552 } 553 } 554 setVal = styles[prop]; 555 } 556 557 if (setVal !== null) { 558 elm.style.setProperty(prop, setVal, priority); 559 } 560 } 561 } 562 }, 563 564 565 appendCss : function (css) { 566 var head = document.querySelector('head'); 567 var style = document.createElement('style'); 568 style.innerText = css; 569 head.appendChild(style); 570 }, 571 572 573 appendDefaultCss : function (css) { 574 jsc.appendCss( 575 [ 576 '.jscolor-wrap, .jscolor-wrap div, .jscolor-wrap canvas { ' + 577 'position:static; display:block; visibility:visible; overflow:visible; margin:0; padding:0; ' + 578 'border:none; border-radius:0; outline:none; z-index:auto; float:none; ' + 579 'width:auto; height:auto; left:auto; right:auto; top:auto; bottom:auto; min-width:0; min-height:0; max-width:none; max-height:none; ' + 580 'background:none; clip:auto;
580 opacity:1; transform:none; box-shadow:none; box-sizing:content-box; ' + 581 '}', 582 '.jscolor-wrap { clear:both; }', 583 '.jscolor-wrap .jscolor-picker { position:relative; }', 584 '.jscolor-wrap .jscolor-shadow { position:absolute; left:0; top:0; width:100%; height:100%; }', 585 '.jscolor-wrap .jscolor-border { position:relative; }', 586 '.jscolor-wrap .jscolor-palette { position:absolute; }', 587 '.jscolor-wrap .jscolor-palette-sw { position:absolute; display:block; cursor:pointer; }', 588 '.jscolor-wrap .jscolor-btn { position:absolute; overflow:hidden; white-space:nowrap; font:13px sans-serif; text-align:center; cursor:pointer; }', 589 ].join('\n') 590 ); 591 }, 592 593 594 hexColor : function (r, g, b) { 595 return '#' + ( 596 ('0' + Math.round(r).toString(16)).slice(-2) + 597 ('0' + Math.round(g).toString(16)).slice(-2) + 598 ('0' + Math.round(b).toString(16)).slice(-2) 599 ).toUpperCase(); 600 }, 601 602 603 hexaColor : function (r, g, b, a) { 604 return '#' + ( 605 ('0' + Math.round(r).toString(16)).slice(-2) + 606 ('0' + Math.round(g).toString(16)).slice(-2) + 607 ('0' + Math.round(b).toString(16)).slice(-2) + 608 ('0' + Math.round(a * 255).toString(16)).slice(-2) 609 ).toUpperCase(); 610 }, 611 612 613 rgbColor : function (r, g, b) { 614 return 'rgb(' + 615 Math.round(r) + ',' + 616 Math.round(g) + ',' + 617 Math.round(b) + 618 ')'; 619 }, 620 621 622 rgbaColor : function (r, g, b, a) { 623 return 'rgba(' + 624 Math.round(r) + ',' + 625 Math.round(g) + ',' + 626 Math.round(b) + ',' + 627 (Math.round((a===undefined || a===null ? 1 : a) * 100) / 100) + 628 ')'; 629 }, 630 631 632 linearGradient : (function () { 633 634 function getFuncName () { 635 var stdName = 'linear-gradient'; 636 var prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-']; 637 var helper = window.document.createElement('div'); 638 639 for (var i = 0; i < prefixes.length; i += 1) { 640 var tryFunc = prefixes[i] + stdName; 641 var tryVal = tryFunc + '(to right, rgba(0,0,0,0), rgba(0,0,0,0))'; 642 643 helper.style.background = tryVal; 644 if (helper.style.background) { // CSS background successfully set -> function name is supported 645 return tryFunc; 646 } 647 } 648 return stdName; // fallback to standard 'linear-gradient' without vendor prefix 649 } 650 651 var funcName = getFuncName(); 652 653 return function () { 654 return funcName + '(' + Array.prototype.join.call(arguments, ', ') + ')'; 655 }; 656 657 })(), 658 659 660 setBorderRadius : function (elm, value) { 661 jsc.setStyle(elm, {'border-radius' : value || '0'}); 662 }, 663 664 665 setBoxShadow : function (elm, value) { 666 jsc.setStyle(elm, {'box-shadow': value || 'none'}); 667 }, 668 669 670 getElementPos : function (e, relativeToViewport) { 671 var x=0, y=0; 672 var rect = e.getBoundingClientRect(); 673 x = rect.left; 674 y = rect.top; 675 if (!relativeToViewport) { 676 var viewPos = jsc.getViewPos(); 677 x += viewPos[0]; 678 y += viewPos[1]; 679 } 680 return [x, y]; 681 }, 682 683 684 getElementSize : function (e) { 685 return [e.offsetWidth, e.offsetHeight]; 686 }, 687 688 689 // get pointer's X/Y coordinates relative to viewport 690 getAbsPointerPos : function (e) { 691 var x = 0, y = 0; 692 if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { 693 // touch devices 694 x = e.changedTouches[0].clientX; 695 y = e.changedTouches[0].clientY; 696 } else if (typeof e.clientX === 'number') { 697 x = e.clientX; 698 y = e.clientY; 699 } 700 return { x: x, y: y }; 701 }, 702 703 704 // get pointer's X/Y coordinates relative to target element 705 getRelPointerPos : function (e) { 706 var target = e.target || e.srcElement; 707 var targetRect = target.getBoundingClientRect(); 708 709 var x = 0, y = 0; 710 711 var clientX = 0, clientY = 0; 712 if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { 713 // touch devices 714 clientX = e.changedTouches[0].clientX; 715 clientY = e.changedTouches[0].clientY; 716 } else if (typeof e.clientX === 'number') { 717 clientX = e.clientX; 718 clientY = e.clientY; 719 } 720 721 x = clientX - targetRect.left; 722 y = clientY - targetRect.top; 723 return { x: x, y: y }; 724 }, 725 726 727 getViewPos : function () { 728 var doc = window.document.documentElement; 729 return [ 730 (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), 731 (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) 732 ]; 733 }, 734 735 736 getViewSize : function () { 737 var doc = window.document.documentElement; 738 return [ 739 (window.innerWidth || doc.clientWidth), 740 (window.innerHeight || doc.clientHeight), 741 ]; 742 }, 743 744 745 // r: 0-255 746 // g: 0-255 747 // b: 0-255 748 // 749 // returns: [ 0-360, 0-100, 0-100 ] 750 // 751 RGB_HSV : function (r, g, b) { 752 r /= 255; 753 g /= 255; 754 b /= 255; 755 var n = Math.min(Math.min(r,g),b); 756 var v = Math.max(Math.max(r,g),b); 757 var m = v - n; 758 if (m === 0) { return [ null, 0, 100 * v ]; }
759 var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); 760 return [ 761 60 * (h===6?0:h), 762 100 * (m/v), 763 100 * v 764 ]; 765 }, 766 767 768 // h: 0-360 769 // s: 0-100 770 // v: 0-100 771 // 772 // returns: [ 0-255, 0-255, 0-255 ] 773 // 774 HSV_RGB : function (h, s, v) { 775 var u = 255 * (v / 100); 776 777 if (h === null) { 778 return [ u, u, u ]; 779 } 780 781 h /= 60; 782 s /= 100; 783 784 var i = Math.floor(h); 785 var f = i%2 ? h-i : 1-(h-i); 786 var m = u * (1 - s); 787 var n = u * (1 - s * f); 788 switch (i) { 789 case 6: 790 case 0: return [u,n,m]; 791 case 1: return [n,u,m]; 792 case 2: return [m,u,n]; 793 case 3: return [m,n,u]; 794 case 4: return [n,m,u]; 795 case 5: return [u,m,n]; 796 } 797 }, 798 799 800 parseColorString : function (str) { 801 var ret = { 802 rgba: null, 803 format: null // 'hex' | 'hexa' | 'rgb' | 'rgba' 804 }; 805 806 var m; 807 808 if (m = str.match(/^\W*([0-9A-F]{3,8})\W*$/i)) { 809 // HEX notation 810 811 if (m[1].length === 8) { 812 // 8-char notation (= with alpha) 813 ret.format = 'hexa'; 814 ret.rgba = [ 815 parseInt(m[1].slice(0,2),16), 816 parseInt(m[1].slice(2,4),16), 817 parseInt(m[1].slice(4,6),16), 818 parseInt(m[1].slice(6,8),16) / 255 819 ]; 820 821 } else if (m[1].length === 6) { 822 // 6-char notation 823 ret.format = 'hex'; 824 ret.rgba = [ 825 parseInt(m[1].slice(0,2),16), 826 parseInt(m[1].slice(2,4),16), 827 parseInt(m[1].slice(4,6),16), 828 null 829 ]; 830 831 } else if (m[1].length === 3) { 832 // 3-char notation 833 ret.format = 'hex'; 834 ret.rgba = [ 835 parseInt(m[1].charAt(0) + m[1].charAt(0),16), 836 parseInt(m[1].charAt(1) + m[1].charAt(1),16), 837 parseInt(m[1].charAt(2) + m[1].charAt(2),16), 838 null 839 ]; 840 841 } else { 842 return false; 843 } 844 845 return ret; 846 } 847 848 if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { 849 // rgb(...) or rgba(...) notation 850 851 var par = m[1].split(','); 852 var re = /^\s*(\d+|\d*\.\d+|\d+\.\d*)\s*$/; 853 var mR, mG, mB, mA; 854 if ( 855 par.length >= 3 && 856 (mR = par[0].match(re)) && 857 (mG = par[1].match(re)) && 858 (mB = par[2].match(re)) 859 ) { 860 ret.format = 'rgb'; 861 ret.rgba = [ 862 parseFloat(mR[1]) || 0, 863 parseFloat(mG[1]) || 0, 864 parseFloat(mB[1]) || 0, 865 null 866 ]; 867 868 if ( 869 par.length >= 4 && 870 (mA = par[3].match(re)) 871 ) { 872 ret.format = 'rgba'; 873 ret.rgba[3] = parseFloat(mA[1]) || 0; 874 } 875 return ret; 876 } 877 } 878 879 return false; 880 }, 881 882 883 parsePaletteValue : function (mixed) { 884 var vals = []; 885 886 if (typeof mixed === 'string') { // input is a string of space separated color values 887 // rgb() and rgba() may contain spaces too, so let's find all color values by regex 888 mixed.replace(/#[0-9A-F]{3}\b|#[0-9A-F]{6}([0-9A-F]{2})?\b|rgba?\(([^)]*)\)/ig, function (val) { 889 vals.push(val); 890 }); 891 } else if (Array.isArray(mixed)) { // input is an array of color values 892 vals = mixed; 893 } 894 895 // convert all values into uniform color format 896 897 var colors = []; 898 899 for (var i = 0; i < vals.length; i++) { 900 var color = jsc.parseColorString(vals[i]); 901 if (color) { 902 colors.push(color); 903 } 904 } 905 906 return colors; 907 }, 908 909 910 containsTranparentColor : function (colors) { 911 for (var i = 0; i < colors.length; i++) { 912 var a = colors[i].rgba[3]; 913 if (a !== null && a < 1.0) { 914 return true; 915 } 916 } 917 return false; 918 }, 919 920 921 isAlphaFormat : function (format) { 922 switch (format.toLowerCase()) { 923 case 'hexa': 924 case 'rgba': 925 return true; 926 } 927 return false; 928 }, 929 930 931 // Canvas scaling for retina displays 932 // 933 // adapted from https://www.html5rocks.com/en/tutorials/canvas/hidpi/ 934 // 935 scaleCanvasForHighDPR : function (canvas) { 936 var dpr = window.devicePixelRatio || 1; 937 canvas.width *= dpr; 938 canvas.height *= dpr; 939 var ctx = canvas.getContext('2d'); 940 ctx.scale(dpr, dpr); 941 }, 942 943 944 genColorPreviewCanvas : function (color, separatorPos, specWidth, scaleForHighDPR) { 945 946 var sepW = Math.round(jsc.pub.previewSeparator.length); 947 var sqSize = jsc.pub.chessboardSize; 948 var sqColor1 = jsc.pub.chessboardColor1; 949 var sqColor2 = jsc.pub.chessboardColor2; 950 951 var cWidth = specWidth ? specWidth : sqSize * 2; 952 var cHeight = sqSize * 2; 953 954 var canvas = jsc.createEl('canvas'); 955 var ctx = canvas.getContext('2d'); 956 957 canvas.width = cWidth; 958 canvas.height = cHeight; 959 if (scaleForHighDPR) { 960 jsc.scaleCanvasForHighDPR(canvas); 961 } 962 963 // transparency chessboard - background 964 ctx.fillStyle = sqColor1; 965 ctx.fillRect(0, 0, cWidth, cHeight); 966 967 // transparency chessboard - squares 968 ctx.fillStyle = sqColor2; 969 for (var x = 0; x < cWidth; x += sqSize * 2) { 970 ctx.fillRect(x, 0, sqSize, sqSize); 971 ctx.fillRect(x + sqSize, sqSize, sqSize, sqSize); 972 } 973 974 if (color) { 975 // actual color in foreground 976 ctx.fillStyle = color; 977 ctx.fillRect(0, 0, cWidth, cHeight); 978 } 979 980 var start = null; 981 switch (separatorPos) { 982 case 'left': 983 start = 0; 984 ctx.clearRect(0, 0, sepW/2, cHeight); 985 break; 986 case 'right': 987 start = cWidth - sepW; 988 ctx.clearRect(cWidth - (sepW/2), 0, sepW/2, cHeight); 989 break; 990 } 991 if (start !== null) { 992 ctx.lineWidth = 1; 993 for (var i = 0; i < jsc.pub.previewSeparator.length; i += 1) { 994 ctx.beginPath(); 995 ctx.strokeStyle = jsc.pub.previewSeparator[i]; 996 ctx.moveTo(0.5 + start + i, 0); 997 ctx.lineTo(0.5 + start + i, cHeight); 998 ctx.stroke(); 999 } 1000 } 1001 1002 return { 1003 canvas: canvas, 1004 width: cWidth, 1005 height: cHeight, 1006 }; 1007 }, 1008 1009 1010 // if position or width is not set => fill the entire element (0%-100%) 1011 genColorPreviewGradient : function (color, position, width) { 1012 var params = []; 1013 1014 if (position && width) { 1015 params = [ 1016 'to ' + {'left':'right', 'right':'left'}[position], 1017 color + ' 0%', 1018 color + ' ' + width + 'px', 1019 'rgba(0,0,0,0) ' + (width + 1) + 'px', 1020 'rgba(0,0,0,0) 100%', 1021 ]; 1022 } else { 1023 params = [ 1024 'to right', 1025 color + ' 0%', 1026 color + ' 100%', 1027 ]; 1028 } 1029 1030 return jsc.linearGradient.apply(this, params); 1031 }, 1032 1033 1034 redrawPosition : function () { 1035 1036 if (!jsc.picker || !jsc.picker.owner) { 1037 return; // picker is not shown 1038 } 1039 1040 var thisObj = jsc.picker.owner; 1041 1042 if (thisObj.container !== window.document.body) { 1043 1044 jsc._drawPosition(thisObj, 0, 0, 'relative', false); 1045 1046 } else { 1047 1048 var tp, vp; 1049 1050 if (thisObj.fixed) { 1051 // Fixed elements are positioned relative to viewport, 1052 // therefore we can ignore the scroll offset 1053 tp = jsc.getElementPos(thisObj.targetElement, true); // target pos 1054 vp = [0, 0]; // view pos 1055 } else { 1056 tp = jsc.getElementPos(thisObj.targetElement); // target pos 1057 vp = jsc.getViewPos(); // view pos 1058 } 1059 1060 var ts = jsc.getElementSize(thisObj.targetElement); // target size 1061 var vs = jsc.getViewSize(); // view size 1062 var pd = jsc.getPickerDims(thisObj); 1063 var ps = [pd.borderW, pd.borderH]; // picker outer size 1064 var a, b, c; 1065 switch (thisObj.position.toLowerCase()) { 1066 case 'left': a=1; b=0; c=-1; break; 1067 case 'right':a=1; b=0; c=1; break; 1068 case 'top': a=0; b=1; c=-1; break; 1069 default: a=0; b=1; c=1; break; 1070 } 1071 var l = (ts[b]+ps[b])/2; 1072 1073 // compute picker position 1074 if (!thisObj.smartPosition) { 1075 var pp = [ 1076 tp[a], 1077 tp[b]+ts[b]-l+l*c 1078 ]; 1079 } else { 1080 var pp = [ 1081 -vp[a]+tp[a]+ps[a] > vs[a] ? 1082 (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >
1082= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : 1083 tp[a], 1084 -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? 1085 (-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) : 1086 (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) 1087 ]; 1088 } 1089 1090 var x = pp[a]; 1091 var y = pp[b]; 1092 var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; 1093 var contractShadow = 1094 (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && 1095 (pp[1] + ps[1] < tp[1] + ts[1]); 1096 1097 jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); 1098 1099 } 1100 1101 }, 1102 1103 1104 _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { 1105 var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px 1106 1107 jsc.picker.wrap.style.position = positionValue; 1108 1109 if ( // To avoid unnecessary repositioning during scroll 1110 Math.round(parseFloat(jsc.picker.wrap.style.left)) !== Math.round(x) || 1111 Math.round(parseFloat(jsc.picker.wrap.style.top)) !== Math.round(y) 1112 ) { 1113 jsc.picker.wrap.style.left = x + 'px'; 1114 jsc.picker.wrap.style.top = y + 'px'; 1115 } 1116 1117 jsc.setBoxShadow( 1118 jsc.picker.boxS, 1119 thisObj.shadow ? 1120 new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : 1121 null); 1122 }, 1123 1124 1125 getPickerDims : function (thisObj) { 1126 var w = 2 * thisObj.controlBorderWidth + thisObj.width; 1127 var h = 2 * thisObj.controlBorderWidth + thisObj.height; 1128 1129 var sliderSpace = 2 * thisObj.controlBorderWidth + 2 * jsc.getControlPadding(thisObj) + thisObj.sliderSize; 1130 1131 if (jsc.getSliderChannel(thisObj)) { 1132 w += sliderSpace; 1133 } 1134 if (thisObj.hasAlphaChannel()) { 1135 w += sliderSpace; 1136 } 1137 1138 var pal = jsc.getPaletteDims(thisObj, w); 1139 1140 if (pal.height) { 1141 h += pal.height + thisObj.padding; 1142 } 1143 if (thisObj.closeButton) { 1144 h += 2 * thisObj.controlBorderWidth + thisObj.padding + thisObj.buttonHeight; 1145 } 1146 1147 var pW = w + (2 * thisObj.padding); 1148 var pH = h + (2 * thisObj.padding); 1149 1150 return { 1151 contentW: w, 1152 contentH: h, 1153 paddedW: pW, 1154 paddedH: pH, 1155 borderW: pW + (2 * thisObj.borderWidth), 1156 borderH: pH + (2 * thisObj.borderWidth), 1157 palette: pal, 1158 }; 1159 }, 1160 1161 1162 getPaletteDims : function (thisObj, width) { 1163 var cols = 0, rows = 0, cellW = 0, cellH = 0, height = 0; 1164 var sampleCount = thisObj._palette ? thisObj._palette.length : 0; 1165 1166 if (sampleCount) { 1167 cols = thisObj.paletteCols; 1168 rows = cols > 0 ? Math.ceil(sampleCount / cols) : 0; 1169 1170 // color sample's dimensions (includes border) 1171 cellW = Math.max(1, Math.floor((width - ((cols - 1) * thisObj.paletteSpacing)) / cols)); 1172 cellH = thisObj.paletteHeight ? Math.min(thisObj.paletteHeight, cellW) : cellW; 1173 } 1174 1175 if (rows) { 1176 height = 1177 rows * cellH + 1178 (rows - 1) * thisObj.paletteSpacing; 1179 } 1180 1181 return { 1182 cols: cols, 1183 rows: rows, 1184 cellW: cellW, 1185 cellH: cellH, 1186 width: width, 1187 height: height, 1188 }; 1189 }, 1190 1191 1192 getControlPadding : function (thisObj) { 1193 return Math.max( 1194 thisObj.padding / 2, 1195 (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness) - thisObj.controlBorderWidth 1196 ); 1197 }, 1198 1199 1200 getPadYChannel : function (thisObj) { 1201 switch (thisObj.mode.charAt(1).toLowerCase()) { 1202 case 'v': return 'v'; break; 1203 } 1204 return 's'; 1205 }, 1206 1207 1208 getSliderChannel : function (thisObj) { 1209 if (thisObj.mode.length > 2) { 1210 switch (thisObj.mode.charAt(2).toLowerCase()) { 1211 case 's': return 's'; break; 1212 case 'v': return 'v'; break; 1213 } 1214 } 1215 return null; 1216 }, 1217 1218 1219 // calls function specified in picker's property 1220 triggerCallback : function (thisObj, prop) { 1221 if (!thisObj[prop]) { 1222 return; // callback func not specified 1223 } 1224 var callback = null; 1225 1226 if (typeof thisObj[prop] === 'string') { 1227 // string with code 1228 try { 1229 callback = new Function (thisObj[prop]); 1230 } catch (e) { 1231 console.error(e); 1232 } 1233 } else { 1234 // function 1235 callback = thisObj[prop]; 1236 } 1237 1238 if (callback) { 1239 callback.call(thisObj); 1240 } 1241 }, 1242 1243 1244 // Triggers a color change related event(s) on all picker instances. 1245 // It is possible to specify multiple events separated with a space. 1246 triggerGlobal : function (eventNames) { 1247 var inst = jsc.getInstances(); 1248 for (var i = 0; i < inst.length; i += 1) { 1249 inst[i].trigger(eventNames); 1250 } 1251 }, 1252 1253 1254 _pointerMoveEvent : { 1255 mouse: 'mousemove', 1256 touch: 'touchmove' 1257 }, 1258 _pointerEndEvent : { 1259 mouse: 'mouseup', 1260 touch: 'touchend' 1261 }, 1262 1263 1264 _pointerOrigin : null, 1265 1266 1267 onDocumentKeyUp : function (e) { 1268 if (['Tab', 'Escape'].indexOf(jsc.eventKey(e)) !== -1) { 1269 if (jsc.picker && jsc.picker.owner) { 1270 jsc.picker.owner.tryHide(); 1271 } 1272 } 1273 }, 1274 1275 1276 onWindowResize : function (e) { 1277 jsc.redrawPosition(); 1278 }, 1279 1280 1281 onWindowScroll : function (e) { 1282 jsc.redrawPosition(); 1283 }, 1284 1285 1286 onParentScroll : function (e) { 1287 // hide the picker when one of the parent elements is scrolled 1288 if (jsc.picker && jsc.picker.owner) { 1289 jsc.picker.owner.tryHide(); 1290 } 1291 }, 1292 1293 1294 onDocumentMouseDown : function (e) { 1295 var target = e.target || e.srcElement; 1296 1297 if (target.jscolor && target.jscolor instanceof jsc.pub) { // clicked targetElement -> show picker 1298 if (target.jscolor.showOnClick && !target.disabled) { 1299 target.jscolor.show(); 1300 } 1301 } else if (jsc.getData(target, 'gui')) { // clicked jscolor's GUI element 1302 var control = jsc.getData(target, 'control'); 1303 if (control) { 1304 // jscolor's control 1305 jsc.onControlPointerStart(e, target, jsc.getData(target, 'control'), 'mouse'); 1306 } 1307 } else { 1308 // mouse is outside the picker's controls -> hide the color picker! 1309 if (jsc.picker && jsc.picker.owner) { 1310 jsc.picker.owner.tryHide(); 1311 } 1312 } 1313 }, 1314 1315 1316 onPickerTouchStart : function (e) { 1317 var target = e.target || e.srcElement; 1318 1319 if (jsc.getData(target, 'control')) { 1320 jsc.onControlPointerStart(e, target, jsc.getData(target, 'control'), 'touch'); 1321 } 1322 }, 1323 1324 1325 onControlPointerStart : function (e, target, controlName, pointerType) { 1326 var thisObj = jsc.getData(target, 'instance'); 1327 1328 jsc.preventDefault(e); 1329 1330 var registerDragEvents = function (doc, offset) { 1331 jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], 1332 jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); 1333 jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], 1334 jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); 1335 }; 1336 1337 registerDragEvents(window.document, [0, 0]); 1338 1339 if (window.parent && window.frameElement) { 1340 var rect = window.frameElement.getBoundingClientRect(); 1341 var ofs = [-rect.left, -rect.top]; 1342 registerDragEvents(window.parent.window.document, ofs); 1343 } 1344 1345 var abs = jsc.getAbsPointerPos(e); 1346 var rel = jsc.getRelPointerPos(e); 1347 jsc._pointerOrigin = { 1348 x: abs.x - rel.x, 1349 y: abs.y - rel.y 1350 }; 1351 1352 switch (controlName) { 1353 case 'pad': 1354 // if the value slider is at the bottom, move it up 1355 if (jsc.getSliderChannel(thisObj) === 'v' && thisObj.channels.v === 0) { 1356 thisObj.fromHSVA(null, null, 100, null); 1357 }
1358 jsc.setPad(thisObj, e, 0, 0); 1359 break; 1360 1361 case 'sld': 1362 jsc.setSld(thisObj, e, 0); 1363 break; 1364 1365 case 'asld': 1366 jsc.setASld(thisObj, e, 0); 1367 break; 1368 } 1369 thisObj.trigger('input'); 1370 }, 1371 1372 1373 onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { 1374 return function (e) { 1375 var thisObj = jsc.getData(target, 'instance'); 1376 switch (controlName) { 1377 case 'pad': 1378 jsc.setPad(thisObj, e, offset[0], offset[1]); 1379 break; 1380 1381 case 'sld': 1382 jsc.setSld(thisObj, e, offset[1]); 1383 break; 1384 1385 case 'asld': 1386 jsc.setASld(thisObj, e, offset[1]); 1387 break; 1388 } 1389 thisObj.trigger('input'); 1390 } 1391 }, 1392 1393 1394 onDocumentPointerEnd : function (e, target, controlName, pointerType) { 1395 return function (e) { 1396 var thisObj = jsc.getData(target, 'instance'); 1397 jsc.detachGroupEvents('drag'); 1398 1399 // Always trigger changes AFTER detaching outstanding mouse handlers, 1400 // in case some color change that occured in user-defined onChange/onInput handler 1401 // intruded into current mouse events 1402 thisObj.trigger('input'); 1403 thisObj.trigger('change'); 1404 }; 1405 }, 1406 1407 1408 onPaletteSampleClick : function (e) { 1409 var target = e.currentTarget; 1410 var thisObj = jsc.getData(target, 'instance'); 1411 var color = jsc.getData(target, 'color'); 1412 1413 // when format is flexible, use the original format of this color sample 1414 if (thisObj.format.toLowerCase() === 'any') { 1415 thisObj._setFormat(color.format); // adapt format 1416 if (!jsc.isAlphaFormat(thisObj.getFormat())) { 1417 color.rgba[3] = 1.0; // when switching to a format that doesn't support alpha, set full opacity 1418 } 1419 } 1420 1421 // if this color doesn't specify alpha, use alpha of 1.0 (if applicable) 1422 if (color.rgba[3] === null) { 1423 if (thisObj.paletteSetsAlpha === true || (thisObj.paletteSetsAlpha === 'auto' && thisObj._paletteHasTransparency)) { 1424 color.rgba[3] = 1.0; 1425 } 1426 } 1427 1428 thisObj.fromRGBA.apply(thisObj, color.rgba); 1429 1430 thisObj.trigger('input'); 1431 thisObj.trigger('change'); 1432 1433 if (thisObj.hideOnPaletteClick) { 1434 thisObj.hide(); 1435 } 1436 }, 1437 1438 1439 setPad : function (thisObj, e, ofsX, ofsY) { 1440 var pointerAbs = jsc.getAbsPointerPos(e); 1441 var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.controlBorderWidth; 1442 var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; 1443 1444 var xVal = x * (360 / (thisObj.width - 1)); 1445 var yVal = 100 - (y * (100 / (thisObj.height - 1))); 1446 1447 switch (jsc.getPadYChannel(thisObj)) { 1448 case 's': thisObj.fromHSVA(xVal, yVal, null, null); break; 1449 case 'v': thisObj.fromHSVA(xVal, null, yVal, null); break; 1450 } 1451 }, 1452 1453 1454 setSld : function (thisObj, e, ofsY) { 1455 var pointerAbs = jsc.getAbsPointerPos(e); 1456 var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; 1457 var yVal = 100 - (y * (100 / (thisObj.height - 1))); 1458 1459 switch (jsc.getSliderChannel(thisObj)) { 1460 case 's': thisObj.fromHSVA(null, yVal, null, null); break; 1461 case 'v': thisObj.fromHSVA(null, null, yVal, null); break; 1462 } 1463 }, 1464 1465 1466 setASld : function (thisObj, e, ofsY) { 1467 var pointerAbs = jsc.getAbsPointerPos(e); 1468 var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; 1469 var yVal = 1.0 - (y * (1.0 / (thisObj.height - 1))); 1470 1471 if (yVal < 1.0) { 1472 // if format is flexible and the current format doesn't support alpha, switch to a suitable one 1473 var fmt = thisObj.getFormat(); 1474 if (thisObj.format.toLowerCase() === 'any' && !jsc.isAlphaFormat(fmt)) { 1475 thisObj._setFormat(fmt === 'hex' ? 'hexa' : 'rgba'); 1476 } 1477 } 1478 1479 thisObj.fromHSVA(null, null, null, yVal); 1480 }, 1481 1482 1483 createPadCanvas : function () { 1484 1485 var ret = { 1486 elm: null, 1487 draw: null 1488 }; 1489 1490 var canvas = jsc.createEl('canvas'); 1491 var ctx = canvas.getContext('2d'); 1492 1493 var drawFunc = function (width, height, type) { 1494 canvas.width = width; 1495 canvas.height = height; 1496 1497 ctx.clearRect(0, 0, canvas.width, canvas.height); 1498 1499 var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); 1500 hGrad.addColorStop(0 / 6, '#F00'); 1501 hGrad.addColorStop(1 / 6, '#FF0'); 1502 hGrad.addColorStop(2 / 6, '#0F0'); 1503 hGrad.addColorStop(3 / 6, '#0FF'); 1504 hGrad.addColorStop(4 / 6, '#00F'); 1505 hGrad.addColorStop(5 / 6, '#F0F'); 1506 hGrad.addColorStop(6 / 6, '#F00'); 1507 1508 ctx.fillStyle = hGrad; 1509 ctx.fillRect(0, 0, canvas.width, canvas.height); 1510 1511 var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); 1512 switch (type.toLowerCase()) { 1513 case 's': 1514 vGrad.addColorStop(0, 'rgba(255,255,255,0)'); 1515 vGrad.addColorStop(1, 'rgba(255,255,255,1)'); 1516 break; 1517 case 'v': 1518 vGrad.addColorStop(0, 'rgba(0,0,0,0)'); 1519 vGrad.addColorStop(1, 'rgba(0,0,0,1)'); 1520 break; 1521 } 1522 ctx.fillStyle = vGrad; 1523 ctx.fillRect(0, 0, canvas.width, canvas.height); 1524 }; 1525 1526 ret.elm = canvas; 1527 ret.draw = drawFunc; 1528 1529 return ret; 1530 }, 1531 1532 1533 createSliderGradient : function () { 1534 1535 var ret = { 1536 elm: null, 1537 draw: null 1538 }; 1539 1540 var canvas = jsc.createEl('canvas'); 1541 var ctx = canvas.getContext('2d'); 1542 1543 var drawFunc = function (width, height, color1, color2) { 1544 canvas.width = width; 1545 canvas.height = height; 1546 1547 ctx.clearRect(0, 0, canvas.width, canvas.height); 1548 1549 var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); 1550 grad.addColorStop(0, color1); 1551 grad.addColorStop(1, color2); 1552 1553 ctx.fillStyle = grad; 1554 ctx.fillRect(0, 0, canvas.width, canvas.height); 1555 }; 1556 1557 ret.elm = canvas; 1558 ret.draw = drawFunc; 1559 1560 return ret; 1561 }, 1562 1563 1564 createASliderGradient : function () { 1565 1566 var ret = { 1567 elm: null, 1568 draw: null 1569 }; 1570 1571 var canvas = jsc.createEl('canvas'); 1572 var ctx = canvas.getContext('2d'); 1573 1574 var drawFunc = function (width, height, color) { 1575 canvas.width = width; 1576 canvas.height = height; 1577 1578 ctx.clearRect(0, 0, canvas.width, canvas.height); 1579 1580 var sqSize = canvas.width / 2; 1581 var sqColor1 = jsc.pub.chessboardColor1; 1582 var sqColor2 = jsc.pub.chessboardColor2; 1583 1584 // dark gray background 1585 ctx.fillStyle = sqColor1; 1586 ctx.fillRect(0, 0, canvas.width, canvas.height); 1587 1588 if (sqSize > 0) { // to avoid infinite loop 1589 for (var y = 0; y < canvas.height; y += sqSize * 2) { 1590 // light gray squares 1591 ctx.fillStyle = sqColor2; 1592 ctx.fillRect(0, y, sqSize, sqSize); 1593 ctx.fillRect(sqSize, y + sqSize, sqSize, sqSize); 1594 } 1595 } 1596 1597 var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); 1598 grad.addColorStop(0, color); 1599 grad.addColorStop(1, 'rgba(0,0,0,0)'); 1600 1601 ctx.fillStyle = grad; 1602 ctx.fillRect(0, 0, canvas.width, canvas.height); 1603 }; 1604 1605 ret.elm = canvas; 1606 ret.draw = drawFunc; 1607 1608 return ret; 1609 }, 1610 1611 1612 BoxShadow : (function () { 1613 var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) {
1614 this.hShadow = hShadow; 1615 this.vShadow = vShadow; 1616 this.blur = blur; 1617 this.spread = spread; 1618 this.color = color; 1619 this.inset = !!inset; 1620 }; 1621 1622 BoxShadow.prototype.toString = function () { 1623 var vals = [ 1624 Math.round(this.hShadow) + 'px', 1625 Math.round(this.vShadow) + 'px', 1626 Math.round(this.blur) + 'px', 1627 Math.round(this.spread) + 'px', 1628 this.color 1629 ]; 1630 if (this.inset) { 1631 vals.push('inset'); 1632 } 1633 return vals.join(' '); 1634 }; 1635 1636 return BoxShadow; 1637 })(), 1638 1639 1640 flags : { 1641 leaveValue : 1 << 0, 1642 leaveAlpha : 1 << 1, 1643 leavePreview : 1 << 2, 1644 }, 1645 1646 1647 enumOpts : { 1648 format: ['auto', 'any', 'hex', 'hexa', 'rgb', 'rgba'], 1649 previewPosition: ['left', 'right'], 1650 mode: ['hsv', 'hvs', 'hs', 'hv'], 1651 position: ['left', 'right', 'top', 'bottom'], 1652 alphaChannel: ['auto', true, false], 1653 paletteSetsAlpha: ['auto', true, false], 1654 }, 1655 1656 1657 deprecatedOpts : { 1658 // <old_option>: <new_option> (<new_option> can be null) 1659 'styleElement': 'previewElement', 1660 'onFineChange': 'onInput', 1661 'overwriteImportant': 'forceStyle', 1662 'closable': 'closeButton', 1663 'insetWidth': 'controlBorderWidth', 1664 'insetColor': 'controlBorderColor', 1665 'refine': null, 1666 }, 1667 1668 1669 docsRef : ' ' + 'See https://jscolor.com/docs/', 1670 1671 1672 // 1673 // Usage: 1674 // var myPicker = new JSColor(<targetElement> [, <options>]) 1675 // 1676 // (constructor is accessible via both 'jscolor' and 'JSColor' name) 1677 // 1678 1679 pub : function (targetElement, opts) { 1680 1681 var THIS = this; 1682 1683 if (!opts) { 1684 opts = {}; 1685 } 1686 1687 this.channels = { 1688 r: 255, // red [0-255] 1689 g: 255, // green [0-255] 1690 b: 255, // blue [0-255] 1691 h: 0, // hue [0-360] 1692 s: 0, // saturation [0-100] 1693 v: 100, // value (brightness) [0-100] 1694 a: 1.0, // alpha (opacity) [0.0 - 1.0] 1695 }; 1696 1697 // General options 1698 // 1699 this.format = 'auto'; // 'auto' | 'any' | 'hex' | 'hexa' | 'rgb' | 'rgba' - Format of the input/output value 1700 this.value = undefined; // INITIAL color value in any supported format. To change it later, use method fromString(), fromHSVA(), fromRGBA() or channel() 1701 this.alpha = undefined; // INITIAL alpha value. To change it later, call method channel('A', <value>) 1702 this.random = false; // whether to randomize the initial color. Either true | false, or an array of ranges: [minV, maxV, minS, maxS, minH, maxH, minA, maxA] 1703 this.onChange = undefined; // called when color changes. Value can be either a function or a string with JS code. 1704 this.onInput = undefined; // called repeatedly as the color is being changed, e.g. while dragging a slider. Value can be either a function or a string with JS code. 1705 this.valueElement = undefined; // element that will be used to display and input the color value 1706 this.alphaElement = undefined; // element that will be used to display and input the alpha (opacity) value 1707 this.previewElement = undefined; // element that will preview the picked color using CSS background 1708 this.previewPosition = 'left'; // 'left' | 'right' - position of the color preview in previewElement 1709 this.previewSize = 32; // (px) width of the color preview displayed in previewElement 1710 this.previewPadding = 8; // (px) space between color preview and content of the previewElement 1711 this.required = true; // whether the associated text input must always contain a color value. If false, the input can be left empty. 1712 this.hash = true; // whether to prefix the HEX color code with # symbol (only applicable for HEX format) 1713 this.uppercase = true; // whether to show the HEX color code in upper case (only applicable for HEX format) 1714 this.forceStyle = true; // whether to overwrite CSS style of the previewElement using !important flag 1715 1716 // Color Picker options 1717 // 1718 this.width = 181; // width of the color spectrum (in px) 1719 this.height = 101; // height of the color spectrum (in px) 1720 this.mode = 'HSV'; // 'HSV' | 'HVS' | 'HS' | 'HV' - layout of the color picker controls 1721 this.alphaChannel = 'auto'; // 'auto' | true | false - if alpha channel is enabled, the alpha slider will be visible. If 'auto', it will be determined according to color format 1722 this.position = 'bottom'; // 'left' | 'right' | 'top' | 'bottom' - position relative to the target element 1723 this.smartPosition = true; // automatically change picker position when there is not enough space for it 1724 this.showOnClick = true; // whether to show the picker when user clicks its target element 1725 this.hideOnLeave = true; // whether to automatically hide the picker when user leaves its target element (e.g. upon clicking the document) 1726 this.palette = []; // colors to be displayed in the palette, specified as an array or a string of space separated color values (in any supp
1726orted format) 1727 this.paletteCols = 10; // number of columns in the palette 1728 this.paletteSetsAlpha = 'auto'; // 'auto' | true | false - if true, palette colors that don't specify alpha will set alpha to 1.0 1729 this.paletteHeight = 16; // maximum height (px) of a row in the palette 1730 this.paletteSpacing = 4; // distance (px) between color samples in the palette 1731 this.hideOnPaletteClick = false; // when set to true, clicking the palette will also hide the color picker 1732 this.sliderSize = 16; // px 1733 this.crossSize = 8; // px 1734 this.closeButton = false; // whether to display the Close button 1735 this.closeText = 'Close'; 1736 this.buttonColor = 'rgba(0,0,0,1)'; // CSS color 1737 this.buttonHeight = 18; // px 1738 this.padding = 12; // px 1739 this.backgroundColor = 'rgba(255,255,255,1)'; // CSS color 1740 this.borderWidth = 1; // px 1741 this.borderColor = 'rgba(187,187,187,1)'; // CSS color 1742 this.borderRadius = 8; // px 1743 this.controlBorderWidth = 1; // px 1744 this.controlBorderColor = 'rgba(187,187,187,1)'; // CSS color 1745 this.shadow = true; // whether to display a shadow 1746 this.shadowBlur = 15; // px 1747 this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color 1748 this.pointerColor = 'rgba(76,76,76,1)'; // CSS color 1749 this.pointerBorderWidth = 1; // px 1750 this.pointerBorderColor = 'rgba(255,255,255,1)'; // CSS color 1751 this.pointerThickness = 2; // px 1752 this.zIndex = 5000; 1753 this.container = undefined; // where to append the color picker (BODY element by default) 1754 1755 // Experimental 1756 // 1757 this.minS = 0; // min allowed saturation (0 - 100) 1758 this.maxS = 100; // max allowed saturation (0 - 100) 1759 this.minV = 0; // min allowed value (brightness) (0 - 100) 1760 this.maxV = 100; // max allowed value (brightness) (0 - 100) 1761 this.minA = 0.0; // min allowed alpha (opacity) (0.0 - 1.0) 1762 this.maxA = 1.0; // max allowed alpha (opacity) (0.0 - 1.0) 1763 1764 1765 // Getter: option(name) 1766 // Setter: option(name, value) 1767 // option({name:value, ...}) 1768 // 1769 this.option = function () { 1770 if (!arguments.length) { 1771 throw new Error('No option specified'); 1772 } 1773 1774 if (arguments.length === 1 && typeof arguments[0] === 'string') { 1775 // getting a single option 1776 try { 1777 return getOption(arguments[0]); 1778 } catch (e) { 1779 console.warn(e); 1780 } 1781 return false; 1782 1783 } else if (arguments.length >= 2 && typeof arguments[0] === 'string') { 1784 // setting a single option 1785 try { 1786 if (!setOption(arguments[0], arguments[1])) { 1787 return false; 1788 } 1789 } catch (e) { 1790 console.warn(e); 1791 return false; 1792 } 1793 this.redraw(); // immediately redraws the picker, if it's displayed 1794 this.exposeColor(); // in case some preview-related or format-related option was changed 1795 return true; 1796 1797 } else if (arguments.length === 1 && typeof arguments[0] === 'object') { 1798 // setting multiple options 1799 var opts = arguments[0]; 1800 var success = true; 1801 for (var opt in opts) { 1802 if (opts.hasOwnProperty(opt)) { 1803 try { 1804 if (!setOption(opt, opts[opt])) { 1805 success = false; 1806 } 1807 } catch (e) { 1808 console.warn(e); 1809 success = false; 1810 } 1811 } 1812 } 1813 this.redraw(); // immediately redraws the picker, if it's displayed 1814 this.exposeColor(); // in case some preview-related or format-related option was changed 1815 return success; 1816 } 1817 1818 throw new Error('Invalid arguments'); 1819 } 1820 1821 1822 // Getter: channel(name) 1823 // Setter: channel(name, value) 1824 // 1825 this.channel = function (name, value) { 1826 if (typeof name !== 'string') { 1827 throw new Error('Invalid value for channel name: ' + name); 1828 } 1829 1830 if (value === undefined) { 1831 // getting channel value 1832 if (!this.channels.hasOwnProperty(name.toLowerCase())) { 1833 console.warn('Getting unknown channel: ' + name); 1834 return false; 1835 } 1836 return this.channels[name.toLowerCase()]; 1837 1838 } else { 1839 // setting channel value 1840 var res = false; 1841 switch (name.toLowerCase()) { 1842 case 'r': res = this.fromRGBA(value, null, null, null); break; 1843 case 'g': res = this.fromRGBA(null, value, null, null); break; 1844 case 'b': res = this.fromRGBA(null, null, value, null); break; 1845 case 'h': res = this.fromHSVA(value, null, null, null); break; 1846 case 's': res = this.fromHSVA(null, value, null, null); break; 1847 case 'v': res = this.fromHSVA(null, null, value, null); break; 1848 case 'a': res = this.fromHSVA(null, null, null, value); break; 1849 default: 1850 console.warn('Setting unknown channel: ' + name); 1851 return false; 1852 } 1853 if (res) { 1854 this.redraw(); // immediately redraws the picker, if it's displayed 1855 return true; 1856 } 1857 } 1858 1859 return false; 1860 } 1861 1862
1863 // Triggers given input event(s) by: 1864 // - executing on<Event> callback specified as picker's option 1865 // - triggering standard DOM event listeners attached to the value element 1866 // 1867 // It is possible to specify multiple events separated with a space. 1868 // 1869 this.trigger = function (eventNames) { 1870 var evs = jsc.strList(eventNames); 1871 for (var i = 0; i < evs.length; i += 1) { 1872 var ev = evs[i].toLowerCase(); 1873 1874 // trigger a callback 1875 var callbackProp = null; 1876 switch (ev) { 1877 case 'input': callbackProp = 'onInput'; break; 1878 case 'change': callbackProp = 'onChange'; break; 1879 } 1880 if (callbackProp) { 1881 jsc.triggerCallback(this, callbackProp); 1882 } 1883 1884 // trigger standard DOM event listeners on the value element 1885 jsc.triggerInputEvent(this.valueElement, ev, true, true); 1886 } 1887 }; 1888 1889 1890 // h: 0-360 1891 // s: 0-100 1892 // v: 0-100 1893 // a: 0.0-1.0 1894 // 1895 this.fromHSVA = function (h, s, v, a, flags) { // null = don't change 1896 if (h === undefined) { h = null; } 1897 if (s === undefined) { s = null; } 1898 if (v === undefined) { v = null; } 1899 if (a === undefined) { a = null; } 1900 1901 if (h !== null) { 1902 if (isNaN(h)) { return false; } 1903 this.channels.h = Math.max(0, Math.min(360, h)); 1904 } 1905 if (s !== null) { 1906 if (isNaN(s)) { return false; } 1907 this.channels.s = Math.max(0, Math.min(100, this.maxS, s), this.minS); 1908 } 1909 if (v !== null) { 1910 if (isNaN(v)) { return false; } 1911 this.channels.v = Math.max(0, Math.min(100, this.maxV, v), this.minV); 1912 } 1913 if (a !== null) { 1914 if (isNaN(a)) { return false; } 1915 this.channels.a = this.hasAlphaChannel() ? 1916 Math.max(0, Math.min(1, this.maxA, a), this.minA) : 1917 1.0; // if alpha channel is disabled, the color should stay 100% opaque 1918 } 1919 1920 var rgb = jsc.HSV_RGB( 1921 this.channels.h, 1922 this.channels.s, 1923 this.channels.v 1924 ); 1925 this.channels.r = rgb[0]; 1926 this.channels.g = rgb[1]; 1927 this.channels.b = rgb[2]; 1928 1929 this.exposeColor(flags); 1930 return true; 1931 }; 1932 1933 1934 // r: 0-255 1935 // g: 0-255 1936 // b: 0-255 1937 // a: 0.0-1.0 1938 // 1939 this.fromRGBA = function (r, g, b, a, flags) { // null = don't change 1940 if (r === undefined) { r = null; } 1941 if (g === undefined) { g = null; } 1942 if (b === undefined) { b = null; } 1943 if (a === undefined) { a = null; } 1944 1945 if (r !== null) { 1946 if (isNaN(r)) { return false; } 1947 r = Math.max(0, Math.min(255, r)); 1948 } 1949 if (g !== null) { 1950 if (isNaN(g)) { return false; } 1951 g = Math.max(0, Math.min(255, g)); 1952 } 1953 if (b !== null) { 1954 if (isNaN(b)) { return false; } 1955 b = Math.max(0, Math.min(255, b)); 1956 } 1957 if (a !== null) { 1958 if (isNaN(a)) { return false; } 1959 this.channels.a = this.hasAlphaChannel() ? 1960 Math.max(0, Math.min(1, this.maxA, a), this.minA) : 1961 1.0; // if alpha channel is disabled, the color should stay 100% opaque 1962 } 1963 1964 var hsv = jsc.RGB_HSV( 1965 r===null ? this.channels.r : r, 1966 g===null ? this.channels.g : g, 1967 b===null ? this.channels.b : b 1968 ); 1969 if (hsv[0] !== null) { 1970 this.channels.h = Math.max(0, Math.min(360, hsv[0])); 1971 } 1972 if (hsv[2] !== 0) { // fully black color stays black through entire saturation range, so let's not change saturation 1973 this.channels.s = Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); 1974 } 1975 this.channels.v = Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); 1976 1977 // update RGB according to final HSV, as some values might be trimmed 1978 var rgb = jsc.HSV_RGB(this.channels.h, this.channels.s, this.channels.v); 1979 this.channels.r = rgb[0]; 1980 this.channels.g = rgb[1]; 1981 this.channels.b = rgb[2]; 1982 1983 this.exposeColor(flags); 1984 return true; 1985 }; 1986 1987 1988 // DEPRECATED. Use .fromHSVA() instead 1989 // 1990 this.fromHSV = function (h, s, v, flags) { 1991 console.warn('fromHSV() method is DEPRECATED. Using fromHSVA() instead.' + jsc.docsRef); 1992 return this.fromHSVA(h, s, v, null, flags); 1993 }; 1994 1995 1996 // DEPRECATED. Use .fromRGBA() instead 1997 // 1998 this.fromRGB = function (r, g, b, flags) { 1999 console.warn('fromRGB() method is DEPRECATED. Using fromRGBA() instead.' + jsc.docsRef); 2000 return this.fromRGBA(r, g, b, null, flags); 2001 }; 2002 2003 2004 this.fromString = function (str, flags) { 2005 if (!this.required && str.trim() === '') { 2006 // setting empty string to an optional color input 2007 this.setPreviewElementBg(null); 2008 this.setValueElementValue(''); 2009 return true; 2010 } 2011 2012 var color = jsc.parseColorString(str); 2013 if (!color) { 2014 return false; // could not parse 2015 } 2016 if (this.format.toLowerCase() === 'any') { 2017 this._setFormat(color.format); // adapt format 2018 if (!jsc.isAlphaFormat(this.getFormat())) { 2019 color.rgba[3] = 1.0; // when switching to a format that doesn't support alpha, set full opacity 2020 } 2021 } 2022 this.fromRGBA( 2023 color.rgba[0], 2024 color.rgba[1], 2025 color.rgba[2], 2026 color.rgba[3], 2027 flags 2028 ); 2029 return true; 2030 }; 2031 2032 2033 this.randomize = function (minV, maxV, minS, maxS, minH, maxH, minA, maxA) { 2034 if (minV === undefined) { minV = 0; } 2035 if (maxV === undefined) { maxV = 100; } 2036 if (minS === undefined) { minS = 0; } 2037 if (maxS === undefined) { maxS = 100; } 2038 if (minH === undefined) { minH = 0; } 2039 if (maxH === undefined) { maxH = 359; } 2040 if (minA === undefined) { minA = 1; } 2041 if (maxA === undefined) { maxA = 1; } 2042 2043 this.fromHSVA( 2044 minH + Math.floor(Math.random() * (maxH - minH + 1)), 2045 minS + Math.floor(Math.random() * (maxS - minS + 1)), 2046 minV + Math.floor(Math.random() * (maxV - minV + 1)), 2047 ((100 * minA) + Math.floor(Math.random() * (100 * (maxA - minA) + 1))) / 100 2048 ); 2049 }; 2050 2051 2052 this.toString = function (format) { 2053 if (format === undefined) { 2054 format = this.getFormat(); // format not specified -> use the current format 2055 } 2056 switch (format.toLowerCase()) { 2057 case 'hex': return this.toHEXString(); break; 2058 case 'hexa': return this.toHEXAString(); break; 2059 case 'rgb': return this.toRGBString(); break; 2060 case 'rgba': return this.toRGBAString(); break; 2061 } 2062 return false; 2063 }; 2064 2065 2066 this.toHEXString = function () { 2067 return jsc.hexColor( 2068 this.channels.r, 2069 this.channels.g, 2070 this.channels.b 2071 ); 2072 }; 2073 2074 2075 this.toHEXAString = function () { 2076 return jsc.hexaColor( 2077 this.channels.r, 2078 this.channels.g, 2079 this.channels.b, 2080 this.channels.a 2081 ); 2082 }; 2083 2084 2085 this.toRGBString = function () { 2086 return jsc.rgbColor( 2087 this.channels.r, 2088 this.channels.g, 2089 this.channels.b 2090 ); 2091 }; 2092 2093 2094 this.toRGBAString = function () { 2095 return jsc.rgbaColor( 2096 this.channels.r, 2097 this.channels.g, 2098 this.channels.b, 2099 this.channels.a 2100 ); 2101 }; 2102 2103 2104 this.toGrayscale = function () { 2105 return ( 2106 0.213 * this.channels.r + 2107 0.715 * this.channels.g + 2108 0.072 * this.channels.b 2109 ); 2110 }; 2111 2112 2113 this.toCanvas = function () { 2114 return jsc.genColorPreviewCanvas(this.toRGBAString()).canvas; 2115 }; 2116 2117 2118 this.toDataURL = function () { 2119 return this.toCanvas().toDataURL(); 2120 }; 2121 2122
2123 this.toBackground = function () { 2124 return jsc.pub.background(this.toRGBAString()); 2125 }; 2126 2127 2128 this.isLight = function () { 2129 return this.toGrayscale() > 255 / 2; 2130 }; 2131 2132 2133 this.hide = function () { 2134 if (isPickerOwner()) { 2135 detachPicker(); 2136 } 2137 }; 2138 2139 2140 this.show = function () { 2141 drawPicker(); 2142 }; 2143 2144 2145 this.redraw = function () { 2146 if (isPickerOwner()) { 2147 drawPicker(); 2148 } 2149 }; 2150 2151 2152 this.getFormat = function () { 2153 return this._currentFormat; 2154 }; 2155 2156 2157 this._setFormat = function (format) { 2158 this._currentFormat = format.toLowerCase(); 2159 }; 2160 2161 2162 this.hasAlphaChannel = function () { 2163 if (this.alphaChannel === 'auto') { 2164 return ( 2165 this.format.toLowerCase() === 'any' || // format can change on the fly (e.g. from hex to rgba), so let's consider the alpha channel enabled 2166 jsc.isAlphaFormat(this.getFormat()) || // the current format supports alpha channel 2167 this.alpha !== undefined || // initial alpha value is set, so we're working with alpha channel 2168 this.alphaElement !== undefined // the alpha value is redirected, so we're working with alpha channel 2169 ); 2170 } 2171 2172 return this.alphaChannel; // the alpha channel is explicitly set 2173 }; 2174 2175 2176 this.processValueInput = function (str) { 2177 if (!this.fromString(str)) { 2178 // could not parse the color value - let's just expose the current color 2179 this.exposeColor(); 2180 } 2181 }; 2182 2183 2184 this.processAlphaInput = function (str) { 2185 if (!this.fromHSVA(null, null, null, parseFloat(str))) { 2186 // could not parse the alpha value - let's just expose the current color 2187 this.exposeColor(); 2188 } 2189 }; 2190 2191 2192 this.exposeColor = function (flags) { 2193 var colorStr = this.toString(); 2194 var fmt = this.getFormat(); 2195 2196 // reflect current color in data- attribute 2197 jsc.setDataAttr(this.targetElement, 'current-color', colorStr); 2198 2199 if (!(flags & jsc.flags.leaveValue) && this.valueElement) { 2200 if (fmt === 'hex' || fmt === 'hexa') { 2201 if (!this.uppercase) { colorStr = colorStr.toLowerCase(); } 2202 if (!this.hash) { colorStr = colorStr.replace(/^#/, ''); } 2203 } 2204 this.setValueElementValue(colorStr); 2205 } 2206 2207 if (!(flags & jsc.flags.leaveAlpha) && this.alphaElement) { 2208 var alphaVal = Math.round(this.channels.a * 100) / 100; 2209 this.setAlphaElementValue(alphaVal); 2210 } 2211 2212 if (!(flags & jsc.flags.leavePreview) && this.previewElement) { 2213 var previewPos = null; // 'left' | 'right' (null -> fill the entire element) 2214 2215 if ( 2216 jsc.isTextInput(this.previewElement) || // text input 2217 (jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) // button with text 2218 ) { 2219 previewPos = this.previewPosition; 2220 } 2221 2222 this.setPreviewElementBg(this.toRGBAString()); 2223 } 2224 2225 if (isPickerOwner()) { 2226 redrawPad(); 2227 redrawSld(); 2228 redrawASld(); 2229 } 2230 }; 2231 2232 2233 this.setPreviewElementBg = function (color) { 2234 if (!this.previewElement) { 2235 return; 2236 } 2237 2238 var position = null; // color preview position: null | 'left' | 'right' 2239 var width = null; // color preview width: px | null = fill the entire element 2240 if ( 2241 jsc.isTextInput(this.previewElement) || // text input 2242 (jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) // button with text 2243 ) { 2244 position = this.previewPosition; 2245 width = this.previewSize; 2246 } 2247 2248 var backgrounds = []; 2249 2250 if (!color) { 2251 // there is no color preview to display -> let's remove any previous background image 2252 backgrounds.push({ 2253 image: 'none', 2254 position: 'left top', 2255 size: 'auto', 2256 repeat: 'no-repeat', 2257 origin: 'padding-box', 2258 }); 2259 } else { 2260 // CSS gradient for background color preview 2261 backgrounds.push({ 2262 image: jsc.genColorPreviewGradient( 2263 color, 2264 position, 2265 width ? width - jsc.pub.previewSeparator.length : null 2266 ), 2267 position: 'left top', 2268 size: 'auto', 2269 repeat: position ? 'repeat-y' : 'repeat', 2270 origin: 'padding-box', 2271 }); 2272 2273 // data URL of generated PNG image with a gray transparency chessboard 2274 var preview = jsc.genColorPreviewCanvas( 2275 'rgba(0,0,0,0)', 2276 position ? {'left':'right', 'right':'left'}[position] : null, 2277 width, 2278 true 2279 ); 2280 backgrounds.push({ 2281 image: 'url(\'' + preview.canvas.toDataURL() + '\')', 2282 position: (position || 'left') + ' top', 2283 size: preview.width + 'px ' + preview.height + 'px', 2284 repeat: position ? 'repeat-y' : 'repeat', 2285 origin: 'padding-box', 2286 }); 2287 } 2288 2289 var bg = { 2290 image: [], 2291 position: [], 2292 size: [], 2293 repeat: [], 2294 origin: [], 2295 }; 2296 for (var i = 0; i < backgrounds.length; i += 1) { 2297 bg.image.push(backgrounds[i].image); 2298 bg.position.push(backgrounds[i].position); 2299 bg.size.push(backgrounds[i].size); 2300 bg.repeat.push(backgrounds[i].repeat); 2301 bg.origin.push(backgrounds[i].origin); 2302 } 2303 2304 // set previewElement's background-images 2305 var sty = { 2306 'background-image': bg.image.join(', '), 2307 'background-position': bg.position.join(', '), 2308 'background-size': bg.size.join(', '), 2309 'background-repeat': bg.repeat.join(', '), 2310 'background-origin': bg.origin.join(', '), 2311 }; 2312 jsc.setStyle(this.previewElement, sty, this.forceStyle); 2313 2314 2315 // set/restore previewElement's padding 2316 var padding = { 2317 left: null, 2318 right: null, 2319 }; 2320 if (position) { 2321 padding[position] = (this.previewSize + this.previewPadding) + 'px'; 2322 } 2323 2324 var sty = { 2325 'padding-left': padding.left, 2326 'padding-right': padding.right, 2327 }; 2328 jsc.setStyle(this.previewElement, sty, this.forceStyle, true); 2329 }; 2330 2331 2332 this.setValueElementValue = function (str) { 2333 if (this.valueElement) {
2334 if (jsc.nodeName(this.valueElement) === 'input') { 2335 this.valueElement.value = str; 2336 } else { 2337 this.valueElement.innerHTML = str; 2338 } 2339 } 2340 }; 2341 2342 2343 this.setAlphaElementValue = function (str) { 2344 if (this.alphaElement) { 2345 if (jsc.nodeName(this.alphaElement) === 'input') { 2346 this.alphaElement.value = str; 2347 } else { 2348 this.alphaElement.innerHTML = str; 2349 } 2350 } 2351 }; 2352 2353 2354 this._processParentElementsInDOM = function () { 2355 if (this._parentElementsProcessed) { return; } 2356 this._parentElementsProcessed = true; 2357 2358 var elm = this.targetElement; 2359 do { 2360 // If the target element or one of its parent nodes has fixed position, 2361 // then use fixed positioning instead 2362 var compStyle = jsc.getCompStyle(elm); 2363 if (compStyle.position && compStyle.position.toLowerCase() === 'fixed') { 2364 this.fixed = true; 2365 } 2366 2367 if (elm !== this.targetElement) { 2368 // Ensure to attach onParentScroll only once to each parent element 2369 // (multiple targetElements can share the same parent nodes) 2370 // 2371 // Note: It's not just offsetParents that can be scrollable, 2372 // that's why we loop through all parent nodes 2373 if (!jsc.getData(elm, 'hasScrollListener')) { 2374 elm.addEventListener('scroll', jsc.onParentScroll, false); 2375 jsc.setData(elm, 'hasScrollListener', true); 2376 } 2377 } 2378 } while ((elm = elm.parentNode) && jsc.nodeName(elm) !== 'body'); 2379 }; 2380 2381 2382 this.tryHide = function () { 2383 if (this.hideOnLeave) { 2384 this.hide(); 2385 } 2386 }; 2387 2388 2389 this.set__palette = function (val) { 2390 this.palette = val; 2391 this._palette = jsc.parsePaletteValue(val); 2392 this._paletteHasTransparency = jsc.containsTranparentColor(this._palette); 2393 }; 2394 2395 2396 function setOption (option, value) { 2397 if (typeof option !== 'string') { 2398 throw new Error('Invalid value for option name: ' + option); 2399 } 2400 2401 // enum option 2402 if (jsc.enumOpts.hasOwnProperty(option)) { 2403 if (typeof value === 'string') { // enum string values are case insensitive 2404 value = value.toLowerCase(); 2405 } 2406 if (jsc.enumOpts[option].indexOf(value) === -1) { 2407 throw new Error('Option \'' + option + '\' has invalid value: ' + value); 2408 } 2409 } 2410 2411 // deprecated option 2412 if (jsc.deprecatedOpts.hasOwnProperty(option)) { 2413 var oldOpt = option; 2414 var newOpt = jsc.deprecatedOpts[option]; 2415 if (newOpt) { 2416 // if we have a new name for this option, let's log a warning and use the new name 2417 console.warn('Option \'%s\' is DEPRECATED, using \'%s\' instead.' + jsc.docsRef, oldOpt, newOpt); 2418 option = newOpt; 2419 } else { 2420 // new name not available for the option 2421 throw new Error('Option \'' + option + '\' is DEPRECATED'); 2422 } 2423 } 2424 2425 var setter = 'set__' + option; 2426 2427 if (typeof THIS[setter] === 'function') { // a setter exists for this option 2428 THIS[setter](value); 2429 return true; 2430 2431 } else if (option in THIS) { // option exists as a property 2432 THIS[option] = value; 2433 return true; 2434 } 2435 2436 throw new Error('Unrecognized configuration option: ' + option); 2437 } 2438 2439 2440 function getOption (option) { 2441 if (typeof option !== 'string') { 2442 throw new Error('Invalid value for option name: ' + option); 2443 } 2444 2445 // deprecated option 2446 if (jsc.deprecatedOpts.hasOwnProperty(option)) { 2447 var oldOpt = option; 2448 var newOpt = jsc.deprecatedOpts[option]; 2449 if (newOpt) { 2450 // if we have a new name for this option, let's log a warning and use the new name 2451 console.warn('Option \'%s\' is DEPRECATED, using \'%s\' instead.' + jsc.docsRef, oldOpt, newOpt); 2452 option = newOpt; 2453 } else { 2454 // new name not available for the option 2455 throw new Error('Option \'' + option + '\' is DEPRECATED'); 2456 } 2457 } 2458 2459 var getter = 'get__' + option; 2460 2461 if (typeof THIS[getter] === 'function') { // a getter exists for this option 2462 return THIS[getter](value); 2463 2464 } else if (option in THIS) { // option exists as a property 2465 return THIS[option]; 2466 } 2467 2468 throw new Error('Unrecognized configuration option: ' + option); 2469 } 2470 2471 2472 function detachPicker () { 2473 jsc.removeClass(THIS.targetElement, jsc.pub.activeClassName); 2474 jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); 2475 delete jsc.picker.owner; 2476 } 2477 2478 2479 function drawPicker () { 2480
2481 // At this point, when drawing the picker, we know what the parent elements are 2482 // and we can do all related DOM operations, such as registering events on them 2483 // or checking their positioning 2484 THIS._processParentElementsInDOM(); 2485 2486 if (!jsc.picker) { 2487 jsc.picker = { 2488 owner: null, // owner picker instance 2489 wrap : jsc.createEl('div'), 2490 box : jsc.createEl('div'), 2491 boxS : jsc.createEl('div'), // shadow area 2492 boxB : jsc.createEl('div'), // border 2493 pad : jsc.createEl('div'), 2494 padB : jsc.createEl('div'), // border 2495 padM : jsc.createEl('div'), // mouse/touch area 2496 padCanvas : jsc.createPadCanvas(), 2497 cross : jsc.createEl('div'), 2498 crossBY : jsc.createEl('div'), // border Y 2499 crossBX : jsc.createEl('div'), // border X 2500 crossLY : jsc.createEl('div'), // line Y 2501 crossLX : jsc.createEl('div'), // line X 2502 sld : jsc.createEl('div'), // slider 2503 sldB : jsc.createEl('div'), // border 2504 sldM : jsc.createEl('div'), // mouse/touch area 2505 sldGrad : jsc.createSliderGradient(), 2506 sldPtrS : jsc.createEl('div'), // slider pointer spacer 2507 sldPtrIB : jsc.createEl('div'), // slider pointer inner border 2508 sldPtrMB : jsc.createEl('div'), // slider pointer middle border 2509 sldPtrOB : jsc.createEl('div'), // slider pointer outer border 2510 asld : jsc.createEl('div'), // alpha slider 2511 asldB : jsc.createEl('div'), // border 2512 asldM : jsc.createEl('div'), // mouse/touch area 2513 asldGrad : jsc.createASliderGradient(), 2514 asldPtrS : jsc.createEl('div'), // slider pointer spacer 2515 asldPtrIB : jsc.createEl('div'), // slider pointer inner border 2516 asldPtrMB : jsc.createEl('div'), // slider pointer middle border 2517 asldPtrOB : jsc.createEl('div'), // slider pointer outer border 2518 pal : jsc.createEl('div'), // palette 2519 btn : jsc.createEl('div'), 2520 btnT : jsc.createEl('div'), // text 2521 }; 2522 2523 jsc.picker.pad.appendChild(jsc.picker.padCanvas.elm); 2524 jsc.picker.padB.appendChild(jsc.picker.pad); 2525 jsc.picker.cross.appendChild(jsc.picker.crossBY); 2526 jsc.picker.cross.appendChild(jsc.picker.crossBX); 2527 jsc.picker.cross.appendChild(jsc.picker.crossLY); 2528 jsc.picker.cross.appendChild(jsc.picker.crossLX); 2529 jsc.picker.padB.appendChild(jsc.picker.cross); 2530 jsc.picker.box.appendChild(jsc.picker.padB); 2531 jsc.picker.box.appendChild(jsc.picker.padM); 2532 2533 jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); 2534 jsc.picker.sldB.appendChild(jsc.picker.sld); 2535 jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); 2536 jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); 2537 jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); 2538 jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); 2539 jsc.picker.box.appendChild(jsc.picker.sldB); 2540 jsc.picker.box.appendChild(jsc.picker.sldM); 2541 2542 jsc.picker.asld.appendChild(jsc.picker.asldGrad.elm); 2543 jsc.picker.asldB.appendChild(jsc.picker.asld); 2544 jsc.picker.asldB.appendChild(jsc.picker.asldPtrOB); 2545 jsc.picker.asldPtrOB.appendChild(jsc.picker.asldPtrMB); 2546 jsc.picker.asldPtrMB.appendChild(jsc.picker.asldPtrIB); 2547 jsc.picker.asldPtrIB.appendChild(jsc.picker.asldPtrS); 2548 jsc.picker.box.appendChild(jsc.picker.asldB); 2549 jsc.picker.box.appendChild(jsc.picker.asldM); 2550 2551 jsc.picker.box.appendChild(jsc.picker.pal); 2552 2553 jsc.picker.btn.appendChild(jsc.picker.btnT); 2554 jsc.picker.box.appendChild(jsc.picker.btn); 2555 2556 jsc.picker.boxB.appendChild(jsc.picker.box); 2557 jsc.picker.wrap.appendChild(jsc.picker.boxS); 2558 jsc.picker.wrap.appendChild(jsc.picker.boxB); 2559 2560 jsc.picker.wrap.addEventListener('touchstart', jsc.onPickerTouchStart, 2561 jsc.isPassiveEventSupported ? {passive: false} : false); 2562 } 2563 2564 var p = jsc.picker; 2565 2566 var displaySlider = !!jsc.getSliderChannel(THIS); 2567 var displayAlphaSlider = THIS.hasAlphaChannel(); 2568 var pickerDims = jsc.getPickerDims(THIS); 2569 var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 2570 var controlPadding = jsc.getControlPadding(THIS); 2571 var borderRadius = Math.min( 2572 THIS.borderRadius, 2573 Math.round(THIS.padding * Math.PI)); // px 2574 var padCursor = 'crosshair'; 2575 2576 // wrap 2577 p.wrap.className = 'jscolor-wrap'; 2578 p.wrap.style.width = pickerDims.borderW + 'px'; 2579 p.wrap.style.height = pickerDims.borderH + 'px'; 2580 p.wrap.style.zIndex = THIS.zIndex; 2581 2582 // picker 2583 p.box.className = 'jscolor-picker'; 2584 p.box.style.width = pickerDims.paddedW + 'px'; 2585 p.box.style.height = pickerDims.paddedH + 'px'; 2586 2587 // picker shadow 2588 p.boxS.className = 'jscolor-shadow'; 2589 jsc.setBorderRadius(p.boxS, borderRadius + 'px'); 2590 2591 // picker border 2592 p.boxB.className = 'jscolor-border'; 2593 p.boxB.style.border = THIS.borderWidth + 'px solid'; 2594 p.boxB.style.borderColor = THIS.borderColor; 2595 p.boxB.style.background = THIS.backgroundColor; 2596 jsc.setBorderRadius(p.boxB, borderRadius + 'px'); 2597 2598 // IE hack: 2599 // If the element is transparent, IE will trigger the event on the elements under it, 2600 // e.g. on Canvas or on elements with border 2601 p.padM.style.background = 'rgba(255,0,0,.2)'; 2602 p.sldM.style.background = 'rgba(0,255,0,.2)'; 2603 p.asldM.style.background = 'rgba(0,0,255,.2)'; 2604 2605 p.padM.style.opacity = 2606 p.sldM.style.opacity = 2607 p.asldM.style.opacity = 2608 '0'; 2609 2610 // pad 2611 p.pad.style.position = 'relative'; 2612 p.pad.style.width = THIS.width + 'px'; 2613 p.pad.style.height = THIS.height + 'px'; 2614 2615 // pad - color spectrum (HSV and HVS) 2616 p.padCanvas.draw(THIS.width, THIS.height, jsc.getPadYChannel(THIS)); 2617 2618 // pad border 2619 p.padB.style.position = 'absolute'; 2620 p.padB.style.left = THIS.padding + 'px'; 2621 p.padB.style.top = THIS.padding + 'px'; 2622 p.padB.style.border = THIS.controlBorderWidth + 'px solid'; 2623 p.padB.style.borderColor = THIS.controlBorderColor; 2624 2625 // pad mouse area 2626 p.padM.style.position = 'absolute'; 2627 p.padM.style.left = 0 + 'px'; 2628 p.padM.style.top = 0 + 'px'; 2629 p.padM.style.width = (THIS.padding + 2 * THIS.controlBorderWidth + THIS.width + controlPadding) + 'px'; 2630 p.padM.style.height = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; 2631 p.padM.style.cursor = padCursor; 2632 jsc.setData(p.padM, { 2633 instance: THIS, 2634 control: 'pad', 2635 }) 2636 2637 // pad cross 2638 p.cross.style.position = 'absolute'; 2639 p.cross.style.left = 2640 p.cross.style.top = 2641 '0'; 2642 p.cross.style.width = 2643 p.cross.style.height = 2644 crossOuterSize + 'px'; 2645 2646 // pad cross border Y and X 2647 p.crossBY.style.position = 2648 p.crossBX.style.position = 2649 'absolute'; 2650 p.crossBY.style.background = 2651 p.crossBX.style.background = 2652 THIS.pointerBorderColor; 2653 p.crossBY.style.width = 2654 p.crossBX.style.height = 2655 (2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 2656 p.crossBY.style.height = 2657 p.crossBX.style.width = 2658 crossOuterSize + 'px'; 2659 p.crossBY.style.left = 2660 p.crossBX.style.top = 2661 (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2) - THIS.pointerBorderWidth) + 'px'; 2662 p.crossBY.style.top = 2663 p.crossBX.style.left = 2664 '0'; 2665 2666 // pad cross line Y and X 2667 p.crossLY.style.position = 2668 p.crossLX.style.position = 2669 'absolute'; 2670 p.crossLY.style.background = 2671 p.crossLX.style.background = 2672 THIS.pointerColor; 2673 p.crossLY.style.height = 2674 p.crossLX.style.width = 2675 (crossOuterSize - 2 * THIS.pointerBorderWidth) + 'px'; 2676 p.crossLY.style.width = 2677 p.crossLX.style.height = 2678 THIS.pointerThickness + 'px'; 2679 p.crossLY.style.left = 2680 p.crossLX.style.top = 2681 (Math.floor(crossOuterSize / 2) - Math.floor(THIS.pointerThickness / 2)) + 'px'; 2682 p.crossLY.style.top = 2683 p.crossLX.style.left = 2684 THIS.pointerBorderWidth + 'px'; 2685 2686 2687 // slider 2688 p.sld.style.overflow = 'hidden'; 2689 p.sld.style.width = THIS.sliderSize + 'px'; 2690 p.sld.style.height = THIS.height + 'px'; 2691 2692 // slider gradient 2693 p.sldGrad.draw(THIS.sliderSize, THIS.height, '#000', '#000'); 2694 2695 // slider border 2696 p.sldB.style.display = displaySlider ? 'block' : 'none'; 2697 p.sldB.style.position = 'absolute'; 2698 p.sldB.style.left = (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + 2 * controlPadding) + 'px'; 2699 p.sldB.style.top = THIS.padding + 'px'; 2700 p.sldB.style.border = THIS.controlBorderWidth + 'px solid'; 2701 p.sldB.style.borderColor = THIS.controlBorderColor; 2702 2703 // slider mouse area 2704 p.sldM.style.display = displaySlider ? 'block' : 'none'; 2705 p.sldM.style.position = 'absolute'; 2706 p.sldM.style.left = (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding) + 'px'; 2707 p.sldM.style.top = 0 + 'px'; 2708 p.sldM.style.width = ( 2709 (THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth) + 2710 (displayAlphaSlider ? 0 : Math.max(0, THIS.padding - controlPadding)) // remaining padding to the right edge 2711 ) + 'px'; 2712 p.sldM.style.height = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; 2713 p.sldM.style.cursor = 'default'; 2714 jsc.setData(p.sldM, { 2715 instance: THIS, 2716 control: 'sld', 2717 }); 2718 2719 // slider pointer inner and outer border 2720 p.sldPtrIB.style.border = 2721 p.sldPtrOB.style.border = 2722 THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; 2723 2724 // slider pointer outer border 2725 p.sldPtrOB.style.position = 'absolute'; 2726 p.sldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 2727 p.sldPtrOB.style.top = '0'; 2728 2729 // slider pointer middle border 2730 p.sldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; 2731 2732 // slider pointer spacer
2733 p.sldPtrS.style.width = THIS.sliderSize + 'px'; 2734 p.sldPtrS.style.height = jsc.pub.sliderInnerSpace + 'px'; 2735 2736 2737 // alpha slider 2738 p.asld.style.overflow = 'hidden'; 2739 p.asld.style.width = THIS.sliderSize + 'px'; 2740 p.asld.style.height = THIS.height + 'px'; 2741 2742 // alpha slider gradient 2743 p.asldGrad.draw(THIS.sliderSize, THIS.height, '#000'); 2744 2745 // alpha slider border 2746 p.asldB.style.display = displayAlphaSlider ? 'block' : 'none'; 2747 p.asldB.style.position = 'absolute'; 2748 p.asldB.style.left = ( 2749 (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding) + 2750 (displaySlider ? (THIS.sliderSize + 3 * controlPadding + 2 * THIS.controlBorderWidth) : 0) 2751 ) + 'px'; 2752 p.asldB.style.top = THIS.padding + 'px'; 2753 p.asldB.style.border = THIS.controlBorderWidth + 'px solid'; 2754 p.asldB.style.borderColor = THIS.controlBorderColor; 2755 2756 // alpha slider mouse area 2757 p.asldM.style.display = displayAlphaSlider ? 'block' : 'none'; 2758 p.asldM.style.position = 'absolute'; 2759 p.asldM.style.left = ( 2760 (THIS.padding + THIS.width + 2 * THIS.controlBorderWidth + controlPadding) + 2761 (displaySlider ? (THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth) : 0) 2762 ) + 'px'; 2763 p.asldM.style.top = 0 + 'px'; 2764 p.asldM.style.width = ( 2765 (THIS.sliderSize + 2 * controlPadding + 2 * THIS.controlBorderWidth) + 2766 Math.max(0, THIS.padding - controlPadding) // remaining padding to the right edge 2767 ) + 'px'; 2768 p.asldM.style.height = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; 2769 p.asldM.style.cursor = 'default'; 2770 jsc.setData(p.asldM, { 2771 instance: THIS, 2772 control: 'asld', 2773 }) 2774 2775 // alpha slider pointer inner and outer border 2776 p.asldPtrIB.style.border = 2777 p.asldPtrOB.style.border = 2778 THIS.pointerBorderWidth + 'px solid ' + THIS.pointerBorderColor; 2779 2780 // alpha slider pointer outer border 2781 p.asldPtrOB.style.position = 'absolute'; 2782 p.asldPtrOB.style.left = -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + 'px'; 2783 p.asldPtrOB.style.top = '0'; 2784 2785 // alpha slider pointer middle border 2786 p.asldPtrMB.style.border = THIS.pointerThickness + 'px solid ' + THIS.pointerColor; 2787 2788 // alpha slider pointer spacer 2789 p.asldPtrS.style.width = THIS.sliderSize + 'px'; 2790 p.asldPtrS.style.height = jsc.pub.sliderInnerSpace + 'px'; 2791 2792 2793 // palette 2794 p.pal.className = 'jscolor-palette'; 2795 p.pal.style.display = pickerDims.palette.rows ? 'block' : 'none'; 2796 p.pal.style.left = THIS.padding + 'px'; 2797 p.pal.style.top = (2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height) + 'px'; 2798 2799 // palette's color samples 2800 2801 p.pal.innerHTML = ''; 2802 2803 var chessboard = jsc.genColorPreviewCanvas('rgba(0,0,0,0)'); 2804 2805 var si = 0; // color sample's index 2806 for (var r = 0; r < pickerDims.palette.rows; r++) { 2807 for (var c = 0; c < pickerDims.palette.cols && si < THIS._palette.length; c++, si++) { 2808 var sampleColor = THIS._palette[si]; 2809 var sampleCssColor = jsc.rgbaColor.apply(null, sampleColor.rgba); 2810 2811 var sc = jsc.createEl('div'); // color sample's color 2812 sc.style.width = (pickerDims.palette.cellW - 2 * THIS.controlBorderWidth) + 'px'; 2813 sc.style.height = (pickerDims.palette.cellH - 2 * THIS.controlBorderWidth) + 'px'; 2814 sc.style.backgroundColor = sampleCssColor; 2815 2816 var sw = jsc.createEl('div'); // color sample's wrap 2817 sw.className = 'jscolor-palette-sw'; 2818 sw.style.left = 2819 ( 2820 pickerDims.palette.cols <= 1 ? 0 : 2821 Math.round(10 * (c * ((pickerDims.contentW - pickerDims.palette.cellW) / (pickerDims.palette.cols - 1)))) / 10 2822 ) + 'px'; 2823 sw.style.top = (r * (pickerDims.palette.cellH + THIS.paletteSpacing)) + 'px'; 2824 sw.style.border = THIS.controlBorderWidth + 'px solid'; 2825 sw.style.borderColor = THIS.controlBorderColor; 2826 if (sampleColor.rgba[3] !== null && sampleColor.rgba[3] < 1.0) { // only create chessboard background if the sample has transparency 2827 sw.style.backgroundImage = 'url(\'' + chessboard.canvas.toDataURL() + '\')'; 2828 sw.style.backgroundRepeat = 'repeat'; 2829 sw.style.backgroundPosition = 'center center'; 2830 } 2831 jsc.setData(sw, { 2832 instance: THIS, 2833 control: 'palette-sw',
2834 color: sampleColor, 2835 }); 2836 sw.addEventListener('click', jsc.onPaletteSampleClick, false); 2837 sw.appendChild(sc); 2838 p.pal.appendChild(sw); 2839 } 2840 } 2841 2842 2843 // the Close button 2844 function setBtnBorder () { 2845 var insetColors = THIS.controlBorderColor.split(/\s+/); 2846 var outsetColor = insetColors.length < 2 ? insetColors[0] : insetColors[1] + ' ' + insetColors[0] + ' ' + insetColors[0] + ' ' + insetColors[1]; 2847 p.btn.style.borderColor = outsetColor; 2848 } 2849 var btnPadding = 15; // px 2850 p.btn.className = 'jscolor-btn jscolor-btn-close'; 2851 p.btn.style.display = THIS.closeButton ? 'block' : 'none'; 2852 p.btn.style.left = THIS.padding + 'px'; 2853 p.btn.style.bottom = THIS.padding + 'px'; 2854 p.btn.style.padding = '0 ' + btnPadding + 'px'; 2855 p.btn.style.maxWidth = (pickerDims.contentW - 2 * THIS.controlBorderWidth - 2 * btnPadding) + 'px'; 2856 p.btn.style.height = THIS.buttonHeight + 'px'; 2857 p.btn.style.border = THIS.controlBorderWidth + 'px solid'; 2858 setBtnBorder(); 2859 p.btn.style.color = THIS.buttonColor; 2860 p.btn.onmousedown = function () { 2861 THIS.hide(); 2862 }; 2863 p.btnT.style.display = 'inline'; 2864 p.btnT.style.lineHeight = THIS.buttonHeight + 'px'; 2865 p.btnT.innerText = THIS.closeText; 2866 2867 // reposition the pointers 2868 redrawPad(); 2869 redrawSld(); 2870 redrawASld(); 2871 2872 // If we are changing the owner without first closing the picker, 2873 // make sure to first deal with the old owner 2874 if (jsc.picker.owner && jsc.picker.owner !== THIS) { 2875 jsc.removeClass(jsc.picker.owner.targetElement, jsc.pub.activeClassName); 2876 } 2877 2878 // Set a new picker owner 2879 jsc.picker.owner = THIS; 2880 2881 // The redrawPosition() method needs picker.owner to be set, that's why we call it here, 2882 // after setting the owner 2883 jsc.redrawPosition(); 2884 2885 if (p.wrap.parentNode !== THIS.container) { 2886 THIS.container.appendChild(p.wrap); 2887 } 2888 2889 jsc.addClass(THIS.targetElement, jsc.pub.activeClassName); 2890 } 2891 2892 2893 function redrawPad () { 2894 // redraw the pad pointer 2895 var yChannel = jsc.getPadYChannel(THIS); 2896 var x = Math.round((THIS.channels.h / 360) * (THIS.width - 1)); 2897 var y = Math.round((1 - THIS.channels[yChannel] / 100) * (THIS.height - 1)); 2898 var crossOuterSize = (2 * THIS.pointerBorderWidth + THIS.pointerThickness + 2 * THIS.crossSize); 2899 var ofs = -Math.floor(crossOuterSize / 2); 2900 jsc.picker.cross.style.left = (x + ofs) + 'px'; 2901 jsc.picker.cross.style.top = (y + ofs) + 'px'; 2902 2903 // redraw the slider 2904 switch (jsc.getSliderChannel(THIS)) { 2905 case 's': 2906 var rgb1 = jsc.HSV_RGB(THIS.channels.h, 100, THIS.channels.v); 2907 var rgb2 = jsc.HSV_RGB(THIS.channels.h, 0, THIS.channels.v); 2908 var color1 = 'rgb(' + 2909 Math.round(rgb1[0]) + ',' + 2910 Math.round(rgb1[1]) + ',' + 2911 Math.round(rgb1[2]) + ')'; 2912 var color2 = 'rgb(' + 2913 Math.round(rgb2[0]) + ',' + 2914 Math.round(rgb2[1]) + ',' + 2915 Math.round(rgb2[2]) + ')'; 2916 jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 2917 break; 2918 case 'v': 2919 var rgb = jsc.HSV_RGB(THIS.channels.h, THIS.channels.s, 100); 2920 var color1 = 'rgb(' + 2921 Math.round(rgb[0]) + ',' + 2922 Math.round(rgb[1]) + ',' + 2923 Math.round(rgb[2]) + ')'; 2924 var color2 = '#000'; 2925 jsc.picker.sldGrad.draw(THIS.sliderSize, THIS.height, color1, color2); 2926 break; 2927 } 2928 2929 // redraw the alpha slider 2930 jsc.picker.asldGrad.draw(THIS.sliderSize, THIS.height, THIS.toHEXString()); 2931 } 2932 2933 2934 function redrawSld () { 2935 var sldChannel = jsc.getSliderChannel(THIS); 2936 if (sldChannel) { 2937 // redraw the slider pointer 2938 var y = Math.round((1 - THIS.channels[sldChannel] / 100) * (THIS.height - 1)); 2939 jsc.picker.sldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(jsc.pub.sliderInnerSpace / 2)) + 'px'; 2940 } 2941 2942 // redraw the alpha slider 2943 jsc.picker.asldGrad.draw(THIS.sliderSize, THIS.height, THIS.toHEXString()); 2944 } 2945 2946 2947 function redrawASld () { 2948 var y = Math.round((1 - THIS.channels.a) * (THIS.height - 1)); 2949 jsc.picker.asldPtrOB.style.top = (y - (2 * THIS.pointerBorderWidth + THIS.pointerThickness) - Math.floor(jsc.pub.sliderInnerSpace / 2)) + 'px'; 2950 } 2951 2952 2953 function isPickerOwner () { 2954 return jsc.picker && jsc.picker.owner === THIS; 2955 } 2956 2957 2958 function onValueKeyDown (ev) { 2959 if (jsc.eventKey(ev) === 'Enter') { 2960 if (THIS.valueElement) { 2961 THIS.processValueInput(THIS.valueElement.value); 2962 } 2963 THIS.tryHide(); 2964 } 2965 } 2966 2967 2968 function onAlphaKeyDown (ev) { 2969 if (jsc.eventKey(ev) === 'Enter') { 2970 if (THIS.alphaElement) { 2971 THIS.processAlphaInput(THIS.alphaElement.value); 2972 } 2973 THIS.tryHide(); 2974 } 2975 } 2976 2977 2978 function onValueChange (ev) { 2979 if (jsc.getData(ev, 'internal')) { 2980 return;
2980 // skip if the event was internally triggered by jscolor 2981 } 2982 2983 var oldVal = THIS.valueElement.value; 2984 2985 THIS.processValueInput(THIS.valueElement.value); // this might change the value 2986 2987 jsc.triggerCallback(THIS, 'onChange'); 2988 2989 if (THIS.valueElement.value !== oldVal) { 2990 // value was additionally changed -> let's trigger the change event again, even though it was natively dispatched 2991 jsc.triggerInputEvent(THIS.valueElement, 'change', true, true); 2992 } 2993 } 2994 2995 2996 function onAlphaChange (ev) { 2997 if (jsc.getData(ev, 'internal')) { 2998 return; // skip if the event was internally triggered by jscolor 2999 } 3000 3001 var oldVal = THIS.alphaElement.value; 3002 3003 THIS.processAlphaInput(THIS.alphaElement.value); // this might change the value 3004 3005 jsc.triggerCallback(THIS, 'onChange'); 3006 3007 // triggering valueElement's onChange (because changing alpha changes the entire color, e.g. with rgba format) 3008 jsc.triggerInputEvent(THIS.valueElement, 'change', true, true); 3009 3010 if (THIS.alphaElement.value !== oldVal) { 3011 // value was additionally changed -> let's trigger the change event again, even though it was natively dispatched 3012 jsc.triggerInputEvent(THIS.alphaElement, 'change', true, true); 3013 } 3014 } 3015 3016 3017 function onValueInput (ev) { 3018 if (jsc.getData(ev, 'internal')) { 3019 return; // skip if the event was internally triggered by jscolor 3020 } 3021 3022 if (THIS.valueElement) { 3023 THIS.fromString(THIS.valueElement.value, jsc.flags.leaveValue); 3024 } 3025 3026 jsc.triggerCallback(THIS, 'onInput'); 3027 3028 // triggering valueElement's onInput 3029 // (not needed, it was dispatched normally by the browser) 3030 } 3031 3032 3033 function onAlphaInput (ev) { 3034 if (jsc.getData(ev, 'internal')) { 3035 return; // skip if the event was internally triggered by jscolor 3036 } 3037 3038 if (THIS.alphaElement) { 3039 THIS.fromHSVA(null, null, null, parseFloat(THIS.alphaElement.value), jsc.flags.leaveAlpha); 3040 } 3041 3042 jsc.triggerCallback(THIS, 'onInput'); 3043 3044 // triggering valueElement's onInput (because changing alpha changes the entire color, e.g. with rgba format) 3045 jsc.triggerInputEvent(THIS.valueElement, 'input', true, true); 3046 } 3047 3048 3049 // let's process the DEPRECATED 'options' property (this will be later removed) 3050 if (jsc.pub.options) { 3051 // let's set custom default options, if specified 3052 for (var opt in jsc.pub.options) { 3053 if (jsc.pub.options.hasOwnProperty(opt)) { 3054 try { 3055 setOption(opt, jsc.pub.options[opt]); 3056 } catch (e) { 3057 console.warn(e); 3058 } 3059 } 3060 } 3061 } 3062 3063 3064 // let's apply configuration presets 3065 // 3066 var presetsArr = []; 3067 3068 if (opts.preset) { 3069 if (typeof opts.preset === 'string') { 3070 presetsArr = opts.preset.split(/\s+/); 3071 } else if (Array.isArray(opts.preset)) { 3072 presetsArr = opts.preset.slice(); // slice() to clone 3073 } else { 3074 console.warn('Unrecognized preset value'); 3075 } 3076 } 3077 3078 // always use the 'default' preset. If it's not listed, append it to the end. 3079 if (presetsArr.indexOf('default') === -1) { 3080 presetsArr.push('default'); 3081 } 3082 3083 // let's apply the presets in reverse order, so that should there be any overlapping options, 3084 // the formerly listed preset will override the latter 3085 for (var i = presetsArr.length - 1; i >= 0; i -= 1) { 3086 var pres = presetsArr[i]; 3087 if (!pres) { 3088 continue; // preset is empty string 3089 } 3090 if (!jsc.pub.presets.hasOwnProperty(pres)) { 3091 console.warn('Unknown preset: %s', pres); 3092 continue; 3093 } 3094 for (var opt in jsc.pub.presets[pres]) { 3095 if (jsc.pub.presets[pres].hasOwnProperty(opt)) { 3096 try { 3097 setOption(opt, jsc.pub.presets[pres][opt]); 3098 } catch (e) { 3099 console.warn(e); 3100 } 3101 } 3102 } 3103 } 3104 3105 3106 // let's set specific options for this color picker 3107 var nonProperties = [ 3108 // these options won't be set as instance properties 3109 'preset', 3110 ]; 3111 for (var opt in opts) { 3112 if (opts.hasOwnProperty(opt)) { 3113 if (nonProperties.indexOf(opt) === -1) { 3114 try { 3115 setOption(opt, opts[opt]); 3116 } catch (e) { 3117 console.warn(e); 3118 } 3119 } 3120 } 3121 } 3122 3123 3124 // 3125 // Install the color picker on chosen element(s) 3126 // 3127 3128 3129 // Determine picker's container element 3130 if (this.container === undefined) { 3131 this.container = window.document.body; // default container is BODY element 3132 3133 } else { // explicitly set to custom element 3134 this.container = jsc.node(this.container); 3135 } 3136 3137 if (!this.container) { 3138 throw new Error('Cannot instantiate color picker without a container element'); 3139 } 3140 3141 3142 // Fetch the target element 3143 this.targetElement = jsc.node(targetElement); 3144 3145 if (!this.targetElement) { 3146 // temporarily customized error message to help with migrating from versions prior to 2.2
3147 if (typeof targetElement === 'string' && /^[a-zA-Z][\w:.-]*$/.test(targetElement)) { 3148 // targetElement looks like valid ID 3149 var possiblyId = targetElement; 3150 throw new Error('If \'' + possiblyId + '\' is supposed to be an ID, please use \'#' + possiblyId + '\' or any valid CSS selector.'); 3151 } 3152 3153 throw new Error('Cannot instantiate color picker without a target element'); 3154 } 3155 3156 if (this.targetElement.jscolor && this.targetElement.jscolor instanceof jsc.pub) { 3157 throw new Error('Color picker already installed on this element'); 3158 } 3159 3160 3161 // link this instance with the target element 3162 this.targetElement.jscolor = this; 3163 jsc.addClass(this.targetElement, jsc.pub.className); 3164 3165 // register this instance 3166 jsc.instances.push(this); 3167 3168 3169 // if target is BUTTON 3170 if (jsc.isButton(this.targetElement)) { 3171 3172 if (this.targetElement.type.toLowerCase() !== 'button') { 3173 // on buttons, always force type to be 'button', e.g. in situations the target <button> has no type 3174 // and thus defaults to 'submit' and would submit the form when clicked 3175 this.targetElement.type = 'button'; 3176 } 3177 3178 if (jsc.isButtonEmpty(this.targetElement)) { // empty button 3179 // it is important to clear element's contents first. 3180 // if we're re-instantiating color pickers on DOM that has been modified by changing page's innerHTML, 3181 // we would keep adding more non-breaking spaces to element's content (because element's contents survive 3182 // innerHTML changes, but picker instances don't) 3183 jsc.removeChildren(this.targetElement); 3184 3185 // let's insert a non-breaking space 3186 this.targetElement.appendChild(window.document.createTextNode('\xa0')); 3187 3188 // set min-width = previewSize, if not already greater 3189 var compStyle = jsc.getCompStyle(this.targetElement); 3190 var currMinWidth = parseFloat(compStyle['min-width']) || 0; 3191 if (currMinWidth < this.previewSize) { 3192 jsc.setStyle(this.targetElement, { 3193 'min-width': this.previewSize + 'px', 3194 }, this.forceStyle); 3195 } 3196 } 3197 } 3198 3199 // Determine the value element 3200 if (this.valueElement === undefined) { 3201 if (jsc.isTextInput(this.targetElement)) { 3202 // for text inputs, default valueElement is targetElement 3203 this.valueElement = this.targetElement; 3204 } else { 3205 // leave it undefined 3206 } 3207 3208 } else if (this.valueElement === null) { // explicitly set to null 3209 // leave it null 3210 3211 } else { // explicitly set to custom element 3212 this.valueElement = jsc.node(this.valueElement); 3213 } 3214 3215 // Determine the alpha element 3216 if (this.alphaElement) { 3217 this.alphaElement = jsc.node(this.alphaElement); 3218 } 3219 3220 // Determine the preview element 3221 if (this.previewElement === undefined) { 3222 this.previewElement = this.targetElement; // default previewElement is targetElement 3223 3224 } else if (this.previewElement === null) { // explicitly set to null 3225 // leave it null 3226 3227 } else { // explicitly set to custom element 3228 this.previewElement = jsc.node(this.previewElement); 3229 } 3230 3231 // valueElement 3232 if (this.valueElement && jsc.isTextInput(this.valueElement)) { 3233 3234 // If the value element has onInput event already set, we need to detach it and attach AFTER our listener. 3235 // otherwise the picker instance would still contain the old color when accessed from the onInput handler. 3236 var valueElementOrigEvents = { 3237 onInput: this.valueElement.oninput 3238 }; 3239 this.valueElement.oninput = null; 3240 3241 this.valueElement.addEventListener('keydown', onValueKeyDown, false); 3242 this.valueElement.addEventListener('change', onValueChange, false); 3243 this.valueElement.addEventListener('input', onValueInput, false); 3244 // the original event listener must be attached AFTER our handler (to let it first set picker's color) 3245 if (valueElementOrigEvents.onInput) { 3246 this.valueElement.addEventListener('input', valueElementOrigEvents.onInput, false); 3247 } 3248 3249 this.valueElement.setAttribute('autocomplete', 'off'); 3250 this.valueElement.setAttribute('autocorrect', 'off'); 3251 this.valueElement.setAttribute('autocapitalize', 'off'); 3252 this.valueElement.setAttribute('spellcheck', false); 3253 } 3254 3255 // alphaElement 3256 if (this.alphaElement && jsc.isTextInput(this.alphaElement)) { 3257 this.alphaElement.addEventListener('keydown', onAlphaKeyDown, false); 3258 this.alphaElement.addEventListener('change', onAlphaChange, false); 3259 this.alphaElement.addEventListener('input', onAlphaInput, false); 3260 3261 this.alphaElement.setAttribute('autocomplete', 'off'); 3262 this.alphaElement.setAttribute('autocorrect', 'off'); 3263 this.alphaElement.setAttribute('autocapitalize', 'off'); 3264 this.alphaElement.setAttribute('spellcheck', false); 3265 } 3266 3267 // determine initial color value 3268 // 3269 var initValue = 'FFFFFF'; 3270 3271 if (this.value !== undefined) { 3272 initValue = this.value; // get initial color from the 'value' property 3273 } else if (this.valueElement && this.valueElement.value !== undefined) { 3274 initValue = this.valueElement.value; // get initial color from valueElement's value 3275 } 3276 3277 // determine initial alpha value 3278 // 3279 var initAlpha = undefined; 3280 3281 if (this.alpha !== undefined) { 3282 initAlpha = (''+this.alpha); // get initial alpha value from the 'alpha' property 3283 } else if (this.alphaElement && this.alphaElement.value !== undefined) { 3284 initAlpha = this.alphaElement.value; // get initial color from alphaElement's value 3285 } 3286 3287 // determine current format based on the initial color value 3288 // 3289 this._currentFormat = null; 3290 3291 if (['auto', 'any'].indexOf(this.format.toLowerCase()) > -1) { 3292 // format is 'auto' or 'any' -> let's auto-detect current format 3293 var color = jsc.parseColorString(initValue); 3294 this._currentFormat = color ? color.format : 'hex'; 3295 } else { 3296 // format is specified 3297 this._currentFormat = this.format.toLowerCase(); 3298 } 3299 3300 3301 // let's parse the initial color value and expose color's preview 3302 this.processValueInput(initValue); 3303 3304 // let's also parse and expose the initial alpha value, if any 3305 // 3306 // Note: If the initial color value contains alpha value in it (e.g. in rgba format),
3307 // this will overwrite it. So we should only process alpha input if there was initial 3308 // alpha explicitly set, otherwise we could needlessly lose initial value's alpha 3309 if (initAlpha !== undefined) { 3310 this.processAlphaInput(initAlpha); 3311 } 3312 3313 if (this.random) { 3314 // randomize the initial color value 3315 this.randomize.apply(this, Array.isArray(this.random) ? this.random : []); 3316 } 3317 3318 } 3319 3320}; 3321 3322 3323//================================ 3324// Public properties and methods 3325//================================ 3326 3327// 3328// These will be publicly available via jscolor.<name> and JSColor.<name> 3329// 3330 3331 3332// class that will be set to elements having jscolor installed on them 3333jsc.pub.className = 'jscolor'; 3334 3335 3336// class that will be set to elements having jscolor active on them 3337jsc.pub.activeClassName = 'jscolor-active'; 3338 3339 3340// whether to try to parse the options string by evaluating it using 'new Function()' 3341// in case it could not be parsed with JSON.parse() 3342jsc.pub.looseJSON = true; 3343 3344 3345// presets 3346jsc.pub.presets = {}; 3347 3348// built-in presets 3349jsc.pub.presets['default'] = {}; // baseline for customization 3350 3351jsc.pub.presets['light'] = { // default color scheme 3352 backgroundColor: 'rgba(255,255,255,1)', 3353 controlBorderColor: 'rgba(187,187,187,1)', 3354 buttonColor: 'rgba(0,0,0,1)', 3355}; 3356jsc.pub.presets['dark'] = { 3357 backgroundColor: 'rgba(51,51,51,1)', 3358 controlBorderColor: 'rgba(153,153,153,1)', 3359 buttonColor: 'rgba(240,240,240,1)', 3360}; 3361 3362jsc.pub.presets['small'] = { width:101, height:101, padding:10, sliderSize:14, paletteCols:8 }; 3363jsc.pub.presets['medium'] = { width:181, height:101, padding:12, sliderSize:16, paletteCols:10 }; // default size 3364jsc.pub.presets['large'] = { width:271, height:151, padding:12, sliderSize:24, paletteCols:15 }; 3365 3366jsc.pub.presets['thin'] = { borderWidth:1, controlBorderWidth:1, pointerBorderWidth:1 }; // default thickness 3367jsc.pub.presets['thick'] = { borderWidth:2, controlBorderWidth:2, pointerBorderWidth:2 }; 3368 3369 3370// size of space in the sliders 3371jsc.pub.sliderInnerSpace = 3; // px 3372 3373// transparency chessboard 3374jsc.pub.chessboardSize = 8; // px 3375jsc.pub.chessboardColor1 = '#666666'; 3376jsc.pub.chessboardColor2 = '#999999'; 3377 3378// preview separator 3379jsc.pub.previewSeparator = ['rgba(255,255,255,.65)', 'rgba(128,128,128,.65)']; 3380 3381 3382// Initializes jscolor 3383jsc.pub.init = function () { 3384 if (jsc.initialized) { 3385 return; 3386 } 3387 3388 // attach some necessary handlers 3389 window.document.addEventListener('mousedown', jsc.onDocumentMouseDown, false); 3390 window.document.addEventListener('keyup', jsc.onDocumentKeyUp, false); 3391 window.addEventListener('resize', jsc.onWindowResize, false); 3392 window.addEventListener('scroll', jsc.onWindowScroll, false); 3393 3394 // append default CSS to HEAD 3395 jsc.appendDefaultCss(); 3396 3397 // install jscolor on current DOM 3398 jsc.pub.install(); 3399 3400 jsc.initialized = true; 3401 3402 // call functions waiting in the queue 3403 while (jsc.readyQueue.length) { 3404 var func = jsc.readyQueue.shift(); 3405 func(); 3406 } 3407}; 3408 3409 3410// Installs jscolor on current DOM tree 3411jsc.pub.install = function (rootNode) { 3412 var success = true; 3413 3414 try { 3415 jsc.installBySelector('[data-jscolor]', rootNode); 3416 } catch (e) { 3417 success = false; 3418 console.warn(e); 3419 } 3420 3421 // for backward compatibility with DEPRECATED installation using class name 3422 if (jsc.pub.lookupClass) { 3423 try { 3424 jsc.installBySelector( 3425 ( 3426 'input.' + jsc.pub.lookupClass + ', ' + 3427 'button.' + jsc.pub.lookupClass 3428 ), 3429 rootNode 3430 ); 3431 } catch (e) {} 3432 } 3433 3434 return success; 3435}; 3436 3437 3438// Registers function to be called as soon as jscolor is initialized (or immediately, if it already is). 3439// 3440jsc.pub.ready = function (func) { 3441 if (typeof func !== 'function') { 3442 console.warn('Passed value is not a function'); 3443 return false; 3444 } 3445 3446 if (jsc.initialized) { 3447 func(); 3448 } else { 3449 jsc.readyQueue.push(func); 3450 } 3451 return true; 3452}; 3453 3454 3455// Triggers given input event(s) (e.g. 'input' or 'change') on all color pickers. 3456// 3457// It is possible to specify multiple events separated with a space. 3458// If called before jscolor is initialized, then the events will be triggered after initialization. 3459// 3460jsc.pub.trigger = function (eventNames) { 3461 var triggerNow = function () { 3462 jsc.triggerGlobal(eventNames); 3463 }; 3464 3465 if (jsc.initialized) { 3466 triggerNow(); 3467 } else { 3468 jsc.pub.ready(triggerNow); 3469 } 3470}; 3471 3472 3473// Hides current color picker box 3474jsc.pub.hide = function () { 3475 if (jsc.picker && jsc.picker.owner) { 3476 jsc.picker.owner.hide(); 3477 } 3478}; 3479 3480 3481// Returns a data URL of a gray chessboard image that indicates transparency 3482jsc.pub.chessboard = function (color) { 3483 if (!color) { 3484 color = 'rgba(0,0,0,0)'; 3485 } 3486 var preview = jsc.genColorPreviewCanvas(color); 3487 return preview.canvas.toDataURL(); 3488}; 3489 3490 3491// Returns a data URL of a gray chessboard image that indicates transparency 3492jsc.pub.background = function (color) { 3493 var backgrounds = []; 3494 3495 // CSS gradient for background color preview 3496 backgrounds.push(jsc.genColorPreviewGradient(color)); 3497 3498 // data URL of generated PNG image with a gray transparency chessboard 3499 var preview = jsc.genColorPreviewCanvas(); 3500 backgrounds.push([ 3501 'url(\'' + preview.canvas.toDataURL() + '\')', 3502 'left top', 3503 'repeat', 3504 ].join(' '));
3505 3506 return backgrounds.join(', '); 3507}; 3508 3509 3510// 3511// DEPRECATED properties and methods 3512// 3513 3514 3515// DEPRECATED. Use jscolor.presets.default instead. 3516// 3517// Custom default options for all color pickers, e.g. { hash: true, width: 300 } 3518jsc.pub.options = {}; 3519 3520 3521// DEPRECATED. Use data-jscolor attribute instead, which installs jscolor on given element. 3522// 3523// By default, we'll search for all elements with class="jscolor" and install a color picker on them. 3524// 3525// You can change what class name will be looked for by setting the property jscolor.lookupClass 3526// anywhere in your HTML document. To completely disable the automatic lookup, set it to null. 3527// 3528jsc.pub.lookupClass = 'jscolor'; 3529 3530 3531// DEPRECATED. Use data-jscolor attribute instead, which installs jscolor on given element. 3532// 3533// Install jscolor on all elements that have the specified class name 3534jsc.pub.installByClassName = function () { 3535 console.error('jscolor.installByClassName() is DEPRECATED. Use data-jscolor="" attribute instead of a class name.' + jsc.docsRef); 3536 return false; 3537}; 3538 3539 3540jsc.register(); 3541 3542 3543return jsc.pub; 3544 3545 3546})(); // END jscolor 3547 3548 3549if (typeof window.jscolor === 'undefined') { 3550 window.jscolor = window.JSColor = jscolor; 3551} 3552 3553 3554// END jscolor code 3555 3556return jscolor; 3557 3558}); // END factory
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.