diff --git a/jscolor/jscolor.js b/jscolor/jscolor.js index df9d094..ef3bce5 100644 --- a/jscolor/jscolor.js +++ b/jscolor/jscolor.js @@ -1,3524 +1,997 @@ /** - * jscolor - JavaScript Color Picker + * jscolor, JavaScript Color Picker * + * @version 1.4.3 + * @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html + * @author Jan Odvarko, http://odvarko.cz + * @created 2008-06-15 + * @updated 2014-07-16 * @link http://jscolor.com - * @license For open source use: GPLv3 - * For commercial use: JSColor Commercial License - * @author Jan Odvarko - East Desire - * @version 2.4.5 - * - * See usage examples at http://jscolor.com/examples/ */ -(function (global, factory) { - - 'use strict'; - - if (typeof module === 'object' && typeof module.exports === 'object') { - // Export jscolor as a module - module.exports = global.document ? - factory (global) : - function (win) { - if (!win.document) { - throw new Error('jscolor needs a window with document'); - } - return factory(win); - } - return; - } - - // Default use (no module export) - factory(global); - -})(typeof window !== 'undefined' ? window : this, function (window) { // BEGIN factory - -// BEGIN jscolor code +var jscolor = { -'use strict'; + dir : '', // location of jscolor directory (leave empty to autodetect) + bindClass : 'color', // class name + binding : true, // automatic binding via + preloading : true, // use image preloading? -var jscolor = (function () { // BEGIN jscolor - -var jsc = { + install : function() { + jscolor.addEvent(window, 'load', jscolor.init); + }, - initialized : false, - - instances : [], // created instances of jscolor - - readyQueue : [], // functions waiting to be called after init - - - register : function () { - if (typeof window !== 'undefined' && window.document) { - window.document.addEventListener('DOMContentLoaded', jsc.pub.init, false); + init : function() { + if(jscolor.binding) { + jscolor.bind(); + } + if(jscolor.preloading) { + jscolor.preload(); } }, - installBySelector : function (selector, rootNode) { - rootNode = rootNode ? jsc.node(rootNode) : window.document; - if (!rootNode) { - throw new Error('Missing root node'); + getDir : function() { + if(!jscolor.dir) { + var detected = jscolor.detectDir(); + jscolor.dir = detected!==false ? detected : 'jscolor/'; + } + return jscolor.dir; + }, + + + detectDir : function() { + var base = location.href; + + var e = document.getElementsByTagName('base'); + for(var i=0; i try to evaluate the options string as JavaScript object - try { - opts = (new Function ('var opts = (' + str + '); return typeof opts === "object" ? opts : {};'))(); - } catch (eEval) { - throw new Error('Could not evaluate jscolor options: ' + eEval); - } + preload : function() { + for(var fn in jscolor.imgRequire) { + if(jscolor.imgRequire.hasOwnProperty(fn)) { + jscolor.loadImage(fn); } } - return opts; }, - getInstances : function () { - var inst = []; - for (var i = 0; i < jsc.instances.length; i += 1) { - // if the targetElement still exists, the instance is considered "alive" - if (jsc.instances[i] && jsc.instances[i].targetElement) { - inst.push(jsc.instances[i]); - } - } - return inst; + images : { + pad : [ 181, 101 ], + sld : [ 16, 101 ], + cross : [ 15, 15 ], + arrow : [ 7, 11 ] }, - createEl : function (tagName) { - var el = window.document.createElement(tagName); - jsc.setData(el, 'gui', true); - return el; + imgRequire : {}, + imgLoaded : {}, + + + requireImage : function(filename) { + jscolor.imgRequire[filename] = true; }, - node : function (nodeOrSelector) { - if (!nodeOrSelector) { - return null; - } - - if (typeof nodeOrSelector === 'string') { - // query selector - var sel = nodeOrSelector; - var el = null; - try { - el = window.document.querySelector(sel); - } catch (e) { - console.warn(e); - return null; - } - if (!el) { - console.warn('No element matches the selector: %s', sel); - } - return el; - } - - if (jsc.isNode(nodeOrSelector)) { - // DOM node - return nodeOrSelector; - } - - console.warn('Invalid node of type %s: %s', typeof nodeOrSelector, nodeOrSelector); - return null; - }, - - - // See https://stackoverflow.com/questions/384286/ - isNode : function (val) { - if (typeof Node === 'object') { - return val instanceof Node; - } - return val && typeof val === 'object' && typeof val.nodeType === 'number' && typeof val.nodeName === 'string'; - }, - - - nodeName : function (node) { - if (node && node.nodeName) { - return node.nodeName.toLowerCase(); - } - return false; - }, - - - removeChildren : function (node) { - while (node.firstChild) { - node.removeChild(node.firstChild); + loadImage : function(filename) { + if(!jscolor.imgLoaded[filename]) { + jscolor.imgLoaded[filename] = new Image(); + jscolor.imgLoaded[filename].src = jscolor.getDir()+filename; } }, - isTextInput : function (el) { - return el && jsc.nodeName(el) === 'input' && el.type.toLowerCase() === 'text'; + fetchElement : function(mixed) { + return typeof mixed === 'string' ? document.getElementById(mixed) : mixed; }, - isButton : function (el) { - if (!el) { - return false; - } - var n = jsc.nodeName(el); - return ( - (n === 'button') || - (n === 'input' && ['button', 'submit', 'reset'].indexOf(el.type.toLowerCase()) > -1) - ); - }, - - - isButtonEmpty : function (el) { - switch (jsc.nodeName(el)) { - case 'input': return (!el.value || el.value.trim() === ''); - case 'button': return (el.textContent.trim() === ''); - } - return null; // could not determine element's text - }, - - - // See https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md - isPassiveEventSupported : (function () { - var supported = false; - - try { - var opts = Object.defineProperty({}, 'passive', { - get: function () { supported = true; } - }); - window.addEventListener('testPassive', null, opts); - window.removeEventListener('testPassive', null, opts); - } catch (e) {} - - return supported; - })(), - - - isColorAttrSupported : (function () { - var elm = window.document.createElement('input'); - if (elm.setAttribute) { - elm.setAttribute('type', 'color'); - if (elm.type.toLowerCase() == 'color') { - return true; - } - } - return false; - })(), - - - dataProp : '_data_jscolor', - - - // usage: - // setData(obj, prop, value) - // setData(obj, {prop:value, ...}) - // - setData : function () { - var obj = arguments[0]; - - if (arguments.length === 3) { - // setting a single property - var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : (obj[jsc.dataProp] = {}); - var prop = arguments[1]; - var value = arguments[2]; - - data[prop] = value; - return true; - - } else if (arguments.length === 2 && typeof arguments[1] === 'object') { - // setting multiple properties - var data = obj.hasOwnProperty(jsc.dataProp) ? obj[jsc.dataProp] : (obj[jsc.dataProp] = {}); - var map = arguments[1]; - - for (var prop in map) { - if (map.hasOwnProperty(prop)) { - data[prop] = map[prop]; - } - } - return true; - } - - throw new Error('Invalid arguments'); - }, - - - // usage: - // removeData(obj, prop, [prop...]) - // - removeData : function () { - var obj = arguments[0]; - if (!obj.hasOwnProperty(jsc.dataProp)) { - return true; // data object does not exist - } - for (var i = 1; i < arguments.length; i += 1) { - var prop = arguments[i]; - delete obj[jsc.dataProp][prop]; - } - return true; - }, - - - getData : function (obj, prop, setDefault) { - if (!obj.hasOwnProperty(jsc.dataProp)) { - // data object does not exist - if (setDefault !== undefined) { - obj[jsc.dataProp] = {}; // create data object - } else { - return undefined; // no value to return - } - } - var data = obj[jsc.dataProp]; - - if (!data.hasOwnProperty(prop) && setDefault !== undefined) { - data[prop] = setDefault; - } - return data[prop]; - }, - - - getDataAttr : function (el, name) { - var attrName = 'data-' + name; - var attrValue = el.getAttribute(attrName); - return attrValue; - }, - - - setDataAttr : function (el, name, value) { - var attrName = 'data-' + name; - el.setAttribute(attrName, value); - }, - - - _attachedGroupEvents : {}, - - - attachGroupEvent : function (groupName, el, evnt, func) { - if (!jsc._attachedGroupEvents.hasOwnProperty(groupName)) { - jsc._attachedGroupEvents[groupName] = []; - } - jsc._attachedGroupEvents[groupName].push([el, evnt, func]); - el.addEventListener(evnt, func, false); - }, - - - detachGroupEvents : function (groupName) { - if (jsc._attachedGroupEvents.hasOwnProperty(groupName)) { - for (var i = 0; i < jsc._attachedGroupEvents[groupName].length; i += 1) { - var evt = jsc._attachedGroupEvents[groupName][i]; - evt[0].removeEventListener(evt[1], evt[2], false); - } - delete jsc._attachedGroupEvents[groupName]; + addEvent : function(el, evnt, func) { + if(el.addEventListener) { + el.addEventListener(evnt, func, false); + } else if(el.attachEvent) { + el.attachEvent('on'+evnt, func); } }, - preventDefault : function (e) { - if (e.preventDefault) { e.preventDefault(); } - e.returnValue = false; - }, - - - captureTarget : function (target) { - // IE - if (target.setCapture) { - jsc._capturedTarget = target; - jsc._capturedTarget.setCapture(); - } - }, - - - releaseTarget : function () { - // IE - if (jsc._capturedTarget) { - jsc._capturedTarget.releaseCapture(); - jsc._capturedTarget = null; - } - }, - - - triggerEvent : function (el, eventName, bubbles, cancelable) { - if (!el) { + fireEvent : function(el, evnt) { + if(!el) { return; } - - var ev = null; - - if (typeof Event === 'function') { - ev = new Event(eventName, { - bubbles: bubbles, - cancelable: cancelable - }); - } else { - // IE - ev = window.document.createEvent('Event'); - ev.initEvent(eventName, bubbles, cancelable); - } - - if (!ev) { - return false; - } - - // so that we know that the event was triggered internally - jsc.setData(ev, 'internal', true); - - el.dispatchEvent(ev); - return true; - }, - - - triggerInputEvent : function (el, eventName, bubbles, cancelable) { - if (!el) { - return; - } - if (jsc.isTextInput(el)) { - jsc.triggerEvent(el, eventName, bubbles, cancelable); + if(document.createEvent) { + var ev = document.createEvent('HTMLEvents'); + ev.initEvent(evnt, true, true); + el.dispatchEvent(ev); + } else if(document.createEventObject) { + var ev = document.createEventObject(); + el.fireEvent('on'+evnt, ev); + } else if(el['on'+evnt]) { // alternatively use the traditional event model (IE5) + el['on'+evnt](); } }, - eventKey : function (ev) { - var keys = { - 9: 'Tab', - 13: 'Enter', - 27: 'Escape', - }; - if (typeof ev.code === 'string') { - return ev.code; - } else if (ev.keyCode !== undefined && keys.hasOwnProperty(ev.keyCode)) { - return keys[ev.keyCode]; - } - return null; - }, - - - strList : function (str) { - if (!str) { - return []; - } - return str.replace(/^\s+|\s+$/g, '').split(/\s+/); - }, - - - // The className parameter (str) can only contain a single class name - hasClass : function (elm, className) { - if (!className) { - return false; - } - if (elm.classList !== undefined) { - return elm.classList.contains(className); - } - // polyfill - return -1 != (' ' + elm.className.replace(/\s+/g, ' ') + ' ').indexOf(' ' + className + ' '); - }, - - - // The className parameter (str) can contain multiple class names separated by whitespace - addClass : function (elm, className) { - var classNames = jsc.strList(className); - - if (elm.classList !== undefined) { - for (var i = 0; i < classNames.length; i += 1) { - elm.classList.add(classNames[i]); - } - return; - } - // polyfill - for (var i = 0; i < classNames.length; i += 1) { - if (!jsc.hasClass(elm, classNames[i])) { - elm.className += (elm.className ? ' ' : '') + classNames[i]; - } - } - }, - - - // The className parameter (str) can contain multiple class names separated by whitespace - removeClass : function (elm, className) { - var classNames = jsc.strList(className); - - if (elm.classList !== undefined) { - for (var i = 0; i < classNames.length; i += 1) { - elm.classList.remove(classNames[i]); - } - return; - } - // polyfill - for (var i = 0; i < classNames.length; i += 1) { - var repl = new RegExp( - '^\\s*' + classNames[i] + '\\s*|' + - '\\s*' + classNames[i] + '\\s*$|' + - '\\s+' + classNames[i] + '(\\s+)', - 'g' - ); - elm.className = elm.className.replace(repl, '$1'); - } - }, - - - getCompStyle : function (elm) { - var compStyle = window.getComputedStyle ? window.getComputedStyle(elm) : elm.currentStyle; - - // Note: In Firefox, getComputedStyle returns null in a hidden iframe, - // that's why we need to check if the returned value is non-empty - if (!compStyle) { - return {}; - } - return compStyle; - }, - - - // Note: - // Setting a property to NULL reverts it to the state before it was first set - // with the 'reversible' flag enabled - // - setStyle : function (elm, styles, important, reversible) { - // using '' for standard priority (IE10 apparently doesn't like value undefined) - var priority = important ? 'important' : ''; - var origStyle = null; - - for (var prop in styles) { - if (styles.hasOwnProperty(prop)) { - var setVal = null; - - if (styles[prop] === null) { - // reverting a property value - - if (!origStyle) { - // get the original style object, but dont't try to create it if it doesn't exist - origStyle = jsc.getData(elm, 'origStyle'); - } - if (origStyle && origStyle.hasOwnProperty(prop)) { - // we have property's original value -> use it - setVal = origStyle[prop]; - } - - } else { - // setting a property value - - if (reversible) { - if (!origStyle) { - // get the original style object and if it doesn't exist, create it - origStyle = jsc.getData(elm, 'origStyle', {}); - } - if (!origStyle.hasOwnProperty(prop)) { - // original property value not yet stored -> store it - origStyle[prop] = elm.style[prop]; - } - } - setVal = styles[prop]; - } - - if (setVal !== null) { - elm.style.setProperty(prop, setVal, priority); - } - } - } - }, - - - hexColor : function (r, g, b) { - return '#' + ( - ('0' + Math.round(r).toString(16)).substr(-2) + - ('0' + Math.round(g).toString(16)).substr(-2) + - ('0' + Math.round(b).toString(16)).substr(-2) - ).toUpperCase(); - }, - - - hexaColor : function (r, g, b, a) { - return '#' + ( - ('0' + Math.round(r).toString(16)).substr(-2) + - ('0' + Math.round(g).toString(16)).substr(-2) + - ('0' + Math.round(b).toString(16)).substr(-2) + - ('0' + Math.round(a * 255).toString(16)).substr(-2) - ).toUpperCase(); - }, - - - rgbColor : function (r, g, b) { - return 'rgb(' + - Math.round(r) + ',' + - Math.round(g) + ',' + - Math.round(b) + - ')'; - }, - - - rgbaColor : function (r, g, b, a) { - return 'rgba(' + - Math.round(r) + ',' + - Math.round(g) + ',' + - Math.round(b) + ',' + - (Math.round((a===undefined || a===null ? 1 : a) * 100) / 100) + - ')'; - }, - - - linearGradient : (function () { - - function getFuncName () { - var stdName = 'linear-gradient'; - var prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-']; - var helper = window.document.createElement('div'); - - for (var i = 0; i < prefixes.length; i += 1) { - var tryFunc = prefixes[i] + stdName; - var tryVal = tryFunc + '(to right, rgba(0,0,0,0), rgba(0,0,0,0))'; - - helper.style.background = tryVal; - if (helper.style.background) { // CSS background successfully set -> function name is supported - return tryFunc; - } - } - return stdName; // fallback to standard 'linear-gradient' without vendor prefix - } - - var funcName = getFuncName(); - - return function () { - return funcName + '(' + Array.prototype.join.call(arguments, ', ') + ')'; - }; - - })(), - - - setBorderRadius : function (elm, value) { - jsc.setStyle(elm, {'border-radius' : value || '0'}); - }, - - - setBoxShadow : function (elm, value) { - jsc.setStyle(elm, {'box-shadow': value || 'none'}); - }, - - - getElementPos : function (e, relativeToViewport) { + getElementPos : function(e) { + var e1=e, e2=e; var x=0, y=0; - var rect = e.getBoundingClientRect(); - x = rect.left; - y = rect.top; - if (!relativeToViewport) { - var viewPos = jsc.getViewPos(); - x += viewPos[0]; - y += viewPos[1]; + if(e1.offsetParent) { + do { + x += e1.offsetLeft; + y += e1.offsetTop; + } while(e1 = e1.offsetParent); + } + while((e2 = e2.parentNode) && e2.nodeName.toUpperCase() !== 'BODY') { + x -= e2.scrollLeft; + y -= e2.scrollTop; } return [x, y]; }, - getElementSize : function (e) { + getElementSize : function(e) { return [e.offsetWidth, e.offsetHeight]; }, - // get pointer's X/Y coordinates relative to viewport - getAbsPointerPos : function (e) { + getRelMousePos : function(e) { var x = 0, y = 0; - if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { - // touch devices - x = e.changedTouches[0].clientX; - y = e.changedTouches[0].clientY; - } else if (typeof e.clientX === 'number') { - x = e.clientX; - y = e.clientY; + if (!e) { e = window.event; } + if (typeof e.offsetX === 'number') { + x = e.offsetX; + y = e.offsetY; + } else if (typeof e.layerX === 'number') { + x = e.layerX; + y = e.layerY; } return { x: x, y: y }; }, - // get pointer's X/Y coordinates relative to target element - getRelPointerPos : function (e) { - var target = e.target || e.srcElement; - var targetRect = target.getBoundingClientRect(); - - var x = 0, y = 0; - - var clientX = 0, clientY = 0; - if (typeof e.changedTouches !== 'undefined' && e.changedTouches.length) { - // touch devices - clientX = e.changedTouches[0].clientX; - clientY = e.changedTouches[0].clientY; - } else if (typeof e.clientX === 'number') { - clientX = e.clientX; - clientY = e.clientY; - } - - x = clientX - targetRect.left; - y = clientY - targetRect.top; - return { x: x, y: y }; - }, - - - getViewPos : function () { - var doc = window.document.documentElement; - return [ - (window.pageXOffset || doc.scrollLeft) - (doc.clientLeft || 0), - (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0) - ]; - }, - - - getViewSize : function () { - var doc = window.document.documentElement; - return [ - (window.innerWidth || doc.clientWidth), - (window.innerHeight || doc.clientHeight), - ]; - }, - - - // r: 0-255 - // g: 0-255 - // b: 0-255 - // - // returns: [ 0-360, 0-100, 0-100 ] - // - RGB_HSV : function (r, g, b) { - r /= 255; - g /= 255; - b /= 255; - var n = Math.min(Math.min(r,g),b); - var v = Math.max(Math.max(r,g),b); - var m = v - n; - if (m === 0) { return [ null, 0, 100 * v ]; } - var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); - return [ - 60 * (h===6?0:h), - 100 * (m/v), - 100 * v - ]; - }, - - - // h: 0-360 - // s: 0-100 - // v: 0-100 - // - // returns: [ 0-255, 0-255, 0-255 ] - // - HSV_RGB : function (h, s, v) { - var u = 255 * (v / 100); - - if (h === null) { - return [ u, u, u ]; - } - - h /= 60; - s /= 100; - - var i = Math.floor(h); - var f = i%2 ? h-i : 1-(h-i); - var m = u * (1 - s); - var n = u * (1 - s * f); - switch (i) { - case 6: - case 0: return [u,n,m]; - case 1: return [n,u,m]; - case 2: return [m,u,n]; - case 3: return [m,n,u]; - case 4: return [n,m,u]; - case 5: return [u,m,n]; + getViewPos : function() { + if(typeof window.pageYOffset === 'number') { + return [window.pageXOffset, window.pageYOffset]; + } else if(document.body && (document.body.scrollLeft || document.body.scrollTop)) { + return [document.body.scrollLeft, document.body.scrollTop]; + } else if(document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) { + return [document.documentElement.scrollLeft, document.documentElement.scrollTop]; + } else { + return [0, 0]; } }, - parseColorString : function (str) { - var ret = { - rgba: null, - format: null // 'hex' | 'hexa' | 'rgb' | 'rgba' + getViewSize : function() { + if(typeof window.innerWidth === 'number') { + return [window.innerWidth, window.innerHeight]; + } else if(document.body && (document.body.clientWidth || document.body.clientHeight)) { + return [document.body.clientWidth, document.body.clientHeight]; + } else if(document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) { + return [document.documentElement.clientWidth, document.documentElement.clientHeight]; + } else { + return [0, 0]; + } + }, + + + URI : function(uri) { // See RFC3986 + + this.scheme = null; + this.authority = null; + this.path = ''; + this.query = null; + this.fragment = null; + + this.parse = function(uri) { + var m = uri.match(/^(([A-Za-z][0-9A-Za-z+.-]*)(:))?((\/\/)([^\/?#]*))?([^?#]*)((\?)([^#]*))?((#)(.*))?/); + this.scheme = m[3] ? m[2] : null; + this.authority = m[5] ? m[6] : null; + this.path = m[7]; + this.query = m[9] ? m[10] : null; + this.fragment = m[12] ? m[13] : null; + return this; }; - var m; + this.toString = function() { + var result = ''; + if(this.scheme !== null) { result = result + this.scheme + ':'; } + if(this.authority !== null) { result = result + '//' + this.authority; } + if(this.path !== null) { result = result + this.path; } + if(this.query !== null) { result = result + '?' + this.query; } + if(this.fragment !== null) { result = result + '#' + this.fragment; } + return result; + }; - if (m = str.match(/^\W*([0-9A-F]{3,8})\W*$/i)) { - // HEX notation + this.toAbsolute = function(base) { + var base = new jscolor.URI(base); + var r = this; + var t = new jscolor.URI; - if (m[1].length === 8) { - // 8-char notation (= with alpha) - ret.format = 'hexa'; - ret.rgba = [ - parseInt(m[1].substr(0,2),16), - parseInt(m[1].substr(2,2),16), - parseInt(m[1].substr(4,2),16), - parseInt(m[1].substr(6,2),16) / 255 - ]; + if(base.scheme === null) { return false; } - } else if (m[1].length === 6) { - // 6-char notation - ret.format = 'hex'; - ret.rgba = [ - parseInt(m[1].substr(0,2),16), - parseInt(m[1].substr(2,2),16), - parseInt(m[1].substr(4,2),16), - null - ]; - - } else if (m[1].length === 3) { - // 3-char notation - ret.format = 'hex'; - ret.rgba = [ - parseInt(m[1].charAt(0) + m[1].charAt(0),16), - parseInt(m[1].charAt(1) + m[1].charAt(1),16), - parseInt(m[1].charAt(2) + m[1].charAt(2),16), - null - ]; + if(r.scheme !== null && r.scheme.toLowerCase() === base.scheme.toLowerCase()) { + r.scheme = null; + } + if(r.scheme !== null) { + t.scheme = r.scheme; + t.authority = r.authority; + t.path = removeDotSegments(r.path); + t.query = r.query; } else { - return false; - } - - return ret; - } - - if (m = str.match(/^\W*rgba?\(([^)]*)\)\W*$/i)) { - // rgb(...) or rgba(...) notation - - var par = m[1].split(','); - var re = /^\s*(\d+|\d*\.\d+|\d+\.\d*)\s*$/; - var mR, mG, mB, mA; - if ( - par.length >= 3 && - (mR = par[0].match(re)) && - (mG = par[1].match(re)) && - (mB = par[2].match(re)) - ) { - ret.format = 'rgb'; - ret.rgba = [ - parseFloat(mR[1]) || 0, - parseFloat(mG[1]) || 0, - parseFloat(mB[1]) || 0, - null - ]; - - if ( - par.length >= 4 && - (mA = par[3].match(re)) - ) { - ret.format = 'rgba'; - ret.rgba[3] = parseFloat(mA[1]) || 0; - } - return ret; - } - } - - return false; - }, - - - parsePaletteValue : function (mixed) { - var vals = []; - - if (typeof mixed === 'string') { // input is a string of space separated color values - // rgb() and rgba() may contain spaces too, so let's find all color values by regex - mixed.replace(/#[0-9A-F]{3}([0-9A-F]{3})?|rgba?\(([^)]*)\)/ig, function (val) { - vals.push(val); - }); - } else if (Array.isArray(mixed)) { // input is an array of color values - vals = mixed; - } - - // convert all values into uniform color format - - var colors = []; - - for (var i = 0; i < vals.length; i++) { - var color = jsc.parseColorString(vals[i]); - if (color) { - colors.push(color); - } - } - - return colors; - }, - - - containsTranparentColor : function (colors) { - for (var i = 0; i < colors.length; i++) { - var a = colors[i].rgba[3]; - if (a !== null && a < 1.0) { - return true; - } - } - return false; - }, - - - isAlphaFormat : function (format) { - switch (format.toLowerCase()) { - case 'hexa': - case 'rgba': - return true; - } - return false; - }, - - - // Canvas scaling for retina displays - // - // adapted from https://www.html5rocks.com/en/tutorials/canvas/hidpi/ - // - scaleCanvasForHighDPR : function (canvas) { - var dpr = window.devicePixelRatio || 1; - canvas.width *= dpr; - canvas.height *= dpr; - var ctx = canvas.getContext('2d'); - ctx.scale(dpr, dpr); - }, - - - genColorPreviewCanvas : function (color, separatorPos, specWidth, scaleForHighDPR) { - - var sepW = Math.round(jsc.pub.previewSeparator.length); - var sqSize = jsc.pub.chessboardSize; - var sqColor1 = jsc.pub.chessboardColor1; - var sqColor2 = jsc.pub.chessboardColor2; - - var cWidth = specWidth ? specWidth : sqSize * 2; - var cHeight = sqSize * 2; - - var canvas = jsc.createEl('canvas'); - var ctx = canvas.getContext('2d'); - - canvas.width = cWidth; - canvas.height = cHeight; - if (scaleForHighDPR) { - jsc.scaleCanvasForHighDPR(canvas); - } - - // transparency chessboard - background - ctx.fillStyle = sqColor1; - ctx.fillRect(0, 0, cWidth, cHeight); - - // transparency chessboard - squares - ctx.fillStyle = sqColor2; - for (var x = 0; x < cWidth; x += sqSize * 2) { - ctx.fillRect(x, 0, sqSize, sqSize); - ctx.fillRect(x + sqSize, sqSize, sqSize, sqSize); - } - - if (color) { - // actual color in foreground - ctx.fillStyle = color; - ctx.fillRect(0, 0, cWidth, cHeight); - } - - var start = null; - switch (separatorPos) { - case 'left': - start = 0; - ctx.clearRect(0, 0, sepW/2, cHeight); - break; - case 'right': - start = cWidth - sepW; - ctx.clearRect(cWidth - (sepW/2), 0, sepW/2, cHeight); - break; - } - if (start !== null) { - ctx.lineWidth = 1; - for (var i = 0; i < jsc.pub.previewSeparator.length; i += 1) { - ctx.beginPath(); - ctx.strokeStyle = jsc.pub.previewSeparator[i]; - ctx.moveTo(0.5 + start + i, 0); - ctx.lineTo(0.5 + start + i, cHeight); - ctx.stroke(); - } - } - - return { - canvas: canvas, - width: cWidth, - height: cHeight, - }; - }, - - - // if position or width is not set => fill the entire element (0%-100%) - genColorPreviewGradient : function (color, position, width) { - var params = []; - - if (position && width) { - params = [ - 'to ' + {'left':'right', 'right':'left'}[position], - color + ' 0%', - color + ' ' + width + 'px', - 'rgba(0,0,0,0) ' + (width + 1) + 'px', - 'rgba(0,0,0,0) 100%', - ]; - } else { - params = [ - 'to right', - color + ' 0%', - color + ' 100%', - ]; - } - - return jsc.linearGradient.apply(this, params); - }, - - - redrawPosition : function () { - - if (!jsc.picker || !jsc.picker.owner) { - return; // picker is not shown - } - - var thisObj = jsc.picker.owner; - - var tp, vp; - - if (thisObj.fixed) { - // Fixed elements are positioned relative to viewport, - // therefore we can ignore the scroll offset - tp = jsc.getElementPos(thisObj.targetElement, true); // target pos - vp = [0, 0]; // view pos - } else { - tp = jsc.getElementPos(thisObj.targetElement); // target pos - vp = jsc.getViewPos(); // view pos - } - - var ts = jsc.getElementSize(thisObj.targetElement); // target size - var vs = jsc.getViewSize(); // view size - var pd = jsc.getPickerDims(thisObj); - var ps = [pd.borderW, pd.borderH]; // picker outer size - var a, b, c; - switch (thisObj.position.toLowerCase()) { - case 'left': a=1; b=0; c=-1; break; - case 'right':a=1; b=0; c=1; break; - case 'top': a=0; b=1; c=-1; break; - default: a=0; b=1; c=1; break; - } - var l = (ts[b]+ps[b])/2; - - // compute picker position - if (!thisObj.smartPosition) { - var pp = [ - tp[a], - tp[b]+ts[b]-l+l*c - ]; - } else { - var pp = [ - -vp[a]+tp[a]+ps[a] > vs[a] ? - (-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]) : - tp[a], - -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? - (-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) : - (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) - ]; - } - - var x = pp[a]; - var y = pp[b]; - var positionValue = thisObj.fixed ? 'fixed' : 'absolute'; - var contractShadow = - (pp[0] + ps[0] > tp[0] || pp[0] < tp[0] + ts[0]) && - (pp[1] + ps[1] < tp[1] + ts[1]); - - jsc._drawPosition(thisObj, x, y, positionValue, contractShadow); - }, - - - _drawPosition : function (thisObj, x, y, positionValue, contractShadow) { - var vShadow = contractShadow ? 0 : thisObj.shadowBlur; // px - - jsc.picker.wrap.style.position = positionValue; - jsc.picker.wrap.style.left = x + 'px'; - jsc.picker.wrap.style.top = y + 'px'; - - jsc.setBoxShadow( - jsc.picker.boxS, - thisObj.shadow ? - new jsc.BoxShadow(0, vShadow, thisObj.shadowBlur, 0, thisObj.shadowColor) : - null); - }, - - - getPickerDims : function (thisObj) { - var w = 2 * thisObj.controlBorderWidth + thisObj.width; - var h = 2 * thisObj.controlBorderWidth + thisObj.height; - - var sliderSpace = 2 * thisObj.controlBorderWidth + 2 * jsc.getControlPadding(thisObj) + thisObj.sliderSize; - - if (jsc.getSliderChannel(thisObj)) { - w += sliderSpace; - } - if (thisObj.hasAlphaChannel()) { - w += sliderSpace; - } - - var pal = jsc.getPaletteDims(thisObj, w); - - if (pal.height) { - h += pal.height + thisObj.padding; - } - if (thisObj.closeButton) { - h += 2 * thisObj.controlBorderWidth + thisObj.padding + thisObj.buttonHeight; - } - - var pW = w + (2 * thisObj.padding); - var pH = h + (2 * thisObj.padding); - - return { - contentW: w, - contentH: h, - paddedW: pW, - paddedH: pH, - borderW: pW + (2 * thisObj.borderWidth), - borderH: pH + (2 * thisObj.borderWidth), - palette: pal, - }; - }, - - - getPaletteDims : function (thisObj, width) { - var cols = 0, rows = 0, cellW = 0, cellH = 0, height = 0; - var sampleCount = thisObj._palette ? thisObj._palette.length : 0; - - if (sampleCount) { - cols = thisObj.paletteCols; - rows = cols > 0 ? Math.ceil(sampleCount / cols) : 0; - - // color sample's dimensions (includes border) - cellW = Math.max(1, Math.floor((width - ((cols - 1) * thisObj.paletteSpacing)) / cols)); - cellH = thisObj.paletteHeight ? Math.min(thisObj.paletteHeight, cellW) : cellW; - } - - if (rows) { - height = - rows * cellH + - (rows - 1) * thisObj.paletteSpacing; - } - - return { - cols: cols, - rows: rows, - cellW: cellW, - cellH: cellH, - width: width, - height: height, - }; - }, - - - getControlPadding : function (thisObj) { - return Math.max( - thisObj.padding / 2, - (2 * thisObj.pointerBorderWidth + thisObj.pointerThickness) - thisObj.controlBorderWidth - ); - }, - - - getPadYChannel : function (thisObj) { - switch (thisObj.mode.charAt(1).toLowerCase()) { - case 'v': return 'v'; break; - } - return 's'; - }, - - - getSliderChannel : function (thisObj) { - if (thisObj.mode.length > 2) { - switch (thisObj.mode.charAt(2).toLowerCase()) { - case 's': return 's'; break; - case 'v': return 'v'; break; - } - } - return null; - }, - - - // calls function specified in picker's property - triggerCallback : function (thisObj, prop) { - if (!thisObj[prop]) { - return; // callback func not specified - } - var callback = null; - - if (typeof thisObj[prop] === 'string') { - // string with code - try { - callback = new Function (thisObj[prop]); - } catch (e) { - console.error(e); - } - } else { - // function - callback = thisObj[prop]; - } - - if (callback) { - callback.call(thisObj); - } - }, - - - // Triggers a color change related event(s) on all picker instances. - // It is possible to specify multiple events separated with a space. - triggerGlobal : function (eventNames) { - var inst = jsc.getInstances(); - for (var i = 0; i < inst.length; i += 1) { - inst[i].trigger(eventNames); - } - }, - - - _pointerMoveEvent : { - mouse: 'mousemove', - touch: 'touchmove' - }, - _pointerEndEvent : { - mouse: 'mouseup', - touch: 'touchend' - }, - - - _pointerOrigin : null, - _capturedTarget : null, - - - onDocumentKeyUp : function (e) { - if (['Tab', 'Escape'].indexOf(jsc.eventKey(e)) !== -1) { - if (jsc.picker && jsc.picker.owner) { - jsc.picker.owner.tryHide(); - } - } - }, - - - onWindowResize : function (e) { - jsc.redrawPosition(); - }, - - - onWindowScroll : function (e) { - jsc.redrawPosition(); - }, - - - onParentScroll : function (e) { - // hide the picker when one of the parent elements is scrolled - if (jsc.picker && jsc.picker.owner) { - jsc.picker.owner.tryHide(); - } - }, - - - onDocumentMouseDown : function (e) { - var target = e.target || e.srcElement; - - if (target.jscolor && target.jscolor instanceof jsc.pub) { // clicked targetElement -> show picker - if (target.jscolor.showOnClick && !target.disabled) { - target.jscolor.show(); - } - } else if (jsc.getData(target, 'gui')) { // clicked jscolor's GUI element - var control = jsc.getData(target, 'control'); - if (control) { - // jscolor's control - jsc.onControlPointerStart(e, target, jsc.getData(target, 'control'), 'mouse'); - } - } else { - // mouse is outside the picker's controls -> hide the color picker! - if (jsc.picker && jsc.picker.owner) { - jsc.picker.owner.tryHide(); - } - } - }, - - - onPickerTouchStart : function (e) { - var target = e.target || e.srcElement; - - if (jsc.getData(target, 'control')) { - jsc.onControlPointerStart(e, target, jsc.getData(target, 'control'), 'touch'); - } - }, - - - onControlPointerStart : function (e, target, controlName, pointerType) { - var thisObj = jsc.getData(target, 'instance'); - - jsc.preventDefault(e); - jsc.captureTarget(target); - - var registerDragEvents = function (doc, offset) { - jsc.attachGroupEvent('drag', doc, jsc._pointerMoveEvent[pointerType], - jsc.onDocumentPointerMove(e, target, controlName, pointerType, offset)); - jsc.attachGroupEvent('drag', doc, jsc._pointerEndEvent[pointerType], - jsc.onDocumentPointerEnd(e, target, controlName, pointerType)); - }; - - registerDragEvents(window.document, [0, 0]); - - if (window.parent && window.frameElement) { - var rect = window.frameElement.getBoundingClientRect(); - var ofs = [-rect.left, -rect.top]; - registerDragEvents(window.parent.window.document, ofs); - } - - var abs = jsc.getAbsPointerPos(e); - var rel = jsc.getRelPointerPos(e); - jsc._pointerOrigin = { - x: abs.x - rel.x, - y: abs.y - rel.y - }; - - switch (controlName) { - case 'pad': - // if the value slider is at the bottom, move it up - if (jsc.getSliderChannel(thisObj) === 'v' && thisObj.channels.v === 0) { - thisObj.fromHSVA(null, null, 100, null); - } - jsc.setPad(thisObj, e, 0, 0); - break; - - case 'sld': - jsc.setSld(thisObj, e, 0); - break; - - case 'asld': - jsc.setASld(thisObj, e, 0); - break; - } - thisObj.trigger('input'); - }, - - - onDocumentPointerMove : function (e, target, controlName, pointerType, offset) { - return function (e) { - var thisObj = jsc.getData(target, 'instance'); - switch (controlName) { - case 'pad': - jsc.setPad(thisObj, e, offset[0], offset[1]); - break; - - case 'sld': - jsc.setSld(thisObj, e, offset[1]); - break; - - case 'asld': - jsc.setASld(thisObj, e, offset[1]); - break; - } - thisObj.trigger('input'); - } - }, - - - onDocumentPointerEnd : function (e, target, controlName, pointerType) { - return function (e) { - var thisObj = jsc.getData(target, 'instance'); - jsc.detachGroupEvents('drag'); - jsc.releaseTarget(); - - // Always trigger changes AFTER detaching outstanding mouse handlers, - // in case some color change that occured in user-defined onChange/onInput handler - // intruded into current mouse events - thisObj.trigger('input'); - thisObj.trigger('change'); - }; - }, - - - onPaletteSampleClick : function (e) { - var target = e.currentTarget; - var thisObj = jsc.getData(target, 'instance'); - var color = jsc.getData(target, 'color'); - - // when format is flexible, use the original format of this color sample - if (thisObj.format.toLowerCase() === 'any') { - thisObj._setFormat(color.format); // adapt format - if (!jsc.isAlphaFormat(thisObj.getFormat())) { - color.rgba[3] = 1.0; // when switching to a format that doesn't support alpha, set full opacity - } - } - - // if this color doesn't specify alpha, use alpha of 1.0 (if applicable) - if (color.rgba[3] === null) { - if (thisObj.paletteSetsAlpha === true || (thisObj.paletteSetsAlpha === 'auto' && thisObj._paletteHasTransparency)) { - color.rgba[3] = 1.0; - } - } - - thisObj.fromRGBA.apply(thisObj, color.rgba); - - thisObj.trigger('input'); - thisObj.trigger('change'); - - if (thisObj.hideOnPaletteClick) { - thisObj.hide(); - } - }, - - - setPad : function (thisObj, e, ofsX, ofsY) { - var pointerAbs = jsc.getAbsPointerPos(e); - var x = ofsX + pointerAbs.x - jsc._pointerOrigin.x - thisObj.padding - thisObj.controlBorderWidth; - var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; - - var xVal = x * (360 / (thisObj.width - 1)); - var yVal = 100 - (y * (100 / (thisObj.height - 1))); - - switch (jsc.getPadYChannel(thisObj)) { - case 's': thisObj.fromHSVA(xVal, yVal, null, null); break; - case 'v': thisObj.fromHSVA(xVal, null, yVal, null); break; - } - }, - - - setSld : function (thisObj, e, ofsY) { - var pointerAbs = jsc.getAbsPointerPos(e); - var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; - var yVal = 100 - (y * (100 / (thisObj.height - 1))); - - switch (jsc.getSliderChannel(thisObj)) { - case 's': thisObj.fromHSVA(null, yVal, null, null); break; - case 'v': thisObj.fromHSVA(null, null, yVal, null); break; - } - }, - - - setASld : function (thisObj, e, ofsY) { - var pointerAbs = jsc.getAbsPointerPos(e); - var y = ofsY + pointerAbs.y - jsc._pointerOrigin.y - thisObj.padding - thisObj.controlBorderWidth; - var yVal = 1.0 - (y * (1.0 / (thisObj.height - 1))); - - if (yVal < 1.0) { - // if format is flexible and the current format doesn't support alpha, switch to a suitable one - var fmt = thisObj.getFormat(); - if (thisObj.format.toLowerCase() === 'any' && !jsc.isAlphaFormat(fmt)) { - thisObj._setFormat(fmt === 'hex' ? 'hexa' : 'rgba'); - } - } - - thisObj.fromHSVA(null, null, null, yVal); - }, - - - createPadCanvas : function () { - - var ret = { - elm: null, - draw: null - }; - - var canvas = jsc.createEl('canvas'); - var ctx = canvas.getContext('2d'); - - var drawFunc = function (width, height, type) { - canvas.width = width; - canvas.height = height; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - - var hGrad = ctx.createLinearGradient(0, 0, canvas.width, 0); - hGrad.addColorStop(0 / 6, '#F00'); - hGrad.addColorStop(1 / 6, '#FF0'); - hGrad.addColorStop(2 / 6, '#0F0'); - hGrad.addColorStop(3 / 6, '#0FF'); - hGrad.addColorStop(4 / 6, '#00F'); - hGrad.addColorStop(5 / 6, '#F0F'); - hGrad.addColorStop(6 / 6, '#F00'); - - ctx.fillStyle = hGrad; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - var vGrad = ctx.createLinearGradient(0, 0, 0, canvas.height); - switch (type.toLowerCase()) { - case 's': - vGrad.addColorStop(0, 'rgba(255,255,255,0)'); - vGrad.addColorStop(1, 'rgba(255,255,255,1)'); - break; - case 'v': - vGrad.addColorStop(0, 'rgba(0,0,0,0)'); - vGrad.addColorStop(1, 'rgba(0,0,0,1)'); - break; - } - ctx.fillStyle = vGrad; - ctx.fillRect(0, 0, canvas.width, canvas.height); - }; - - ret.elm = canvas; - ret.draw = drawFunc; - - return ret; - }, - - - createSliderGradient : function () { - - var ret = { - elm: null, - draw: null - }; - - var canvas = jsc.createEl('canvas'); - var ctx = canvas.getContext('2d'); - - var drawFunc = function (width, height, color1, color2) { - canvas.width = width; - canvas.height = height; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - - var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); - grad.addColorStop(0, color1); - grad.addColorStop(1, color2); - - ctx.fillStyle = grad; - ctx.fillRect(0, 0, canvas.width, canvas.height); - }; - - ret.elm = canvas; - ret.draw = drawFunc; - - return ret; - }, - - - createASliderGradient : function () { - - var ret = { - elm: null, - draw: null - }; - - var canvas = jsc.createEl('canvas'); - var ctx = canvas.getContext('2d'); - - var drawFunc = function (width, height, color) { - canvas.width = width; - canvas.height = height; - - ctx.clearRect(0, 0, canvas.width, canvas.height); - - var sqSize = canvas.width / 2; - var sqColor1 = jsc.pub.chessboardColor1; - var sqColor2 = jsc.pub.chessboardColor2; - - // dark gray background - ctx.fillStyle = sqColor1; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - if (sqSize > 0) { // to avoid infinite loop - for (var y = 0; y < canvas.height; y += sqSize * 2) { - // light gray squares - ctx.fillStyle = sqColor2; - ctx.fillRect(0, y, sqSize, sqSize); - ctx.fillRect(sqSize, y + sqSize, sqSize, sqSize); - } - } - - var grad = ctx.createLinearGradient(0, 0, 0, canvas.height); - grad.addColorStop(0, color); - grad.addColorStop(1, 'rgba(0,0,0,0)'); - - ctx.fillStyle = grad; - ctx.fillRect(0, 0, canvas.width, canvas.height); - }; - - ret.elm = canvas; - ret.draw = drawFunc; - - return ret; - }, - - - BoxShadow : (function () { - var BoxShadow = function (hShadow, vShadow, blur, spread, color, inset) { - this.hShadow = hShadow; - this.vShadow = vShadow; - this.blur = blur; - this.spread = spread; - this.color = color; - this.inset = !!inset; - }; - - BoxShadow.prototype.toString = function () { - var vals = [ - Math.round(this.hShadow) + 'px', - Math.round(this.vShadow) + 'px', - Math.round(this.blur) + 'px', - Math.round(this.spread) + 'px', - this.color - ]; - if (this.inset) { - vals.push('inset'); - } - return vals.join(' '); - }; - - return BoxShadow; - })(), - - - flags : { - leaveValue : 1 << 0, - leaveAlpha : 1 << 1, - leavePreview : 1 << 2, - }, - - - enumOpts : { - format: ['auto', 'any', 'hex', 'hexa', 'rgb', 'rgba'], - previewPosition: ['left', 'right'], - mode: ['hsv', 'hvs', 'hs', 'hv'], - position: ['left', 'right', 'top', 'bottom'], - alphaChannel: ['auto', true, false], - paletteSetsAlpha: ['auto', true, false], - }, - - - deprecatedOpts : { - // : ( can be null) - 'styleElement': 'previewElement', - 'onFineChange': 'onInput', - 'overwriteImportant': 'forceStyle', - 'closable': 'closeButton', - 'insetWidth': 'controlBorderWidth', - 'insetColor': 'controlBorderColor', - 'refine': null, - }, - - - docsRef : ' ' + 'See https://jscolor.com/docs/', - - - // - // Usage: - // var myPicker = new JSColor( [, ]) - // - // (constructor is accessible via both 'jscolor' and 'JSColor' name) - // - - pub : function (targetElement, opts) { - - var THIS = this; - - if (!opts) { - opts = {}; - } - - this.channels = { - r: 255, // red [0-255] - g: 255, // green [0-255] - b: 255, // blue [0-255] - h: 0, // hue [0-360] - s: 0, // saturation [0-100] - v: 100, // value (brightness) [0-100] - a: 1.0, // alpha (opacity) [0.0 - 1.0] - }; - - // General options - // - this.format = 'auto'; // 'auto' | 'any' | 'hex' | 'hexa' | 'rgb' | 'rgba' - Format of the input/output value - this.value = undefined; // INITIAL color value in any supported format. To change it later, use method fromString(), fromHSVA(), fromRGBA() or channel() - this.alpha = undefined; // INITIAL alpha value. To change it later, call method channel('A', ) - this.onChange = undefined; // called when color changes. Value can be either a function or a string with JS code. - 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. - this.valueElement = undefined; // element that will be used to display and input the color value - this.alphaElement = undefined; // element that will be used to display and input the alpha (opacity) value - this.previewElement = undefined; // element that will preview the picked color using CSS background - this.previewPosition = 'left'; // 'left' | 'right' - position of the color preview in previewElement - this.previewSize = 32; // (px) width of the color preview displayed in previewElement - this.previewPadding = 8; // (px) space between color preview and content of the previewElement - this.required = true; // whether the associated text input must always contain a color value. If false, the input can be left empty. - this.hash = true; // whether to prefix the HEX color code with # symbol (only applicable for HEX format) - this.uppercase = true; // whether to show the HEX color code in upper case (only applicable for HEX format) - this.forceStyle = true; // whether to overwrite CSS style of the previewElement using !important flag - - // Color Picker options - // - this.width = 181; // width of the color spectrum (in px) - this.height = 101; // height of the color spectrum (in px) - this.mode = 'HSV'; // 'HSV' | 'HVS' | 'HS' | 'HV' - layout of the color picker controls - 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 - this.position = 'bottom'; // 'left' | 'right' | 'top' | 'bottom' - position relative to the target element - this.smartPosition = true; // automatically change picker position when there is not enough space for it - this.showOnClick = true; // whether to show the picker when user clicks its target element - this.hideOnLeave = true; // whether to automatically hide the picker when user leaves its target element (e.g. upon clicking the document) - 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) - this.paletteCols = 10; // number of columns in the palette - this.paletteSetsAlpha = 'auto'; // 'auto' | true | false - if true, palette colors that don't specify alpha will set alpha to 1.0 - this.paletteHeight = 16; // maximum height (px) of a row in the palette - this.paletteSpacing = 4; // distance (px) between color samples in the palette - this.hideOnPaletteClick = false; // when set to true, clicking the palette will also hide the color picker - this.sliderSize = 16; // px - this.crossSize = 8; // px - this.closeButton = false; // whether to display the Close button - this.closeText = 'Close'; - this.buttonColor = 'rgba(0,0,0,1)'; // CSS color - this.buttonHeight = 18; // px - this.padding = 12; // px - this.backgroundColor = 'rgba(255,255,255,1)'; // CSS color - this.borderWidth = 1; // px - this.borderColor = 'rgba(187,187,187,1)'; // CSS color - this.borderRadius = 8; // px - this.controlBorderWidth = 1; // px - this.controlBorderColor = 'rgba(187,187,187,1)'; // CSS color - this.shadow = true; // whether to display a shadow - this.shadowBlur = 15; // px - this.shadowColor = 'rgba(0,0,0,0.2)'; // CSS color - this.pointerColor = 'rgba(76,76,76,1)'; // CSS color - this.pointerBorderWidth = 1; // px - this.pointerBorderColor = 'rgba(255,255,255,1)'; // CSS color - this.pointerThickness = 2; // px - this.zIndex = 5000; - this.container = undefined; // where to append the color picker (BODY element by default) - - // Experimental - // - this.minS = 0; // min allowed saturation (0 - 100) - this.maxS = 100; // max allowed saturation (0 - 100) - this.minV = 0; // min allowed value (brightness) (0 - 100) - this.maxV = 100; // max allowed value (brightness) (0 - 100) - this.minA = 0.0; // min allowed alpha (opacity) (0.0 - 1.0) - this.maxA = 1.0; // max allowed alpha (opacity) (0.0 - 1.0) - - - // Getter: option(name) - // Setter: option(name, value) - // option({name:value, ...}) - // - this.option = function () { - if (!arguments.length) { - throw new Error('No option specified'); - } - - if (arguments.length === 1 && typeof arguments[0] === 'string') { - // getting a single option - try { - return getOption(arguments[0]); - } catch (e) { - console.warn(e); - } - return false; - - } else if (arguments.length >= 2 && typeof arguments[0] === 'string') { - // setting a single option - try { - if (!setOption(arguments[0], arguments[1])) { - return false; - } - } catch (e) { - console.warn(e); - return false; - } - this.redraw(); // immediately redraws the picker, if it's displayed - this.exposeColor(); // in case some preview-related or format-related option was changed - return true; - - } else if (arguments.length === 1 && typeof arguments[0] === 'object') { - // setting multiple options - var opts = arguments[0]; - var success = true; - for (var opt in opts) { - if (opts.hasOwnProperty(opt)) { - try { - if (!setOption(opt, opts[opt])) { - success = false; - } - } catch (e) { - console.warn(e); - success = false; + if(r.authority !== null) { + t.authority = r.authority; + t.path = removeDotSegments(r.path); + t.query = r.query; + } else { + if(r.path === '') { + t.path = base.path; + if(r.query !== null) { + t.query = r.query; + } else { + t.query = base.query; } + } else { + if(r.path.substr(0,1) === '/') { + t.path = removeDotSegments(r.path); + } else { + if(base.authority !== null && base.path === '') { + t.path = '/'+r.path; + } else { + t.path = base.path.replace(/[^\/]+$/,'')+r.path; + } + t.path = removeDotSegments(t.path); + } + t.query = r.query; } + t.authority = base.authority; } - this.redraw(); // immediately redraws the picker, if it's displayed - this.exposeColor(); // in case some preview-related or format-related option was changed - return success; + t.scheme = base.scheme; } + t.fragment = r.fragment; - throw new Error('Invalid arguments'); + return t; + }; + + function removeDotSegments(path) { + var out = ''; + while(path) { + if(path.substr(0,3)==='../' || path.substr(0,2)==='./') { + path = path.replace(/^\.+/,'').substr(1); + } else if(path.substr(0,3)==='/./' || path==='/.') { + path = '/'+path.substr(3); + } else if(path.substr(0,4)==='/../' || path==='/..') { + path = '/'+path.substr(4); + out = out.replace(/\/?[^\/]*$/, ''); + } else if(path==='.' || path==='..') { + path = ''; + } else { + var rm = path.match(/^\/?[^\/]*/)[0]; + path = path.substr(rm.length); + out = out + rm; + } + } + return out; + } + + if(uri) { + this.parse(uri); + } + + }, + + + // + // Usage example: + // var myColor = new jscolor.color(myInputElement) + // + + color : function(target, prop) { + + + this.required = true; // refuse empty values? + this.adjust = true; // adjust value to uniform notation? + this.hash = false; // prefix color with # symbol? + this.caps = true; // uppercase? + this.slider = true; // show the value/saturation slider? + this.valueElement = target; // value holder + this.styleElement = target; // where to reflect current color + this.onImmediateChange = null; // onchange callback (can be either string or function) + this.hsv = [0, 0, 1]; // read-only 0-6, 0-1, 0-1 + this.rgb = [1, 1, 1]; // read-only 0-1, 0-1, 0-1 + this.minH = 0; // read-only 0-6 + this.maxH = 6; // read-only 0-6 + this.minS = 0; // read-only 0-1 + this.maxS = 1; // read-only 0-1 + this.minV = 0; // read-only 0-1 + this.maxV = 1; // read-only 0-1 + + this.pickerOnfocus = true; // display picker on focus? + this.pickerMode = 'HSV'; // HSV | HVS + this.pickerPosition = 'bottom'; // left | right | top | bottom + this.pickerSmartPosition = true; // automatically adjust picker position when necessary + this.pickerButtonHeight = 20; // px + this.pickerClosable = false; + this.pickerCloseText = 'Close'; + this.pickerButtonColor = 'ButtonText'; // px + this.pickerFace = 10; // px + this.pickerFaceColor = 'ThreeDFace'; // CSS color + this.pickerBorder = 1; // px + this.pickerBorderColor = 'ThreeDHighlight ThreeDShadow ThreeDShadow ThreeDHighlight'; // CSS color + this.pickerInset = 1; // px + this.pickerInsetColor = 'ThreeDShadow ThreeDHighlight ThreeDHighlight ThreeDShadow'; // CSS color + this.pickerZIndex = 10000; + + + for(var p in prop) { + if(prop.hasOwnProperty(p)) { + this[p] = prop[p]; + } } - // Getter: channel(name) - // Setter: channel(name, value) - // - this.channel = function (name, value) { - if (typeof name !== 'string') { - throw new Error('Invalid value for channel name: ' + name); + this.hidePicker = function() { + if(isPickerOwner()) { + removePicker(); } + }; - if (value === undefined) { - // getting channel value - if (!this.channels.hasOwnProperty(name.toLowerCase())) { - console.warn('Getting unknown channel: ' + name); - return false; + + this.showPicker = function() { + if(!isPickerOwner()) { + var tp = jscolor.getElementPos(target); // target pos + var ts = jscolor.getElementSize(target); // target size + var vp = jscolor.getViewPos(); // view pos + var vs = jscolor.getViewSize(); // view size + var ps = getPickerDims(this); // picker size + var a, b, c; + switch(this.pickerPosition.toLowerCase()) { + case 'left': a=1; b=0; c=-1; break; + case 'right':a=1; b=0; c=1; break; + case 'top': a=0; b=1; c=-1; break; + default: a=0; b=1; c=1; break; } - return this.channels[name.toLowerCase()]; + var l = (ts[b]+ps[b])/2; + // picker pos + if (!this.pickerSmartPosition) { + var pp = [ + tp[a], + tp[b]+ts[b]-l+l*c + ]; + } else { + var pp = [ + -vp[a]+tp[a]+ps[a] > vs[a] ? + (-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]) : + tp[a], + -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? + (-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) : + (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) + ]; + } + drawPicker(pp[a], pp[b]); + } + }; + + + this.importColor = function() { + if(!valueElement) { + this.exportColor(); } else { - // setting channel value - var res = false; - switch (name.toLowerCase()) { - case 'r': res = this.fromRGBA(value, null, null, null); break; - case 'g': res = this.fromRGBA(null, value, null, null); break; - case 'b': res = this.fromRGBA(null, null, value, null); break; - case 'h': res = this.fromHSVA(value, null, null, null); break; - case 's': res = this.fromHSVA(null, value, null, null); break; - case 'v': res = this.fromHSVA(null, null, value, null); break; - case 'a': res = this.fromHSVA(null, null, null, value); break; - default: - console.warn('Setting unknown channel: ' + name); - return false; + if(!this.adjust) { + if(!this.fromString(valueElement.value, leaveValue)) { + styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; + styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; + styleElement.style.color = styleElement.jscStyle.color; + this.exportColor(leaveValue | leaveStyle); + } + } else if(!this.required && /^\s*$/.test(valueElement.value)) { + valueElement.value = ''; + styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; + styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; + styleElement.style.color = styleElement.jscStyle.color; + this.exportColor(leaveValue | leaveStyle); + + } else if(this.fromString(valueElement.value)) { + // OK + } else { + this.exportColor(); } - if (res) { - this.redraw(); // immediately redraws the picker, if it's displayed - return true; - } - } - - return false; - } - - - // Triggers given input event(s) by: - // - executing on callback specified as picker's option - // - triggering standard DOM event listeners attached to the value element - // - // It is possible to specify multiple events separated with a space. - // - this.trigger = function (eventNames) { - var evs = jsc.strList(eventNames); - for (var i = 0; i < evs.length; i += 1) { - var ev = evs[i].toLowerCase(); - - // trigger a callback - var callbackProp = null; - switch (ev) { - case 'input': callbackProp = 'onInput'; break; - case 'change': callbackProp = 'onChange'; break; - } - if (callbackProp) { - jsc.triggerCallback(this, callbackProp); - } - - // trigger standard DOM event listeners on the value element - jsc.triggerInputEvent(this.valueElement, ev, true, true); } }; - // h: 0-360 - // s: 0-100 - // v: 0-100 - // a: 0.0-1.0 - // - this.fromHSVA = function (h, s, v, a, flags) { // null = don't change - if (h === undefined) { h = null; } - if (s === undefined) { s = null; } - if (v === undefined) { v = null; } - if (a === undefined) { a = null; } - - if (h !== null) { - if (isNaN(h)) { return false; } - this.channels.h = Math.max(0, Math.min(360, h)); + this.exportColor = function(flags) { + if(!(flags & leaveValue) && valueElement) { + var value = this.toString(); + if(this.caps) { value = value.toUpperCase(); } + if(this.hash) { value = '#'+value; } + valueElement.value = value; } - if (s !== null) { - if (isNaN(s)) { return false; } - this.channels.s = Math.max(0, Math.min(100, this.maxS, s), this.minS); + if(!(flags & leaveStyle) && styleElement) { + styleElement.style.backgroundImage = "none"; + styleElement.style.backgroundColor = + '#'+this.toString(); + styleElement.style.color = + 0.213 * this.rgb[0] + + 0.715 * this.rgb[1] + + 0.072 * this.rgb[2] + < 0.5 ? '#FFF' : '#000'; } - if (v !== null) { - if (isNaN(v)) { return false; } - this.channels.v = Math.max(0, Math.min(100, this.maxV, v), this.minV); + if(!(flags & leavePad) && isPickerOwner()) { + redrawPad(); } - if (a !== null) { - if (isNaN(a)) { return false; } - this.channels.a = this.hasAlphaChannel() ? - Math.max(0, Math.min(1, this.maxA, a), this.minA) : - 1.0; // if alpha channel is disabled, the color should stay 100% opaque + if(!(flags & leaveSld) && isPickerOwner()) { + redrawSld(); } - - var rgb = jsc.HSV_RGB( - this.channels.h, - this.channels.s, - this.channels.v - ); - this.channels.r = rgb[0]; - this.channels.g = rgb[1]; - this.channels.b = rgb[2]; - - this.exposeColor(flags); - return true; }; - // r: 0-255 - // g: 0-255 - // b: 0-255 - // a: 0.0-1.0 - // - this.fromRGBA = function (r, g, b, a, flags) { // null = don't change - if (r === undefined) { r = null; } - if (g === undefined) { g = null; } - if (b === undefined) { b = null; } - if (a === undefined) { a = null; } + this.fromHSV = function(h, s, v, flags) { // null = don't change + if(h !== null) { h = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, h)); } + if(s !== null) { s = Math.max(0.0, this.minS, Math.min(1.0, this.maxS, s)); } + if(v !== null) { v = Math.max(0.0, this.minV, Math.min(1.0, this.maxV, v)); } - if (r !== null) { - if (isNaN(r)) { return false; } - r = Math.max(0, Math.min(255, r)); - } - if (g !== null) { - if (isNaN(g)) { return false; } - g = Math.max(0, Math.min(255, g)); - } - if (b !== null) { - if (isNaN(b)) { return false; } - b = Math.max(0, Math.min(255, b)); - } - if (a !== null) { - if (isNaN(a)) { return false; } - this.channels.a = this.hasAlphaChannel() ? - Math.max(0, Math.min(1, this.maxA, a), this.minA) : - 1.0; // if alpha channel is disabled, the color should stay 100% opaque - } - - var hsv = jsc.RGB_HSV( - r===null ? this.channels.r : r, - g===null ? this.channels.g : g, - b===null ? this.channels.b : b + this.rgb = HSV_RGB( + h===null ? this.hsv[0] : (this.hsv[0]=h), + s===null ? this.hsv[1] : (this.hsv[1]=s), + v===null ? this.hsv[2] : (this.hsv[2]=v) ); - if (hsv[0] !== null) { - this.channels.h = Math.max(0, Math.min(360, hsv[0])); + + this.exportColor(flags); + }; + + + this.fromRGB = function(r, g, b, flags) { // null = don't change + if(r !== null) { r = Math.max(0.0, Math.min(1.0, r)); } + if(g !== null) { g = Math.max(0.0, Math.min(1.0, g)); } + if(b !== null) { b = Math.max(0.0, Math.min(1.0, b)); } + + var hsv = RGB_HSV( + r===null ? this.rgb[0] : r, + g===null ? this.rgb[1] : g, + b===null ? this.rgb[2] : b + ); + if(hsv[0] !== null) { + this.hsv[0] = Math.max(0.0, this.minH, Math.min(6.0, this.maxH, hsv[0])); } - if (hsv[2] !== 0) { // fully black color stays black through entire saturation range, so let's not change saturation - this.channels.s = Math.max(0, this.minS, Math.min(100, this.maxS, hsv[1])); + if(hsv[2] !== 0) { + this.hsv[1] = hsv[1]===null ? null : Math.max(0.0, this.minS, Math.min(1.0, this.maxS, hsv[1])); } - this.channels.v = Math.max(0, this.minV, Math.min(100, this.maxV, hsv[2])); + this.hsv[2] = hsv[2]===null ? null : Math.max(0.0, this.minV, Math.min(1.0, this.maxV, hsv[2])); // update RGB according to final HSV, as some values might be trimmed - var rgb = jsc.HSV_RGB(this.channels.h, this.channels.s, this.channels.v); - this.channels.r = rgb[0]; - this.channels.g = rgb[1]; - this.channels.b = rgb[2]; + var rgb = HSV_RGB(this.hsv[0], this.hsv[1], this.hsv[2]); + this.rgb[0] = rgb[0]; + this.rgb[1] = rgb[1]; + this.rgb[2] = rgb[2]; - this.exposeColor(flags); - return true; + this.exportColor(flags); }; - // DEPRECATED. Use .fromHSVA() instead - // - this.fromHSV = function (h, s, v, flags) { - console.warn('fromHSV() method is DEPRECATED. Using fromHSVA() instead.' + jsc.docsRef); - return this.fromHSVA(h, s, v, null, flags); - }; - - - // DEPRECATED. Use .fromRGBA() instead - // - this.fromRGB = function (r, g, b, flags) { - console.warn('fromRGB() method is DEPRECATED. Using fromRGBA() instead.' + jsc.docsRef); - return this.fromRGBA(r, g, b, null, flags); - }; - - - this.fromString = function (str, flags) { - if (!this.required && str.trim() === '') { - // setting empty string to an optional color input - this.setPreviewElementBg(null); - this.setValueElementValue(''); - return true; - } - - var color = jsc.parseColorString(str); - if (!color) { - return false; // could not parse - } - if (this.format.toLowerCase() === 'any') { - this._setFormat(color.format); // adapt format - if (!jsc.isAlphaFormat(this.getFormat())) { - color.rgba[3] = 1.0; // when switching to a format that doesn't support alpha, set full opacity - } - } - this.fromRGBA( - color.rgba[0], - color.rgba[1], - color.rgba[2], - color.rgba[3], - flags - ); - return true; - }; - - - this.toString = function (format) { - if (format === undefined) { - format = this.getFormat(); // format not specified -> use the current format - } - switch (format.toLowerCase()) { - case 'hex': return this.toHEXString(); break; - case 'hexa': return this.toHEXAString(); break; - case 'rgb': return this.toRGBString(); break; - case 'rgba': return this.toRGBAString(); break; - } - return false; - }; - - - this.toHEXString = function () { - return jsc.hexColor( - this.channels.r, - this.channels.g, - this.channels.b - ); - }; - - - this.toHEXAString = function () { - return jsc.hexaColor( - this.channels.r, - this.channels.g, - this.channels.b, - this.channels.a - ); - }; - - - this.toRGBString = function () { - return jsc.rgbColor( - this.channels.r, - this.channels.g, - this.channels.b - ); - }; - - - this.toRGBAString = function () { - return jsc.rgbaColor( - this.channels.r, - this.channels.g, - this.channels.b, - this.channels.a - ); - }; - - - this.toGrayscale = function () { - return ( - 0.213 * this.channels.r + - 0.715 * this.channels.g + - 0.072 * this.channels.b - ); - }; - - - this.toCanvas = function () { - return jsc.genColorPreviewCanvas(this.toRGBAString()).canvas; - }; - - - this.toDataURL = function () { - return this.toCanvas().toDataURL(); - }; - - - this.toBackground = function () { - return jsc.pub.background(this.toRGBAString()); - }; - - - this.isLight = function () { - return this.toGrayscale() > 255 / 2; - }; - - - this.hide = function () { - if (isPickerOwner()) { - detachPicker(); - } - }; - - - this.show = function () { - drawPicker(); - }; - - - this.redraw = function () { - if (isPickerOwner()) { - drawPicker(); - } - }; - - - this.getFormat = function () { - return this._currentFormat; - }; - - - this._setFormat = function (format) { - this._currentFormat = format.toLowerCase(); - }; - - - this.hasAlphaChannel = function () { - if (this.alphaChannel === 'auto') { - return ( - this.format.toLowerCase() === 'any' || // format can change on the fly (e.g. from hex to rgba), so let's consider the alpha channel enabled - jsc.isAlphaFormat(this.getFormat()) || // the current format supports alpha channel - this.alpha !== undefined || // initial alpha value is set, so we're working with alpha channel - this.alphaElement !== undefined // the alpha value is redirected, so we're working with alpha channel - ); - } - - return this.alphaChannel; // the alpha channel is explicitly set - }; - - - this.processValueInput = function (str) { - if (!this.fromString(str)) { - // could not parse the color value - let's just expose the current color - this.exposeColor(); - } - }; - - - this.processAlphaInput = function (str) { - if (!this.fromHSVA(null, null, null, parseFloat(str))) { - // could not parse the alpha value - let's just expose the current color - this.exposeColor(); - } - }; - - - this.exposeColor = function (flags) { - var colorStr = this.toString(); - var fmt = this.getFormat(); - - // reflect current color in data- attribute - jsc.setDataAttr(this.targetElement, 'current-color', colorStr); - - if (!(flags & jsc.flags.leaveValue) && this.valueElement) { - if (fmt === 'hex' || fmt === 'hexa') { - if (!this.uppercase) { colorStr = colorStr.toLowerCase(); } - if (!this.hash) { colorStr = colorStr.replace(/^#/, ''); } - } - this.setValueElementValue(colorStr); - } - - if (!(flags & jsc.flags.leaveAlpha) && this.alphaElement) { - var alphaVal = Math.round(this.channels.a * 100) / 100; - this.setAlphaElementValue(alphaVal); - } - - if (!(flags & jsc.flags.leavePreview) && this.previewElement) { - var previewPos = null; // 'left' | 'right' (null -> fill the entire element) - - if ( - jsc.isTextInput(this.previewElement) || // text input - (jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) // button with text - ) { - previewPos = this.previewPosition; - } - - this.setPreviewElementBg(this.toRGBAString()); - } - - if (isPickerOwner()) { - redrawPad(); - redrawSld(); - redrawASld(); - } - }; - - - this.setPreviewElementBg = function (color) { - if (!this.previewElement) { - return; - } - - var position = null; // color preview position: null | 'left' | 'right' - var width = null; // color preview width: px | null = fill the entire element - if ( - jsc.isTextInput(this.previewElement) || // text input - (jsc.isButton(this.previewElement) && !jsc.isButtonEmpty(this.previewElement)) // button with text - ) { - position = this.previewPosition; - width = this.previewSize; - } - - var backgrounds = []; - - if (!color) { - // there is no color preview to display -> let's remove any previous background image - backgrounds.push({ - image: 'none', - position: 'left top', - size: 'auto', - repeat: 'no-repeat', - origin: 'padding-box', - }); + this.fromString = function(hex, flags) { + var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); + if(!m) { + return false; } else { - // CSS gradient for background color preview - backgrounds.push({ - image: jsc.genColorPreviewGradient( - color, - position, - width ? width - jsc.pub.previewSeparator.length : null - ), - position: 'left top', - size: 'auto', - repeat: position ? 'repeat-y' : 'repeat', - origin: 'padding-box', - }); - - // data URL of generated PNG image with a gray transparency chessboard - var preview = jsc.genColorPreviewCanvas( - 'rgba(0,0,0,0)', - position ? {'left':'right', 'right':'left'}[position] : null, - width, - true - ); - backgrounds.push({ - image: 'url(\'' + preview.canvas.toDataURL() + '\')', - position: (position || 'left') + ' top', - size: preview.width + 'px ' + preview.height + 'px', - repeat: position ? 'repeat-y' : 'repeat', - origin: 'padding-box', - }); - } - - var bg = { - image: [], - position: [], - size: [], - repeat: [], - origin: [], - }; - for (var i = 0; i < backgrounds.length; i += 1) { - bg.image.push(backgrounds[i].image); - bg.position.push(backgrounds[i].position); - bg.size.push(backgrounds[i].size); - bg.repeat.push(backgrounds[i].repeat); - bg.origin.push(backgrounds[i].origin); - } - - // set previewElement's background-images - var sty = { - 'background-image': bg.image.join(', '), - 'background-position': bg.position.join(', '), - 'background-size': bg.size.join(', '), - 'background-repeat': bg.repeat.join(', '), - 'background-origin': bg.origin.join(', '), - }; - jsc.setStyle(this.previewElement, sty, this.forceStyle); - - - // set/restore previewElement's padding - var padding = { - left: null, - right: null, - }; - if (position) { - padding[position] = (this.previewSize + this.previewPadding) + 'px'; - } - - var sty = { - 'padding-left': padding.left, - 'padding-right': padding.right, - }; - jsc.setStyle(this.previewElement, sty, this.forceStyle, true); - }; - - - this.setValueElementValue = function (str) { - if (this.valueElement) { - if (jsc.nodeName(this.valueElement) === 'input') { - this.valueElement.value = str; - } else { - this.valueElement.innerHTML = str; + if(m[1].length === 6) { // 6-char notation + this.fromRGB( + parseInt(m[1].substr(0,2),16) / 255, + parseInt(m[1].substr(2,2),16) / 255, + parseInt(m[1].substr(4,2),16) / 255, + flags + ); + } else { // 3-char notation + this.fromRGB( + parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, + parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, + parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, + flags + ); } - } - }; - - - this.setAlphaElementValue = function (str) { - if (this.alphaElement) { - if (jsc.nodeName(this.alphaElement) === 'input') { - this.alphaElement.value = str; - } else { - this.alphaElement.innerHTML = str; - } - } - }; - - - this._processParentElementsInDOM = function () { - if (this._parentElementsProcessed) { return; } - this._parentElementsProcessed = true; - - var elm = this.targetElement; - do { - // If the target element or one of its parent nodes has fixed position, - // then use fixed positioning instead - var compStyle = jsc.getCompStyle(elm); - if (compStyle.position && compStyle.position.toLowerCase() === 'fixed') { - this.fixed = true; - } - - if (elm !== this.targetElement) { - // Ensure to attach onParentScroll only once to each parent element - // (multiple targetElements can share the same parent nodes) - // - // Note: It's not just offsetParents that can be scrollable, - // that's why we loop through all parent nodes - if (!jsc.getData(elm, 'hasScrollListener')) { - elm.addEventListener('scroll', jsc.onParentScroll, false); - jsc.setData(elm, 'hasScrollListener', true); - } - } - } while ((elm = elm.parentNode) && jsc.nodeName(elm) !== 'body'); - }; - - - this.tryHide = function () { - if (this.hideOnLeave) { - this.hide(); - } - }; - - - this.set__palette = function (val) { - this.palette = val; - this._palette = jsc.parsePaletteValue(val); - this._paletteHasTransparency = jsc.containsTranparentColor(this._palette); - }; - - - function setOption (option, value) { - if (typeof option !== 'string') { - throw new Error('Invalid value for option name: ' + option); - } - - // enum option - if (jsc.enumOpts.hasOwnProperty(option)) { - if (typeof value === 'string') { // enum string values are case insensitive - value = value.toLowerCase(); - } - if (jsc.enumOpts[option].indexOf(value) === -1) { - throw new Error('Option \'' + option + '\' has invalid value: ' + value); - } - } - - // deprecated option - if (jsc.deprecatedOpts.hasOwnProperty(option)) { - var oldOpt = option; - var newOpt = jsc.deprecatedOpts[option]; - if (newOpt) { - // if we have a new name for this option, let's log a warning and use the new name - console.warn('Option \'%s\' is DEPRECATED, using \'%s\' instead.' + jsc.docsRef, oldOpt, newOpt); - option = newOpt; - } else { - // new name not available for the option - throw new Error('Option \'' + option + '\' is DEPRECATED'); - } - } - - var setter = 'set__' + option; - - if (typeof THIS[setter] === 'function') { // a setter exists for this option - THIS[setter](value); - return true; - - } else if (option in THIS) { // option exists as a property - THIS[option] = value; return true; } + }; - throw new Error('Unrecognized configuration option: ' + option); + + this.toString = function() { + return ( + (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + + (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + + (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) + ); + }; + + + function RGB_HSV(r, g, b) { + var n = Math.min(Math.min(r,g),b); + var v = Math.max(Math.max(r,g),b); + var m = v - n; + if(m === 0) { return [ null, 0, v ]; } + var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); + return [ h===6?0:h, m/v, v ]; } - function getOption (option) { - if (typeof option !== 'string') { - throw new Error('Invalid value for option name: ' + option); + function HSV_RGB(h, s, v) { + if(h === null) { return [ v, v, v ]; } + var i = Math.floor(h); + var f = i%2 ? h-i : 1-(h-i); + var m = v * (1 - s); + var n = v * (1 - s*f); + switch(i) { + case 6: + case 0: return [v,n,m]; + case 1: return [n,v,m]; + case 2: return [m,v,n]; + case 3: return [m,n,v]; + case 4: return [n,m,v]; + case 5: return [v,m,n]; } - - // deprecated option - if (jsc.deprecatedOpts.hasOwnProperty(option)) { - var oldOpt = option; - var newOpt = jsc.deprecatedOpts[option]; - if (newOpt) { - // if we have a new name for this option, let's log a warning and use the new name - console.warn('Option \'%s\' is DEPRECATED, using \'%s\' instead.' + jsc.docsRef, oldOpt, newOpt); - option = newOpt; - } else { - // new name not available for the option - throw new Error('Option \'' + option + '\' is DEPRECATED'); - } - } - - var getter = 'get__' + option; - - if (typeof THIS[getter] === 'function') { // a getter exists for this option - return THIS[getter](value); - - } else if (option in THIS) { // option exists as a property - return THIS[option]; - } - - throw new Error('Unrecognized configuration option: ' + option); } - function detachPicker () { - jsc.removeClass(THIS.targetElement, jsc.pub.activeClassName); - jsc.picker.wrap.parentNode.removeChild(jsc.picker.wrap); - delete jsc.picker.owner; + function removePicker() { + delete jscolor.picker.owner; + document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); } - function drawPicker () { - - // At this point, when drawing the picker, we know what the parent elements are - // and we can do all related DOM operations, such as registering events on them - // or checking their positioning - THIS._processParentElementsInDOM(); - - if (!jsc.picker) { - jsc.picker = { - owner: null, // owner picker instance - wrap : jsc.createEl('div'), - box : jsc.createEl('div'), - boxS : jsc.createEl('div'), // shadow area - boxB : jsc.createEl('div'), // border - pad : jsc.createEl('div'), - padB : jsc.createEl('div'), // border - padM : jsc.createEl('div'), // mouse/touch area - padCanvas : jsc.createPadCanvas(), - cross : jsc.createEl('div'), - crossBY : jsc.createEl('div'), // border Y - crossBX : jsc.createEl('div'), // border X - crossLY : jsc.createEl('div'), // line Y - crossLX : jsc.createEl('div'), // line X - sld : jsc.createEl('div'), // slider - sldB : jsc.createEl('div'), // border - sldM : jsc.createEl('div'), // mouse/touch area - sldGrad : jsc.createSliderGradient(), - sldPtrS : jsc.createEl('div'), // slider pointer spacer - sldPtrIB : jsc.createEl('div'), // slider pointer inner border - sldPtrMB : jsc.createEl('div'), // slider pointer middle border - sldPtrOB : jsc.createEl('div'), // slider pointer outer border - asld : jsc.createEl('div'), // alpha slider - asldB : jsc.createEl('div'), // border - asldM : jsc.createEl('div'), // mouse/touch area - asldGrad : jsc.createASliderGradient(), - asldPtrS : jsc.createEl('div'), // slider pointer spacer - asldPtrIB : jsc.createEl('div'), // slider pointer inner border - asldPtrMB : jsc.createEl('div'), // slider pointer middle border - asldPtrOB : jsc.createEl('div'), // slider pointer outer border - pal : jsc.createEl('div'), // palette - btn : jsc.createEl('div'), - btnT : jsc.createEl('span'), // text + function drawPicker(x, y) { + if(!jscolor.picker) { + jscolor.picker = { + box : document.createElement('div'), + boxB : document.createElement('div'), + pad : document.createElement('div'), + padB : document.createElement('div'), + padM : document.createElement('div'), + sld : document.createElement('div'), + sldB : document.createElement('div'), + sldM : document.createElement('div'), + btn : document.createElement('div'), + btnS : document.createElement('span'), + btnT : document.createTextNode(THIS.pickerCloseText) }; - - jsc.picker.pad.appendChild(jsc.picker.padCanvas.elm); - jsc.picker.padB.appendChild(jsc.picker.pad); - jsc.picker.cross.appendChild(jsc.picker.crossBY); - jsc.picker.cross.appendChild(jsc.picker.crossBX); - jsc.picker.cross.appendChild(jsc.picker.crossLY); - jsc.picker.cross.appendChild(jsc.picker.crossLX); - jsc.picker.padB.appendChild(jsc.picker.cross); - jsc.picker.box.appendChild(jsc.picker.padB); - jsc.picker.box.appendChild(jsc.picker.padM); - - jsc.picker.sld.appendChild(jsc.picker.sldGrad.elm); - jsc.picker.sldB.appendChild(jsc.picker.sld); - jsc.picker.sldB.appendChild(jsc.picker.sldPtrOB); - jsc.picker.sldPtrOB.appendChild(jsc.picker.sldPtrMB); - jsc.picker.sldPtrMB.appendChild(jsc.picker.sldPtrIB); - jsc.picker.sldPtrIB.appendChild(jsc.picker.sldPtrS); - jsc.picker.box.appendChild(jsc.picker.sldB); - jsc.picker.box.appendChild(jsc.picker.sldM); - - jsc.picker.asld.appendChild(jsc.picker.asldGrad.elm); - jsc.picker.asldB.appendChild(jsc.picker.asld); - jsc.picker.asldB.appendChild(jsc.picker.asldPtrOB); - jsc.picker.asldPtrOB.appendChild(jsc.picker.asldPtrMB); - jsc.picker.asldPtrMB.appendChild(jsc.picker.asldPtrIB); - jsc.picker.asldPtrIB.appendChild(jsc.picker.asldPtrS); - jsc.picker.box.appendChild(jsc.picker.asldB); - jsc.picker.box.appendChild(jsc.picker.asldM); - - jsc.picker.box.appendChild(jsc.picker.pal); - - jsc.picker.btn.appendChild(jsc.picker.btnT); - jsc.picker.box.appendChild(jsc.picker.btn); - - jsc.picker.boxB.appendChild(jsc.picker.box); - jsc.picker.wrap.appendChild(jsc.picker.boxS); - jsc.picker.wrap.appendChild(jsc.picker.boxB); - - jsc.picker.wrap.addEventListener('touchstart', jsc.onPickerTouchStart, - jsc.isPassiveEventSupported ? {passive: false} : false); + for(var i=0,segSize=4; i let's trigger the change event again, even though it was natively dispatched - jsc.triggerInputEvent(THIS.valueElement, 'change', true, true); - } - } - - - function onAlphaChange (ev) { - if (jsc.getData(ev, 'internal')) { - return; // skip if the event was internally triggered by jscolor - } - - var oldVal = THIS.alphaElement.value; - - THIS.processAlphaInput(THIS.alphaElement.value); // this might change the value - - jsc.triggerCallback(THIS, 'onChange'); - - // triggering valueElement's onChange (because changing alpha changes the entire color, e.g. with rgba format) - jsc.triggerInputEvent(THIS.valueElement, 'change', true, true); - - if (THIS.alphaElement.value !== oldVal) { - // value was additionally changed -> let's trigger the change event again, even though it was natively dispatched - jsc.triggerInputEvent(THIS.alphaElement, 'change', true, true); - } - } - - - function onValueInput (ev) { - if (jsc.getData(ev, 'internal')) { - return; // skip if the event was internally triggered by jscolor - } - - if (THIS.valueElement) { - THIS.fromString(THIS.valueElement.value, jsc.flags.leaveValue); - } - - jsc.triggerCallback(THIS, 'onInput'); - - // triggering valueElement's onInput - // (not needed, it was dispatched normally by the browser) - } - - - function onAlphaInput (ev) { - if (jsc.getData(ev, 'internal')) { - return; // skip if the event was internally triggered by jscolor - } - - if (THIS.alphaElement) { - THIS.fromHSVA(null, null, null, parseFloat(THIS.alphaElement.value), jsc.flags.leaveAlpha); - } - - jsc.triggerCallback(THIS, 'onInput'); - - // triggering valueElement's onInput (because changing alpha changes the entire color, e.g. with rgba format) - jsc.triggerInputEvent(THIS.valueElement, 'input', true, true); - } - - - // let's process the DEPRECATED 'options' property (this will be later removed) - if (jsc.pub.options) { - // let's set custom default options, if specified - for (var opt in jsc.pub.options) { - if (jsc.pub.options.hasOwnProperty(opt)) { - try { - setOption(opt, jsc.pub.options[opt]); - } catch (e) { - console.warn(e); + switch(modeID) { + case 0: + var rgb = HSV_RGB(THIS.hsv[0], THIS.hsv[1], 1); + for(var i=0; i= 0; i -= 1) { - var pres = presetsArr[i]; - if (!pres) { - continue; // preset is empty string - } - if (!jsc.pub.presets.hasOwnProperty(pres)) { - console.warn('Unknown preset: %s', pres); - continue; - } - for (var opt in jsc.pub.presets[pres]) { - if (jsc.pub.presets[pres].hasOwnProperty(opt)) { - try { - setOption(opt, jsc.pub.presets[pres][opt]); - } catch (e) { - console.warn(e); - } - } - } - } - - - // let's set specific options for this color picker - var nonProperties = [ - // these options won't be set as instance properties - 'preset', - ]; - for (var opt in opts) { - if (opts.hasOwnProperty(opt)) { - if (nonProperties.indexOf(opt) === -1) { - try { - setOption(opt, opts[opt]); - } catch (e) { - console.warn(e); - } - } - } - } - - - // - // Install the color picker on chosen element(s) - // - - - // Determine picker's container element - if (this.container === undefined) { - this.container = window.document.body; // default container is BODY element - - } else { // explicitly set to custom element - this.container = jsc.node(this.container); - } - - if (!this.container) { - throw new Error('Cannot instantiate color picker without a container element'); - } - - - // Fetch the target element - this.targetElement = jsc.node(targetElement); - - if (!this.targetElement) { - // temporarily customized error message to help with migrating from versions prior to 2.2 - if (typeof targetElement === 'string' && /^[a-zA-Z][\w:.-]*$/.test(targetElement)) { - // targetElement looks like valid ID - var possiblyId = targetElement; - throw new Error('If \'' + possiblyId + '\' is supposed to be an ID, please use \'#' + possiblyId + '\' or any valid CSS selector.'); - } - - throw new Error('Cannot instantiate color picker without a target element'); - } - - if (this.targetElement.jscolor && this.targetElement.jscolor instanceof jsc.pub) { - throw new Error('Color picker already installed on this element'); - } - - - // link this instance with the target element - this.targetElement.jscolor = this; - jsc.addClass(this.targetElement, jsc.pub.className); - - // register this instance - jsc.instances.push(this); - - - // if target is BUTTON - if (jsc.isButton(this.targetElement)) { - - if (this.targetElement.type.toLowerCase() !== 'button') { - // on buttons, always force type to be 'button', e.g. in situations the target