/* * Copyright (C) 2014 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. */ WebInspector.Gradient = class Gradient { constructor(type, stops) { this.type = type; this.stops = stops; } // Static static fromString(cssString) { var type; var openingParenthesisIndex = cssString.indexOf("("); var typeString = cssString.substring(0, openingParenthesisIndex); if (typeString.indexOf(WebInspector.Gradient.Types.Linear) !== -1) type = WebInspector.Gradient.Types.Linear; else if (typeString.indexOf(WebInspector.Gradient.Types.Radial) !== -1) type = WebInspector.Gradient.Types.Radial; 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++; } var gradient; if (type === WebInspector.Gradient.Types.Linear) gradient = WebInspector.LinearGradient.fromComponents(components); else gradient = WebInspector.RadialGradient.fromComponents(components); 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 = WebInspector.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 * 10000) / 100 + "%"; return str; }).join(", "); } // Public copy() { // Implemented by subclasses. } toString() { // Implemented by subclasses. } }; WebInspector.Gradient.Types = { Linear: "linear-gradient", Radial: "radial-gradient" }; WebInspector.LinearGradient = class LinearGradient extends WebInspector.Gradient { constructor(angle, stops) { super(WebInspector.Gradient.Types.Linear, stops); this.angle = angle; } // Static static fromComponents(components) { var angle = 180; if (components[0].length === 1 && components[0][0].substr(-3) === "deg") { angle = (parseFloat(components[0][0]) % 360 + 360) % 360; components.shift(); } else if (components[0][0] === "to") { components[0].shift(); switch (components[0].sort().join(" ")) { case "top": angle = 0; break; case "right top": angle = 45; break; case "right": angle = 90; break; case "bottom right": angle = 135; break; case "bottom": angle = 180; break; case "bottom left": angle = 225; break; case "left": angle = 270; break; case "left top": angle = 315; break; default: return null; } components.shift(); } else if (components[0].length !== 1 && !WebInspector.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; } var stops = WebInspector.Gradient.stopsWithComponents(components); if (!stops) return null; return new WebInspector.LinearGradient(angle, stops); } // Public copy() { return new WebInspector.LinearGradient(this.angle, this.stops.concat()); } toString() { var str = ""; if (this.angle === 0) str += "to top"; else if (this.angle === 45) str += "to top right"; else if (this.angle === 90) str += "to right"; else if (this.angle === 135) str += "to bottom right"; else if (this.angle === 225) str += "to bottom left"; else if (this.angle === 270) str += "to left"; else if (this.angle === 315) str += "to top left"; else if (this.angle !== 180) str += this.angle + "deg"; if (str !== "") str += ", "; str += this.stringFromStops(this.stops); return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")"; } }; WebInspector.RadialGradient = class RadialGradient extends WebInspector.Gradient { constructor(sizing, stops) { super(WebInspector.Gradient.Types.Radial, stops); this.sizing = sizing; } // Static static fromComponents(components) { var sizing = !WebInspector.Color.fromString(components[0].join(" ")) ? components.shift().join(" ") : ""; var stops = WebInspector.Gradient.stopsWithComponents(components); if (!stops) return null; return new WebInspector.RadialGradient(sizing, stops); } // Public copy() { return new WebInspector.RadialGradient(this.sizing, this.stops.concat()); } toString() { var str = this.sizing; if (str !== "") str += ", "; str += this.stringFromStops(this.stops); return (this.repeats ? "repeating-" : "") + this.type + "(" + str + ")"; } };