Skip to main content
Go to Latest
File
import { parser } from "../deps.js";import { range } from "../deps.js";import OldValue from "../old-value.js";import Value from "../value.js";import * as utils from "../utils.js";
let IS_DIRECTION = /top|left|right|bottom/gi;
class Gradient extends Value { /** * Change degrees for webkit prefix */ replace(string, prefix) { let ast = parser(string); for (let node of ast.nodes) { if (node.type === "function" && node.value === this.name) { node.nodes = this.newDirection(node.nodes); node.nodes = this.normalize(node.nodes); if (prefix === "-webkit- old") { let changes = this.oldWebkit(node); if (!changes) { return false; } } else { node.nodes = this.convertDirection(node.nodes); node.value = prefix + node.value; } } } return ast.toString(); }
/** * Replace first token */ replaceFirst(params, ...words) { let prefix = words.map((i) => { if (i === " ") { return { type: "space", value: i }; } return { type: "word", value: i }; }); return prefix.concat(params.slice(1)); }
/** * Convert angle unit to deg */ normalizeUnit(str, full) { let num = parseFloat(str); let deg = (num / full) * 360; return `${deg}deg`; }
/** * Normalize angle */ normalize(nodes) { if (!nodes[0]) return nodes;
if (/-?\d+(.\d+)?grad/.test(nodes[0].value)) { nodes[0].value = this.normalizeUnit(nodes[0].value, 400); } else if (/-?\d+(.\d+)?rad/.test(nodes[0].value)) { nodes[0].value = this.normalizeUnit(nodes[0].value, 2 * Math.PI); } else if (/-?\d+(.\d+)?turn/.test(nodes[0].value)) { nodes[0].value = this.normalizeUnit(nodes[0].value, 1); } else if (nodes[0].value.includes("deg")) { let num = parseFloat(nodes[0].value); num = range.wrap(0, 360, num); nodes[0].value = `${num}deg`; }
if (nodes[0].value === "0deg") { nodes = this.replaceFirst(nodes, "to", " ", "top"); } else if (nodes[0].value === "90deg") { nodes = this.replaceFirst(nodes, "to", " ", "right"); } else if (nodes[0].value === "180deg") { nodes = this.replaceFirst(nodes, "to", " ", "bottom"); } else if (nodes[0].value === "270deg") { nodes = this.replaceFirst(nodes, "to", " ", "left"); }
return nodes; }
/** * Replace old direction to new */ newDirection(params) { if (params[0].value === "to") { return params; } IS_DIRECTION.lastIndex = 0; // reset search index of global regexp if (!IS_DIRECTION.test(params[0].value)) { return params; }
params.unshift( { type: "word", value: "to", }, { type: "space", value: " ", }, );
for (let i = 2; i < params.length; i++) { if (params[i].type === "div") { break; } if (params[i].type === "word") { params[i].value = this.revertDirection(params[i].value); } }
return params; }
/** * Look for at word */ isRadial(params) { let state = "before"; for (let param of params) { if (state === "before" && param.type === "space") { state = "at"; } else if (state === "at" && param.value === "at") { state = "after"; } else if (state === "after" && param.type === "space") { return true; } else if (param.type === "div") { break; } else { state = "before"; } } return false; }
/** * Change new direction to old */ convertDirection(params) { if (params.length > 0) { if (params[0].value === "to") { this.fixDirection(params); } else if (params[0].value.includes("deg")) { this.fixAngle(params); } else if (this.isRadial(params)) { this.fixRadial(params); } } return params; }
/** * Replace `to top left` to `bottom right` */ fixDirection(params) { params.splice(0, 2);
for (let param of params) { if (param.type === "div") { break; } if (param.type === "word") { param.value = this.revertDirection(param.value); } } }
/** * Add 90 degrees */ fixAngle(params) { let first = params[0].value; first = parseFloat(first); first = Math.abs(450 - first) % 360; first = this.roundFloat(first, 3); params[0].value = `${first}deg`; }
/** * Fix radial direction syntax */ fixRadial(params) { let first = []; let second = []; let a, b, c, i, next;
for (i = 0; i < params.length - 2; i++) { a = params[i]; b = params[i + 1]; c = params[i + 2]; if (a.type === "space" && b.value === "at" && c.type === "space") { next = i + 3; break; } else { first.push(a); } }
let div; for (i = next; i < params.length; i++) { if (params[i].type === "div") { div = params[i]; break; } else { second.push(params[i]); } }
params.splice(0, i, ...second, div, ...first); }
revertDirection(word) { return Gradient.directions[word.toLowerCase()] || word; }
/** * Round float and save digits under dot */ roundFloat(float, digits) { return parseFloat(float.toFixed(digits)); }
/** * Convert to old webkit syntax */ oldWebkit(node) { let { nodes } = node; let string = parser.stringify(node.nodes);
if (this.name !== "linear-gradient") { return false; } if (nodes[0] && nodes[0].value.includes("deg")) { return false; } if ( string.includes("px") || string.includes("-corner") || string.includes("-side") ) { return false; }
let params = [[]]; for (let i of nodes) { params[params.length - 1].push(i); if (i.type === "div" && i.value === ",") { params.push([]); } }
this.oldDirection(params); this.colorStops(params);
node.nodes = []; for (let param of params) { node.nodes = node.nodes.concat(param); }
node.nodes.unshift( { type: "word", value: "linear" }, this.cloneDiv(node.nodes), ); node.value = "-webkit-gradient";
return true; }
/** * Change direction syntax to old webkit */ oldDirection(params) { let div = this.cloneDiv(params[0]);
if (params[0][0].value !== "to") { return params.unshift([ { type: "word", value: Gradient.oldDirections.bottom }, div, ]); } else { let words = []; for (let node of params[0].slice(2)) { if (node.type === "word") { words.push(node.value.toLowerCase()); } }
words = words.join(" "); let old = Gradient.oldDirections[words] || words;
params[0] = [{ type: "word", value: old }, div]; return params[0]; } }
/** * Get div token from exists parameters */ cloneDiv(params) { for (let i of params) { if (i.type === "div" && i.value === ",") { return i; } } return { type: "div", value: ",", after: " " }; }
/** * Change colors syntax to old webkit */ colorStops(params) { let result = []; for (let i = 0; i < params.length; i++) { let pos; let param = params[i]; let item; if (i === 0) { continue; }
let color = parser.stringify(param[0]); if (param[1] && param[1].type === "word") { pos = param[1].value; } else if (param[2] && param[2].type === "word") { pos = param[2].value; }
let stop; if (i === 1 && (!pos || pos === "0%")) { stop = `from(${color})`; } else if (i === params.length - 1 && (!pos || pos === "100%")) { stop = `to(${color})`; } else if (pos) { stop = `color-stop(${pos}, ${color})`; } else { stop = `color-stop(${color})`; }
let div = param[param.length - 1]; params[i] = [{ type: "word", value: stop }]; if (div.type === "div" && div.value === ",") { item = params[i].push(div); } result.push(item); } return result; }
/** * Remove old WebKit gradient too */ old(prefix) { if (prefix === "-webkit-") { let type = this.name === "linear-gradient" ? "linear" : "radial"; let string = "-gradient"; let regexp = utils.regexp( `-webkit-(${type}-gradient|gradient\\(\\s*${type})`, false, );
return new OldValue(this.name, prefix + this.name, string, regexp); } else { return super.old(prefix); } }
/** * Do not add non-webkit prefixes for list-style and object */ add(decl, prefix) { let p = decl.prop; if (p.includes("mask")) { if (prefix === "-webkit-" || prefix === "-webkit- old") { return super.add(decl, prefix); } } else if ( p === "list-style" || p === "list-style-image" || p === "content" ) { if (prefix === "-webkit-" || prefix === "-webkit- old") { return super.add(decl, prefix); } } else { return super.add(decl, prefix); } return undefined; }}
Gradient.names = [ "linear-gradient", "repeating-linear-gradient", "radial-gradient", "repeating-radial-gradient",];
Gradient.directions = { top: "bottom", left: "right", bottom: "top", right: "left",};
// Direction to replaceGradient.oldDirections = { "top": "left bottom, left top", "left": "right top, left top", "bottom": "left top, left bottom", "right": "left top, right top",
"top right": "left bottom, right top", "top left": "right bottom, left top", "right top": "left bottom, right top", "right bottom": "left top, right bottom", "bottom right": "left top, right bottom", "bottom left": "right top, left bottom", "left top": "right bottom, left top", "left bottom": "right top, left bottom",};
export default Gradient;