123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515 |
- 'use strict'
- var config = require('./config.js')
- var util = require('./util.js')
- module.exports = {
- 'name': 'rtlcss',
- 'priority': 100,
- 'directives': {
- 'control': {
- 'ignore': {
- 'expect': { 'atrule': true, 'comment': true, 'decl': true, 'rule': true },
- 'endNode': null,
- 'begin': function (node, metadata, context) {
- // find the ending node in case of self closing directive
- if (!this.endNode && metadata.begin && metadata.end) {
- var n = node
- while (n && n.nodes) {
- n = n.nodes[n.nodes.length - 1]
- }
- this.endNode = n
- }
- var prevent = true
- if (node.type === 'comment' && node.text.match(/^\s*!?\s*rtl:end:ignore/)) {
- prevent = false
- }
- return prevent
- },
- 'end': function (node, metadata, context) {
- // end if:
- // 1. block directive and the node is comment
- // 2. self closing directive and node is endNode
- if (metadata.begin !== metadata.end && node.type === 'comment' || metadata.begin && metadata.end && node === this.endNode) {
- // clear ending node
- this.endNode = null
- return true
- }
- return false
- }
- },
- 'rename': {
- 'expect': {'rule': true},
- 'begin': function (node, metadata, context) {
- node.selector = context.util.applyStringMap(node.selector, false)
- return false
- },
- 'end': function (node, context) {
- return true
- }
- },
- 'raw': {
- 'expect': {'self': true},
- 'begin': function (node, metadata, context) {
- var nodes = context.postcss.parse(metadata.param)
- node.parent.insertBefore(node, nodes)
- return true
- },
- 'end': function (node, context) {
- return true
- }
- },
- 'remove': {
- 'expect': {'atrule': true, 'rule': true, 'decl': true},
- 'begin': function (node, metadata, context) {
- var prevent = false
- switch (node.type) {
- case 'atrule':
- case 'rule':
- case 'decl':
- prevent = true
- node.remove()
- }
- return prevent
- },
- 'end': function (node, metadata, context) {
- return true
- }
- },
- 'options': {
- 'expect': {'self': true},
- 'stack': [],
- 'begin': function (node, metadata, context) {
- this.stack.push(util.extend({}, context.config))
- var options
- try {
- options = JSON.parse(metadata.param)
- } catch (e) {
- throw node.error('Invlaid options object', { 'details': e })
- }
- context.config = config.configure(options, context.config.plugins)
- context.util = util.configure(context.config)
- return true
- },
- 'end': function (node, metadata, context) {
- var config = this.stack.pop()
- if (config && !metadata.begin) {
- context.config = config
- context.util = util.configure(context.config)
- }
- return true
- }
- },
- 'config': {
- 'expect': {'self': true},
- 'expr': {
- 'fn': /function([^\(]*)\(([^\(\)]*?)\)[^\{]*\{([^]*)\}/ig,
- 'rx': /\/([^\/]*)\/(.*)/ig
- },
- 'stack': [],
- 'begin': function (node, metadata, context) {
- this.stack.push(util.extend({}, context.config))
- var configuration
- try {
- configuration = eval('(' + metadata.param + ')') // eslint-disable-line no-eval
- } catch (e) {
- throw node.error('Invlaid config object', { 'details': e })
- }
- context.config = config.configure(configuration.options, configuration.plugins)
- context.util = util.configure(context.config)
- return true
- },
- 'end': function (node, metadata, context) {
- var config = this.stack.pop()
- if (config && !metadata.begin) {
- context.config = config
- context.util = util.configure(context.config)
- }
- return true
- }
- }
- },
- 'value': [
- {
- 'name': 'ignore',
- 'action': function (decl, expr, context) {
- return true
- }
- },
- {
- 'name': 'prepend',
- 'action': function (decl, expr, context) {
- var prefix = ''
- decl.raws.value.raw.replace(expr, function (m, v) {
- prefix += v
- })
- decl.value = decl.raws.value.raw = prefix + decl.raws.value.raw
- return true
- }
- },
- {
- 'name': 'append',
- 'action': function (decl, expr, context) {
- decl.value = decl.raws.value.raw = decl.raws.value.raw.replace(expr, function (match, value) {
- return match + value
- })
- return true
- }
- },
- {
- 'name': 'insert',
- 'action': function (decl, expr, context) {
- decl.value = decl.raws.value.raw = decl.raws.value.raw.replace(expr, function (match, value) {
- return value + match
- })
- return true
- }
- },
- {
- 'name': '',
- 'action': function (decl, expr, context) {
- decl.raws.value.raw.replace(expr, function (match, value) {
- decl.value = decl.raws.value.raw = value + match
- })
- return true
- }
- }
- ]
- },
- 'processors': [
- {
- 'name': 'variable',
- 'expr': /^--/im,
- 'action': function (prop, value) {
- return { 'prop': prop, 'value': value }
- }
- },
- {
- 'name': 'direction',
- 'expr': /direction/im,
- 'action': function (prop, value, context) {
- return { 'prop': prop, 'value': context.util.swapLtrRtl(value) }
- }
- },
- {
- 'name': 'left',
- 'expr': /left/im,
- 'action': function (prop, value, context) {
- return { 'prop': prop.replace(this.expr, function () { return 'right' }), 'value': value }
- }
- },
- {
- 'name': 'right',
- 'expr': /right/im,
- 'action': function (prop, value, context) {
- return { 'prop': prop.replace(this.expr, function () { return 'left' }), 'value': value }
- }
- },
- {
- 'name': 'four-value syntax',
- 'expr': /^(margin|padding|border-(color|style|width))$/ig,
- 'cache': null,
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'match': /[^\s\uFFFD]+/g
- }
- }
- var state = context.util.guardFunctions(value)
- var result = state.value.match(this.cache.match)
- if (result && result.length === 4 && (state.store.length > 0 || result[1] !== result[3])) {
- var i = 0
- state.value = state.value.replace(this.cache.match, function () {
- return result[(4 - i++) % 4]
- })
- }
- return { 'prop': prop, 'value': context.util.unguardFunctions(state) }
- }
- },
- {
- 'name': 'border radius',
- 'expr': /border-radius/ig,
- 'cache': null,
- 'flip': function (value) {
- var parts = value.match(this.cache.match)
- var i
- if (parts) {
- switch (parts.length) {
- case 2:
- i = 1
- if (parts[0] !== parts[1]) {
- value = value.replace(this.cache.match, function () {
- return parts[i--]
- })
- }
- break
- case 3:
- // preserve leading whitespace.
- value = value.replace(this.cache.white, function (m) {
- return m + parts[1] + ' '
- })
- break
- case 4:
- i = 0
- if (parts[0] !== parts[1] || parts[2] !== parts[3]) {
- value = value.replace(this.cache.match, function () {
- return parts[(5 - i++) % 4]
- })
- }
- break
- }
- }
- return value
- },
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'match': /[^\s\uFFFD]+/g,
- 'slash': /[^\/]+/g,
- 'white': /(^\s*)/
- }
- }
- var state = context.util.guardFunctions(value)
- state.value = state.value.replace(this.cache.slash, function (m) {
- return this.flip(m)
- }.bind(this))
- return { 'prop': prop, 'value': context.util.unguardFunctions(state) }
- }
- },
- {
- 'name': 'shadow',
- 'expr': /shadow/ig,
- 'cache': null,
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'replace': /[^,]+/g
- }
- }
- var colorSafe = context.util.guardHexColors(value)
- var funcSafe = context.util.guardFunctions(colorSafe.value)
- funcSafe.value = funcSafe.value.replace(this.cache.replace, function (m) { return context.util.negate(m) })
- colorSafe.value = context.util.unguardFunctions(funcSafe)
- return { 'prop': prop, 'value': context.util.unguardHexColors(colorSafe) }
- }
- },
- {
- 'name': 'transform origin',
- 'expr': /transform-origin/ig,
- 'cache': null,
- 'flip': function (value, context) {
- if (value === '0') {
- value = '100%'
- } else if (value.match(this.cache.percent)) {
- value = context.util.complement(value)
- } else if (value.match(this.cache.length)) {
- value = context.util.flipLength(value)
- }
- return value
- },
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'match': context.util.regex(['calc', 'percent', 'length'], 'g'),
- 'percent': context.util.regex(['calc', 'percent'], 'i'),
- 'length': context.util.regex(['length'], 'gi'),
- 'xKeyword': /(left|right)/i
- }
- }
- if (value.match(this.cache.xKeyword)) {
- value = context.util.swapLeftRight(value)
- } else {
- var state = context.util.guardFunctions(value)
- var parts = state.value.match(this.cache.match)
- if (parts && parts.length > 0) {
- parts[0] = this.flip(parts[0], context)
- state.value = state.value.replace(this.cache.match, function () { return parts.shift() })
- value = context.util.unguardFunctions(state)
- }
- }
- return { 'prop': prop, 'value': value }
- }
- },
- {
- 'name': 'transform',
- 'expr': /^(?!text\-).*?transform$/ig,
- 'cache': null,
- 'flip': function (value, process, context) {
- var i = 0
- return value.replace(this.cache.unit, function (num) {
- return process(++i, num)
- })
- },
- 'flipMatrix': function (value, context) {
- return this.flip(value, function (i, num) {
- if (i === 2 || i === 3 || i === 5) {
- return context.util.negate(num)
- }
- return num
- }, context)
- },
- 'flipMatrix3D': function (value, context) {
- return this.flip(value, function (i, num) {
- if (i === 2 || i === 4 || i === 5 || i === 13) {
- return context.util.negate(num)
- }
- return num
- }, context)
- },
- 'flipRotate3D': function (value, context) {
- return this.flip(value, function (i, num) {
- if (i === 2 || i === 4) {
- return context.util.negate(num)
- }
- return num
- }, context)
- },
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'negatable': /((translate)(x|3d)?|rotate(z)?)$/ig,
- 'unit': context.util.regex(['calc', 'number'], 'g'),
- 'matrix': /matrix$/i,
- 'matrix3D': /matrix3d$/i,
- 'skewXY': /skew(x|y)?$/i,
- 'rotate3D': /rotate3d$/i
- }
- }
- var state = context.util.guardFunctions(value)
- return {
- 'prop': prop,
- 'value': context.util.unguardFunctions(state, function (v, n) {
- if (n.length) {
- if (n.match(this.cache.matrix3D)) {
- v = this.flipMatrix3D(v, context)
- } else if (n.match(this.cache.matrix)) {
- v = this.flipMatrix(v, context)
- } else if (n.match(this.cache.rotate3D)) {
- v = this.flipRotate3D(v, context)
- } else if (n.match(this.cache.skewXY)) {
- v = context.util.negateAll(v)
- } else if (n.match(this.cache.negatable)) {
- v = context.util.negate(v)
- }
- }
- return v
- }.bind(this))
- }
- }
- },
- {
- 'name': 'transition',
- 'expr': /transition(-property)?$/i,
- 'action': function (prop, value, context) {
- return { 'prop': prop, 'value': context.util.swapLeftRight(value) }
- }
- },
- {
- 'name': 'background',
- 'expr': /background(-position(-x)?|-image)?$/i,
- 'cache': null,
- 'flip': function (value, context) {
- var state = util.saveTokens(value, true)
- var parts = state.value.match(this.cache.match)
- if (parts && parts.length > 0) {
- var keywords = (state.value.match(this.cache.position) || '').length
- if (/* edge offsets */ parts.length >= 3 || /* keywords only */ keywords === 2) {
- state.value = util.swapLeftRight(state.value)
- } else {
- parts[0] = parts[0] === '0'
- ? '100%'
- : (parts[0].match(this.cache.percent)
- ? context.util.complement(parts[0])
- : (parts[0].match(this.cache.length)
- ? context.util.flipLength(parts[0])
- : context.util.swapLeftRight(parts[0])))
- state.value = state.value.replace(this.cache.match, function () { return parts.shift() })
- }
- }
- return util.restoreTokens(state)
- },
- 'update': function (context, value, name) {
- if (name.match(this.cache.gradient)) {
- value = context.util.swapLeftRight(value)
- if (value.match(this.cache.angle)) {
- value = context.util.negate(value)
- }
- } else if (context.config.processUrls === true || context.config.processUrls.decl === true && name.match(this.cache.url)) {
- value = context.util.applyStringMap(value, true)
- }
- return value
- },
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'match': context.util.regex(['position', 'percent', 'length', 'calc'], 'ig'),
- 'percent': context.util.regex(['calc', 'percent'], 'i'),
- 'position': context.util.regex(['position'], 'g'),
- 'length': context.util.regex(['length'], 'gi'),
- 'gradient': /gradient$/i,
- 'angle': /\d+(deg|g?rad|turn)/i,
- 'url': /^url/i
- }
- }
- var colorSafe = context.util.guardHexColors(value)
- var funcSafe = context.util.guardFunctions(colorSafe.value)
- var parts = funcSafe.value.split(',')
- var lprop = prop.toLowerCase()
- if (lprop !== 'background-image') {
- for (var x = 0; x < parts.length; x++) {
- parts[x] = this.flip(parts[x], context)
- }
- }
- funcSafe.value = parts.join(',')
- colorSafe.value = context.util.unguardFunctions(funcSafe, this.update.bind(this, context))
- return {
- 'prop': prop,
- 'value': context.util.unguardHexColors(colorSafe)
- }
- }
- },
- {
- 'name': 'keyword',
- 'expr': /float|clear|text-align/i,
- 'action': function (prop, value, context) {
- return { 'prop': prop, 'value': context.util.swapLeftRight(value) }
- }
- },
- {
- 'name': 'cursor',
- 'expr': /cursor/i,
- 'cache': null,
- 'update': function (context, value, name) {
- if (context.config.processUrls === true || context.config.processUrls.decl === true && name.match(this.cache.url)) {
- value = context.util.applyStringMap(value, true)
- }
- return value
- },
- 'flip': function (value) {
- return value.replace(this.cache.replace, function (s, m) {
- return s.replace(m, m.replace(this.cache.e, '*').replace(this.cache.w, 'e').replace(this.cache.star, 'w'))
- }.bind(this))
- },
- 'action': function (prop, value, context) {
- if (this.cache === null) {
- this.cache = {
- 'replace': /\b(ne|nw|se|sw|nesw|nwse)-resize/ig,
- 'url': /^url/i,
- 'e': /e/i,
- 'w': /w/i,
- 'star': /\*/i
- }
- }
- var state = context.util.guardFunctions(value)
- var parts = state.value.split(',')
- for (var x = 0; x < parts.length; x++) {
- parts[x] = this.flip(parts[x])
- }
- state.value = parts.join(',')
- return {
- 'prop': prop,
- 'value': context.util.unguardFunctions(state, this.update.bind(this, context))
- }
- }
- }
- ]
- }
|