123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports["default"] = findOperators;
- exports.isInsideFunctionCall = isInsideFunctionCall;
- 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.globalIndex - 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);
- var lastMode = modesEntered[lastModeIndex];
- // If entering/exiting a string
- if (character === '"' || character === "'") {
- if (lastMode && lastMode.isCalculationEnabled === true) {
- modesEntered.push({
- mode: "string",
- isCalculationEnabled: false,
- character: character
- });
- lastModeIndex++;
- } else if (lastMode && lastMode.mode === "string" && lastMode.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 (lastMode && lastMode.isCalculationEnabled === false) {
- continue;
- }
- // If it's a math operator
- if (mathOperators.includes(character) && 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 (!["+", "/", "-", "*", "%"].includes(string[index])) {
- return "char";
- }
- var character = string[index];
- var prevCharacter = string[index - 1];
- if (prevCharacter !== "\\") {
- // ---- Processing + characters
- if (character === "+") {
- return checkPlus(string, index, isAfterColon);
- }
- // ---- Processing - characters
- if (character === "-") {
- return checkMinus(string, index);
- }
- // ---- Processing * character
- if (character === "*") {
- return checkMultiplication(string, index);
- }
- // ---- Processing % character
- if (character === "%") {
- return checkPercent(string, index);
- }
- // ---- Processing / character
- // https://sass-lang.com/documentation/operators/numeric#slash-separated-values
- if (character === "/") {
- return checkSlash(string, index, isAfterColon);
- }
- }
- return "char";
- }
- // --------------------------------------------------------------------------
- // Functions for checking particular characters (+, -, /)
- // --------------------------------------------------------------------------
- /**
- * Checks the specified `*` char type: operator, sign (*), 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 positive number,
- * • "char" if it is a part of a string or identifier,
- * • false - if it is none from above (most likely an error)
- */
- function checkMultiplication(string, index) {
- var insideFn = isInsideFunctionCall(string, index);
- if (insideFn.is && insideFn.fn) {
- var fnArgsReg = new RegExp(insideFn.fn + "\\(([^)]+)\\)");
- var fnArgs = string.match(fnArgsReg);
- var isSingleMultiplicationChar = Array.isArray(fnArgs) && fnArgs[1] === "*";
- // e.g. selector(:has(*))
- if (isSingleMultiplicationChar) {
- return "char";
- }
- }
- return "op";
- }
- /**
- * 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 different,
- // 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);
- var isInsideFunctionCall_ = isInsideFunctionCall(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. `#{10px -1}`, `#{math.acos(-0.5)}`
- if (isInsideInterpolation(string, index)) {
- // e.g. `url(https://my-url.com/image-#{$i -2}-dark.svg)`
- if (isInsideFunctionCall_.fn === "url") {
- return "op";
- }
- if (isInsideFunctionCall_.is && (isValueWithUnitAfter_.is && !isValueWithUnitAfter_.opsBetween || isNumberAfter_.is && !isNumberAfter_.opsBetween)) {
- return "sign";
- }
- // e.g. `#{$i * -10}px`
- if (isWhitespaceBefore && isNumberAfter_.is && isPrecedingOperator_) {
- return "sign";
- }
- 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";
- }
- // e.g. `#{10px- 1}`
- if (isInsideInterpolation(string, index)) {
- 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 character 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";
- }
- // having a dot before probably means a relative path.
- // e.g. url(../../image.png)
- if (isDotBefore(before)) {
- return "char";
- }
- // e.g. `(1px/1)`, `fn(7 / 15)`, but not `url(8/11)`
- var isInsideFn = isInsideFunctionCall(string, index);
- if (isInsideFn.is && isInsideFn.fn === "url") {
- // e.g. `url(https://my-url.com/image-#{$i /2}-dark.svg)`
- if (isInsideInterpolation(string, index)) {
- return "op";
- }
- 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*[+*%][^(){},]+$/) !== -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]+-(\.)?\d/) !== -1 ||
- // Or if there is a number before anything but string after (not `10s/1-str`,)
- after.search(/^(\d*\.)?\d+-\s*[^#a-zA-Z_\s]/) !== -1) {
- // console.log("/, math op around")
- return "op";
- }
- 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 interpolation
- // 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();
- return before.search(/(?:^|[,{\s])\([^(){},]+$/) !== -1 && after.search(/^[^(){},\s]+\s*\)/) !== -1;
- }
- function isInsideInterpolation(string, index) {
- var before = string.substring(0, index).trim();
- return before.search(/#{[^}]*$/) !== -1;
- }
- /**
- * Checks if the character is inside a function arguments
- *
- * @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_-][\w-]*\()?(:?[a-zA-Z_-][\w-]*)\(/);
- 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_][\w-]*|-+[a-zA-Z_][\w-]*)$/) !== -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`
- return stringTrimmed.search(/^([a-zA-Z_][\w-]*|-+[a-zA-Z_][\w-]*)(?:$|[)}, ])/) !== -1;
- }
- 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
- return before.trim().search(/(^|[/(, .])\d[\w-]+$/) !== -1;
- }
- 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+)?|\.\d+)[\w-%]+(?:$|[)}, ])/);
- 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+)?|\.\d+)(?:$|[)}, ])/);
- if (matches) {
- if (matches[0]) {
- result.is = true;
- }
- if (matches[1]) {
- result.opsBetween = true;
- }
- }
- return result;
- }
- function isNumberBefore(before) {
- return before.trim().search(/(?:^|[/(){},\s])(\d+(\.\d+)?|\.\d+)$/) !== -1;
- }
- function isVariableBefore(before) {
- return before.trim().search(/\$[\w-]+$/) !== -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 isDotBefore(before) {
- return before.slice(-1) === ".";
- }
- function isFunctionBefore(before) {
- return before.trim().search(/[\w-]\(.*?\)\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_-][\w-]*\(/);
- 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(/^#([\da-fA-F]{3}|[\da-fA-F]{6})$/) !== -1;
- }
- function isHexColorAfter(after) {
- var afterTrimmed = after.match(/(.*?)(?:[)},+/*%\-\s]|$)/)[1].trim();
- return isHexColor(afterTrimmed);
- }
- function isHexColorBefore(before) {
- return before.search(/(?:[/(){},+*%-\s]|^)#([\da-fA-F]{3}|[\da-fA-F]{6})$/) !== -1;
- }
- /**
- * Checks if there is no operand before the current 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;
- }
|