WME-JSColor

JSColor color picker - http://jscolor.com/

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/27023/1191918/WME-JSColor.js

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