123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- 'use strict'
- var config
- var CHAR_COMMENT_REPLACEMENT = '\uFFFD' // �
- var CHAR_TOKEN_REPLACEMENT = '\u00A4'// ¤
- var CHAR_TOKEN_START = '\u00AB' // «
- var CHAR_TOKEN_END = '\u00BB' // »
- var REGEX_COMMENT_REPLACEMENT = new RegExp(CHAR_COMMENT_REPLACEMENT, 'ig')
- var REGEX_TOKEN_REPLACEMENT = new RegExp(CHAR_TOKEN_REPLACEMENT, 'ig')
- var PATTERN_NUMBER = '\\-?(\\d*?\\.\\d+|\\d+)'
- var PATTERN_NUMBER_WITH_CALC = '(calc' + CHAR_TOKEN_REPLACEMENT + ')|(' + PATTERN_NUMBER + ')(?!d\\()'
- var PATTERN_TOKEN = CHAR_TOKEN_START + '\\d+:\\d+' + CHAR_TOKEN_END // «offset:index»
- var PATTERN_TOKEN_WITH_NAME = '\\w*?' + CHAR_TOKEN_START + '\\d+:\\d+' + CHAR_TOKEN_END // «offset:index»
- var REGEX_COMMENT = /\/\*[^]*?\*\//igm // none-greedy
- var REGEX_DIRECTIVE = /\/\*\s*(?:!)?\s*rtl:[^]*?\*\//img
- var REGEX_ESCAPE = /[.*+?^${}()|[\]\\]/g
- var REGEX_FUNCTION = /\([^\(\)]+\)/i
- var REGEX_HEX_COLOR = /#[a-f0-9]{3,6}/ig
- var REGEX_CALC = /calc/
- var REGEX_TOKENS = new RegExp(PATTERN_TOKEN, 'ig')
- var REGEX_TOKENS_WITH_NAME = new RegExp(PATTERN_TOKEN_WITH_NAME, 'ig')
- var REGEX_COMPLEMENT = new RegExp(PATTERN_NUMBER_WITH_CALC, 'i')
- var REGEX_NEGATE_ALL = new RegExp(PATTERN_NUMBER_WITH_CALC, 'ig')
- var REGEX_NEGATE_ONE = new RegExp(PATTERN_NUMBER_WITH_CALC, 'i')
- var DEFAULT_STRING_MAP_OPTIONS = { scope: '*', ignoreCase: true }
- var TOKEN_ID = 0
- function compare (what, to, ignoreCase) {
- if (ignoreCase) {
- return what.toLowerCase() === to.toLowerCase()
- }
- return what === to
- }
- function escapeRegExp (string) {
- return string.replace(REGEX_ESCAPE, '\\$&')
- }
- module.exports = {
- extend: function (dest, src) {
- if (typeof dest === 'undefined' || typeof dest !== 'object') {
- dest = {}
- }
- for (var prop in src) {
- if (!dest.hasOwnProperty(prop)) {
- dest[prop] = src[prop]
- }
- }
- return dest
- },
- swap: function (value, a, b, options) {
- var expr = escapeRegExp(a) + '|' + escapeRegExp(b)
- options = options || DEFAULT_STRING_MAP_OPTIONS
- var greedy = options.hasOwnProperty('greedy') ? options.greedy : config.greedy
- if (!greedy) {
- expr = '\\b(' + expr + ')\\b'
- }
- var flags = options.ignoreCase ? 'img' : 'mg'
- return value.replace(new RegExp(expr, flags), function (m) { return compare(m, a, options.ignoreCase) ? b : a })
- },
- swapLeftRight: function (value) {
- return this.swap(value, 'left', 'right')
- },
- swapLtrRtl: function (value) {
- return this.swap(value, 'ltr', 'rtl')
- },
- applyStringMap: function (value, isUrl) {
- var result = value
- for (var x = 0; x < config.stringMap.length; x++) {
- var map = config.stringMap[x]
- var options = this.extend(map.options, DEFAULT_STRING_MAP_OPTIONS)
- if (options.scope === '*' || (isUrl && options.scope === 'url') || (!isUrl && options.scope === 'selector')) {
- if (Array.isArray(map.search) && Array.isArray(map.replace)) {
- for (var mapIndex = 0; mapIndex < map.search.length; mapIndex++) {
- result = this.swap(result, map.search[mapIndex], map.replace[mapIndex % map.search.length], options)
- }
- } else {
- result = this.swap(result, map.search, map.replace, options)
- }
- if (map.exclusive === true) {
- break
- }
- }
- }
- return result
- },
- negate: function (value) {
- var state = this.saveTokens(value)
- state.value = state.value.replace(REGEX_NEGATE_ONE, function (num) {
- return REGEX_TOKEN_REPLACEMENT.test(num) ? num.replace(REGEX_TOKEN_REPLACEMENT, function (m) { return '(-1*' + m + ')' }) : parseFloat(num, 10) * -1
- })
- return this.restoreTokens(state)
- },
- negateAll: function (value) {
- var state = this.saveTokens(value)
- state.value = state.value.replace(REGEX_NEGATE_ALL, function (num) {
- return REGEX_TOKEN_REPLACEMENT.test(num) ? num.replace(REGEX_TOKEN_REPLACEMENT, function (m) { return '(-1*' + m + ')' }) : parseFloat(num, 10) * -1
- })
- return this.restoreTokens(state)
- },
- complement: function (value) {
- var state = this.saveTokens(value)
- state.value = state.value.replace(REGEX_COMPLEMENT, function (num) {
- return REGEX_TOKEN_REPLACEMENT.test(num) ? num.replace(REGEX_TOKEN_REPLACEMENT, function (m) { return '(100% - ' + m + ')' }) : 100 - parseFloat(num, 10)
- })
- return this.restoreTokens(state)
- },
- flipLength: function (value) {
- return config.useCalc ? 'calc(100% - ' + value + ')' : value
- },
- save: function (what, who, replacement, restorer, exclude) {
- var state = {
- value: who,
- store: [],
- replacement: replacement,
- restorer: restorer
- }
- state.value = state.value.replace(what, function (c) {
- if (exclude && c.match(exclude)) {
- return c
- } else {
- state.store.push(c); return state.replacement
- }
- })
- return state
- },
- restore: function (state) {
- var index = 0
- var result = state.value.replace(state.restorer, function () {
- return state.store[index++]
- })
- state.store.length = 0
- return result
- },
- saveComments: function (value) {
- return this.save(REGEX_COMMENT, value, CHAR_COMMENT_REPLACEMENT, REGEX_COMMENT_REPLACEMENT)
- },
- restoreComments: function (state) {
- return this.restore(state)
- },
- saveTokens: function (value, excludeCalc) {
- return excludeCalc === true
- ? this.save(REGEX_TOKENS_WITH_NAME, value, CHAR_TOKEN_REPLACEMENT, REGEX_TOKEN_REPLACEMENT, REGEX_CALC)
- : this.save(REGEX_TOKENS, value, CHAR_TOKEN_REPLACEMENT, REGEX_TOKEN_REPLACEMENT)
- },
- restoreTokens: function (state) {
- return this.restore(state)
- },
- guard: function (what, who, indexed) {
- var state = {
- value: who,
- store: [],
- offset: TOKEN_ID++,
- token: CHAR_TOKEN_START + TOKEN_ID,
- indexed: indexed === true
- }
- if (state.indexed === true) {
- while (what.test(state.value)) {
- state.value = state.value.replace(what, function (m) { state.store.push(m); return state.token + ':' + state.store.length + CHAR_TOKEN_END })
- }
- } else {
- state.value = state.value.replace(what, function (m) { state.store.push(m); return state.token + CHAR_TOKEN_END })
- }
- return state
- },
- unguard: function (state, callback) {
- if (state.indexed === true) {
- var detokenizer = new RegExp('(\\w*?)' + state.token + ':(\\d+)' + CHAR_TOKEN_END, 'i')
- while (detokenizer.test(state.value)) {
- state.value = state.value.replace(detokenizer, function (match, name, index) {
- var value = state.store[index - 1]
- if (typeof callback === 'function') {
- return name + callback(value, name)
- }
- return name + value
- })
- }
- return state.value
- } else {
- return state.value.replace(new RegExp('(\\w*?)' + state.token + CHAR_TOKEN_END, 'i'), function (match, name) {
- var value = state.store.shift()
- if (typeof callback === 'function') {
- return name + callback(value, name)
- }
- return name + value
- })
- }
- },
- guardHexColors: function (value) {
- return this.guard(REGEX_HEX_COLOR, value, true)
- },
- unguardHexColors: function (state, callback) {
- return this.unguard(state, callback)
- },
- guardFunctions: function (value) {
- return this.guard(REGEX_FUNCTION, value, true)
- },
- unguardFunctions: function (state, callback) {
- return this.unguard(state, callback)
- },
- trimDirective: function (value) {
- return value.replace(REGEX_DIRECTIVE, '')
- },
- regexCache: {},
- regexDirective: function (name) {
- // /(?:\/\*(?:!)?rtl:ignore(?::)?)([^]*?)(?:\*\/)/img
- this.regexCache[name] = this.regexCache[name] || new RegExp('(?:\\/\\*\\s*(?:!)?\\s*rtl:' + (name ? escapeRegExp(name) + '(?::)?' : '') + ')([^]*?)(?:\\*\\/)', 'img')
- return this.regexCache[name]
- },
- regex: function (what, options) {
- what = what || []
- var expression = ''
- for (var x = 0; x < what.length; x++) {
- switch (what[x]) {
- case 'percent':
- expression += '|(' + PATTERN_NUMBER + '%)'
- break
- case 'length':
- expression += '|(' + PATTERN_NUMBER + ')(?:ex|ch|r?em|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc)?'
- break
- case 'number':
- expression += '|(' + PATTERN_NUMBER + ')'
- break
- case 'position':
- expression += '|(left|center|right|top|bottom)'
- break
- case 'calc':
- expression += '|(calc' + PATTERN_TOKEN + ')'
- break
- }
- }
- return new RegExp(expression.slice(1), options)
- },
- isLastOfType: function (node) {
- var isLast = true
- var next = node.next()
- while (next) {
- if (next && next.type === node.type) {
- isLast = false
- break
- }
- next = next.next()
- }
- return isLast
- },
- /**
- * Simple breakable each: returning false in the callback will break the loop
- * returns false if the loop was broken, otherwise true
- */
- each: function (array, callback) {
- for (var len = 0; len < array.length; len++) {
- if (callback(array[len]) === false) {
- return false
- }
- }
- return true
- }
- }
- module.exports.configure = function (configuration) {
- config = configuration
- return this
- }
|