您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
a simple map drawer for df2profiler.com by clicking on the map to draw color on area
当前为
- // ==UserScript==
- // @name df2profiler map drawer
- // @name:zh-TW df2profiler 地圖繪製器
- // @namespace http://tampermonkey.net/
- // @version 0.4.0
- // @description a simple map drawer for df2profiler.com by clicking on the map to draw color on area
- // @description:zh-TW 透過點擊地圖來繪製顏色的簡易地圖繪製器
- // @author Archer_Wn
- // @match https://df2profiler.com/gamemap/
- // @icon https://www.google.com/s2/favicons?sz=64&domain=df2profiler.com
- // @grant none
- // @license GPL-3.0
- // ==/UserScript==
- // Options
- let color = "rgba(255, 255, 0, 0.3)"; // default color for drawing
- const pvpColor = "rgba(255, 0, 0, 0.4)"; // color for pvp area
- const outpostColor = "rgba(0, 255, 0, 0.4)"; // color for outpost area
- const chunkSize = 6; // echo chunk have (chunkSize * chunkSize) cells
- // Main
- let mouseDown = false;
- let drawingMode = false;
- let drewCells = [];
- function onPickerInput(value) {
- color = value;
- }
- function onSavedMap() {
- const tbody = document.querySelector("#map tbody");
- const backgroundImgUrl = window
- .getComputedStyle(document.querySelector("#map"))
- .getPropertyValue("background-image");
- const canvas = document.createElement("canvas");
- canvas.width = tbody.clientWidth;
- canvas.height = tbody.clientHeight;
- const ctx = canvas.getContext("2d");
- const img = new Image();
- img.src = backgroundImgUrl.slice(5, -2);
- img.onload = () => {
- // draw background image
- ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
- // draw pvp area
- const pvpCells = tbody.querySelectorAll(".pvpZone");
- pvpCells.forEach((cell) => {
- const rect = cell.getBoundingClientRect();
- const x = rect.left - tbody.getBoundingClientRect().left;
- const y = rect.top - tbody.getBoundingClientRect().top;
- ctx.fillStyle = pvpColor;
- ctx.fillRect(x, y, rect.width, rect.height);
- });
- // draw outpost area
- const outpostCells = tbody.querySelectorAll(".outpost");
- outpostCells.forEach((cell) => {
- const rect = cell.getBoundingClientRect();
- const x = rect.left - tbody.getBoundingClientRect().left;
- const y = rect.top - tbody.getBoundingClientRect().top;
- ctx.fillStyle = outpostColor;
- ctx.fillRect(x, y, rect.width, rect.height);
- });
- // draw area
- const cells = tbody.querySelectorAll("td");
- cells.forEach((cell) => {
- if (cell.style.backgroundColor === "") return;
- const rect = cell.getBoundingClientRect();
- const x = rect.left - tbody.getBoundingClientRect().left;
- const y = rect.top - tbody.getBoundingClientRect().top;
- ctx.fillStyle = cell.style.backgroundColor;
- ctx.fillRect(x, y, rect.width, rect.height);
- });
- // draw grid (6x6 cells)
- const td = tbody.querySelector("td");
- const chunkWidth = td.clientWidth * chunkSize;
- const chunkHeight = td.clientHeight * chunkSize;
- ctx.strokeStyle = "white";
- ctx.lineWidth = 1;
- ctx.beginPath();
- for (let x = chunkWidth; x < canvas.width; x += chunkWidth) {
- ctx.moveTo(x, 0);
- ctx.lineTo(x, canvas.height);
- }
- for (let y = chunkHeight; y < canvas.height; y += chunkHeight) {
- ctx.moveTo(0, y);
- ctx.lineTo(canvas.width, y);
- }
- ctx.stroke();
- // download image
- const a = document.createElement("a");
- a.href = canvas.toDataURL("image/png");
- a.download = "map.png";
- a.click();
- };
- }
- (function () {
- "use strict";
- const tbody = document.querySelector("#map tbody");
- tbody.addEventListener("click", (e) => {
- const cell = e.target.closest("td");
- if (!cell) return;
- if (drawingMode) {
- cell.style.backgroundColor = color;
- } else {
- cell.style.backgroundColor = "";
- }
- });
- tbody.addEventListener("mousedown", (e) => {
- mouseDown = true;
- const cell = e.target.closest("td");
- if (!cell) return;
- if (cell.style.backgroundColor === "") {
- drawingMode = true;
- } else {
- drawingMode = false;
- }
- });
- tbody.addEventListener("mousemove", (e) => {
- if (!mouseDown) return;
- const cell = e.target.closest("td");
- if (!cell) return;
- if (drewCells.includes(cell)) return;
- if (drawingMode) {
- cell.style.backgroundColor = color;
- } else {
- cell.style.backgroundColor = "";
- }
- drewCells.push(cell);
- });
- tbody.addEventListener("mouseup", (e) => {
- mouseDown = false;
- drewCells = [];
- });
- const navbarLinks = document.querySelector("#navbar-links");
- // color picker (rgba)
- const colorPicker = document.createElement("input");
- colorPicker.value = color;
- colorPicker.style.padding = "0 0.5rem";
- colorPicker.style.margin = "0.3rem 1rem";
- colorPicker.style.borderRadius =
- navbarLinks.getBoundingClientRect().height / 2 + "px";
- colorPicker.style.marginLeft = "auto";
- colorPicker.style.cursor = "pointer";
- colorPicker.setAttribute("data-jscolor", {});
- colorPicker.addEventListener("input", function () {
- onPickerInput(this.jscolor.toRGBAString());
- });
- navbarLinks.appendChild(colorPicker);
- // screenshot button
- const screenshotBtn = document.createElement("button");
- screenshotBtn.textContent = "Screenshot Map";
- screenshotBtn.style.padding = "0 1rem";
- screenshotBtn.style.margin = "0.3rem 0rem";
- screenshotBtn.style.borderRadius =
- navbarLinks.getBoundingClientRect().height / 2 + "px";
- screenshotBtn.style.cursor = "pointer";
- navbarLinks.appendChild(screenshotBtn);
- screenshotBtn.addEventListener("click", onSavedMap);
- })();
- /**
- * jscolor - JavaScript Color Picker
- *
- * @link http://jscolor.com
- * @license For open source use: GPLv3
- * For commercial use: JSColor Commercial License
- * @author Jan Odvarko - East Desire
- * @version 2.5.2
- *
- * 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
- "use strict";
- var jscolor = (function () {
- // BEGIN jscolor
- var jsc = {
- initialized: false,
- instances: [], // created instances of jscolor
- readyQueue: [], // functions waiting to be called after init
- register: function () {
- if (typeof window !== "undefined" && window.document) {
- if (window.document.readyState !== "loading") {
- jsc.pub.init();
- } else {
- window.document.addEventListener(
- "DOMContentLoaded",
- jsc.pub.init,
- false
- );
- }
- }
- },
- installBySelector: function (selector, rootNode) {
- rootNode = rootNode ? jsc.node(rootNode) : window.document;
- if (!rootNode) {
- throw new Error("Missing root node");
- }
- var elms = rootNode.querySelectorAll(selector);
- // for backward compatibility with DEPRECATED installation/configuration using className
- var matchClass = new RegExp(
- "(^|\\s)(" + jsc.pub.lookupClass + ")(\\s*(\\{[^}]*\\})|\\s|$)",
- "i"
- );
- for (var i = 0; i < elms.length; i += 1) {
- if (elms[i].jscolor && elms[i].jscolor instanceof jsc.pub) {
- continue; // jscolor already installed on this element
- }
- if (
- elms[i].type !== undefined &&
- elms[i].type.toLowerCase() == "color" &&
- jsc.isColorAttrSupported
- ) {
- continue; // skips inputs of type 'color' if supported by the browser
- }
- var dataOpts, m;
- if (
- (dataOpts = jsc.getDataAttr(elms[i], "jscolor")) !== null ||
- (elms[i].className && (m = elms[i].className.match(matchClass))) // installation using className (DEPRECATED)
- ) {
- var targetElm = elms[i];
- var optsStr = "";
- if (dataOpts !== null) {
- optsStr = dataOpts;
- } else if (m) {
- // installation using className (DEPRECATED)
- console.warn(
- 'Installation using class name is DEPRECATED. Use data-jscolor="" attribute instead.' +
- jsc.docsRef
- );
- if (m[4]) {
- optsStr = m[4];
- }
- }
- var opts = null;
- if (optsStr.trim()) {
- try {
- opts = jsc.parseOptionsStr(optsStr);
- } catch (e) {
- console.warn(e + "\n" + optsStr);
- }
- }
- try {
- new jsc.pub(targetElm, opts);
- } catch (e) {
- console.warn(e);
- }
- }
- }
- },
- parseOptionsStr: function (str) {
- var opts = null;
- try {
- opts = JSON.parse(str);
- } catch (eParse) {
- if (!jsc.pub.looseJSON) {
- throw new Error(
- "Could not parse jscolor options as JSON: " + eParse
- );
- } else {
- // loose JSON syntax is enabled -> 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);
- }
- }
- }
- 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;
- },
- createEl: function (tagName) {
- var el = window.document.createElement(tagName);
- jsc.setData(el, "gui", true);
- return el;
- },
- 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);
- }
- },
- isTextInput: function (el) {
- return (
- el && jsc.nodeName(el) === "input" && el.type.toLowerCase() === "text"
- );
- },
- 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];
- }
- },
- preventDefault: function (e) {
- if (e.preventDefault) {
- e.preventDefault();
- }
- e.returnValue = false;
- },
- triggerEvent: function (el, eventName, bubbles, cancelable) {
- 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);
- }
- },
- 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);
- }
- }
- }
- },
- appendCss: function (css) {
- var head = document.querySelector("head");
- var style = document.createElement("style");
- style.innerText = css;
- head.appendChild(style);
- },
- appendDefaultCss: function (css) {
- jsc.appendCss(
- [
- ".jscolor-wrap, .jscolor-wrap div, .jscolor-wrap canvas { " +
- "position:static; display:block; visibility:visible; overflow:visible; margin:0; padding:0; " +
- "border:none; border-radius:0; outline:none; z-index:auto; float:none; " +
- "width:auto; height:auto; left:auto; right:auto; top:auto; bottom:auto; min-width:0; min-height:0; max-width:none; max-height:none; " +
- "background:none; clip:auto; opacity:1; transform:none; box-shadow:none; box-sizing:content-box; " +
- "}",
- ".jscolor-wrap { clear:both; }",
- ".jscolor-wrap .jscolor-picker { position:relative; }",
- ".jscolor-wrap .jscolor-shadow { position:absolute; left:0; top:0; width:100%; height:100%; }",
- ".jscolor-wrap .jscolor-border { position:relative; }",
- ".jscolor-wrap .jscolor-palette { position:absolute; }",
- ".jscolor-wrap .jscolor-palette-sw { position:absolute; display:block; cursor:pointer; }",
- ".jscolor-wrap .jscolor-btn { position:absolute; overflow:hidden; white-space:nowrap; font:13px sans-serif; text-align:center; cursor:pointer; }",
- ].join("\n")
- );
- },
- hexColor: function (r, g, b) {
- return (
- "#" +
- (
- ("0" + Math.round(r).toString(16)).slice(-2) +
- ("0" + Math.round(g).toString(16)).slice(-2) +
- ("0" + Math.round(b).toString(16)).slice(-2)
- ).toUpperCase()
- );
- },
- hexaColor: function (r, g, b, a) {
- return (
- "#" +
- (
- ("0" + Math.round(r).toString(16)).slice(-2) +
- ("0" + Math.round(g).toString(16)).slice(-2) +
- ("0" + Math.round(b).toString(16)).slice(-2) +
- ("0" + Math.round(a * 255).toString(16)).slice(-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) {
- 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];
- }
- return [x, y];
- },
- getElementSize: function (e) {
- return [e.offsetWidth, e.offsetHeight];
- },
- // get pointer's X/Y coordinates relative to viewport
- getAbsPointerPos: 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;
- }
- 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];
- }
- },
- parseColorString: function (str) {
- var ret = {
- rgba: null,
- format: null, // 'hex' | 'hexa' | 'rgb' | 'rgba'
- };
- var m;
- if ((m = str.match(/^\W*([0-9A-F]{3,8})\W*$/i))) {
- // HEX notation
- if (m[1].length === 8) {
- // 8-char notation (= with alpha)
- ret.format = "hexa";
- ret.rgba = [
- parseInt(m[1].slice(0, 2), 16),
- parseInt(m[1].slice(2, 4), 16),
- parseInt(m[1].slice(4, 6), 16),
- parseInt(m[1].slice(6, 8), 16) / 255,
- ];
- } else if (m[1].length === 6) {
- // 6-char notation
- ret.format = "hex";
- ret.rgba = [
- parseInt(m[1].slice(0, 2), 16),
- parseInt(m[1].slice(2, 4), 16),
- parseInt(m[1].slice(4, 6), 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,
- ];
- } 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}\b|#[0-9A-F]{6}([0-9A-F]{2})?\b|rgba?\(([^)]*)\)/gi,
- 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;
- if (thisObj.container !== window.document.body) {
- jsc._drawPosition(thisObj, 0, 0, "relative", false);
- } else {
- 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;
- if (
- // To avoid unnecessary repositioning during scroll
- Math.round(parseFloat(jsc.picker.wrap.style.left)) !==
- Math.round(x) ||
- Math.round(parseFloat(jsc.picker.wrap.style.top)) !== Math.round(y)
- ) {
- 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,
- 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);
- 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");
- // 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: {
- // <old_option>: <new_option> (<new_option> 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(<targetElement> [, <options>])
- //
- // (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', <value>)
- 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]
- 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;
- }
- }
- }
- 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;
- }
- throw new Error("Invalid arguments");
- };
- // 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);
- }
- if (value === undefined) {
- // getting channel value
- if (!this.channels.hasOwnProperty(name.toLowerCase())) {
- console.warn("Getting unknown channel: " + name);
- return false;
- }
- return this.channels[name.toLowerCase()];
- } 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 (res) {
- this.redraw(); // immediately redraws the picker, if it's displayed
- return true;
- }
- }
- return false;
- };
- // Triggers given input event(s) by:
- // - executing on<Event> 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));
- }
- if (s !== null) {
- if (isNaN(s)) {
- return false;
- }
- this.channels.s = Math.max(
- 0,
- Math.min(100, this.maxS, s),
- this.minS
- );
- }
- if (v !== null) {
- if (isNaN(v)) {
- return false;
- }
- this.channels.v = Math.max(
- 0,
- Math.min(100, this.maxV, v),
- this.minV
- );
- }
- 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 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;
- }
- 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
- );
- if (hsv[0] !== null) {
- this.channels.h = Math.max(0, Math.min(360, 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])
- );
- }
- this.channels.v = Math.max(
- 0,
- this.minV,
- Math.min(100, 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];
- this.exposeColor(flags);
- return true;
- };
- // 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.randomize = function (
- minV,
- maxV,
- minS,
- maxS,
- minH,
- maxH,
- minA,
- maxA
- ) {
- if (minV === undefined) {
- minV = 0;
- }
- if (maxV === undefined) {
- maxV = 100;
- }
- if (minS === undefined) {
- minS = 0;
- }
- if (maxS === undefined) {
- maxS = 100;
- }
- if (minH === undefined) {
- minH = 0;
- }
- if (maxH === undefined) {
- maxH = 359;
- }
- if (minA === undefined) {
- minA = 1;
- }
- if (maxA === undefined) {
- maxA = 1;
- }
- this.fromHSVA(
- minH + Math.floor(Math.random() * (maxH - minH + 1)),
- minS + Math.floor(Math.random() * (maxS - minS + 1)),
- minV + Math.floor(Math.random() * (maxV - minV + 1)),
- (100 * minA +
- Math.floor(Math.random() * (100 * (maxA - minA) + 1))) /
- 100
- );
- };
- 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",
- });
- } 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;
- }
- }
- };
- 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);
- }
- function getOption(option) {
- if (typeof option !== "string") {
- throw new Error("Invalid value for option name: " + option);
- }
- // 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 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("div"), // text
- };
- 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
- );
- }
- var p = jsc.picker;
- var displaySlider = !!jsc.getSliderChannel(THIS);
- var displayAlphaSlider = THIS.hasAlphaChannel();
- var pickerDims = jsc.getPickerDims(THIS);
- var crossOuterSize =
- 2 * THIS.pointerBorderWidth +
- THIS.pointerThickness +
- 2 * THIS.crossSize;
- var controlPadding = jsc.getControlPadding(THIS);
- var borderRadius = Math.min(
- THIS.borderRadius,
- Math.round(THIS.padding * Math.PI)
- ); // px
- var padCursor = "crosshair";
- // wrap
- p.wrap.className = "jscolor-wrap";
- p.wrap.style.width = pickerDims.borderW + "px";
- p.wrap.style.height = pickerDims.borderH + "px";
- p.wrap.style.zIndex = THIS.zIndex;
- // picker
- p.box.className = "jscolor-picker";
- p.box.style.width = pickerDims.paddedW + "px";
- p.box.style.height = pickerDims.paddedH + "px";
- // picker shadow
- p.boxS.className = "jscolor-shadow";
- jsc.setBorderRadius(p.boxS, borderRadius + "px");
- // picker border
- p.boxB.className = "jscolor-border";
- p.boxB.style.border = THIS.borderWidth + "px solid";
- p.boxB.style.borderColor = THIS.borderColor;
- p.boxB.style.background = THIS.backgroundColor;
- jsc.setBorderRadius(p.boxB, borderRadius + "px");
- // IE hack:
- // If the element is transparent, IE will trigger the event on the elements under it,
- // e.g. on Canvas or on elements with border
- p.padM.style.background = "rgba(255,0,0,.2)";
- p.sldM.style.background = "rgba(0,255,0,.2)";
- p.asldM.style.background = "rgba(0,0,255,.2)";
- p.padM.style.opacity =
- p.sldM.style.opacity =
- p.asldM.style.opacity =
- "0";
- // pad
- p.pad.style.position = "relative";
- p.pad.style.width = THIS.width + "px";
- p.pad.style.height = THIS.height + "px";
- // pad - color spectrum (HSV and HVS)
- p.padCanvas.draw(THIS.width, THIS.height, jsc.getPadYChannel(THIS));
- // pad border
- p.padB.style.position = "absolute";
- p.padB.style.left = THIS.padding + "px";
- p.padB.style.top = THIS.padding + "px";
- p.padB.style.border = THIS.controlBorderWidth + "px solid";
- p.padB.style.borderColor = THIS.controlBorderColor;
- // pad mouse area
- p.padM.style.position = "absolute";
- p.padM.style.left = 0 + "px";
- p.padM.style.top = 0 + "px";
- p.padM.style.width =
- THIS.padding +
- 2 * THIS.controlBorderWidth +
- THIS.width +
- controlPadding +
- "px";
- p.padM.style.height =
- 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px";
- p.padM.style.cursor = padCursor;
- jsc.setData(p.padM, {
- instance: THIS,
- control: "pad",
- });
- // pad cross
- p.cross.style.position = "absolute";
- p.cross.style.left = p.cross.style.top = "0";
- p.cross.style.width = p.cross.style.height = crossOuterSize + "px";
- // pad cross border Y and X
- p.crossBY.style.position = p.crossBX.style.position = "absolute";
- p.crossBY.style.background = p.crossBX.style.background =
- THIS.pointerBorderColor;
- p.crossBY.style.width = p.crossBX.style.height =
- 2 * THIS.pointerBorderWidth + THIS.pointerThickness + "px";
- p.crossBY.style.height = p.crossBX.style.width =
- crossOuterSize + "px";
- p.crossBY.style.left = p.crossBX.style.top =
- Math.floor(crossOuterSize / 2) -
- Math.floor(THIS.pointerThickness / 2) -
- THIS.pointerBorderWidth +
- "px";
- p.crossBY.style.top = p.crossBX.style.left = "0";
- // pad cross line Y and X
- p.crossLY.style.position = p.crossLX.style.position = "absolute";
- p.crossLY.style.background = p.crossLX.style.background =
- THIS.pointerColor;
- p.crossLY.style.height = p.crossLX.style.width =
- crossOuterSize - 2 * THIS.pointerBorderWidth + "px";
- p.crossLY.style.width = p.crossLX.style.height =
- THIS.pointerThickness + "px";
- p.crossLY.style.left = p.crossLX.style.top =
- Math.floor(crossOuterSize / 2) -
- Math.floor(THIS.pointerThickness / 2) +
- "px";
- p.crossLY.style.top = p.crossLX.style.left =
- THIS.pointerBorderWidth + "px";
- // slider
- p.sld.style.overflow = "hidden";
- p.sld.style.width = THIS.sliderSize + "px";
- p.sld.style.height = THIS.height + "px";
- // slider gradient
- p.sldGrad.draw(THIS.sliderSize, THIS.height, "#000", "#000");
- // slider border
- p.sldB.style.display = displaySlider ? "block" : "none";
- p.sldB.style.position = "absolute";
- p.sldB.style.left =
- THIS.padding +
- THIS.width +
- 2 * THIS.controlBorderWidth +
- 2 * controlPadding +
- "px";
- p.sldB.style.top = THIS.padding + "px";
- p.sldB.style.border = THIS.controlBorderWidth + "px solid";
- p.sldB.style.borderColor = THIS.controlBorderColor;
- // slider mouse area
- p.sldM.style.display = displaySlider ? "block" : "none";
- p.sldM.style.position = "absolute";
- p.sldM.style.left =
- THIS.padding +
- THIS.width +
- 2 * THIS.controlBorderWidth +
- controlPadding +
- "px";
- p.sldM.style.top = 0 + "px";
- p.sldM.style.width =
- THIS.sliderSize +
- 2 * controlPadding +
- 2 * THIS.controlBorderWidth +
- (displayAlphaSlider
- ? 0
- : Math.max(0, THIS.padding - controlPadding)) + // remaining padding to the right edge
- "px";
- p.sldM.style.height =
- 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px";
- p.sldM.style.cursor = "default";
- jsc.setData(p.sldM, {
- instance: THIS,
- control: "sld",
- });
- // slider pointer inner and outer border
- p.sldPtrIB.style.border = p.sldPtrOB.style.border =
- THIS.pointerBorderWidth + "px solid " + THIS.pointerBorderColor;
- // slider pointer outer border
- p.sldPtrOB.style.position = "absolute";
- p.sldPtrOB.style.left =
- -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + "px";
- p.sldPtrOB.style.top = "0";
- // slider pointer middle border
- p.sldPtrMB.style.border =
- THIS.pointerThickness + "px solid " + THIS.pointerColor;
- // slider pointer spacer
- p.sldPtrS.style.width = THIS.sliderSize + "px";
- p.sldPtrS.style.height = jsc.pub.sliderInnerSpace + "px";
- // alpha slider
- p.asld.style.overflow = "hidden";
- p.asld.style.width = THIS.sliderSize + "px";
- p.asld.style.height = THIS.height + "px";
- // alpha slider gradient
- p.asldGrad.draw(THIS.sliderSize, THIS.height, "#000");
- // alpha slider border
- p.asldB.style.display = displayAlphaSlider ? "block" : "none";
- p.asldB.style.position = "absolute";
- p.asldB.style.left =
- THIS.padding +
- THIS.width +
- 2 * THIS.controlBorderWidth +
- controlPadding +
- (displaySlider
- ? THIS.sliderSize +
- 3 * controlPadding +
- 2 * THIS.controlBorderWidth
- : 0) +
- "px";
- p.asldB.style.top = THIS.padding + "px";
- p.asldB.style.border = THIS.controlBorderWidth + "px solid";
- p.asldB.style.borderColor = THIS.controlBorderColor;
- // alpha slider mouse area
- p.asldM.style.display = displayAlphaSlider ? "block" : "none";
- p.asldM.style.position = "absolute";
- p.asldM.style.left =
- THIS.padding +
- THIS.width +
- 2 * THIS.controlBorderWidth +
- controlPadding +
- (displaySlider
- ? THIS.sliderSize +
- 2 * controlPadding +
- 2 * THIS.controlBorderWidth
- : 0) +
- "px";
- p.asldM.style.top = 0 + "px";
- p.asldM.style.width =
- THIS.sliderSize +
- 2 * controlPadding +
- 2 * THIS.controlBorderWidth +
- Math.max(0, THIS.padding - controlPadding) + // remaining padding to the right edge
- "px";
- p.asldM.style.height =
- 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px";
- p.asldM.style.cursor = "default";
- jsc.setData(p.asldM, {
- instance: THIS,
- control: "asld",
- });
- // alpha slider pointer inner and outer border
- p.asldPtrIB.style.border = p.asldPtrOB.style.border =
- THIS.pointerBorderWidth + "px solid " + THIS.pointerBorderColor;
- // alpha slider pointer outer border
- p.asldPtrOB.style.position = "absolute";
- p.asldPtrOB.style.left =
- -(2 * THIS.pointerBorderWidth + THIS.pointerThickness) + "px";
- p.asldPtrOB.style.top = "0";
- // alpha slider pointer middle border
- p.asldPtrMB.style.border =
- THIS.pointerThickness + "px solid " + THIS.pointerColor;
- // alpha slider pointer spacer
- p.asldPtrS.style.width = THIS.sliderSize + "px";
- p.asldPtrS.style.height = jsc.pub.sliderInnerSpace + "px";
- // palette
- p.pal.className = "jscolor-palette";
- p.pal.style.display = pickerDims.palette.rows ? "block" : "none";
- p.pal.style.left = THIS.padding + "px";
- p.pal.style.top =
- 2 * THIS.controlBorderWidth + 2 * THIS.padding + THIS.height + "px";
- // palette's color samples
- p.pal.innerHTML = "";
- var chessboard = jsc.genColorPreviewCanvas("rgba(0,0,0,0)");
- var si = 0; // color sample's index
- for (var r = 0; r < pickerDims.palette.rows; r++) {
- for (
- var c = 0;
- c < pickerDims.palette.cols && si < THIS._palette.length;
- c++, si++
- ) {
- var sampleColor = THIS._palette[si];
- var sampleCssColor = jsc.rgbaColor.apply(null, sampleColor.rgba);
- var sc = jsc.createEl("div"); // color sample's color
- sc.style.width =
- pickerDims.palette.cellW - 2 * THIS.controlBorderWidth + "px";
- sc.style.height =
- pickerDims.palette.cellH - 2 * THIS.controlBorderWidth + "px";
- sc.style.backgroundColor = sampleCssColor;
- var sw = jsc.createEl("div"); // color sample's wrap
- sw.className = "jscolor-palette-sw";
- sw.style.left =
- (pickerDims.palette.cols <= 1
- ? 0
- : Math.round(
- 10 *
- (c *
- ((pickerDims.contentW - pickerDims.palette.cellW) /
- (pickerDims.palette.cols - 1)))
- ) / 10) + "px";
- sw.style.top =
- r * (pickerDims.palette.cellH + THIS.paletteSpacing) + "px";
- sw.style.border = THIS.controlBorderWidth + "px solid";
- sw.style.borderColor = THIS.controlBorderColor;
- if (sampleColor.rgba[3] !== null && sampleColor.rgba[3] < 1.0) {
- // only create chessboard background if the sample has transparency
- sw.style.backgroundImage =
- "url('" + chessboard.canvas.toDataURL() + "')";
- sw.style.backgroundRepeat = "repeat";
- sw.style.backgroundPosition = "center center";
- }
- jsc.setData(sw, {
- instance: THIS,
- control: "palette-sw",
- color: sampleColor,
- });
- sw.addEventListener("click", jsc.onPaletteSampleClick, false);
- sw.appendChild(sc);
- p.pal.appendChild(sw);
- }
- }
- // the Close button
- function setBtnBorder() {
- var insetColors = THIS.controlBorderColor.split(/\s+/);
- var outsetColor =
- insetColors.length < 2
- ? insetColors[0]
- : insetColors[1] +
- " " +
- insetColors[0] +
- " " +
- insetColors[0] +
- " " +
- insetColors[1];
- p.btn.style.borderColor = outsetColor;
- }
- var btnPadding = 15; // px
- p.btn.className = "jscolor-btn jscolor-btn-close";
- p.btn.style.display = THIS.closeButton ? "block" : "none";
- p.btn.style.left = THIS.padding + "px";
- p.btn.style.bottom = THIS.padding + "px";
- p.btn.style.padding = "0 " + btnPadding + "px";
- p.btn.style.maxWidth =
- pickerDims.contentW -
- 2 * THIS.controlBorderWidth -
- 2 * btnPadding +
- "px";
- p.btn.style.height = THIS.buttonHeight + "px";
- p.btn.style.border = THIS.controlBorderWidth + "px solid";
- setBtnBorder();
- p.btn.style.color = THIS.buttonColor;
- p.btn.onmousedown = function () {
- THIS.hide();
- };
- p.btnT.style.display = "inline";
- p.btnT.style.lineHeight = THIS.buttonHeight + "px";
- p.btnT.innerText = THIS.closeText;
- // reposition the pointers
- redrawPad();
- redrawSld();
- redrawASld();
- // If we are changing the owner without first closing the picker,
- // make sure to first deal with the old owner
- if (jsc.picker.owner && jsc.picker.owner !== THIS) {
- jsc.removeClass(
- jsc.picker.owner.targetElement,
- jsc.pub.activeClassName
- );
- }
- // Set a new picker owner
- jsc.picker.owner = THIS;
- // The redrawPosition() method needs picker.owner to be set, that's why we call it here,
- // after setting the owner
- jsc.redrawPosition();
- if (p.wrap.parentNode !== THIS.container) {
- THIS.container.appendChild(p.wrap);
- }
- jsc.addClass(THIS.targetElement, jsc.pub.activeClassName);
- }
- function redrawPad() {
- // redraw the pad pointer
- var yChannel = jsc.getPadYChannel(THIS);
- var x = Math.round((THIS.channels.h / 360) * (THIS.width - 1));
- var y = Math.round(
- (1 - THIS.channels[yChannel] / 100) * (THIS.height - 1)
- );
- var crossOuterSize =
- 2 * THIS.pointerBorderWidth +
- THIS.pointerThickness +
- 2 * THIS.crossSize;
- var ofs = -Math.floor(crossOuterSize / 2);
- jsc.picker.cross.style.left = x + ofs + "px";
- jsc.picker.cross.style.top = y + ofs + "px";
- // redraw the slider
- switch (jsc.getSliderChannel(THIS)) {
- case "s":
- var rgb1 = jsc.HSV_RGB(THIS.channels.h, 100, THIS.channels.v);
- var rgb2 = jsc.HSV_RGB(THIS.channels.h, 0, THIS.channels.v);
- var color1 =
- "rgb(" +
- Math.round(rgb1[0]) +
- "," +
- Math.round(rgb1[1]) +
- "," +
- Math.round(rgb1[2]) +
- ")";
- var color2 =
- "rgb(" +
- Math.round(rgb2[0]) +
- "," +
- Math.round(rgb2[1]) +
- "," +
- Math.round(rgb2[2]) +
- ")";
- jsc.picker.sldGrad.draw(
- THIS.sliderSize,
- THIS.height,
- color1,
- color2
- );
- break;
- case "v":
- var rgb = jsc.HSV_RGB(THIS.channels.h, THIS.channels.s, 100);
- var color1 =
- "rgb(" +
- Math.round(rgb[0]) +
- "," +
- Math.round(rgb[1]) +
- "," +
- Math.round(rgb[2]) +
- ")";
- var color2 = "#000";
- jsc.picker.sldGrad.draw(
- THIS.sliderSize,
- THIS.height,
- color1,
- color2
- );
- break;
- }
- // redraw the alpha slider
- jsc.picker.asldGrad.draw(
- THIS.sliderSize,
- THIS.height,
- THIS.toHEXString()
- );
- }
- function redrawSld() {
- var sldChannel = jsc.getSliderChannel(THIS);
- if (sldChannel) {
- // redraw the slider pointer
- var y = Math.round(
- (1 - THIS.channels[sldChannel] / 100) * (THIS.height - 1)
- );
- jsc.picker.sldPtrOB.style.top =
- y -
- (2 * THIS.pointerBorderWidth + THIS.pointerThickness) -
- Math.floor(jsc.pub.sliderInnerSpace / 2) +
- "px";
- }
- // redraw the alpha slider
- jsc.picker.asldGrad.draw(
- THIS.sliderSize,
- THIS.height,
- THIS.toHEXString()
- );
- }
- function redrawASld() {
- var y = Math.round((1 - THIS.channels.a) * (THIS.height - 1));
- jsc.picker.asldPtrOB.style.top =
- y -
- (2 * THIS.pointerBorderWidth + THIS.pointerThickness) -
- Math.floor(jsc.pub.sliderInnerSpace / 2) +
- "px";
- }
- function isPickerOwner() {
- return jsc.picker && jsc.picker.owner === THIS;
- }
- function onValueKeyDown(ev) {
- if (jsc.eventKey(ev) === "Enter") {
- if (THIS.valueElement) {
- THIS.processValueInput(THIS.valueElement.value);
- }
- THIS.tryHide();
- }
- }
- function onAlphaKeyDown(ev) {
- if (jsc.eventKey(ev) === "Enter") {
- if (THIS.alphaElement) {
- THIS.processAlphaInput(THIS.alphaElement.value);
- }
- THIS.tryHide();
- }
- }
- function onValueChange(ev) {
- if (jsc.getData(ev, "internal")) {
- return; // skip if the event was internally triggered by jscolor
- }
- var oldVal = THIS.valueElement.value;
- THIS.processValueInput(THIS.valueElement.value); // this might change the value
- jsc.triggerCallback(THIS, "onChange");
- if (THIS.valueElement.value !== oldVal) {
- // value was additionally changed -> 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);
- }
- }
- }
- }
- // let's apply configuration presets
- //
- var presetsArr = [];
- if (opts.preset) {
- if (typeof opts.preset === "string") {
- presetsArr = opts.preset.split(/\s+/);
- } else if (Array.isArray(opts.preset)) {
- presetsArr = opts.preset.slice(); // slice() to clone
- } else {
- console.warn("Unrecognized preset value");
- }
- }
- // always use the 'default' preset. If it's not listed, append it to the end.
- if (presetsArr.indexOf("default") === -1) {
- presetsArr.push("default");
- }
- // let's apply the presets in reverse order, so that should there be any overlapping options,
- // the formerly listed preset will override the latter
- for (var i = presetsArr.length - 1; 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 <button> has no type
- // and thus defaults to 'submit' and would submit the form when clicked
- this.targetElement.type = "button";
- }
- if (jsc.isButtonEmpty(this.targetElement)) {
- // empty button
- // it is important to clear element's contents first.
- // if we're re-instantiating color pickers on DOM that has been modified by changing page's innerHTML,
- // we would keep adding more non-breaking spaces to element's content (because element's contents survive
- // innerHTML changes, but picker instances don't)
- jsc.removeChildren(this.targetElement);
- // let's insert a non-breaking space
- this.targetElement.appendChild(
- window.document.createTextNode("\xa0")
- );
- // set min-width = previewSize, if not already greater
- var compStyle = jsc.getCompStyle(this.targetElement);
- var currMinWidth = parseFloat(compStyle["min-width"]) || 0;
- if (currMinWidth < this.previewSize) {
- jsc.setStyle(
- this.targetElement,
- {
- "min-width": this.previewSize + "px",
- },
- this.forceStyle
- );
- }
- }
- }
- // Determine the value element
- if (this.valueElement === undefined) {
- if (jsc.isTextInput(this.targetElement)) {
- // for text inputs, default valueElement is targetElement
- this.valueElement = this.targetElement;
- } else {
- // leave it undefined
- }
- } else if (this.valueElement === null) {
- // explicitly set to null
- // leave it null
- } else {
- // explicitly set to custom element
- this.valueElement = jsc.node(this.valueElement);
- }
- // Determine the alpha element
- if (this.alphaElement) {
- this.alphaElement = jsc.node(this.alphaElement);
- }
- // Determine the preview element
- if (this.previewElement === undefined) {
- this.previewElement = this.targetElement; // default previewElement is targetElement
- } else if (this.previewElement === null) {
- // explicitly set to null
- // leave it null
- } else {
- // explicitly set to custom element
- this.previewElement = jsc.node(this.previewElement);
- }
- // valueElement
- if (this.valueElement && jsc.isTextInput(this.valueElement)) {
- // If the value element has onInput event already set, we need to detach it and attach AFTER our listener.
- // otherwise the picker instance would still contain the old color when accessed from the onInput handler.
- var valueElementOrigEvents = {
- onInput: this.valueElement.oninput,
- };
- this.valueElement.oninput = null;
- this.valueElement.addEventListener("keydown", onValueKeyDown, false);
- this.valueElement.addEventListener("change", onValueChange, false);
- this.valueElement.addEventListener("input", onValueInput, false);
- // the original event listener must be attached AFTER our handler (to let it first set picker's color)
- if (valueElementOrigEvents.onInput) {
- this.valueElement.addEventListener(
- "input",
- valueElementOrigEvents.onInput,
- false
- );
- }
- this.valueElement.setAttribute("autocomplete", "off");
- this.valueElement.setAttribute("autocorrect", "off");
- this.valueElement.setAttribute("autocapitalize", "off");
- this.valueElement.setAttribute("spellcheck", false);
- }
- // alphaElement
- if (this.alphaElement && jsc.isTextInput(this.alphaElement)) {
- this.alphaElement.addEventListener("keydown", onAlphaKeyDown, false);
- this.alphaElement.addEventListener("change", onAlphaChange, false);
- this.alphaElement.addEventListener("input", onAlphaInput, false);
- this.alphaElement.setAttribute("autocomplete", "off");
- this.alphaElement.setAttribute("autocorrect", "off");
- this.alphaElement.setAttribute("autocapitalize", "off");
- this.alphaElement.setAttribute("spellcheck", false);
- }
- // determine initial color value
- //
- var initValue = "FFFFFF";
- if (this.value !== undefined) {
- initValue = this.value; // get initial color from the 'value' property
- } else if (this.valueElement && this.valueElement.value !== undefined) {
- initValue = this.valueElement.value; // get initial color from valueElement's value
- }
- // determine initial alpha value
- //
- var initAlpha = undefined;
- if (this.alpha !== undefined) {
- initAlpha = "" + this.alpha; // get initial alpha value from the 'alpha' property
- } else if (this.alphaElement && this.alphaElement.value !== undefined) {
- initAlpha = this.alphaElement.value; // get initial color from alphaElement's value
- }
- // determine current format based on the initial color value
- //
- this._currentFormat = null;
- if (["auto", "any"].indexOf(this.format.toLowerCase()) > -1) {
- // format is 'auto' or 'any' -> let's auto-detect current format
- var color = jsc.parseColorString(initValue);
- this._currentFormat = color ? color.format : "hex";
- } else {
- // format is specified
- this._currentFormat = this.format.toLowerCase();
- }
- // let's parse the initial color value and expose color's preview
- this.processValueInput(initValue);
- // let's also parse and expose the initial alpha value, if any
- //
- // Note: If the initial color value contains alpha value in it (e.g. in rgba format),
- // this will overwrite it. So we should only process alpha input if there was initial
- // alpha explicitly set, otherwise we could needlessly lose initial value's alpha
- if (initAlpha !== undefined) {
- this.processAlphaInput(initAlpha);
- }
- if (this.random) {
- // randomize the initial color value
- this.randomize.apply(
- this,
- Array.isArray(this.random) ? this.random : []
- );
- }
- },
- };
- //================================
- // Public properties and methods
- //================================
- //
- // These will be publicly available via jscolor.<name> and JSColor.<name>
- //
- // class that will be set to elements having jscolor installed on them
- jsc.pub.className = "jscolor";
- // class that will be set to elements having jscolor active on them
- jsc.pub.activeClassName = "jscolor-active";
- // whether to try to parse the options string by evaluating it using 'new Function()'
- // in case it could not be parsed with JSON.parse()
- jsc.pub.looseJSON = true;
- // presets
- jsc.pub.presets = {};
- // built-in presets
- jsc.pub.presets["default"] = {}; // baseline for customization
- jsc.pub.presets["light"] = {
- // default color scheme
- backgroundColor: "rgba(255,255,255,1)",
- controlBorderColor: "rgba(187,187,187,1)",
- buttonColor: "rgba(0,0,0,1)",
- };
- jsc.pub.presets["dark"] = {
- backgroundColor: "rgba(51,51,51,1)",
- controlBorderColor: "rgba(153,153,153,1)",
- buttonColor: "rgba(240,240,240,1)",
- };
- jsc.pub.presets["small"] = {
- width: 101,
- height: 101,
- padding: 10,
- sliderSize: 14,
- paletteCols: 8,
- };
- jsc.pub.presets["medium"] = {
- width: 181,
- height: 101,
- padding: 12,
- sliderSize: 16,
- paletteCols: 10,
- }; // default size
- jsc.pub.presets["large"] = {
- width: 271,
- height: 151,
- padding: 12,
- sliderSize: 24,
- paletteCols: 15,
- };
- jsc.pub.presets["thin"] = {
- borderWidth: 1,
- controlBorderWidth: 1,
- pointerBorderWidth: 1,
- }; // default thickness
- jsc.pub.presets["thick"] = {
- borderWidth: 2,
- controlBorderWidth: 2,
- pointerBorderWidth: 2,
- };
- // size of space in the sliders
- jsc.pub.sliderInnerSpace = 3; // px
- // transparency chessboard
- jsc.pub.chessboardSize = 8; // px
- jsc.pub.chessboardColor1 = "#666666";
- jsc.pub.chessboardColor2 = "#999999";
- // preview separator
- jsc.pub.previewSeparator = [
- "rgba(255,255,255,.65)",
- "rgba(128,128,128,.65)",
- ];
- // Initializes jscolor
- jsc.pub.init = function () {
- if (jsc.initialized) {
- return;
- }
- // attach some necessary handlers
- window.document.addEventListener(
- "mousedown",
- jsc.onDocumentMouseDown,
- false
- );
- window.document.addEventListener("keyup", jsc.onDocumentKeyUp, false);
- window.addEventListener("resize", jsc.onWindowResize, false);
- window.addEventListener("scroll", jsc.onWindowScroll, false);
- // append default CSS to HEAD
- jsc.appendDefaultCss();
- // install jscolor on current DOM
- jsc.pub.install();
- jsc.initialized = true;
- // call functions waiting in the queue
- while (jsc.readyQueue.length) {
- var func = jsc.readyQueue.shift();
- func();
- }
- };
- // Installs jscolor on current DOM tree
- jsc.pub.install = function (rootNode) {
- var success = true;
- try {
- jsc.installBySelector("[data-jscolor]", rootNode);
- } catch (e) {
- success = false;
- console.warn(e);
- }
- // for backward compatibility with DEPRECATED installation using class name
- if (jsc.pub.lookupClass) {
- try {
- jsc.installBySelector(
- "input." +
- jsc.pub.lookupClass +
- ", " +
- "button." +
- jsc.pub.lookupClass,
- rootNode
- );
- } catch (e) {}
- }
- return success;
- };
- // Registers function to be called as soon as jscolor is initialized (or immediately, if it already is).
- //
- jsc.pub.ready = function (func) {
- if (typeof func !== "function") {
- console.warn("Passed value is not a function");
- return false;
- }
- if (jsc.initialized) {
- func();
- } else {
- jsc.readyQueue.push(func);
- }
- return true;
- };
- // Triggers given input event(s) (e.g. 'input' or 'change') on all color pickers.
- //
- // It is possible to specify multiple events separated with a space.
- // If called before jscolor is initialized, then the events will be triggered after initialization.
- //
- jsc.pub.trigger = function (eventNames) {
- var triggerNow = function () {
- jsc.triggerGlobal(eventNames);
- };
- if (jsc.initialized) {
- triggerNow();
- } else {
- jsc.pub.ready(triggerNow);
- }
- };
- // Hides current color picker box
- jsc.pub.hide = function () {
- if (jsc.picker && jsc.picker.owner) {
- jsc.picker.owner.hide();
- }
- };
- // Returns a data URL of a gray chessboard image that indicates transparency
- jsc.pub.chessboard = function (color) {
- if (!color) {
- color = "rgba(0,0,0,0)";
- }
- var preview = jsc.genColorPreviewCanvas(color);
- return preview.canvas.toDataURL();
- };
- // Returns a data URL of a gray chessboard image that indicates transparency
- jsc.pub.background = function (color) {
- var backgrounds = [];
- // CSS gradient for background color preview
- backgrounds.push(jsc.genColorPreviewGradient(color));
- // data URL of generated PNG image with a gray transparency chessboard
- var preview = jsc.genColorPreviewCanvas();
- backgrounds.push(
- [
- "url('" + preview.canvas.toDataURL() + "')",
- "left top",
- "repeat",
- ].join(" ")
- );
- return backgrounds.join(", ");
- };
- //
- // DEPRECATED properties and methods
- //
- // DEPRECATED. Use jscolor.presets.default instead.
- //
- // Custom default options for all color pickers, e.g. { hash: true, width: 300 }
- jsc.pub.options = {};
- // DEPRECATED. Use data-jscolor attribute instead, which installs jscolor on given element.
- //
- // By default, we'll search for all elements with class="jscolor" and install a color picker on them.
- //
- // You can change what class name will be looked for by setting the property jscolor.lookupClass
- // anywhere in your HTML document. To completely disable the automatic lookup, set it to null.
- //
- jsc.pub.lookupClass = "jscolor";
- // DEPRECATED. Use data-jscolor attribute instead, which installs jscolor on given element.
- //
- // Install jscolor on all elements that have the specified class name
- jsc.pub.installByClassName = function () {
- console.error(
- 'jscolor.installByClassName() is DEPRECATED. Use data-jscolor="" attribute instead of a class name.' +
- jsc.docsRef
- );
- return false;
- };
- jsc.register();
- return jsc.pub;
- })(); // END jscolor
- if (typeof window.jscolor === "undefined") {
- window.jscolor = window.JSColor = jscolor;
- }
- // END jscolor code
- return jscolor;
- }); // END factory