mirror of
https://github.com/snachodog/just-the-docs.git
synced 2025-04-14 15:22:23 -06:00
860 lines
27 KiB
JavaScript
860 lines
27 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.default = findOperators;
|
|
exports.mathOperatorCharType = mathOperatorCharType;
|
|
|
|
/**
|
|
* Processes a string and finds Sass operators in it
|
|
*
|
|
* @param {Object} args - Named arguments object
|
|
* @param {String} args.string - the input string
|
|
* @param {Number} args.index - the position of args.string from the start of the line
|
|
* @param {Boolean} args.isAfterColon - pass "true" if the string is
|
|
* a variable value, a mixin/function parameter default.
|
|
* In such cases + and / tend to be operations more often
|
|
* @param {Function} args.callback - will be called on every instance of
|
|
* an operator. Accepts parameters:
|
|
* • string - the default source string
|
|
* • globalIndex - the string's position in the outer input
|
|
* • startIndex - index in string, where the operator starts
|
|
* • endIndex - index in string, where the operator ends (for `==`, etc.)
|
|
*
|
|
* @return {Array} array of { symbol, globalIndex, startIndex, endIndex }
|
|
* for each operator found within a string
|
|
*/
|
|
function findOperators(_ref) {
|
|
var string = _ref.string,
|
|
globalIndex = _ref.globalIndex,
|
|
isAfterColon = _ref.isAfterColon,
|
|
callback = _ref.callback;
|
|
|
|
|
|
var mathOperators = ["+", "/", "-", "*", "%"];
|
|
// A stack of modes activated for the current char: string, interpolation
|
|
// Calculations inside strings are not processed, so spaces are not linted
|
|
var modesEntered = [{
|
|
mode: "normal",
|
|
isCalculationEnabled: true,
|
|
character: null
|
|
}];
|
|
var result = [];
|
|
var lastModeIndex = 0;
|
|
|
|
for (var i = 0; i < string.length; i++) {
|
|
var character = string[i];
|
|
var substringStartingWithIndex = string.substring(i);
|
|
|
|
// If entering/exiting a string
|
|
if (character === "\"" || character === "'") {
|
|
if (modesEntered[lastModeIndex].isCalculationEnabled === true) {
|
|
modesEntered.push({
|
|
mode: "string",
|
|
isCalculationEnabled: false,
|
|
character: character
|
|
});
|
|
lastModeIndex++;
|
|
} else if (modesEntered[lastModeIndex].mode === "string" && modesEntered[lastModeIndex].character === character && string[i - 1] !== "\\") {
|
|
modesEntered.pop();
|
|
lastModeIndex--;
|
|
}
|
|
}
|
|
|
|
// If entering/exiting interpolation (may be inside a string)
|
|
// Comparing with length-2 because `#{` at the very end doesnt matter
|
|
if (character === "#" && i + 1 < string.length - 2 && string[i + 1] === "{") {
|
|
modesEntered.push({
|
|
mode: "interpolation",
|
|
isCalculationEnabled: true
|
|
});
|
|
lastModeIndex++;
|
|
} else if (character === "}") {
|
|
modesEntered.pop();
|
|
lastModeIndex--;
|
|
}
|
|
|
|
// Don't lint if inside a string
|
|
if (modesEntered[lastModeIndex].isCalculationEnabled === false) {
|
|
continue;
|
|
}
|
|
|
|
// If it's a math operator
|
|
if (mathOperators.indexOf(character) !== -1 && mathOperatorCharType(string, i, isAfterColon) === "op" ||
|
|
// or is "<" or ">"
|
|
substringStartingWithIndex.search(/^[<>]([^=]|$)/) !== -1) {
|
|
result.push({
|
|
symbol: string[i],
|
|
globalIndex: globalIndex,
|
|
startIndex: i,
|
|
endIndex: i
|
|
});
|
|
if (callback) {
|
|
callback(string, globalIndex, i, i);
|
|
}
|
|
}
|
|
|
|
// "<=", ">=", "!=", "=="
|
|
if (substringStartingWithIndex.search(/^[><=!]=/) !== -1) {
|
|
result.push({
|
|
symbol: string[i],
|
|
globalIndex: globalIndex,
|
|
startIndex: i,
|
|
endIndex: i + 1
|
|
});
|
|
if (callback) {
|
|
callback(string, globalIndex, i, i + 1);
|
|
}
|
|
}
|
|
}
|
|
// result.length > 0 && console.log(string, result)
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if a character is an operator, a sign (+ or -), or part of a string
|
|
*
|
|
* @param {String} string - the source string
|
|
* @param {Number} index - the index of the character in string to check
|
|
* @param {Boolean} isAfterColon - if the value string a variable
|
|
* value, a mixin/function parameter default. In such cases + and / tend
|
|
* to be operations more often
|
|
* @return {String|false}
|
|
* • "op", if the character is a operator in a math/string operation
|
|
* • "sign" if it is a + or - before a numeric,
|
|
* • "char" if it is a part of a string,
|
|
* • false - if it is none from above (most likely an error)
|
|
*/
|
|
function mathOperatorCharType(string, index, isAfterColon) {
|
|
// !Checking here to prevent unnecessary calculations and deep recursion
|
|
// when calling isPrecedingOperator()
|
|
if (["+", "/", "-", "*", "%"].indexOf(string[index]) === -1) {
|
|
return "char";
|
|
}
|
|
|
|
var character = string[index];
|
|
// console.log(string)
|
|
|
|
// ---- Processing + characters
|
|
if (character === "+") {
|
|
// console.log('checking plus')
|
|
return checkPlus(string, index, isAfterColon);
|
|
}
|
|
|
|
// ---- Processing - characters
|
|
if (character === "-") {
|
|
return checkMinus(string, index);
|
|
}
|
|
|
|
// ---- Processing * character
|
|
if (character === "*") {
|
|
return "op";
|
|
}
|
|
|
|
// ---- Processing % character
|
|
if (character === "%") {
|
|
return checkPercent(string, index);
|
|
}
|
|
|
|
// ---- Processing / character
|
|
// http://sass-lang.com/documentation/file.SASS_REFERENCE.html#division-and-slash
|
|
if (character === "/") {
|
|
return checkSlash(string, index, isAfterColon);
|
|
}
|
|
|
|
// console.log("nothing applies")
|
|
return "char";
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Functions for checking particular characterd (+, -, /)
|
|
// --------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Checks the specified `+` char type: operator, sign (+ or -), part of string
|
|
*
|
|
* @param {String} string - the source string
|
|
* @param {Number} index - the index of the character in string to check
|
|
* @param {Boolean} isAftercolon - if the value string a variable
|
|
* value, a mixin/function parameter default. In such cases + is always an
|
|
* operator if surrounded by numbers/values with units
|
|
* @return {String|false}
|
|
* • "op", if the character is a operator in a math/string operation
|
|
* • "sign" if it is a sign before a positive number,
|
|
* • false - if it is none from above (most likely an error)
|
|
*/
|
|
function checkPlus(string, index, isAftercolon) {
|
|
var before = string.substring(0, index);
|
|
var after = string.substring(index + 1);
|
|
|
|
// If the character is at the beginning of the input
|
|
var isAtStart_ = isAtStart(string, index);
|
|
// If the character is at the end of the input
|
|
var isAtEnd_ = isAtEnd(string, index);
|
|
var isWhitespaceBefore = before.search(/\s$/) !== -1;
|
|
var isWhitespaceAfter = after.search(/^\s/) !== -1;
|
|
|
|
var isValueWithUnitAfter_ = isValueWithUnitAfter(after);
|
|
var isNumberAfter_ = isNumberAfter(after);
|
|
var isInterpolationAfter_ = isInterpolationAfter(after);
|
|
// The early check above helps prevent deep recursion here
|
|
var isPrecedingOperator_ = isPrecedingOperator(string, index);
|
|
|
|
if (isAtStart_) {
|
|
// console.log("+, `+<sth>` or `+ <sth>`")
|
|
return "sign";
|
|
}
|
|
|
|
// E.g. `1+1`, `string+#fff`
|
|
if (!isAtStart_ && !isWhitespaceBefore && !isAtEnd_ && !isWhitespaceAfter) {
|
|
// E.g. `1-+1`
|
|
if (isPrecedingOperator_) {
|
|
// console.log('1+1')
|
|
return "sign";
|
|
}
|
|
// console.log("+, no spaces")
|
|
return "op";
|
|
}
|
|
// e.g. `something +something`
|
|
if (!isAtEnd_ && !isWhitespaceAfter) {
|
|
// e.g. `+something`, ` ... , +something`, etc.
|
|
if (isNoOperandBefore(string, index)) {
|
|
// console.log("+, nothing before")
|
|
return "sign";
|
|
}
|
|
|
|
// e.g. `sth +10px`, `sth +1`
|
|
if (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween) {
|
|
if (isAftercolon === true) {
|
|
// console.log(": 10px +1")
|
|
return "op";
|
|
}
|
|
|
|
// e.g. `(sth +10px)`, `fun(sth +1)`
|
|
if (isInsideParens(string, index) || isInsideFunctionCall(string, index).is) {
|
|
// console.log("+10px or +1, inside function or parens")
|
|
return "op";
|
|
}
|
|
|
|
// e.g. `#{10px +1}`
|
|
if (isInsideInterpolation(string, index)) {
|
|
// console.log('+, #{10px +1}')
|
|
return "op";
|
|
}
|
|
|
|
// console.log('+, default')
|
|
return "sign";
|
|
}
|
|
|
|
// e.g. `sth +#fff`, `sth +string`, `sth +#{...}`, `sth +$var`
|
|
if (isStringAfter(after) || isHexColorAfter(after) || after[0] === "$" || isInterpolationAfter_.is && !isInterpolationAfter_.opsBefore) {
|
|
// e.g. `sth+ +string`
|
|
if (isPrecedingOperator_) {
|
|
// console.log("+10px or +1, before is an operator")
|
|
return "sign";
|
|
}
|
|
// console.log("+#000, +string, +#{sth}, +$var")
|
|
return "op";
|
|
}
|
|
|
|
// console.log('sth +sth, default')
|
|
return "op";
|
|
}
|
|
|
|
// If the + is after a value, e.g. `$var+`
|
|
if (!isAtStart_ && !isWhitespaceBefore) {
|
|
// It is always an operator. Prior to Sass 4, `#{...}+` was differernt,
|
|
// but that's not logical and had been fixed.
|
|
// console.log('1+ sth')
|
|
return "op";
|
|
}
|
|
|
|
// If it has whitespaces on both sides
|
|
// console.log('sth + sth')
|
|
return "op";
|
|
}
|
|
|
|
/**
|
|
* Checks the specified `-` character: operator, sign (+ or -), part of string
|
|
*
|
|
* @param {String} string - the source string
|
|
* @param {Number} index - the index of the character in string to check
|
|
* @return {String|false}
|
|
* • "op", if the character is a operator in a math/string operation
|
|
* • "sign" if it is a sign before a negative number,
|
|
* • "char" if it is a part of a string or identifier,
|
|
* • false - if it is none from above (most likely an error)
|
|
*/
|
|
function checkMinus(string, index) {
|
|
var before = string.substring(0, index);
|
|
var after = string.substring(index + 1);
|
|
// If the character is at the beginning of the input
|
|
var isAtStart_ = isAtStart(string, index);
|
|
// If the character is at the end of the input
|
|
var isAtEnd_ = isAtEnd(string, index);
|
|
var isWhitespaceBefore = before.search(/\s$/) !== -1;
|
|
var isWhitespaceAfter = after.search(/^\s/) !== -1;
|
|
|
|
var isValueWithUnitAfter_ = isValueWithUnitAfter(after);
|
|
var isValueWithUnitBefore_ = isValueWithUnitBefore(before);
|
|
var isNumberAfter_ = isNumberAfter(after);
|
|
var isNumberBefore_ = isNumberBefore(before);
|
|
var isInterpolationAfter_ = isInterpolationAfter(after);
|
|
var isParensAfter_ = isParensAfter(after);
|
|
var isParensBefore_ = isParensBefore(before);
|
|
// The early check above helps prevent deep recursion here
|
|
var isPrecedingOperator_ = isPrecedingOperator(string, index);
|
|
|
|
if (isAtStart_) {
|
|
// console.log("-, -<sth> or - <sth>")
|
|
return "sign";
|
|
}
|
|
|
|
// `10 - 11`
|
|
if (!isAtEnd_ && !isAtStart_ && isWhitespaceBefore && isWhitespaceAfter) {
|
|
// console.log("-, Op: 10px - 10px")
|
|
return "op";
|
|
}
|
|
|
|
// e.g. `something -10px`
|
|
if (!isAtEnd_ && !isAtStart_ && isWhitespaceBefore && !isWhitespaceAfter) {
|
|
if (isParensAfter_.is && !isParensAfter_.opsBefore) {
|
|
// console.log("-, Op: <sth> -(...)")
|
|
return "op";
|
|
}
|
|
|
|
// e.g. `sth -1px`, `sth -1`.
|
|
// Always a sign, even inside parens/function args
|
|
if (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween) {
|
|
// console.log("-, sign: -1px or -1")
|
|
return "sign";
|
|
}
|
|
|
|
// e.g. `sth --1`, `sth +-2px`
|
|
if (isValueWithUnitAfter_.is && isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && isNumberAfter_.opsBetween) {
|
|
// console.log("-, op: --1px or --1")
|
|
return "op";
|
|
}
|
|
|
|
// `<sth> -string`, `<sth> -#{...}`
|
|
if (isStringAfter(after) || isInterpolationAfter_.is && !isInterpolationAfter_.opsBefore) {
|
|
// console.log("-, char: -#{...}")
|
|
return "char";
|
|
}
|
|
|
|
// e.g. `#0af -#f0a`, and edge-cases can take a hike
|
|
if (isHexColorAfter(after) && isHexColorBefore(before.trim())) {
|
|
// console.log("-, op: #fff-, -#fff")
|
|
return "op";
|
|
}
|
|
|
|
// If the - is before a variable, than it's most likely an operator
|
|
if (after[0] === "$") {
|
|
if (isPrecedingOperator_) {
|
|
// console.log("-, sign: -$var, another operator before")
|
|
return "sign";
|
|
}
|
|
// console.log("-, op: -$var, NO other operator before")
|
|
return "op";
|
|
}
|
|
|
|
// By default let's make it an sign for now
|
|
// console.log('-, sign: default in <sth> -<sth>')
|
|
return "sign";
|
|
}
|
|
|
|
// No whitespace before,
|
|
// e.g. `10x- something`
|
|
if (!isAtEnd_ && !isAtStart_ && !isWhitespaceBefore && isWhitespaceAfter) {
|
|
if (isParensBefore_) {
|
|
// console.log('-, op: `(...)- <sth>`')
|
|
return "op";
|
|
}
|
|
|
|
if (isNumberBefore(before) || isHexColorBefore(before)) {
|
|
// console.log('`-, op: 10- <sth>, #aff- <sth>`')
|
|
return "op";
|
|
}
|
|
// console.log('-, char: default in <sth>- <sth>')
|
|
return "char";
|
|
}
|
|
|
|
// NO Whitespace,
|
|
// e.g. `10px-1`
|
|
if (!isAtEnd_ && !isAtStart_ && !isWhitespaceBefore && !isWhitespaceAfter) {
|
|
// console.log('no spaces')
|
|
// `<something>-1`, `<something>-10px`
|
|
if (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween) {
|
|
// `10px-1`, `1-10px`, `1-1`, `1x-1x`
|
|
if (isValueWithUnitBefore_ || isNumberBefore_) {
|
|
// console.log("-, op: 1-10px")
|
|
return "op";
|
|
}
|
|
|
|
// The - could be a "sign" here, but for now "char" does the job
|
|
}
|
|
// `1-$var`
|
|
if (isNumberBefore_ && after[0] === "$") {
|
|
// console.log("-, op: 1-$var")
|
|
return "op";
|
|
}
|
|
|
|
// `fn()-10px`
|
|
if (isFunctionBefore(before) && (isNumberAfter_.is && !isNumberAfter_.opsBetween || isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween)) {
|
|
// console.log("-, op: fn()-10px")
|
|
return "op";
|
|
}
|
|
}
|
|
|
|
// And in all the other cases it's a characher inside a string
|
|
// console.log("-, default: char")
|
|
return "char";
|
|
}
|
|
|
|
/**
|
|
* Checks the specified `/` character: operator, sign (+ or -), part of string
|
|
*
|
|
* @param {String} string - the source string
|
|
* @param {Number} index - the index of the character in string to check
|
|
* @param {Boolean} isAfterColon - if the value string a variable
|
|
* value, a mixin/function parameter default. In such cases / is always an
|
|
* operator if surrounded by numbers/values with units
|
|
* @return {String|false}
|
|
* • "op", if the character is a operator in a math/string operation
|
|
* • "char" if it gets compiled as-is, e.g. `font: 10px/1.2;`,
|
|
* • false - if it is none from above (most likely an error)
|
|
*/
|
|
function checkSlash(string, index, isAftercolon) {
|
|
// Trimming these, as spaces before/after a slash don't matter
|
|
var before = string.substring(0, index).trim();
|
|
var after = string.substring(index + 1).trim();
|
|
|
|
var isValueWithUnitAfter_ = isValueWithUnitAfter(after);
|
|
var isValueWithUnitBefore_ = isValueWithUnitBefore(before);
|
|
var isNumberAfter_ = isNumberAfter(after);
|
|
var isNumberBefore_ = isNumberBefore(before);
|
|
var isParensAfter_ = isParensAfter(after);
|
|
var isParensBefore_ = isParensBefore(before);
|
|
|
|
// FIRST OFF. Interpolation on any of the sides is a NO-GO for division op
|
|
if (isInterpolationBefore(before).is || isInterpolationAfter(after).is) {
|
|
// console.log("/, interpolation")
|
|
return "char";
|
|
}
|
|
|
|
// e.g. `10px/normal`
|
|
if (isStringBefore(before).is || isStringAfter(after)) {
|
|
// console.log("/, string")
|
|
return "char";
|
|
}
|
|
|
|
// For all other value options (numbers, value+unit, hex color)
|
|
|
|
// `$var/1`, `#fff/-$var`
|
|
// Here we don't care if there is a sign before the var
|
|
if (isVariableBefore(before) || isVariableAfter(after).is) {
|
|
// console.log("/, variable")
|
|
return "op";
|
|
}
|
|
|
|
if (isFunctionBefore(before) || isFunctionAfter(after).is) {
|
|
// console.log("/, function as operand")
|
|
return "op";
|
|
}
|
|
|
|
if (isParensBefore_ || isParensAfter_.is) {
|
|
// console.log("/, function as operand")
|
|
return "op";
|
|
}
|
|
|
|
// `$var: 10px/2; // 5px`
|
|
if (isAftercolon === true && (isValueWithUnitAfter_.is || isNumberAfter_.is) && (isValueWithUnitBefore_ || isNumberBefore_)) {
|
|
return "op";
|
|
}
|
|
|
|
// Quick check of the following operator symbol - if it is a math operator
|
|
if (
|
|
// +, *, % count as operators unless after interpolation or at the start
|
|
before.search(/[^{,(}\s]\s*[+*%]\s*[^(){},]+$/) !== -1 ||
|
|
// We consider minus as op only if surrounded by whitespaces (` - `);
|
|
before.search(/[^{,(}\s]\s+-\s+[^(){},]+$/) !== -1 ||
|
|
|
|
// `10/2 * 3`, `10/2 % 3`, with or without spaces
|
|
after.search(/^[^(){},]+[*%]/) !== -1 ||
|
|
// `10px/2px+1`, `10px/2px+ 1`
|
|
after.search(/^[^(){},\s]+[+]/) !== -1 ||
|
|
// Anything but `10px/2px +1`, `10px/2px +1px`
|
|
after.search(/^[^(){},\s]+\s+(\+\D)/) !== -1 ||
|
|
// Following ` -`: only if `$var` after (`10/10 -$var`)
|
|
after.search(/^[^(){},\s]+\s+-(\$|\s)/) !== -1 ||
|
|
// Following `-`: only if number after (`10s/10s-10`, `10s/10s-.1`)
|
|
after.search(/^[^(){},\s]+-(\.){0,1}\d/) !== -1 ||
|
|
// Or if there is a number before anything but string after (not `10s/1-str`,)
|
|
after.search(/^(\d*\.){0,1}\d+-\s*[^#a-zA-Z_\s]/) !== -1) {
|
|
// console.log("/, math op around")
|
|
return "op";
|
|
}
|
|
|
|
// e.g. `(1px/1)`, `fn(7 / 15)`, but not `url(8/11)`
|
|
var isInsideFn = isInsideFunctionCall(string, index);
|
|
if (isInsideParens(string, index) || isInsideFn.is && isInsideFn.fn !== "url") {
|
|
// console.log("/, parens or function arg")
|
|
return "op";
|
|
}
|
|
|
|
// console.log("/, default")
|
|
return "char";
|
|
}
|
|
|
|
/**
|
|
* Checks the specified `%` character: operator or part of value
|
|
*
|
|
* @param {String} string - the source string
|
|
* @param {Number} index - the index of the character in string to check
|
|
* @return {String|false}
|
|
* • "op", if the character is a operator in a math/string operation
|
|
* • "char" if it gets compiled as-is, e.g. `width: 10%`,
|
|
* • false - if it is none from above (most likely an error)
|
|
*/
|
|
function checkPercent(string, index) {
|
|
// Trimming these, as spaces before/after a slash don't matter
|
|
var before = string.substring(0, index);
|
|
var after = string.substring(index + 1);
|
|
|
|
// If the character is at the beginning of the input
|
|
var isAtStart_ = isAtStart(string, index);
|
|
// If the character is at the end of the input
|
|
var isAtEnd_ = isAtEnd(string, index);
|
|
var isWhitespaceBefore = before.search(/\s$/) !== -1;
|
|
var isWhitespaceAfter = after.search(/^\s/) !== -1;
|
|
|
|
var isParensBefore_ = isParensBefore(before);
|
|
|
|
// FIRST OFF. Interpolation on any of the sides is a NO-GO
|
|
if (isInterpolationBefore(before.trim()).is || isInterpolationAfter(after.trim()).is) {
|
|
// console.log("%, interpolation")
|
|
return "char";
|
|
}
|
|
|
|
if (isAtStart_ || isAtEnd_) {
|
|
// console.log("%, start/end")
|
|
return "char";
|
|
}
|
|
|
|
// In `<sth> %<sth>` it's most likely an operator (except for inteprolation
|
|
// checked above)
|
|
if (isWhitespaceBefore && !isWhitespaceAfter) {
|
|
// console.log("%, `<sth> %<sth>`")
|
|
return "op";
|
|
}
|
|
|
|
// `$var% 1`, `$var%1`, `$var%-1`
|
|
if (isVariableBefore(before) || isParensBefore_) {
|
|
// console.log("%, after a variable, function or parens")
|
|
return "op";
|
|
}
|
|
|
|
// in all other cases in `<sth>% <sth>` it is most likely a unit
|
|
if (!isWhitespaceBefore && isWhitespaceAfter) {
|
|
// console.log("%, `<sth>% <sth>`")
|
|
return "char";
|
|
}
|
|
|
|
// console.log("%, default")
|
|
return "char";
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Lots of elementary helpers
|
|
// --------------------------------------------------------------------------
|
|
|
|
function isAtStart(string, index) {
|
|
var before = string.substring(0, index).trim();
|
|
return before.length === 0 || before.search(/[({,]$/) !== -1;
|
|
}
|
|
|
|
function isAtEnd(string, index) {
|
|
var after = string.substring(index + 1).trim();
|
|
return after.length === 0 || after.search(/^[,)}]/) !== -1;
|
|
}
|
|
|
|
function isInsideParens(string, index) {
|
|
var before = string.substring(0, index).trim();
|
|
var after = string.substring(index + 1).trim();
|
|
|
|
if (before.search(/(?:^|[,{]|\s)\(\s*[^(){},]+$/) !== -1 && after.search(/^[^(){},\s]+\s*\)/) !== -1) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function isInsideInterpolation(string, index) {
|
|
var before = string.substring(0, index).trim();
|
|
|
|
if (before.search(/#\{[^}]*$/) !== -1) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if the character is inside a function agruments
|
|
*
|
|
* @param {String} string - the input string
|
|
* @param {Number} index - current character index
|
|
* @return {Object} return
|
|
* {Boolean} return.is - if inside a function arguments
|
|
* {String} return.fn - function name
|
|
*/
|
|
function isInsideFunctionCall(string, index) {
|
|
var result = { is: false, fn: null };
|
|
var before = string.substring(0, index).trim();
|
|
var after = string.substring(index + 1).trim();
|
|
var beforeMatch = before.match(/([a-zA-Z_-][a-zA-Z0-9_-]*)\([^(){},]+$/);
|
|
|
|
if (beforeMatch && beforeMatch[0] && after.search(/^[^({},]+\)/) !== -1) {
|
|
result.is = true;
|
|
result.fn = beforeMatch[1];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if there is a string before the character.
|
|
* Also checks if there is a math operator in between
|
|
*
|
|
* @param {String} before - the input string that preceses the character
|
|
* @return {Object} return
|
|
* {Boolean} return.is - if there is a string
|
|
* {String} return.opsBetween - if there are operators in between
|
|
*/
|
|
function isStringBefore(before) {
|
|
var result = { is: false, opsBetween: false };
|
|
var stringOpsClipped = before.replace(/(\s*[+/*%]|\s+-)+$/, "");
|
|
|
|
if (stringOpsClipped !== before) {
|
|
result.opsBetween = true;
|
|
}
|
|
// If it is quoted
|
|
if (stringOpsClipped[stringOpsClipped.length - 1] == "\"" || stringOpsClipped[stringOpsClipped.length - 1] == "'") {
|
|
result.is = true;
|
|
} else if (stringOpsClipped.search(/(?:^|[/(){},: ])([a-zA-Z_][a-zA-Z_0-9-]*|-+[a-zA-Z_]+[a-zA-Z_0-9-]*)$/) !== -1) {
|
|
// First pattern: a1, a1a, a-1,
|
|
result.is = true;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
function isStringAfter(after) {
|
|
var stringTrimmed = after.trim();
|
|
|
|
// If it is quoted
|
|
if (stringTrimmed[0] == "\"" || stringTrimmed[0] == "'") return true;
|
|
|
|
// e.g. `a1`, `a1a`, `a-1`, and even `--s323`
|
|
if (stringTrimmed.search(/^([a-zA-Z_][a-zA-Z_0-9-]*|-+[a-zA-Z_]+[a-zA-Z_0-9-]*)(?:$|[)}, ])/) !== -1) return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
function isInterpolationAfter(after) {
|
|
var result = { is: false, opsBetween: false };
|
|
var matches = after.match(/^\s*([+/*%-]\s*)*#{/);
|
|
if (matches) {
|
|
if (matches[0]) {
|
|
result.is = true;
|
|
}
|
|
if (matches[1]) {
|
|
result.opsBetween = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isParensAfter(after) {
|
|
var result = { is: false, opsBetween: false };
|
|
var matches = after.match(/^\s*([+/*%-]\s*)*\(/);
|
|
if (matches) {
|
|
if (matches[0]) {
|
|
result.is = true;
|
|
}
|
|
if (matches[1]) {
|
|
result.opsBetween = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isParensBefore(before) {
|
|
return before.search(/\)\s*$/) !== -1;
|
|
}
|
|
|
|
/**
|
|
* Checks if there is an interpolation before the character.
|
|
* Also checks if there is a math operator in between
|
|
*
|
|
* @param {String} before - the input string that preceses the character
|
|
* @return {Object} return
|
|
* {Boolean} return.is - if there is an interpolation
|
|
* {String} return.opsBetween - if there are operators in between
|
|
*/
|
|
function isInterpolationBefore(before) {
|
|
var result = { is: false, opsBetween: false };
|
|
// Removing preceding operators if any
|
|
var beforeOpsClipped = before.replace(/(\s*[+/*%-])+$/, "");
|
|
if (beforeOpsClipped !== before) {
|
|
result.opsBetween = true;
|
|
}
|
|
if (beforeOpsClipped[beforeOpsClipped.length - 1] === "}") {
|
|
result.is = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isValueWithUnitBefore(before) {
|
|
// 1px, 0.1p-x, .2p-, 11.2pdf-df1df_
|
|
// Surprisingly, ` d.10px` - .10px is separated from a sequence
|
|
// and is considered a value with a unit
|
|
if (before.trim().search(/(^|[/(, ]|\.)\d[a-zA-Z_0-9-]+$/) !== -1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isValueWithUnitAfter(after) {
|
|
var result = { is: false, opsBetween: false };
|
|
// 1px, 0.1p-x, .2p-, 11.2pdf-dfd1f_
|
|
// Again, ` d.10px` - .10px is separated from a sequence
|
|
// and is considered a value with a unit
|
|
var matches = after.match(/^\s*([+/*%-]\s*)*(\d+(\.\d+){0,1}|\.\d+)[a-zA-Z_0-9-]+(?:$|[)}, ])/);
|
|
if (matches) {
|
|
if (matches[0]) {
|
|
result.is = true;
|
|
}
|
|
if (matches[1]) {
|
|
result.opsBetween = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isNumberAfter(after) {
|
|
var result = { is: false, opsBetween: false };
|
|
var matches = after.match(/^\s*([+/*%-]\s*)*(\d+(\.\d+){0,1}|\.\d+)(?:$|[)}, ])/);
|
|
if (matches) {
|
|
if (matches[0]) {
|
|
result.is = true;
|
|
}
|
|
if (matches[1]) {
|
|
result.opsBetween = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isNumberBefore(before) {
|
|
if (before.trim().search(/(?:^|[/(){},\s])(\d+(\.\d+){0,1}|\.\d+)$/) !== -1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function isVariableBefore(before) {
|
|
return before.trim().search(/\$[a-zA-Z_0-9-]+$/) !== -1;
|
|
}
|
|
|
|
function isVariableAfter(after) {
|
|
var result = { is: false, opsBetween: false };
|
|
var matches = after.match(/^\s*([+/*%-]\s*)*\$/);
|
|
if (matches) {
|
|
if (matches[0]) {
|
|
result.is = true;
|
|
}
|
|
if (matches[1]) {
|
|
result.opsBetween = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function isFunctionBefore(before) {
|
|
return before.trim().search(/[a-zA-Z0-9_-]\(.*?\)\s*$/) !== -1;
|
|
}
|
|
|
|
function isFunctionAfter(after) {
|
|
var result = { is: false, opsBetween: false };
|
|
// `-fn()` is a valid function name, so if a - should be a sign/operator,
|
|
// it must have a space after
|
|
var matches = after.match(/^\s*(-\s+|[+/*%]\s*)*[a-zA_Z_-][a-zA-Z_0-9-]*\(/);
|
|
if (matches) {
|
|
if (matches[0]) {
|
|
result.is = true;
|
|
}
|
|
if (matches[1]) {
|
|
result.opsBetween = true;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks if the input string is a hex color value
|
|
*
|
|
* @param {String} string - the input
|
|
* @return {Boolean} true, if the input is a hex color
|
|
*/
|
|
function isHexColor(string) {
|
|
return string.trim().search(/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/) !== -1;
|
|
}
|
|
|
|
function isHexColorAfter(after) {
|
|
var afterTrimmed = after.match(/(.*?)(?:[)},+/*%-]|\s|$)/)[1].trim();
|
|
return isHexColor(afterTrimmed);
|
|
}
|
|
|
|
function isHexColorBefore(before) {
|
|
if (before.search(/(?:[/(){},+/*%-\s]|^)#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/) !== -1) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if there is no operand before the currenc char
|
|
* In other words, the current char is at the start of a possible operation,
|
|
* e.g. at the string start, after the opening paren or after a comma
|
|
*
|
|
* @param {String} string - the input string
|
|
* @param {Number} index - current char's position in string
|
|
* @return {Boolean}
|
|
*/
|
|
function isNoOperandBefore(string, index) {
|
|
var before = string.substring(0, index).trim();
|
|
return before.length === 0 || before.search(/[({,]&/) !== -1;
|
|
}
|
|
|
|
function isPrecedingOperator(string, index) {
|
|
var prevCharIndex = -1;
|
|
for (var i = index - 1; i >= 0; i--) {
|
|
if (string[i].search(/\s/) === -1) {
|
|
prevCharIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (prevCharIndex === -1) {
|
|
return false;
|
|
}
|
|
|
|
if (mathOperatorCharType(string, prevCharIndex) === "op") {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
} |