Added SDK
This commit is contained in:
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* Copyright (C) 2014, 2021 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.Gradient = class Gradient
|
||||
{
|
||||
constructor(type, stops)
|
||||
{
|
||||
this.type = type;
|
||||
this.stops = stops;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static angleFromString(string)
|
||||
{
|
||||
let match = string.match(/([-\d\.]+)(\w+)/);
|
||||
if (!match || !Object.values(WI.Gradient.AngleUnits).includes(match[2]))
|
||||
return null;
|
||||
|
||||
return {value: parseFloat(match[1]), units: match[2]};
|
||||
}
|
||||
|
||||
static fromString(cssString)
|
||||
{
|
||||
var type;
|
||||
var openingParenthesisIndex = cssString.indexOf("(");
|
||||
var typeString = cssString.substring(0, openingParenthesisIndex);
|
||||
if (typeString.includes(WI.Gradient.Types.Linear))
|
||||
type = WI.Gradient.Types.Linear;
|
||||
else if (typeString.includes(WI.Gradient.Types.Radial))
|
||||
type = WI.Gradient.Types.Radial;
|
||||
else if (typeString.includes(WI.Gradient.Types.Conic))
|
||||
type = WI.Gradient.Types.Conic;
|
||||
else
|
||||
return null;
|
||||
|
||||
var components = [];
|
||||
var currentParams = [];
|
||||
var currentParam = "";
|
||||
var openParentheses = 0;
|
||||
var ch = openingParenthesisIndex + 1;
|
||||
var c = null;
|
||||
while (c = cssString[ch]) {
|
||||
if (c === "(")
|
||||
openParentheses++;
|
||||
if (c === ")")
|
||||
openParentheses--;
|
||||
|
||||
var isComma = c === ",";
|
||||
var isSpace = /\s/.test(c);
|
||||
|
||||
if (openParentheses === 0) {
|
||||
if (isSpace) {
|
||||
if (currentParam !== "")
|
||||
currentParams.push(currentParam);
|
||||
currentParam = "";
|
||||
} else if (isComma) {
|
||||
currentParams.push(currentParam);
|
||||
components.push(currentParams);
|
||||
currentParams = [];
|
||||
currentParam = "";
|
||||
}
|
||||
}
|
||||
|
||||
if (openParentheses === -1) {
|
||||
currentParams.push(currentParam);
|
||||
components.push(currentParams);
|
||||
break;
|
||||
}
|
||||
|
||||
if (openParentheses > 0 || (!isComma && !isSpace))
|
||||
currentParam += c;
|
||||
|
||||
ch++;
|
||||
}
|
||||
|
||||
if (openParentheses !== -1)
|
||||
return null;
|
||||
|
||||
let gradient = null;
|
||||
switch (type) {
|
||||
case WI.Gradient.Types.Linear:
|
||||
gradient = WI.LinearGradient.fromComponents(components);
|
||||
break;
|
||||
|
||||
case WI.Gradient.Types.Radial:
|
||||
gradient = WI.RadialGradient.fromComponents(components);
|
||||
break;
|
||||
|
||||
case WI.Gradient.Types.Conic:
|
||||
gradient = WI.ConicGradient.fromComponents(components);
|
||||
break;
|
||||
}
|
||||
|
||||
if (gradient)
|
||||
gradient.repeats = typeString.startsWith("repeating");
|
||||
|
||||
return gradient;
|
||||
}
|
||||
|
||||
static stopsWithComponents(components)
|
||||
{
|
||||
// FIXME: handle lengths.
|
||||
var stops = components.map(function(component) {
|
||||
while (component.length) {
|
||||
var color = WI.Color.fromString(component.shift());
|
||||
if (!color)
|
||||
continue;
|
||||
|
||||
var stop = {color};
|
||||
if (component.length && component[0].substr(-1) === "%")
|
||||
stop.offset = parseFloat(component.shift()) / 100;
|
||||
return stop;
|
||||
}
|
||||
});
|
||||
|
||||
if (!stops.length)
|
||||
return null;
|
||||
|
||||
for (var i = 0, count = stops.length; i < count; ++i) {
|
||||
var stop = stops[i];
|
||||
|
||||
// If one of the stops failed to parse, then this is not a valid
|
||||
// set of components for a gradient. So the whole thing is invalid.
|
||||
if (!stop)
|
||||
return null;
|
||||
|
||||
if (!stop.offset)
|
||||
stop.offset = i / (count - 1);
|
||||
}
|
||||
|
||||
return stops;
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
stringFromStops(stops)
|
||||
{
|
||||
var count = stops.length - 1;
|
||||
return stops.map(function(stop, index) {
|
||||
var str = stop.color;
|
||||
if (stop.offset !== index / count)
|
||||
str += " " + Math.round(stop.offset * 10_000) / 100 + "%";
|
||||
return str;
|
||||
}).join(", ");
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get angleValue()
|
||||
{
|
||||
return this._angle.value.maxDecimals(2);
|
||||
}
|
||||
|
||||
set angleValue(value)
|
||||
{
|
||||
this._angle.value = value;
|
||||
}
|
||||
|
||||
get angleUnits()
|
||||
{
|
||||
return this._angle.units;
|
||||
}
|
||||
|
||||
set angleUnits(units)
|
||||
{
|
||||
if (units === this._angle.units)
|
||||
return;
|
||||
|
||||
this._angle.value = this._angleValueForUnits(units);
|
||||
this._angle.units = units;
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
// Implemented by subclasses.
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_angleValueForUnits(units)
|
||||
{
|
||||
if (units === this._angle.units)
|
||||
return this._angle.value;
|
||||
|
||||
let deg = 0;
|
||||
|
||||
switch (this._angle.units) {
|
||||
case WI.Gradient.AngleUnits.DEG:
|
||||
deg = this._angle.value;
|
||||
break;
|
||||
|
||||
case WI.Gradient.AngleUnits.RAD:
|
||||
deg = this._angle.value * 180 / Math.PI;
|
||||
break;
|
||||
|
||||
case WI.Gradient.AngleUnits.GRAD:
|
||||
deg = this._angle.value / 400 * 360;
|
||||
break;
|
||||
|
||||
case WI.Gradient.AngleUnits.TURN:
|
||||
deg = this._angle.value * 360;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (units) {
|
||||
case WI.Gradient.AngleUnits.DEG:
|
||||
return deg;
|
||||
|
||||
case WI.Gradient.AngleUnits.RAD:
|
||||
return deg * Math.PI / 180;
|
||||
|
||||
case WI.Gradient.AngleUnits.GRAD:
|
||||
return deg / 360 * 400;
|
||||
|
||||
case WI.Gradient.AngleUnits.TURN:
|
||||
return deg / 360;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
WI.Gradient.Types = {
|
||||
Linear: "linear-gradient",
|
||||
Radial: "radial-gradient",
|
||||
Conic: "conic-gradient",
|
||||
};
|
||||
|
||||
WI.Gradient.AngleUnits = {
|
||||
DEG: "deg",
|
||||
RAD: "rad",
|
||||
GRAD: "grad",
|
||||
TURN: "turn",
|
||||
};
|
||||
|
||||
WI.LinearGradient = class LinearGradient extends WI.Gradient
|
||||
{
|
||||
constructor(angle, stops)
|
||||
{
|
||||
super(WI.Gradient.Types.Linear, stops);
|
||||
this._angle = angle;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromComponents(components)
|
||||
{
|
||||
let angle = {value: 180, units: WI.Gradient.AngleUnits.DEG};
|
||||
|
||||
if (components[0].length === 1 && !WI.Color.fromString(components[0][0])) {
|
||||
angle = WI.Gradient.angleFromString(components[0][0]);
|
||||
|
||||
if (!angle)
|
||||
return null;
|
||||
|
||||
components.shift();
|
||||
} else if (components[0][0] === "to") {
|
||||
components[0].shift();
|
||||
switch (components[0].sort().join(" ")) {
|
||||
case "top":
|
||||
angle.value = 0;
|
||||
break;
|
||||
case "right top":
|
||||
angle.value = 45;
|
||||
break;
|
||||
case "right":
|
||||
angle.value = 90;
|
||||
break;
|
||||
case "bottom right":
|
||||
angle.value = 135;
|
||||
break;
|
||||
case "bottom":
|
||||
angle.value = 180;
|
||||
break;
|
||||
case "bottom left":
|
||||
angle.value = 225;
|
||||
break;
|
||||
case "left":
|
||||
angle.value = 270;
|
||||
break;
|
||||
case "left top":
|
||||
angle.value = 315;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
components.shift();
|
||||
} else if (components[0].length !== 1 && !WI.Color.fromString(components[0][0])) {
|
||||
// If the first component is not a color, then we're dealing with a
|
||||
// legacy linear gradient format that we don't support.
|
||||
return null;
|
||||
}
|
||||
|
||||
let stops = WI.Gradient.stopsWithComponents(components);
|
||||
if (!stops)
|
||||
return null;
|
||||
|
||||
return new WI.LinearGradient(angle, stops);
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.LinearGradient(this._angle, this.stops.concat());
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
let str = "";
|
||||
|
||||
let deg = this._angleValueForUnits(WI.LinearGradient.AngleUnits.DEG);
|
||||
if (deg === 0)
|
||||
str += "to top";
|
||||
else if (deg === 45)
|
||||
str += "to top right";
|
||||
else if (deg === 90)
|
||||
str += "to right";
|
||||
else if (deg === 135)
|
||||
str += "to bottom right";
|
||||
else if (deg === 225)
|
||||
str += "to bottom left";
|
||||
else if (deg === 270)
|
||||
str += "to left";
|
||||
else if (deg === 315)
|
||||
str += "to top left";
|
||||
else if (deg !== 180)
|
||||
str += this.angleValue + this.angleUnits;
|
||||
|
||||
if (str)
|
||||
str += ", ";
|
||||
|
||||
str += this.stringFromStops(this.stops);
|
||||
|
||||
return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
|
||||
}
|
||||
};
|
||||
|
||||
WI.RadialGradient = class RadialGradient extends WI.Gradient
|
||||
{
|
||||
constructor(sizing, stops)
|
||||
{
|
||||
super(WI.Gradient.Types.Radial, stops);
|
||||
this.sizing = sizing;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromComponents(components)
|
||||
{
|
||||
let sizing = !WI.Color.fromString(components[0].join(" ")) ? components.shift().join(" ") : "";
|
||||
|
||||
let stops = WI.Gradient.stopsWithComponents(components);
|
||||
if (!stops)
|
||||
return null;
|
||||
|
||||
return new WI.RadialGradient(sizing, stops);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
get angleValue()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
set angleValue(value)
|
||||
{
|
||||
console.assert(false, "CSS radial gradients do not have an angle");
|
||||
}
|
||||
|
||||
get angleUnits()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
set angleUnits(units)
|
||||
{
|
||||
console.assert(false, "CSS radial gradients do not have an angle");
|
||||
}
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.RadialGradient(this.sizing, this.stops.concat());
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
let str = this.sizing;
|
||||
|
||||
if (str)
|
||||
str += ", ";
|
||||
|
||||
str += this.stringFromStops(this.stops);
|
||||
|
||||
return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
|
||||
}
|
||||
};
|
||||
|
||||
WI.ConicGradient = class ConicGradient extends WI.Gradient
|
||||
{
|
||||
constructor(angle, position, stops)
|
||||
{
|
||||
super(WI.Gradient.Types.Conic, stops);
|
||||
|
||||
this._angle = angle;
|
||||
this._position = position;
|
||||
}
|
||||
|
||||
// Static
|
||||
|
||||
static fromComponents(components)
|
||||
{
|
||||
let angle = {value: 0, units: WI.Gradient.AngleUnits.DEG};
|
||||
let position = null;
|
||||
let hasCustomAngleOrPosition = false;
|
||||
|
||||
if (components[0][0] == "from") {
|
||||
components[0].shift();
|
||||
angle = WI.Gradient.angleFromString(components[0][0]);
|
||||
if (!angle)
|
||||
return null;
|
||||
components[0].shift();
|
||||
hasCustomAngleOrPosition = true;
|
||||
}
|
||||
if (components[0][0] == "at") {
|
||||
components[0].shift();
|
||||
// FIXME: <https://webkit.org/b/234643> (Web Inspector: allow editing positions in gradient editor)
|
||||
if (components[0].length <= 0)
|
||||
return null;
|
||||
position = components[0].join(" ");
|
||||
hasCustomAngleOrPosition = true;
|
||||
}
|
||||
if (hasCustomAngleOrPosition)
|
||||
components.shift();
|
||||
|
||||
let stops = WI.Gradient.stopsWithComponents(components);
|
||||
if (!stops)
|
||||
return null;
|
||||
|
||||
return new WI.ConicGradient(angle, position, stops);
|
||||
}
|
||||
|
||||
// Public
|
||||
|
||||
copy()
|
||||
{
|
||||
return new WI.ConicGradient(this._angle, this._position, this.stops.concat());
|
||||
}
|
||||
|
||||
toString()
|
||||
{
|
||||
let str = "";
|
||||
|
||||
if (this._angle.value)
|
||||
str += `from ${this._angle.value}${this._angle.units}`;
|
||||
|
||||
if (this._position) {
|
||||
if (str)
|
||||
str += " ";
|
||||
str += `at ${this._position}`;
|
||||
}
|
||||
|
||||
if (str)
|
||||
str += ", ";
|
||||
|
||||
str += this.stringFromStops(this.stops);
|
||||
|
||||
return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")";
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user