You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
648 lines
15 KiB
648 lines
15 KiB
/**
|
|
* jQuery asGradient v0.3.3
|
|
* https://github.com/amazingSurge/jquery-asGradient
|
|
*
|
|
* Copyright (c) amazingSurge
|
|
* Released under the LGPL-3.0 license
|
|
*/
|
|
import $ from 'jquery';
|
|
import Color from 'jquery-asColor';
|
|
|
|
var DEFAULTS = {
|
|
prefixes: ['-webkit-', '-moz-', '-ms-', '-o-'],
|
|
forceStandard: true,
|
|
angleUseKeyword: true,
|
|
emptyString: '',
|
|
degradationFormat: false,
|
|
cleanPosition: true,
|
|
color: {
|
|
format: false, // rgb, rgba, hsl, hsla, hex
|
|
hexUseName: false,
|
|
reduceAlpha: true,
|
|
shortenHex: true,
|
|
zeroAlphaAsTransparent: false,
|
|
invalidValue: {
|
|
r: 0,
|
|
g: 0,
|
|
b: 0,
|
|
a: 1
|
|
}
|
|
}
|
|
};
|
|
|
|
/* eslint no-extend-native: "off" */
|
|
if (!String.prototype.includes) {
|
|
String.prototype.includes = function(search, start) {
|
|
'use strict';
|
|
if (typeof start !== 'number') {
|
|
start = 0;
|
|
}
|
|
|
|
if (start + search.length > this.length) {
|
|
return false;
|
|
}
|
|
return this.indexOf(search, start) !== -1;
|
|
};
|
|
}
|
|
|
|
function getPrefix() {
|
|
const ua = window.navigator.userAgent;
|
|
let prefix = '';
|
|
if (/MSIE/g.test(ua)) {
|
|
prefix = '-ms-';
|
|
} else if (/Firefox/g.test(ua)) {
|
|
prefix = '-moz-';
|
|
} else if (/(WebKit)/i.test(ua)) {
|
|
prefix = '-webkit-';
|
|
} else if (/Opera/g.test(ua)) {
|
|
prefix = '-o-';
|
|
}
|
|
return prefix;
|
|
}
|
|
|
|
function flip(o) {
|
|
const flipped = {};
|
|
for (const i in o) {
|
|
if (o.hasOwnProperty(i)) {
|
|
flipped[o[i]] = i;
|
|
}
|
|
}
|
|
return flipped;
|
|
}
|
|
|
|
function reverseDirection(direction) {
|
|
const mapping = {
|
|
'top': 'bottom',
|
|
'right': 'left',
|
|
'bottom': 'top',
|
|
'left': 'right',
|
|
'right top': 'left bottom',
|
|
'top right': 'bottom left',
|
|
'bottom right': 'top left',
|
|
'right bottom': 'left top',
|
|
'left bottom': 'right top',
|
|
'bottom left': 'top right',
|
|
'top left': 'bottom right',
|
|
'left top': 'right bottom'
|
|
};
|
|
return mapping.hasOwnProperty(direction) ? mapping[direction] : direction;
|
|
}
|
|
|
|
function isDirection(n) {
|
|
const reg = /^(top|left|right|bottom)$/i;
|
|
return reg.test(n);
|
|
}
|
|
|
|
var keywordAngleMap = {
|
|
'to top': 0,
|
|
'to right': 90,
|
|
'to bottom': 180,
|
|
'to left': 270,
|
|
'to right top': 45,
|
|
'to top right': 45,
|
|
'to bottom right': 135,
|
|
'to right bottom': 135,
|
|
'to left bottom': 225,
|
|
'to bottom left': 225,
|
|
'to top left': 315,
|
|
'to left top': 315
|
|
};
|
|
|
|
const angleKeywordMap = flip(keywordAngleMap);
|
|
|
|
const RegExpStrings = (() => {
|
|
const color = /(?:rgba|rgb|hsla|hsl)\s*\([\s\d\.,%]+\)|#[a-z0-9]{3,6}|[a-z]+/i;
|
|
const position = /\d{1,3}%/i;
|
|
const angle = /(?:to ){0,1}(?:(?:top|left|right|bottom)\s*){1,2}|\d+deg/i;
|
|
const stop = new RegExp(`(${color.source})\\s*(${position.source}){0,1}`, 'i');
|
|
const stops = new RegExp(stop.source, 'gi');
|
|
const parameters = new RegExp(`(?:(${angle.source})){0,1}\\s*,{0,1}\\s*(.*?)\\s*`, 'i');
|
|
const full = new RegExp(`^(-webkit-|-moz-|-ms-|-o-){0,1}(linear|radial|repeating-linear)-gradient\\s*\\(\\s*(${parameters.source})\\s*\\)$`, 'i');
|
|
|
|
return {
|
|
FULL: full,
|
|
ANGLE: angle,
|
|
COLOR: color,
|
|
POSITION: position,
|
|
STOP: stop,
|
|
STOPS: stops,
|
|
PARAMETERS: new RegExp(`^${parameters.source}$`, 'i')
|
|
};
|
|
})();
|
|
|
|
var GradientString = {
|
|
matchString: function(string) {
|
|
const matched = this.parseString(string);
|
|
if(matched && matched.value && matched.value.stops && matched.value.stops.length > 1){
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
parseString: function(string) {
|
|
string = $.trim(string);
|
|
let matched;
|
|
if ((matched = RegExpStrings.FULL.exec(string)) !== null) {
|
|
let value = this.parseParameters(matched[3]);
|
|
|
|
return {
|
|
prefix: (typeof matched[1] === 'undefined') ? null : matched[1],
|
|
type: matched[2],
|
|
value: value
|
|
};
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
parseParameters: function(string) {
|
|
let matched;
|
|
if ((matched = RegExpStrings.PARAMETERS.exec(string)) !== null) {
|
|
let stops = this.parseStops(matched[2]);
|
|
return {
|
|
angle: (typeof matched[1] === 'undefined') ? 0 : matched[1],
|
|
stops: stops
|
|
};
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
parseStops: function(string) {
|
|
let matched;
|
|
const result = [];
|
|
if ((matched = string.match(RegExpStrings.STOPS)) !== null) {
|
|
|
|
$.each(matched, (i, item) => {
|
|
const stop = this.parseStop(item);
|
|
if (stop) {
|
|
result.push(stop);
|
|
}
|
|
});
|
|
return result;
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
formatStops: function(stops, cleanPosition) {
|
|
let stop;
|
|
const output = [];
|
|
let positions = [];
|
|
const colors = [];
|
|
let position;
|
|
|
|
for (let i = 0; i < stops.length; i++) {
|
|
stop = stops[i];
|
|
if (typeof stop.position === 'undefined' || stop.position === null) {
|
|
if (i === 0) {
|
|
position = 0;
|
|
} else if (i === stops.length - 1) {
|
|
position = 1;
|
|
} else {
|
|
position = undefined;
|
|
}
|
|
} else {
|
|
position = stop.position;
|
|
}
|
|
positions.push(position);
|
|
colors.push(stop.color.toString());
|
|
}
|
|
|
|
positions = ((data => {
|
|
let start = null;
|
|
let average;
|
|
for (let i = 0; i < data.length; i++) {
|
|
if (isNaN(data[i])) {
|
|
if (start === null) {
|
|
start = i;
|
|
continue;
|
|
}
|
|
} else if (start) {
|
|
average = (data[i] - data[start - 1]) / (i - start + 1);
|
|
for (let j = start; j < i; j++) {
|
|
data[j] = data[start - 1] + (j - start + 1) * average;
|
|
}
|
|
start = null;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}))(positions);
|
|
|
|
for (let x = 0; x < stops.length; x++) {
|
|
if (cleanPosition && ((x === 0 && positions[x] === 0) || (x === stops.length - 1 && positions[x] === 1))) {
|
|
position = '';
|
|
} else {
|
|
position = ` ${this.formatPosition(positions[x])}`;
|
|
}
|
|
|
|
output.push(colors[x] + position);
|
|
}
|
|
return output.join(', ');
|
|
},
|
|
|
|
parseStop: function(string) {
|
|
let matched;
|
|
if ((matched = RegExpStrings.STOP.exec(string)) !== null) {
|
|
let position = this.parsePosition(matched[2]);
|
|
|
|
return {
|
|
color: matched[1],
|
|
position: position
|
|
};
|
|
} else {
|
|
return false;
|
|
}
|
|
},
|
|
|
|
parsePosition: function(string) {
|
|
if (typeof string === 'string' && string.substr(-1) === '%') {
|
|
string = parseFloat(string.slice(0, -1) / 100);
|
|
}
|
|
|
|
if(typeof string !== 'undefined' && string !== null) {
|
|
return parseFloat(string, 10);
|
|
} else {
|
|
return null;
|
|
}
|
|
},
|
|
|
|
formatPosition: function(value) {
|
|
return `${parseInt(value * 100, 10)}%`;
|
|
},
|
|
|
|
parseAngle: function(string, notStandard) {
|
|
if (typeof string === 'string' && string.includes('deg')) {
|
|
string = string.replace('deg', '');
|
|
}
|
|
if (!isNaN(string)) {
|
|
if (notStandard) {
|
|
string = this.fixOldAngle(string);
|
|
}
|
|
}
|
|
if (typeof string === 'string') {
|
|
const directions = string.split(' ');
|
|
|
|
const filtered = [];
|
|
for (const i in directions) {
|
|
if (isDirection(directions[i])) {
|
|
filtered.push(directions[i].toLowerCase());
|
|
}
|
|
}
|
|
let keyword = filtered.join(' ');
|
|
|
|
if (!string.includes('to ')) {
|
|
keyword = reverseDirection(keyword);
|
|
}
|
|
keyword = `to ${keyword}`;
|
|
if (keywordAngleMap.hasOwnProperty(keyword)) {
|
|
string = keywordAngleMap[keyword];
|
|
}
|
|
}
|
|
let value = parseFloat(string, 10);
|
|
|
|
if (value > 360) {
|
|
value %= 360;
|
|
} else if (value < 0) {
|
|
value %= -360;
|
|
|
|
if (value !== 0) {
|
|
value += 360;
|
|
}
|
|
}
|
|
return value;
|
|
},
|
|
|
|
fixOldAngle: function(value) {
|
|
value = parseFloat(value);
|
|
value = Math.abs(450 - value) % 360;
|
|
value = parseFloat(value.toFixed(3));
|
|
return value;
|
|
},
|
|
|
|
formatAngle: function(value, notStandard, useKeyword) {
|
|
value = parseInt(value, 10);
|
|
if (useKeyword && angleKeywordMap.hasOwnProperty(value)) {
|
|
value = angleKeywordMap[value];
|
|
if (notStandard) {
|
|
value = reverseDirection(value.substr(3));
|
|
}
|
|
} else {
|
|
if (notStandard) {
|
|
value = this.fixOldAngle(value);
|
|
}
|
|
value = `${value}deg`;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
};
|
|
|
|
class ColorStop {
|
|
constructor(color, position, gradient) {
|
|
this.color = Color(color, gradient.options.color);
|
|
this.position = GradientString.parsePosition(position);
|
|
this.id = ++gradient._stopIdCount;
|
|
this.gradient = gradient;
|
|
}
|
|
|
|
setPosition(string) {
|
|
const position = GradientString.parsePosition(string);
|
|
if(this.position !== position){
|
|
this.position = position;
|
|
this.gradient.reorder();
|
|
}
|
|
}
|
|
|
|
setColor(string) {
|
|
this.color.fromString(string);
|
|
}
|
|
|
|
remove() {
|
|
this.gradient.removeById(this.id);
|
|
}
|
|
}
|
|
|
|
var GradientTypes = {
|
|
LINEAR: {
|
|
parse(result) {
|
|
return {
|
|
r: (result[1].substr(-1) === '%') ? parseInt(result[1].slice(0, -1) * 2.55, 10) : parseInt(result[1], 10),
|
|
g: (result[2].substr(-1) === '%') ? parseInt(result[2].slice(0, -1) * 2.55, 10) : parseInt(result[2], 10),
|
|
b: (result[3].substr(-1) === '%') ? parseInt(result[3].slice(0, -1) * 2.55, 10) : parseInt(result[3], 10),
|
|
a: 1
|
|
};
|
|
},
|
|
to(gradient, instance, prefix) {
|
|
if (gradient.stops.length === 0) {
|
|
return instance.options.emptyString;
|
|
}
|
|
if (gradient.stops.length === 1) {
|
|
return gradient.stops[0].color.to(instance.options.degradationFormat);
|
|
}
|
|
|
|
let standard = instance.options.forceStandard;
|
|
let _prefix = instance._prefix;
|
|
|
|
if (!_prefix) {
|
|
standard = true;
|
|
}
|
|
if (prefix && -1 !== $.inArray(prefix, instance.options.prefixes)) {
|
|
standard = false;
|
|
_prefix = prefix;
|
|
}
|
|
|
|
const angle = GradientString.formatAngle(gradient.angle, !standard, instance.options.angleUseKeyword);
|
|
const stops = GradientString.formatStops(gradient.stops, instance.options.cleanPosition);
|
|
|
|
const output = `linear-gradient(${angle}, ${stops})`;
|
|
if (standard) {
|
|
return output;
|
|
} else {
|
|
return _prefix + output;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
class AsGradient {
|
|
constructor(string, options) {
|
|
if (typeof string === 'object' && typeof options === 'undefined') {
|
|
options = string;
|
|
string = undefined;
|
|
}
|
|
this.value = {
|
|
angle: 0,
|
|
stops: []
|
|
};
|
|
this.options = $.extend(true, {}, DEFAULTS, options);
|
|
|
|
this._type = 'LINEAR';
|
|
this._prefix = null;
|
|
this.length = this.value.stops.length;
|
|
this.current = 0;
|
|
this._stopIdCount = 0;
|
|
|
|
this.init(string);
|
|
}
|
|
|
|
init(string) {
|
|
if (string) {
|
|
this.fromString(string);
|
|
}
|
|
}
|
|
|
|
val(value) {
|
|
if (typeof value === 'undefined') {
|
|
return this.toString();
|
|
} else {
|
|
this.fromString(value);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
angle(value) {
|
|
if (typeof value === 'undefined') {
|
|
return this.value.angle;
|
|
} else {
|
|
this.value.angle = GradientString.parseAngle(value);
|
|
return this;
|
|
}
|
|
}
|
|
|
|
append(color, position) {
|
|
return this.insert(color, position, this.length);
|
|
}
|
|
|
|
reorder() {
|
|
if(this.length < 2){
|
|
return;
|
|
}
|
|
|
|
this.value.stops = this.value.stops.sort((a, b) => a.position - b.position);
|
|
}
|
|
|
|
insert(color, position, index) {
|
|
if (typeof index === 'undefined') {
|
|
index = this.current;
|
|
}
|
|
|
|
const stop = new ColorStop(color, position, this);
|
|
|
|
this.value.stops.splice(index, 0, stop);
|
|
|
|
this.length = this.length + 1;
|
|
this.current = index;
|
|
return stop;
|
|
}
|
|
|
|
getById(id) {
|
|
if(this.length > 0){
|
|
for(const i in this.value.stops){
|
|
if(id === this.value.stops[i].id){
|
|
return this.value.stops[i];
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
removeById(id) {
|
|
const index = this.getIndexById(id);
|
|
if(index){
|
|
this.remove(index);
|
|
}
|
|
}
|
|
|
|
getIndexById(id) {
|
|
let index = 0;
|
|
for(const i in this.value.stops){
|
|
if(id === this.value.stops[i].id){
|
|
return index;
|
|
}
|
|
index ++;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
getCurrent() {
|
|
return this.value.stops[this.current];
|
|
}
|
|
|
|
setCurrentById(id) {
|
|
let index = 0;
|
|
for(const i in this.value.stops){
|
|
if(this.value.stops[i].id !== id){
|
|
index ++;
|
|
} else {
|
|
this.current = index;
|
|
}
|
|
}
|
|
}
|
|
|
|
get(index) {
|
|
if (typeof index === 'undefined') {
|
|
index = this.current;
|
|
}
|
|
if (index >= 0 && index < this.length) {
|
|
this.current = index;
|
|
return this.value.stops[index];
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
remove(index) {
|
|
if (typeof index === 'undefined') {
|
|
index = this.current;
|
|
}
|
|
if (index >= 0 && index < this.length) {
|
|
this.value.stops.splice(index, 1);
|
|
this.length = this.length - 1;
|
|
this.current = index - 1;
|
|
}
|
|
}
|
|
|
|
empty() {
|
|
this.value.stops = [];
|
|
this.length = 0;
|
|
this.current = 0;
|
|
}
|
|
|
|
reset() {
|
|
this.value._angle = 0;
|
|
this.empty();
|
|
this._prefix = null;
|
|
this._type = 'LINEAR';
|
|
}
|
|
|
|
type(type) {
|
|
if (typeof type === 'string' && (type = type.toUpperCase()) && typeof GradientTypes[type] !== 'undefined') {
|
|
this._type = type;
|
|
return this;
|
|
} else {
|
|
return this._type;
|
|
}
|
|
}
|
|
|
|
fromString(string) {
|
|
this.reset();
|
|
|
|
const result = GradientString.parseString(string);
|
|
|
|
if (result) {
|
|
this._prefix = result.prefix;
|
|
this.type(result.type);
|
|
if (result.value) {
|
|
this.value.angle = GradientString.parseAngle(result.value.angle, this._prefix !== null);
|
|
|
|
$.each(result.value.stops, (i, stop) => {
|
|
this.append(stop.color, stop.position);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
toString(prefix) {
|
|
if(prefix === true){
|
|
prefix = getPrefix();
|
|
}
|
|
return GradientTypes[this.type()].to(this.value, this, prefix);
|
|
}
|
|
|
|
matchString(string) {
|
|
return GradientString.matchString(string);
|
|
}
|
|
|
|
toStringWithAngle(angle, prefix) {
|
|
const value = $.extend(true, {}, this.value);
|
|
value.angle = GradientString.parseAngle(angle);
|
|
|
|
if(prefix === true){
|
|
prefix = getPrefix();
|
|
}
|
|
|
|
return GradientTypes[this.type()].to(value, this, prefix);
|
|
}
|
|
|
|
getPrefixedStrings() {
|
|
const strings = [];
|
|
for (let i in this.options.prefixes) {
|
|
if(Object.hasOwnProperty.call(this.options.prefixes, i)){
|
|
strings.push(this.toString(this.options.prefixes[i]));
|
|
}
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
static setDefaults(options) {
|
|
$.extend(true, DEFAULTS, $.isPlainObject(options) && options);
|
|
}
|
|
}
|
|
|
|
var info = {
|
|
version:'0.3.3'
|
|
};
|
|
|
|
const OtherAsGradient = $.asGradient;
|
|
|
|
const jQueryAsGradient = function(...args) {
|
|
return new AsGradient(...args);
|
|
};
|
|
|
|
$.asGradient = jQueryAsGradient;
|
|
$.asGradient.Constructor = AsGradient;
|
|
|
|
$.extend($.asGradient, {
|
|
setDefaults: AsGradient.setDefaults,
|
|
noConflict: function() {
|
|
$.asGradient = OtherAsGradient;
|
|
return jQueryAsGradient;
|
|
}
|
|
}, GradientString, info);
|
|
|
|
var main = $.asGradient;
|
|
|
|
export default main;
|