Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

rtlcss

Package Overview
Dependencies
Maintainers
1
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

rtlcss - npm Package Compare versions

Comparing version 1.7.3 to 2.0.0

CHANGELOG.md

442

bin/rtlcss.js
#!/usr/bin/env node
var path = require('path'),
fs = require('fs'),
sys = require('util'),
chalk = require('chalk'),
mkdirp = require('mkdirp'),
rtlcss = require('../lib/rtlcss'),
configLoader = require('../lib/config-loader')
;
var path = require('path')
var fs = require('fs')
var sys = require('util')
var chalk = require('chalk')
var mkdirp = require('mkdirp')
var postcss = require('postcss')
var rtlcss = require('../lib/rtlcss')
var configLoader = require('../lib/config-loader')
var input, output, directory, ext,
config, currentErrorcode,
arg, args = process.argv.slice(2),
shouldBreak = false
;
var input, output, directory, ext, config, currentErrorcode, arg
var args = process.argv.slice(2)
var shouldBreak = false
process.on('exit', function() { process.reallyExit(currentErrorcode) });
process.on('exit', function () { process.reallyExit(currentErrorcode) })
function printWarning (){
console.log(chalk.yellow.apply(this,printWarning.arguments));
function printWarning () {
console.warn(chalk.yellow.apply(this, printWarning.arguments))
}
function printInfo (){
console.log(chalk.green.apply(this,printInfo.arguments));
function printInfo () {
console.info(chalk.green.apply(this, printInfo.arguments))
}
function printError (){
console.log(chalk.red.apply(this,printError.arguments));
function printError () {
console.error(chalk.red.apply(this, printError.arguments))
}
function printHelp(){
console.log('Usage: rtlcss [option option=parameter ...] [source] [destination]');
console.log('');
var options =
[
'Option ' , 'Description ',
'--------------' , '----------------------------------------------',
'-h,--help' , 'Print help (this message) and exit.',
'-v,--version' , 'Print version number and exit.',
'-c,--config' , 'Path to configuration settings file.',
'- ,--stdin' , 'Read from stdin stream.',
'-d,--dirctory' , 'Process all *.css files from input directory (recursive).',
'-e,--ext' , 'Used with -d option to set the output files extension.\n\t\t Default: ".rtl.css".',
'-s,--silent' , 'Silent mode, no warnings or errors are printed.'
];
for(var x=0;x<options.length;x++){
console.log(options[x++],'\t',options[x]);
function printHelp () {
console.log('Usage: rtlcss [option option=parameter ...] [source] [destination]')
console.log('')
/*eslint-disable*/
var options = [
'Option ' , 'Description ',
'--------------', '----------------------------------------------',
'-h,--help' , 'Print help (this message) and exit.',
'-v,--version' , 'Print version number and exit.',
'-c,--config' , 'Path to configuration settings file.',
'- ,--stdin' , 'Read from stdin stream.',
'-d,--dirctory' , 'Process all *.css files from input directory (recursive).',
'-e,--ext' , 'Used with -d option to set the output files extension.\n\t\t Default: ".rtl.css".',
'-s,--silent' , 'Silent mode, no warnings or errors are printed.'
]
/*eslint-enable */
for (var x = 0; x < options.length; x++) {
console.log(options[x++], '\t', options[x])
}
console.log('');
console.log('*If no destination is specified, output will be written to the same input folder as {source}.rtl.{ext}');
console.log('');
printInfo('RTLCSS version: ' + require('../package.json').version);
printInfo('Report issues to: https://github.com/MohammadYounes/rtlcss/issues');
console.log('')
console.log('*If no destination is specified, output will be written to the same input folder as {source}.rtl.{ext}')
console.log('')
printInfo('RTLCSS version: ' + require('../package.json').version)
printInfo('Report issues to: https://github.com/MohammadYounes/rtlcss/issues')
}
while(arg = args.shift()){
switch(arg){
while ((arg = args.shift())) {
switch (arg) {
case '-h':
case '--help':
printHelp();
shouldBreak = true;
break;
printHelp()
shouldBreak = true
break
case '-v':
case '--version':
printInfo('rtlcss version: ' + require('../package.json').version);
shouldBreak = true;
break;
printInfo('rtlcss version: ' + require('../package.json').version)
shouldBreak = true
break
case '-c':
case '--config':
arg = args.shift();
try
{
config = configLoader.load(path.resolve(arg));
arg = args.shift()
try {
config = configLoader.load(path.resolve(arg))
} catch (e) {
printError('rtlcss: invalid config file. ', e)
shouldBreak = true
currentErrorcode = 1
}
catch(e)
{
printError('rtlcss: invalid config file. ', e);
shouldBreak = true;
currentErrorcode = 1;
}
break;
break
case '-d':
case '--directory':
directory = true;
break;
directory = true
break
case '-e':
case '--ext':
ext = args.shift();
break;
ext = args.shift()
break
case '-s':
case '--silent':
console.log = function(){};
break;
console.log = console.info = console.warn = function () {}
break
case '-':
case '--stdin':
input = '-';
break;
input = '-'
break
default:
if(arg[0] == '-'){
printError('rtlcss: unknown option. ' + arg);
shouldBreak = true;
}else{
if(!input)
input = path.resolve(arg);
else if(!output)
output = path.resolve(arg);
if (arg[0] === '-') {
printError('rtlcss: unknown option. ' + arg)
shouldBreak = true
} else {
if (!input) {
input = path.resolve(arg)
} else if (!output) {
output = path.resolve(arg)
}
}
break;
break
}
}
if(shouldBreak){
return;
}
if (!directory && !input) {
printError('rtlcss: no input file');
console.log('');
printHelp();
return;
}
if(!config && input !== '-'){
try
{
var cwd = input;
if(directory !== true){
cwd = path.dirname(input);
if (!shouldBreak) {
if (!directory && !input) {
printError('rtlcss: no input file')
console.log('')
printHelp()
shouldBreak = true
}
if (!config && input !== '-') {
try {
var cwd = input
if (directory !== true) {
cwd = path.dirname(input)
}
config = configLoader.load(null, cwd)
} catch (e) {
printError('rtlcss: invalid config file. ', e)
currentErrorcode = 1
shouldBreak = true
}
config = configLoader.load(null, cwd);
}catch(e){
printError('rtlcss: invalid config file. ', e);
currentErrorcode = 1;
return;
}
}
if(!output && input !== '-'){
if(directory !== true){
output = path.extname(input) ? input.replace(/\.[^.]*$/, function(ext){ return '.rtl' + ext;}) : input + '.rtl';
if (!shouldBreak) {
if (!output && input !== '-') {
if (directory !== true) {
output = path.extname(input) ? input.replace(/\.[^.]*$/, function (ext) { return '.rtl' + ext }) : input + '.rtl'
} else {
output = input
}
}
else{
output = input;
}
}
var processCSSFile = function (e, data, outputName) {
if (e) {
printError('rtlcss: ' + e.message);
return;
}
var result, opt = { map: false };
if(input !== '-'){
opt.from = input;
opt.to = output;
}
if(!config){
printWarning('rtlcss: Warning! No config present, using defaults.');
result = rtlcss().process(data, opt);
}else{
if('map' in config === true && input !== '-'){
opt.map = config.map;
var processCSSFile = function (e, data, outputName) {
if (e) {
printError('rtlcss: ' + e.message)
return
}
result = rtlcss(config.options,
config.rules,
config.declarations,
config.properties).process(data, opt);
}
var result
var opt = { map: false }
if (input !== '-') {
opt.from = input
opt.to = output
}
if (!config) {
printWarning('rtlcss: Warning! No config present, using defaults.')
result = postcss([rtlcss]).process(data, opt)
} else {
if ('map' in config === true && input !== '-') {
opt.map = config.map
}
result = postcss([rtlcss.configure(config)]).process(data, opt)
}
if (output) {
var savePath = outputName;
if(directory !== true){
savePath = output;
if (output) {
var savePath = outputName
if (directory !== true) {
savePath = output
}
printInfo('Saving:', savePath)
fs.writeFile(savePath, result.css, 'utf8', function (err) { err && printError(err) })
if (result.map) {
fs.writeFile(savePath + '.map', result.map, 'utf8', function (err) { err && printError(err) })
}
} else {
sys.print(result.css)
}
printInfo('Saving:', savePath);
fs.writeFile(savePath, result.css, 'utf8', function(err){ err && printError(err); });
if(result.map){
fs.writeFile(savePath + '.map', result.map, 'utf8', function(err){ err && printError(err); } );
}
} else {
sys.print(result.css);
}
};
var walk = function(dir, done) {
fs.readdir(dir, function (error, list) {
if (error) {
return done(error);
}
var i = 0;
(function next() {
var file = list[i++];
if (!file) {
return done(null);
var walk = function (dir, done) {
fs.readdir(dir, function (error, list) {
if (error) {
return done(error)
}
file = dir + path.sep + file;
fs.stat(file, function (error, stat) {
if (stat && stat.isDirectory()) {
walk(file, function (error) {
next();
});
} else {
//process only *.css
if (/\.(css)$/.test(file)) {
//compute output directory
var relativePath = path.relative(input,file).split(path.sep);
relativePath.pop();
relativePath.push(path.basename(file).replace(".css", ext || ".rtl.css"));
var i = 0
;(function next () {
var file = list[i++]
if (!file) {
return done(null)
}
file = dir + path.sep + file
fs.stat(file, function (err, stat) {
if (err) {
printError(err)
} else if (stat && stat.isDirectory()) {
walk(file, function (err) {
if (err) {
printError(err)
} else {
next()
}
})
} else {
// process only *.css
if (/\.(css)$/.test(file)) {
// compute output directory
var relativePath = path.relative(input, file).split(path.sep)
relativePath.pop()
relativePath.push(path.basename(file).replace('.css', ext || '.rtl.css'))
//set rtl file name
var rtlFile = path.join(output,relativePath.join(path.sep));
// set rtl file name
var rtlFile = path.join(output, relativePath.join(path.sep))
//create output directory if it does not exist
var dirName = path.dirname(rtlFile);
if(!fs.existsSync(dirName))
mkdirp.sync(dirName);
// create output directory if it does not exist
var dirName = path.dirname(rtlFile)
if (!fs.existsSync(dirName)) {
mkdirp.sync(dirName)
}
//read and process the file.
fs.readFile(file, 'utf8',function(e,data){
try{
processCSSFile(e,data,rtlFile);
}catch(e){
printError('rtlcss: error processing file',file);
printError(e);
// read and process the file.
fs.readFile(file, 'utf8', function (e, data) {
try {
processCSSFile(e, data, rtlFile)
} catch (e) {
printError('rtlcss: error processing file', file)
printError(e)
}
});
})
}
next()
}
next();
})
})()
})
}
if (input !== '-') {
if (directory !== true) {
fs.stat(input, function (error, stat) {
if (error) {
printError(error)
} else if (stat && stat.isDirectory()) {
printError('rtlcss: Input expected to be a file, use -d option to process a directory.')
} else {
fs.readFile(input, 'utf8', function (e, data) {
try {
processCSSFile(e, data)
} catch (e) {
printError('rtlcss: error processing file', input)
printError(e)
}
})
}
});
})();
});
};
})
} else {
walk(input, function (error) {
if (error) {
printError('rtlcss: ' + error)
}
})
}
} else {
process.stdin.resume()
process.stdin.setEncoding('utf8')
if (input != '-') {
if(directory !== true){
fs.stat(input, function (error, stat) {
if (stat && stat.isDirectory()){
printError('rtlcss: Input expected to be a file, use -d option to process a directory.');
}else{
fs.readFile(input, 'utf8', function(e,data){
try{
processCSSFile(e,data);
}catch(e){
printError('rtlcss: error processing file',input);
printError(e);
}
});
}
});
}else{
walk(input, function(error){
if(error)
printError('rtlcss: '+ error);
});
var buffer = ''
process.stdin.on('data', function (data) {
buffer += data
})
process.on('SIGINT', function () {
processCSSFile(false, buffer)
process.exit()
})
process.stdin.on('end', function () {
processCSSFile(false, buffer)
})
}
} else {
process.stdin.resume();
process.stdin.setEncoding('utf8');
var buffer = '';
process.stdin.on('data', function(data) {
buffer += data;
});
process.on('SIGINT', function(){
processCSSFile(false, buffer);
process.exit();
});
process.stdin.on('end', function() {
processCSSFile(false, buffer);
});
}
}

@@ -1,35 +0,66 @@

var fs = require('fs'),
path = require('path'),
findup = require('findup'),
stripJSONComments = require('strip-json-comments');
/* global process */
'use strict'
var fs = require('fs')
var path = require('path')
var findup = require('findup')
var stripJSONComments = require('strip-json-comments')
var config = {}
var configSources = ['package.json', '.rtlcssrc', '.rtlcss.json']
var configSources = ['package.json', '.rtlcssrc', '.rtlcss.json'];
module.exports.load = function (configFilePath, cwd, overrides) {
if (configFilePath) {
return override(
JSON.parse(
stripJSONComments(
fs.readFileSync(configFilePath, 'utf-8').trim())), overrides)
}
function loadConfig(dir) {
var directory = cwd || process.cwd()
config = loadConfig(directory)
if (!config) {
var evns = [process.env.USERPROFILE, process.env.HOMEPATH, process.env.HOME]
for (var x = 0; x < evns.length; x++) {
if (!evns[x]) {
continue
}
config = loadConfig(evns[x])
if (config) {
break
}
}
}
if (config) {
override(config, overrides)
}
return config
}
function loadConfig (dir) {
for (var x = 0; x < configSources.length; x++) {
var found, source = configSources[x];
var found
var source = configSources[x]
try {
found = findup.sync(dir, source);
found = findup.sync(dir, source)
} catch (e) {
continue
}
catch (e) {
continue;
}
if (found) {
var configFilePath = path.normalize(path.join(found, source));
var configFilePath = path.normalize(path.join(found, source))
try {
config = JSON.parse(
stripJSONComments(
fs.readFileSync(configFilePath, 'utf-8').trim()));
stripJSONComments(
fs.readFileSync(configFilePath, 'utf-8').trim()))
} catch (e) {
throw new Error(e + ' ' + configFilePath);
throw new Error(e + ' ' + configFilePath)
}
if (source == 'package.json') {
config = config.rtlcssConfig;
if (source === 'package.json') {
config = config.rtlcssConfig
}
if (config)
return config;
if (config) {
return config
}
}

@@ -39,43 +70,13 @@ }

function override(to, from) {
function override (to, from) {
if (to && from) {
for (var p in from) {
if (typeof to[p] === 'object') {
override(to[p], from[p]);
override(to[p], from[p])
} else {
to[p] = from[p];
to[p] = from[p]
}
}
}
return to;
return to
}
module.exports.load = function (configFilePath, cwd, overrides) {
if (configFilePath) {
return override(
JSON.parse(
stripJSONComments(
fs.readFileSync(configFilePath, 'utf-8').trim())), overrides);
}
var directory = cwd || process.cwd();
var config = loadConfig(directory);
if (!config) {
var evns = [process.env.USERPROFILE, process.env.HOMEPATH, process.env.HOME];
for (var x = 0; x < evns.length; x++) {
if (!evns[x]) {
continue;
}
config = loadConfig(evns[x]);
if (config) {
break;
}
}
}
if (config) {
override(config, overrides);
}
return config;
};

@@ -1,88 +0,74 @@

function main(options, rules, declarations, properties) {
'use strict'
var options
var config = {}
var corePlugin = require('./plugin.js')
if (!options)
options = {};
function optionOrDefault (option, def) {
return option in options ? options[option] : def
}
function optionOrDefault(option, def) {
return option in options ? options[option] : def;
}
function addKey (key, def) {
config[key] = optionOrDefault(key, def)
}
var config = {};
function addKey(key, def) {
config[key] = optionOrDefault(key, def);
}
function main (opts, plugins) {
options = opts || {}
addKey('autoRename', false)
addKey('autoRenameStrict', false)
addKey('blacklist', {})
addKey('clean', true)
addKey('greedy', false)
addKey('processUrls', false)
addKey('stringMap', [])
addKey('enableLogging', false);
addKey('preserveComments', true);
addKey('preserveDirectives', false);
addKey('autoRename', true);
addKey('stringMap', []);
addKey('greedy', false);
addKey('minify', false);
//default strings map
//backward comptabile
var urlFlag = optionOrDefault('swapLeftRightInUrl', true);
if (!config.stringMap.some(function (map) { return map.name == 'left-right'; }))
config.stringMap.push({
'name': 'left-right',
'search': ['left', 'Left', 'LEFT'],
'replace': ['right', 'Right', 'RIGHT'],
options: { scope: urlFlag ? '*' : 'selector', ignoreCase: false }
});
urlFlag = optionOrDefault('swapLtrRtlInUrl', true);
if (!config.stringMap.some(function (map) { return map.name == 'ltr-rtl'; }))
config.stringMap.push({
'name': 'ltr-rtl',
'search': ['ltr', 'Ltr', 'LTR'],
'replace': ['rtl', 'Rtl', 'RTL'],
options: { scope: urlFlag ? '*' : 'selector', ignoreCase: false }
});
urlFlag = optionOrDefault('swapWestEastInUrl', true);
if (!config.stringMap.some(function (map) { return map.name == 'west-east'; }))
config.stringMap.push({
'name': 'west-east',
'search': ['west', 'West', 'WEST'],
'replace': ['east', 'East', 'EAST'],
options: { scope: urlFlag ? '*' : 'selector', ignoreCase: false }
});
if (config.minify)
config.preserveComments = config.preserveDirectives = false;
var instructions = require('./instructions.js');
config.instructions = instructions.configure(config);
if (rules != null && rules.length)
for (var x = 0; x < rules.length; x++) {
var rule = rules[x];
if (rule.important)
config.instructions.rules.unshift(rule);
else
config.instructions.rules.push(rule)
// default strings map
if (Array.isArray(config.stringMap)) {
var hasLeftRight, hasLtrRtl
for (var x = 0; x < config.stringMap.length; x++) {
var map = config.stringMap[x]
if (hasLeftRight && hasLtrRtl) {
break
} else if (map.name === 'left-right') {
hasLeftRight = true
} else if (map.name === 'ltr-rtl') {
hasLtrRtl = true
}
}
if (declarations != null && declarations.length)
for (var x = 0; x < declarations.length; x++) {
var decl = declarations[x];
if (decl.important)
config.instructions.declarations.unshift(decl);
else
config.instructions.declarations.push(decl);
if (!hasLeftRight) {
config.stringMap.push({
'name': 'left-right',
'priority': 100,
'exclusive': false,
'search': ['left', 'Left', 'LEFT'],
'replace': ['right', 'Right', 'RIGHT'],
'options': { 'scope': '*', 'ignoreCase': false }
})
}
if (properties != null && properties.length)
for (var x = 0; x < properties.length; x++) {
var prop = properties[x];
if (prop.important)
config.instructions.properties.unshift(prop);
else
config.instructions.properties.push(prop);
if (!hasLtrRtl) {
config.stringMap.push({
'name': 'ltr-rtl',
'priority': 100,
'exclusive': false,
'search': ['ltr', 'Ltr', 'LTR'],
'replace': ['rtl', 'Rtl', 'RTL'],
'options': { 'scope': '*', 'ignoreCase': false }
})
}
config.stringMap.sort(function (a, b) { return a.priority - b.priority })
}
return config;
// plugins
config.plugins = []
if (Array.isArray(plugins)) {
if (!plugins.some(function (plugin) { return plugin.name === 'rtlcss' })) {
config.plugins.push(corePlugin)
}
config.plugins = config.plugins.concat(plugins)
} else if (!plugins || plugins.name !== 'rtlcss') {
config.plugins.push(corePlugin)
}
config.plugins.sort(function (a, b) { return a.priority - b.priority })
return config
}
module.exports.configure = main;
module.exports.configure = main

@@ -1,150 +0,185 @@

(function () {
"use strict";
var config, util, postcss;
/*
* RTLCSS 2.0.0 https://github.com/MohammadYounes/rtlcss
* Framework for transforming Cascading Style Sheets (CSS) from Left-To-Right (LTR) to Right-To-Left (RTL).
* Copyright 2016 Mohammad Younes.
* Licensed under MIT <http://opensource.org/licenses/mit-license.php>
* */
'use strict'
var postcss = require('postcss')
// var directiveParser = require('./directive-parser.js')
var state = require('./state.js')
var config = require('./config.js')
var util = require('./util.js')
var RTLCSS = (function () {
function RTLCSS(options, rules, declarations, properties) {
postcss = require('postcss');
config = require('./config.js').configure(options, rules, declarations, properties);
util = require('./util.js').configure(config);
}
RTLCSS.prototype.process = function (css, options) {
return postcss(this.postcss).process(css, options);
};
RTLCSS.prototype.postcss = function (css) {
css.walkRules(function (rule, idx) {
//read previous comment
var previousComment = { 'text': '', remove: function () { } };
if (idx > 0 && rule.parent.nodes[idx - 1].type == 'comment')
previousComment = rule.parent.nodes[idx - 1];
if (!config.preserveDirectives)
previousComment.remove();
//processing instruction at rule level
for (var x = 0; x < config.instructions.rules.length; x++) {
var pi = config.instructions.rules[x];
if (("/*" + previousComment.text + "*/").match(pi.expr) && pi.action(rule)) {
util.logRuleAction(rule, pi);
return;
module.exports = postcss.plugin('rtlcss', function (options, plugins) {
var configuration = config.configure(options, plugins)
var context = {
// provides access to postcss
'postcss': postcss,
// provides access to the current configuration
'config': configuration,
// provides access to utilities object
'util': util.configure(configuration)
}
return function (css, result) {
var flipped = 0
var toBeRenamed = {}
css.walk(function (node) {
var prevent = false
state.walk(function (current) {
// check if current directive is expecting this node
if (!current.metadata.blacklist && current.directive.expect[node.type]) {
// perform action and prevent further processing if result equals true
if (current.directive.begin(node, current.metadata, context)) {
prevent = true
}
// if should end? end it.
if (current.metadata.end && current.directive.end(node, current.metadata, context)) {
state.pop(current)
}
}
})
var flipped = 0;
rule.walkDecls(function (decl, index) {
if (decl.type == "comment")
return;
//processing instruction at declaration level
for (var x = 0; x < config.instructions.declarations.length; x++) {
var pi = config.instructions.declarations[x];
if (decl.raws.value && decl.raws.value.raw && decl.raws.value.raw.match(pi.expr) && pi.action(decl)) {
flipped++;
util.logDeclAction(decl, pi);
return;
if (prevent === false) {
switch (node.type) {
case 'atrule':
// @rules requires url flipping only
if (context.config.processUrls === true || context.config.processUrls.atrule === true) {
var params = context.util.applyStringMap(node.params, true)
node.params = params
}
}
//processing instruction at property level
for (var x = 0; x < config.instructions.properties.length; x++) {
var pi = config.instructions.properties[x];
if (decl.prop.match(pi.expr)) {
var rawValue = config.preserveComments && decl.raws.value && decl.raws.value.raw ? decl.raws.value.raw : decl.value;
var result;
if (config.preserveComments) {
var commentsLessRawValue = util.saveComments(rawValue);
result = pi.action(decl.prop, commentsLessRawValue);
result.value = util.restoreComments(result.value);
break
case 'comment':
state.parse(node, result, function (current) {
var push = true
if (current.directive === null) {
current.preserve = !context.config.clean
context.util.each(context.config.plugins, function (plugin) {
var blacklist = context.config.blacklist[plugin.name]
if (blacklist && blacklist[current.metadata.name] === true) {
current.metadata.blacklist = true
if (current.metadata.end) {
push = false
}
if (current.metadata.begin) {
result.warn('directive "' + plugin.name + '.' + current.metadata.name + '" is blacklisted.', {node: current.source})
}
// break each
return false
}
current.directive = plugin.directives.control[current.metadata.name]
if (current.directive) {
// break each
return false
}
})
}
else
result = pi.action(decl.prop, rawValue);
if (result.prop != decl.prop || result.value != rawValue) {
flipped++;
util.logPropAction(decl, result, pi);
decl.prop = result.prop;
decl.value = result.value;
if (current.directive) {
if (!current.metadata.begin && current.metadata.end) {
if (current.directive.end(node, current.metadata, context)) {
state.pop(current)
}
push = false
} else if (current.directive.expect.self && current.directive.begin(node, current.metadata, context)) {
if (current.metadata.end && current.directive.end(node, current.metadata, context)) {
push = false
}
}
} else if (!current.metadata.blacklist) {
push = false
result.warn('unsupported directive "' + current.metadata.name + '".', {node: current.source})
}
break;
return push
})
break
case 'decl':
// if broken by a matching value directive .. break
if (!context.util.each(context.config.plugins,
function (plugin) {
return context.util.each(plugin.directives.value, function (directive) {
if (node.raws.value && node.raws.value.raw) {
var expr = context.util.regexDirective(directive.name)
if (expr.test(node.raws.value.raw)) {
expr.lastIndex = 0
if (directive.action(node, expr, context)) {
if (context.config.clean) {
node.raws.value.raw = context.util.trimDirective(node.raws.value.raw)
}
flipped++
// break
return false
}
}
}
})
})) break
// loop over all plugins/property processors
context.util.each(context.config.plugins, function (plugin) {
return context.util.each(plugin.processors, function (directive) {
if (node.prop.match(directive.expr)) {
var raw = node.raws.value && node.raws.value.raw ? node.raws.value.raw : node.value
var commentsLessRawValue = context.util.saveComments(raw)
var pair = directive.action(node.prop, commentsLessRawValue, context)
pair.value = context.util.restoreComments(pair.value)
if (pair.prop !== node.prop || pair.value !== raw) {
flipped++
node.prop = pair.prop
node.value = pair.value
}
// match found, break
return false
}
})
})
// if last decl, apply auto rename
if (context.config.autoRename && !flipped && context.util.isLastOfType(node)) {
var renamed = context.util.applyStringMap(node.parent.selector)
if (context.config.autoRenameStrict === true) {
var pair = toBeRenamed[renamed]
if (pair) {
pair.selector = node.parent.selector
node.parent.selector = renamed
} else {
toBeRenamed[node.parent.selector] = node.parent
}
} else {
node.parent.selector = renamed
}
}
}
});
if (config.autoRename && flipped == 0) {
rule.selector = util.applyStringMap(rule.selector, true, false);
break
case 'rule':
// new rule, reset flipped decl count to zero
flipped = 0
break
}
});
//loop @ rules
css.walkAtRules(function (atRule) {
atRule.params = util.applyStringMap(atRule.params, false, true);
if (config.minify) {
atRule.before = '';
atRule.between = '';
atRule.after = '';
}
});
if (config.minify) {
css.walkDecls(function (decl) {
decl.raws.before = '';
decl.raws.between = ':';
});
css.walkRules(function (rule) {
rule.raws.before = '';
rule.raws.between = '';
rule.raws.after = '';
});
css.walkComments(function (comment) {
comment.remove();
});
}
})
state.walk(function (item) {
result.warn('unclosed directive "' + item.metadata.name + '".', {node: item.source})
})
Object.keys(toBeRenamed).forEach(function (key) {
result.warn('renaming skipped due to lack of a matching pair.', {node: toBeRenamed[key]})
})
}
})
};
return RTLCSS;
})();
/**
* Creates a new RTLCSS instance, process the input and return its result.
* @param {String} css A string containing input CSS.
* @param {Object} options An object containing RTLCSS settings.
* @param {Object|Array} plugins An array containing a list of RTLCSS plugins or a single RTLCSS plugin.
* @returns {String} A string contining the RTLed css.
*/
module.exports.process = function (css, options, plugins) {
return postcss([this(options, plugins)]).process(css).css
}
/**
* Creates a new RTLCSS instance.
*
* @options {Object} An object containing RTLCSS settings.
* @rules {Object} An object containing RTLCSS rule level processing directives.
* @declarations {Object} An object containing RTLCSS declaration level processing directives.
* @properties {Object} An object containing RTLCSS property level processing directives.
*
* @return {Object} new RTLCSS instance.
*/
var exports = function (options, rules, declarations, properties) {
return new RTLCSS(options, rules, declarations, properties);
};
/**
* Creates a new RTLCSS instance, process the input and return its result.
*
* @css {String} A string containing input CSS.
* @options {Object} An object containing RTLCSS settings.
* @rules {Object} An object containing RTLCSS rule level processing directives.
* @declarations {Object} An object containing RTLCSS declaration level processing directives.
* @properties {Object} An object containing RTLCSS property level processing directives.
*
* @return {String} A string contining the RTLed css.
*/
exports.process = function (css, options, rules, declarations, properties) {
return new RTLCSS(options, rules, declarations, properties).process(css).css;
};
/**
* Creates a new instance of RTLCSS using the passed configuration object
*
* @config {Object} An object containing RTLCSS options, rules, declarations and properties processing directives.
*
* @return {Object} A new RTLCSS instance.
*/
exports.configure = function (config) {
config = config || {};
return new RTLCSS(config.options, config.rules, config.declarations, config.properties);
};
module.exports = exports;
}).call(this);
/**
* Creates a new instance of RTLCSS using the passed configuration object
* @param {Object} config An object containing RTLCSS options and plugins.
* @returns {Object} A new RTLCSS instance.
*/
module.exports.configure = function (config) {
config = config || {}
return postcss([this(config.options, config.plugins)])
}

@@ -1,168 +0,219 @@

function main(configuration) {
var config = configuration,
chalk = require('chalk'),
success = chalk.green,
info = chalk.cyan,
REPLACEMENT_CHARACTER = '\uFFFD',
TOKEN_CHARACTER = '\u00A7',
REGEX_COMMENT = /\/\*(.|\s)*?\*\//gm,
REGEX_FUNCTION = /\([^\(\)]+\)/i,
REGEX_TOKEN = new RegExp(TOKEN_CHARACTER + '(\\d+)', 'i'),
REGEX_COMPLEMENT = new RegExp('(calc' + TOKEN_CHARACTER + '\\d+)|(\\-?(\\d*?\\.\\d+|\\d+))(?!d\\()' ,'i'),
REGEX_NEGATE_ONE = new RegExp('(calc' + TOKEN_CHARACTER + '\\d+)|(\\-?(\\d*?\\.\\d+|\\d+))(?!d\\()' ,'i'),
REGEX_NEGATE_ALL = new RegExp('(calc' + TOKEN_CHARACTER + '\\d+)|(\\-?(\\d*?\\.\\d+|\\d+))(?!d\\()' ,'ig'),
DEFAULT_STRING_MAP_OPTIONS = { scope: '*', ignoreCase: true },
compare = function (what, to, ignoreCase) {
if (ignoreCase) {
return what.toLowerCase() === to.toLowerCase();
'use strict'
var config
var REPLACEMENT_CHARACTER = '\uFFFD'
var REGEX_REPLACEMENT_CHARACTER_PLACEHOLDER = /\uFFFD/ig
var PATTERN_NUMBER = '\\-?(\\d*?\\.\\d+|\\d+)'
var PATTERN_PLACEHOLDER = '\u00AB\\d+\u00BB' // «offset»
var REGEX_DIRECTIVE = /\/\*(?:!)?rtl:[^]*?\*\//img
var REGEX_ESCAPE = /[.*+?^${}()|[\]\\]/g
var REGEX_HEX_COLOR = /#[a-f0-9]{3,6}/ig
var REGEX_COMMENT = /\/\*[^]*?\*\//igm // none-greedy
var REGEX_FUNCTION = /\([^\(\)]+\)/i
var REGEX_TOKEN = new RegExp(PATTERN_PLACEHOLDER + '(\\d+)', 'i')
var REGEX_COMPLEMENT = new RegExp('(calc' + PATTERN_PLACEHOLDER + '\\d+)|(' + PATTERN_NUMBER + ')(?!d\\()', 'i')
var REGEX_NEGATE_ONE = new RegExp('(calc' + PATTERN_PLACEHOLDER + '\\d+)|(' + PATTERN_NUMBER + ')(?!d\\()', 'i')
var REGEX_NEGATE_ALL = new RegExp('(calc' + PATTERN_PLACEHOLDER + '\\d+)|(' + PATTERN_NUMBER + ')(?!d\\()', 'ig')
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)
}
return what === to;
},
extend = function (dest, src) {
if (typeof dest === 'undefined' || typeof dest !== 'object') {
dest = {};
if (map.exclusive === true) {
break
}
for (var prop in src) {
if (!dest.hasOwnProperty(prop)) {
dest[prop] = src[prop];
}
}
return dest;
},
escapeRegExp = function(string){
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
;
var util =
{
log: function () {
if (config.enableLogging)
console.log.apply(this, this.log.arguments);
},
logRuleAction: function (rule, pi) {
this.log("Rule:\t%s",
info(rule.selector));
this.log("Action:\t%s",
success(pi.name));
this.logSource(rule.source);
},
logDeclAction: function (decl, pi) {
this.log("Decl:\t%s",
info(decl.prop));
this.log("Action:\t%s",
success(pi.name));
this.logSource(decl.source);
},
logPropAction: function (decl, result, pi) {
this.log("Prop:\t%s",
info(decl.prop));
this.log("Action:\t%s",
success("flip " + pi.name));
this.logSource(decl.source);
},
logSource: function (source) {
if(source != undefined){
this.log("Source:\t%s\t%s:%s",
info(source.file),
info(source.start.line),
info(source.start.column));
}
return result
},
comments: [],
saveComments: function (value) {
return value.replace(REGEX_COMMENT, function (c) { this.comments.push(c); return REPLACEMENT_CHARACTER }.bind(this))
},
restoreComments: function (value) {
var index = 0
var result = value.replace(REGEX_REPLACEMENT_CHARACTER_PLACEHOLDER, function () {
return this.comments[index++]
}.bind(this))
this.comments.length = 0
return result
},
negate: function (value) {
return value.replace(REGEX_NEGATE_ONE, function (num) {
return REGEX_TOKEN.test(num) ? num.replace(REGEX_TOKEN, function (m) { return '(-1*' + m + ')' }) : parseFloat(num, 10) * -1
})
},
negateAll: function (value) {
return value.replace(REGEX_NEGATE_ALL, function (num) {
return REGEX_TOKEN.test(num) ? num.replace(REGEX_TOKEN, function (m) { return '(-1*' + m + ')' }) : parseFloat(num, 10) * -1
})
},
complement: function (value) {
return value.replace(REGEX_COMPLEMENT, function (num) {
return REGEX_TOKEN.test(num) ? num.replace(REGEX_TOKEN, function (m) { return '(100%-' + m + ')' }) : 100 - parseFloat(num, 10)
})
},
guard: function (what, who, indexed) {
var state = {
value: who,
store: [],
offset: TOKEN_ID++,
token: '\u00AB' + TOKEN_ID + '\u00BB',
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 })
}
},
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, inSelector, inUrl) {
var result = value;
for (var x = 0; x < config.stringMap.length; x++) {
var map = config.stringMap[x];
var options = extend(map.options, DEFAULT_STRING_MAP_OPTIONS);
if (options.scope === '*' || (inSelector && options.scope == 'selector') || (inUrl && options.scope == 'url')) {
this.log(info('Applying string map: ' + (map.name || "<unknown>") + '.'));
if (map.search instanceof Array && map.replace instanceof Array) {
for (var mapIndex = 0; mapIndex < map.search.length; mapIndex++) {
result = util.swap(result, map.search[mapIndex], map.replace[mapIndex % map.search.length], options);
}
} else {
result = util.swap(result, map.search, map.replace, options);
} else {
state.value = state.value.replace(what, function (m) { state.store.push(m); return state.token })
}
return state
},
unguard: function (state, callback) {
if (state.indexed === true) {
var detokenizer = new RegExp('(\\w*?)' + state.token + '(\\d+)', '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 result;
},
comments: [],
saveComments: function (value) {
var self = this;
return value.replace(REGEX_COMMENT, function (c) { self.comments.push(c); return REPLACEMENT_CHARACTER; });
},
restoreComments: function (value) {
var self = this;
return value.replace(new RegExp(REPLACEMENT_CHARACTER, 'g'), function () { return self.comments.shift(); });
},
negate: function (value) {
return value.replace(REGEX_NEGATE_ONE, function (num) {
return REGEX_TOKEN.test(num) ? num.replace(REGEX_TOKEN, function(m){ return '(-1*'+ m + ')';}) : parseFloat(num, 10) * -1;
});
},
negateAll: function (value) {
return value.replace(REGEX_NEGATE_ALL, function (num) {
return REGEX_TOKEN.test(num) ? num.replace(REGEX_TOKEN, function(m){ return '(-1*'+ m + ')';}) : parseFloat(num, 10) * -1;
});
},
complement: function (value) {
return value.replace(REGEX_COMPLEMENT, function (num) {
return REGEX_TOKEN.test(num) ? num.replace(REGEX_TOKEN, function(m){ return '(100%-'+ m + ')';}) : 100 - parseFloat(num, 10);
});
},
saveFunctions: function (value) {
var store = [];
while(REGEX_FUNCTION.test(value))
value = value.replace(REGEX_FUNCTION, function (m) { store.push(m); return TOKEN_CHARACTER + store.length; });
return { store: store, value: value };
},
restoreFunctions:function (result) {
while (REGEX_TOKEN.test(result.value))
result.value = result.value.replace(REGEX_TOKEN, function (m, i) { return result.store[i - 1]; });
return result.value
},
regex:function(what, options){
what = what || [];
var expressions = [];
for(var x=0;x<what.length;x++){
var match = '';
switch(what[x]){
case 'percent':
expressions.push('(\\-?(\\d*?\\.\\d+|\\d+)%)');
break;
case 'length':
expressions.push('(\\-?(\\d*?\\.\\d+|\\d+))(?:ex|ch|r?em|vh|vw|vmin|vmax|px|mm|cm|in|pt|pc)?');
break;
case 'number':
expressions.push('(\\-?(\\d*?\\.\\d+|\\d+))');
break;
case 'position':
expressions.push('(left|center|right|top|bottom)');
break;
case 'calc':
expressions.push('(calc' + TOKEN_CHARACTER + '\\d+)');
break;
return state.value
} else {
return state.value.replace(new RegExp('(\\w*?)' + state.token, 'i'), function (match, name) {
var value = state.store.shift()
if (typeof callback === 'function') {
return name + callback(value, name)
}
return name + value
})
}
},
saveHexColors: function (value) {
return this.guard(REGEX_HEX_COLOR, value, true)
},
restoreHexColors: function (state, callback) {
return this.unguard(state, callback)
},
saveFunctions: function (value) {
return this.guard(REGEX_FUNCTION, value, true)
},
restoreFunctions: 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('(?:\\/\\*(?:!)?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_PLACEHOLDER + '\\d+)'
break
}
return new RegExp(expressions.join('|'), options);
},
};
return util;
}
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 = main;
module.exports.configure = function (configuration) {
config = configuration
return this
}
{
"author": "Mohammad Younes",
"name": "rtlcss",
"version": "1.7.3",
"version": "2.0.0",
"description": "Framework for transforming cascading style sheets (CSS) from left-to-right (LTR) to right-to-left (RTL)",
"bugs": "https://github.com/MohammadYounes/rtlcss/issues?state=open",
"license": "MIT",
"keywords": [ "rtl", "css", "ltr", "rtlcss", "framework", "style", "mirror", "flip", "convert", "transform" ],
"keywords": [
"rtl",
"css",
"ltr",
"rtlcss",
"framework",
"style",
"mirror",
"flip",
"convert",
"transform"
],
"repository": {

@@ -21,11 +32,13 @@ "type": "git",

"mkdirp": "^0.5.1",
"strip-json-comments": "^1.0.0"
"strip-json-comments": "^2.0.0"
},
"devDependencies": {
"mocha": "^2.0.0"
"mocha": "^2.0.0",
"standard": "^6.0.0"
},
"scripts": {
"test": "mocha -R spec"
"lint": "standard && node ./lib/rtlcss.js",
"test": "npm run lint && mocha -R spec"
},
"main": "./lib/rtlcss"
"main": "./lib/rtlcss.js"
}

@@ -1,11 +0,21 @@

# RTLCSS [![GitHub version](https://badge.fury.io/gh/MohammadYounes%2Frtlcss.svg)](http://badge.fury.io/gh/MohammadYounes%2Frtlcss) [![NPM version](https://badge.fury.io/js/rtlcss.svg)](http://badge.fury.io/js/rtlcss) [![Build Status](https://travis-ci.org/MohammadYounes/rtlcss.svg?branch=master)](https://travis-ci.org/MohammadYounes/rtlcss) [![DEPENDENCIES](https://david-dm.org/MohammadYounes/rtlcss.svg)](https://david-dm.org/MohammadYounes/rtlcss) [![Twitter](https://img.shields.io/badge/follow-%40rtlcss-blue.svg)](https://twitter.com/rtlcss)
# RTLCSS
<img style="margin:15px" title="RTL CSS" src="https://cloud.githubusercontent.com/assets/4712046/5889219/190f366a-a425-11e4-8ef5-8b5f60a9e903.png" align="right"/>
[![GitHub version](https://badge.fury.io/gh/MohammadYounes%2Frtlcss.svg)](http://badge.fury.io/gh/MohammadYounes%2Frtlcss)
[![NPM version](https://badge.fury.io/js/rtlcss.svg)](http://badge.fury.io/js/rtlcss)
[![Build Status](https://travis-ci.org/MohammadYounes/rtlcss.svg?branch=master)](https://travis-ci.org/MohammadYounes/rtlcss)
[![DEPENDENCIES](https://david-dm.org/MohammadYounes/rtlcss.svg)](https://david-dm.org/MohammadYounes/rtlcss)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-blue.svg)](http://standardjs.com/)
[![editor](https://img.shields.io/badge/editor-vscode-blue.svg)](https://code.visualstudio.com/)
[![Twitter](https://img.shields.io/badge/follow-%40rtlcss-blue.svg)](https://twitter.com/rtlcss)
RTLCSS is a framework for converting Left-To-Right (LTR) Cascading Style Sheets(CSS) to Right-To-Left (RTL).
---
| [Why RTLCSS](#why-rtlcss) | [Install](#install) | [Basic Usage](#basic-usage) | [CLI](#cli) | [Advanced Usage](#advanced-usage) | [Options](#options-object)
| --- | --- | --- | --- | --- | --- |
| [Why RTLCSS](#why-rtlcss) | [Install](#install) | [Basic Usage](#basic-usage) | [CLI](#cli) | [Advanced Usage](#advanced-usage) | [Options](#options-object) |
|---------------------------|---------------------|-----------------------------|-------------|-----------------------------------|----------------------------|
---
## Introduction

@@ -90,93 +100,109 @@

### Supported CSS Properties (a-z)
### Supported CSS Properties
| | | |
|:----------------------------|:----------------------------|:-----------------------|
| `background` | `border-right-color` | `margin` |
| `background-image` | `border-right-style` | `margin-left` |
| `background-position` | `border-right-width` | `margin-right` |
| `background-position-x` | `border-style` | `padding` |
| `border-bottom-left-radius` | `border-top-left-radius` | `padding-left` |
| `border-bottom-right-radius`| `border-top-right-radius` | `padding-right` |
| `border-color` | `border-width` | `right` |
| `border-left` | `box-shadow` | `text-align` |
| `border-left-color` | `clear` | `text-shadow` |
| `border-left-style` | `cursor` | `transform` |
| `border-left-width` | `direction` | `transform-origin` |
| `border-radius` | `float` | `transition` |
| `border-right` | `left` | `transition-property` |
### Supported Directives
`background`
`background-image`
`background-position`
`background-position-x`
`border-bottom-left-radius`
`border-bottom-right-radius`
`border-color`
`border-left`
`border-left-color`
`border-left-style`
`border-left-width`
`border-radius`
`border-right`
`border-right-color`
`border-right-style`
`border-right-width`
`border-style`
`border-top-left-radius`
`border-top-right-radius`
`border-width`
`box-shadow`
`clear`
`cursor`
`direction`
`float`
`left`
`margin`
`margin-left`
`margin-right`
`padding`
`padding-left`
`padding-right`
`right`
`text-align`
`text-shadow`
`transform`
`transform-origin`
`transition`
`transition-property`
When RTLing a CSS document, there are cases where it's impossible to know whether to mirror a property value, whether to change a rule selector, or whether to update a non-directional property. In such cases, RTLCSS provides processing directives in the form of CSS comments.
Both standard ```/*rtl:...*/``` and special/important ```/*!rtl:...*/``` notations are supported.
### Supported Processing Directives
Two sets of directives are available. **Control** and **Value**.
When RTLing a CSS document, there are cases where it's impossible to know whether to mirror a property value, whether to change a rule selector, or whether to update a non-directional property. In such cases, RTLCSS provides processing directives in the form of CSS comments. Both standard ```/*rtl:...*/``` and special/important ```/*!rtl:...*/``` notations are supported.
#### Control Directives:
Two sets of processing directives are available, on Rule and Declaration level.
Control directives are placed between declarations or statements (rules and at-rules), They can target one node (self-closing) or a set of nodes (block-syntax).
##### Rule Level Directives:
> Rule level directives are placed before the CSS rule.
| Directive | Description
|:--------------|:-----------------------
| `/*rtl:ignore*/` | Ignores processing of this rule.
| `/*rtl:rename*/` | Forces selector renaming by applying [String Map](#stringmap-array).
**Example**
* Self-closing
```css
/*rtl:ignore*/
.code{
.code {
/*rtl:ignore*/
direction:ltr;
/*rtl:ignore*/
text-align:left;
}
```
**Output**
* Block-syntax
```css
.code{
.code {
/*rtl:begin:ignore*/
direction:ltr;
text-align:left;
/*rtl:end:ignore*/
}
```
##### Declaration Level Directives:
**Available Control Directives:**
> Declaration level directives are placed any where inside the declaration value.
| Name | Syntax | Description
|:-------------|:--------------------------|:-----------
| Ignore | `/*rtl:ignore*/` | Ignores processing of the following node (self-closing) or nodes within scope (block-syntax).
| Config | `/*rtl:config:{OBJECT}*/` | Evaluates the `{OBJECT}` parameter and updates current RTLCSS config<sup>*1</sup>.
| Options | `/*rtl:options:{JSON}*/` | Parses the `{JSON}` parameter and updates current RTLCSS options<sup>*2</sup>.
| Raw | `/*rtl:raw:{CSS}*/` | Parses the `{CSS}` parameter and inserts it before the comment node that triggered the directive<sup>*3</sup>.
| Remove | `/*rtl:remove*/` | Removes the following node (self-closing) or nodes within scope (block-syntax).
| Rename | `/*rtl:rename*/` | Renames the selector of the following rule (self-closing) or rules within scope (block-syntax) by applying [String Maps](#stringmap-array).
| Directive | Description
|:--------------|:-----------------------
| `/*rtl:ignore*/` | Ignores processing of this declaration.
| `/*rtl:{value}*/` | Replaces the declaration value with `{value}`.
| `/*rtl:append:{value}*/` | Appends `{value}` to the end of the declaration value.
| `/*rtl:prepend:{value}*/` | Prepends `{value}` to the begining of the declaration value.
| `/*rtl:insert:{value}*/` | Inserts `{value}` to where the directive is located inside the declaration value.
#### Remarks
**Example**
* **<sup>1</sup>** Config is evaluated using `eval`, and it can be disabled by adding it to the `blacklist`.
* **<sup>2</sup>** Options parsing is done via `JSON.parse`, and requires a valid json. The new options will override the defaults (not the current context). Plugins will be carried over from the current context.
* **<sup>3</sup>** Due to the nature of *RAW* directive, block-syntax is not supported.
**Example**
```css
/*rtl:begin:options:
{
"autoRename": true,
"stringMap":[
{
"name" : "prev-next",
"search" : ["prev", "Prev", "PREV"],
"replace" : ["next", "Next", "NEXT"],
"options" : {"ignoreCase":false}
}]
}*/
.demo-prev, .demo-Prev, .demo-PREV { content: 'p'; }
.demo-next, .demo-Next, .demo-NEXT { content: 'n'; }
/*rtl:end:options*/
```
**Output**
```css
.demo-next, .demo-Next, .demo-NEXT { content: 'p'; }
.demo-prev, .demo-Prev, .demo-PREV { content: 'n'; }
```
#### Value Directives:
Value directives are placed any where inside the declaration value. They target the containing declaration node.
**Available Value Directives:**
| Name | Syntax | Description
|:-------------|:----------------------------|:-----------------------
| Append | `/*rtl:append:{value}*/` | Appends `{value}` to the end of the declaration value.
| Ignore | `/*rtl:ignore*/` | Ignores processing of this declaration.
| Insert | `/*rtl:insert:{value}*/` | Inserts `{value}` to where the directive is located inside the declaration value.
| Prepend | `/*rtl:prepend:{value}*/` | Prepends `{value}` to the begining of the declaration value.
| Replace | `/*rtl:{value}*/` | Replaces the declaration value with `{value}`.
**Example**
```css
body{

@@ -188,3 +214,3 @@ font-family:"Droid Sans", "Helvetica Neue", Arial/*rtl:prepend:"Droid Arabic Kufi", */;

**Output**
**Output**

@@ -200,3 +226,3 @@ ```css

Convert LTR CSS files to RTL using the command line.
Convert LTR CSS files to RTL using the command line. For usage and available options see [CLI documentaion].

@@ -206,6 +232,2 @@ ```

```
For usage and available options see [CLI documentaion](https://github.com/MohammadYounes/rtlcss/blob/master/CLI.md).
---

@@ -216,7 +238,7 @@ ## Advanced usage

// directly processes css and return a string containing the processed css
output = rtlcss.process(css [, options , rules, declarations, properties]);
output = rtlcss.process(css [, options , plugins]);
output // processed CSS
// create a new RTLCSS instance, then process css with postcss options (such as source map)
result = rtlcss([options , rules, declarations, properties]).process(css, postcssOptions);
result = rtlcss([options , plugins]).process(css, postcssOptions);
result.css // Processed CSS

@@ -236,6 +258,5 @@ result.map // Source map

```javascript
var processed = postcss()
.use(rtlcss([options , rules, declarations, properties]).postcss)
.use(autoprefixer().postcss)
.process(css);
var result = postcss().use(rtlcss([options , plugins]))
.use(autoprefixer())
.process(css);
```

@@ -245,27 +266,28 @@

| Option | Default | Description
|:--------------|:------------|:--------------
|**`preserveComments`** | `true` | Preserves modified declarations comments as much as possible.
|**`preserveDirectives`** | `false` | Preserves processing directives.
|**`swapLeftRightInUrl`** | `true` | Swaps ***left*** and ***right*** in URLs.
|**`swapLtrRtlInUrl`** | `true` | Swaps ***ltr*** and ***rtl*** in URLs.
|**`swapWestEastInUrl`** | `true` | Swaps ***west*** and ***east*** in URLs.
|**`autoRename`** | `true` | Applies to CSS rules containing no directional properties, it will update the selector by applying [String Map](#stringmap-array).(See [Why Auto-Rename?](https://github.com/MohammadYounes/rtlcss/wiki/Why-Auto-Rename%3F))
|**`greedy`** | `false` | A `false` value forces selector renaming and url updates to respect word boundaries, for example: `.ultra { ...}` will not be changed to `.urtla {...}`
|**`stringMap`** | see [String Map](#stringmap-array) | Applies to string replacement in renamed selectors and updated URLs
|**`enableLogging`** | `false` | Outputs information about mirrored declarations to the console.
|**`minify`** | `false` | Minifies output CSS, when set to `true` both `preserveDirectives` and `preserveComments` will be set to `false` .
| Option | Type | Default | Description
|:------------------------|:--------------------:|:--------:|:--------------
|**`autoRename`** | `boolean` | `false` | Applies to CSS rules containing no directional properties, it will update the selector by applying [String Maps](#stringmap-array).(See [Why Auto-Rename?](https://github.com/MohammadYounes/rtlcss/wiki/Why-Auto-Rename%3F))
|**`autoRenameStrict`** | `boolean` | `false` | Ensures `autoRename` is applied only if pair exists.
|**`blacklist`** | `object` | `{}` | An object map of disabled plugins directives, where keys are plugin names and value are object hash of disabled directives. e.g. `{'rtlcss':{'config':true}}`.
|**`clean`** | `boolean` | `true` | Removes directives comments from output CSS.
|**`greedy`** | `boolean` | `false` | Fallback value for [String Maps](#stringmap-array) options.
|**`processUrls`** | `boolean` or `object`| `false` | Applies [String Maps](#stringmap-array) to URLs. You can also target specific node types using an object literal. e.g. `{'atrule': true, 'decl': false}`.
|**`stringMap`** | `array` | | The default array of [String Maps](#stringmap-array).
### stringMap (Array)
String map is a collection of map objects, where each defines a mapping between directional strings. It is used in renaming selectors and URL updates. The following is the default string map:
String map is a collection of map objects, where each defines a mapping between directional strings. It is used in renaming selectors and URL updates.
The following is the default string map:
```javascript
[
{
'name' : 'left-right',
'search' : ['left', 'Left', 'LEFT'],
'replace' : ['right', 'Right', 'RIGHT'],
'name' : 'left-right',
'priority': 100,
'search' : ['left', 'Left', 'LEFT'],
'replace' : ['right', 'Right', 'RIGHT'],
'options' : {
'scope': options.swapLeftRightInUrl ? '*' : 'selector',
'ignoreCase': false
'scope' : '*',
'ignoreCase' : false
}

@@ -275,17 +297,9 @@ },

'name' : 'ltr-rtl',
'priority': 100,
'search' : ['ltr', 'Ltr', 'LTR'],
'replace' : ['rtl', 'Rtl', 'RTL'],
'options' : {
'scope': options.swapLtrRtlInUrl ? '*' : 'selector',
'ignoreCase': false
'scope' : '*',
'ignoreCase' : false
}
},
{
'name':'west-east',
'search': ['west', 'West', 'WEST'],
'replace': ['east', 'East', 'EAST'],
'options' : {
'scope': options.swapWestEastInUrl ? '*' : 'selector',
'ignoreCase': false
}
}

@@ -296,17 +310,21 @@ ]

| Property | Type | Description
|:--------------|:----------|:--------------
| **`name`** | `string` | Name of the map object
| **`search`** | `string` or `Array` | The string or list of strings to search for or replace with.
| Property | Type | Description
|:-----------------|:----------|:--------------
| **`name`** | `string` | Name of the map object
| **`priority`** | `number` | Maps are sorted according to prioirity.
| **`exclusive`**| `boolean` | When enabled, prevents applying the remaining maps.
| **`search`** | `string` or `Array` | The string or list of strings to search for or replace with.
| **`replace`** | `string` or `Array` | The string or list of strings to search for or replace with.
| **`options`** | `object` | Defines map options.
The map `options` is optional, and consists of the following:
The map `options` attribute is optional, and consists of the following:
| Property | Type | Default | Description
|:--------------|:----------|:--------------|:--------------
| **`scope`** | `string` | `*` | Defines the scope in which this map is allowed, `'selector'` for selector renaming, `'url'` for url updates and `'*'` for both.
| **`ignoreCase`** | `Boolean` | `true` | Indicates if the search is case-insensitive or not.
| **`greedy`** | `Boolean` | reverts to `options.greedy` | A false value forces selector renaming and url updates to respect word boundaries.
| Property | Type | Default | Description
|:--------------------|:----------|:----------|:--------------
| **`scope`** | `string` | `*` | Defines the scope in which this map is allowed, `'selector'` for selector renaming, `'url'` for url updates and `'*'` for both.
| **`ignoreCase`** | `boolean` | `true` | Indicates if the search is case-insensitive or not.
| **`greedy`** | `boolean` | `options.greedy` | When enabled, string replacement will NOT respect word boundaries. i.e. `.ultra { ...}` will be changed to `.urtla {...}`
**Example**

@@ -333,84 +351,6 @@

### plugins (array)
### rules (array)
Array of plugins to add more functionality to RTLCSS, or even change it's entire behavior. See [Writing an RTLCSS Plugin].
Array of RTLCSS rule Processing Instructions (PI), these are applied on the CSS rule level:
| Property | Type | Description
|:--------------|:----------|:--------------
| **`name`** | `string` | Name of the PI (used in logging).
| **`expr`** | `RegExp` | Regular expression object that will be matched against the comment preceeding the rule.
| **`important`** | `boolean` | Controls whether to insert the PI at the start or end of the rules PIs list.
| **`action`** | `function` | The action to be called when a match is found, and it will be passed a `rule` node. the functions is expected to return a boolean, `true` to stop further processing of the rule, otherwise `false`.
Visit [PostCSS] to find out more about [`rule`](https://github.com/postcss/postcss/blob/master/docs/api.md#rule-node) node.
##### **Example**
```javascript
// RTLCSS rule processing instruction
{
"name" : "ignore",
"expr" : /\/\*rtl:ignore\*\//img,
"important" : true,
"action" : function (rule) {
return true;
}
}
```
### declarations (array)
Array of RTLCSS declaration Processing Instructions (PI), these are applied on the CSS declaration level:
| Property | Type | Description
|:--------------|:----------|:--------------
| **`name`** | `string` | Name of the PI (used in logging).
| **`expr`** | `RegExp` | Regular expression object that will be matched against the declaration raw value.
| **`important`** | `boolean` | Controls whether to insert the PI at the start or end of the declarations PIs list.
| **`action`** | `function` | The action to be called when a match is found, and it will be passed a `decl` node. the functions is expected to return a boolean, `true` to stop further processing of the declaration, otherwise `false`.
Visit [PostCSS] to find out more about [`decl`](https://github.com/postcss/postcss/blob/master/docs/api.md#declaration-node) node.
##### **Example**
```javascript
// RTLCSS declaration processing instruction
{
"name" : "ignore",
"expr" : /(?:[^]*)(?:\/\*rtl:ignore)([^]*?)(?:\*\/)/img,
"important" : true,
"action" : function (decl) {
if (!config.preserveDirectives)
decl._value.raw = decl._value.raw.replace(/\/\*rtl:[^]*?\*\//img, "");
return true;
}
},
```
### properties (array)
Array of RTLCSS properties Processing Instructions (PI), these are applied on the CSS property level:
| Property | Type | Description
|:--------------|:----------|:--------------
| **`name`** | `string` | Name of the PI (used in logging).
| **`expr`** | `RegExp` | Regular expression object that will be matched against the declaration property name.
| **`important`** | `boolean` | Controls whether to insert the PI at the start or end of the declarations PIs list.
| **`action`** | `function` | The action to be called when a match is found, it will be passed a `prop` (string holding the CSS property name) and `value` (string holding the CSS property raw value). If `options.preserveComments == true`, comments in the raw value will be replaced by the Unicode Character 'REPLACEMENT CHARACTER' (U+FFFD) &#xfffd; (this is to simplify pattern matching). The function is expected to return an object containing the modified version of the property and its value.
##### **Example**
```javascript
// RTLCSS property processing instruction
{
"name" : "direction",
"expr" : /direction/im,
"important" : true,
"action" : function (prop, value) {
return { 'prop': prop, 'value': util.swapLtrRtl(value) };
}
}
```
---

@@ -422,119 +362,7 @@ ## Bugs and Issues

## Release Notes
* **v1.7.3** [30 Jan. 2016]
* Fixes a bug in flipping N-Values containing comments.
* **v1.7.2** [04 Dec. 2015]
* Fixes a compatibility issue with postcss-js (Fixes [#48](https://github.com/MohammadYounes/rtlcss/issues/48)).
* **v1.7.1** [10 Nov. 2015]
* Fixed a bug in flipping backgrounds having functions (Issue [#45](https://github.com/MohammadYounes/rtlcss/issues/45)).
* **v1.7.0** [19 Sep. 2015]
* Add `calc` support.
* Mark rule as flipped when values are updated by decl. directives.
* Allow further processing for rules that uses `rename` directive.
* **v1.6.3** [28 Aug. 2015]
* CLI: fix source map option (issue #40).
* Upgrade to [POSTCSS] v5.0.x
To view changes in recent versions, see the [CHANGELOG](CHANGELOG.md).
* **v1.6.2** [21 Jul. 2015]
* CLI: fix loading custom configuration file manually via the --config flag. **Thanks @KeyKaKiTO**
* **v1.6.1** [17 Mar. 2015]
* Fixed flipping units having more than 1 digit before the decimal point.
* **v1.6.0** [15 Mar. 2015]
* Support flipping `matrix3d` transform.
* **v1.5.2** [28 Feb. 2015]
* Fix flipping string maps containing regular expressions special characters (Fixes [#24](https://github.com/MohammadYounes/rtlcss/issues/24)).
* **v1.5.1** [14 Feb. 2015]
* Fix flipping multiple shadows when a hex color was used. **Thanks @ocean90**
* **v1.5.0** [30 Jan. 2015]
* CLI: New option `-e,--ext` to set output files extension when processing a directory.
* **v1.4.3** [24 Jan. 2015]
* Upgrade to [POSTCSS] v4.0.x **Thanks @necolas**
* **v1.4.2** [24 Oct. 2014]
* CLI: Switch to Unix line endings (Fixes [#14](https://github.com/MohammadYounes/rtlcss/issues/14))
* **v1.4.1** [24 Oct. 2014]
* CLI: Print processing errors.
* **v1.4.0** [10 Oct. 2014]
* CLI: Support processing a directory. see [CLI documentation](https://github.com/MohammadYounes/rtlcss/blob/master/CLI.md#directory)
* **v1.3.1** [29 Sep. 2014]
* Update README.md (typos).
* **v1.3.0** [28 Sep. 2014]
* New feature - String Maps. Add your own set of swappable strings, for example (prev/next).
* Preserves lowercase, UPPERCASE and Capitalization when swapping ***left***, ***right***, ***ltr***, ***rtl***, ***west*** and ***east***.
* **v1.2.0** [26 Sep. 2014]
* Support !important comments for directives (enables flipping minified stylesheets).
* **v1.1.0** [26 Sep. 2014]
* Upgrade to [POSTCSS] v2.2.5
* Support flipping `border-color`, `border-style` and `background-position-x`
* **v1.0.0** [24 Aug. 2014]
* Upgrade to [POSTCSS] v2.2.1
* Support flipping urls in '@import' rule.
* Fix JSON parse error when configuration file is UTF-8 encoded.
* Better minification.
* **v0.9.0** [10 Aug. 2014]
* New configuration loader.
* CLI configuration can be set using one of the following methods:
* Specify the configuration file manually via the --config flag.
* Put your config into your projects package.json file under the `rtlcssConfig` property
* Use a special file `.rtlcssrc` or `.rtlcssrc.json`
* **v0.8.0** [8 Aug. 2014]
* Fix source map generation.
* **v0.7.0** [4 Jul. 2014]
* Fix flipping linear-gradient.
* **v0.6.0** [4 Jul. 2014]
* Allow additional comments inside `ignore`/`rename` rule level directives.
* **v0.5.0** [11 Jun. 2014]
* Add CLI support.
* **v0.4.0** [5 Apr. 2014]
* Fix flipping transform-origin.
* Update autoRename to search for all swappable words.
* **v0.3.0** [5 Apr. 2014]
* Support flipping rotateZ.
* Fix flipping rotate3d.
* Fix flipping skew, skewX and skewY.
* Fix flipping cursor value.
* Fix flipping translate3d.
* Update flipping background horizontal position to treat 0 as 0%
* **v0.2.1** [20 Mar. 2014]
* Upgrade to [POSTCSS] v0.3.4
* **v0.2.0** [20 Mar. 2014]
* Support combining with other processors.
* Support rad, grad & turn angle units when flipping linear-gradient
* Fix typo in config.js
* **v0.1.3** [7 Mar. 2014]
* Fix missing include in rules.js
* **v0.1.2** [5 Mar. 2014]
* New option: minify output CSS.
* Updated README.md
* **v0.1.1** [4 Mar. 2014]
* Initial commit.
[PostCSS]: https://github.com/postcss/postcss
[CLI documentaion]: docs/CLI.md
[Writing an RTLCSS Plugin]: docs/writing-a-plugin.md

@@ -1,1279 +0,41 @@

var assert = require("assert");
var rtlcss = require("../lib/rtlcss.js");
/* global describe */
/* global it */
var assert = require('assert')
var rtlcss = require('../lib/rtlcss.js')
var tests = {
'Background:': [
{
'should': 'Should treat 0 as 0%',
'expected': '.banner { background: 100% top url(topbanner.png) #00D repeat-y fixed; }',
'input': '.banner { background: 0 top url(topbanner.png) #00D repeat-y fixed; }',
'reversable': false
},
{
'should': 'Should complement percentage horizontal position',
'expected': '.banner { background: 81% top url(topbanner.png) #00D repeat-y fixed; }',
'input': '.banner { background: 19% top url(topbanner.png) #00D repeat-y fixed; }',
'reversable': true
},
{
'should': 'Should complement calc horizontal position',
'expected': '.banner { background: calc(100%-(19% + 2px)) top url(topbanner.png) #00D repeat-y fixed; }',
'input': '.banner { background: calc(19% + 2px) top url(topbanner.png) #00D repeat-y fixed; }',
'reversable': false
},
{
'should': 'Should mirror keyword horizontal position',
'expected': '.banner { background: right top url(topbanner.png) #00D repeat-y fixed; }',
'input': '.banner { background: left top url(topbanner.png) #00D repeat-y fixed; }',
'reversable': true
},
{
'should': 'Should swap left with right in url',
'expected': '.banner { background: 10px top url(top-left-banner.png) #00D repeat-y fixed; }',
'input': '.banner { background: 10px top url(top-right-banner.png) #00D repeat-y fixed; }',
'reversable': true
},
{
'should': 'Should swap ltr with rtl in url',
'expected': '.banner { background: 10px top url(rtl-banner.png) #00D repeat-y fixed; }',
'input': '.banner { background: 10px top url(ltr-banner.png) #00D repeat-y fixed; }',
'reversable': true
},
{
'should': 'Should swap west with east in url',
'expected': '.banner { background: 10px top url(east-banner.png) #00D repeat-y fixed; }',
'input': '.banner { background: 10px top url(west-banner.png) #00D repeat-y fixed; }',
'reversable': true
},
{
'should': 'Should not swap bright:bleft, ultra:urtla, westing:easting',
'expected': '.banner { background: 10px top url(ultra/westing/bright.png) #00D repeat-y fixed; }',
'input': '.banner { background: 10px top url(ultra/westing/bright.png) #00D repeat-y fixed; }',
'reversable': true
},
{
'should': 'Should swap bright:bleft, ultra:urtla, westing:easting (greedy)',
'expected': '.banner { background: 10px top url(urtla/easting/bleft.png) #00D repeat-y fixed; }',
'input': '.banner { background: 10px top url(ultra/westing/bright.png) #00D repeat-y fixed; }',
'reversable': true,
'options': { 'greedy': true }
}
],
'Background Image:': [
{
'should': 'Should swap left with right in url',
'expected': 'div { background-image: url(images/left.png), url(images/right.png);}',
'input': 'div { background-image: url(images/right.png), url(images/left.png);}',
'reversable': true
},
{
'should': 'Should swap ltr with rtl in url',
'expected': 'div { background-image: url(images/ltr.png), url(images/rtl.png);}',
'input': 'div { background-image: url(images/rtl.png), url(images/ltr.png);}',
'reversable': true
},
{
'should': 'Should swap west with east in url',
'expected': 'div { background-image: url(images/west.png), url(images/east.png);}',
'input': 'div { background-image: url(images/east.png), url(images/west.png);}',
'reversable': true
},
{
'should': 'Should not negate color value for linear gradient',
'expected': 'div { background-image: linear-gradient(rgba(255, 255, 255, 0.3) 0%, #ff8 100%);}',
'input': 'div { background-image: linear-gradient(rgba(255, 255, 255, 0.3) 0%, #ff8 100%);}',
'reversable': true
},
{
'should': 'Should not negate color value for linear gradient with calc',
'expected': 'div { background-image: linear-gradient(rgba(255, 255, calc((125 * 2) + 5), 0.3) 0%, #ff8 100%);}',
'input': 'div { background-image: linear-gradient(rgba(255, 255, calc((125 * 2) + 5), 0.3) 0%, #ff8 100%);}',
'reversable': true
},
{
'should': 'Should negate angle value for linear gradient',
'expected': 'div { background-image: linear-gradient(13.25deg, rgba(255, 255, 255, .15) 25%, transparent 25%);}',
'input': 'div { background-image: linear-gradient(-13.25deg, rgba(255, 255, 255, .15) 25%, transparent 25%);}',
'reversable': true
}
],
'Background Position:': [
{
'should': 'Should complement percentage horizontal position ',
'expected': 'div {background-position:100% 75%;}',
'input': 'div {background-position:0 75%;}',
'reversable': false
},
{
'should': 'Should complement percentage horizontal position with calc',
'expected': 'div {background-position:calc(100%-(30% + 50px)) 75%;}',
'input': 'div {background-position:calc(30% + 50px) 75%;}',
'reversable': false
},
{
'should': 'Should complement percentage horizontal position ',
'expected': 'div {background-position:81.25% 75%, 10.75% top;}',
'input': 'div {background-position:18.75% 75%, 89.25% top;}',
'reversable': true
},
{
'should': 'Should complement percentage horizontal position with calc',
'expected': 'div {background-position:calc(100%-(30% + 50px)) calc(30% + 50px), 10.75% top;}',
'input': 'div {background-position:calc(30% + 50px) calc(30% + 50px), 89.25% top;}',
'reversable': false
},
{
'should': 'Should swap left with right',
'expected': 'div {background-position:right 75%, left top;}',
'input': 'div {background-position:left 75%, right top;}',
'reversable': true
},
{
'should': 'Should swap left with right wit calc',
'expected': 'div {background-position:right -ms-calc(30% + 50px), left top;}',
'input': 'div {background-position:left -ms-calc(30% + 50px), right top;}',
'reversable': true
},
{
'should': 'Should complement percentage: position-x (treat 0 as 0%)',
'expected': 'div {background-position-x:100%, 0%;}',
'input': 'div {background-position-x:0, 100%;}',
'reversable': false
},
{
'should': 'Should complement percentage: position-x',
'expected': 'div {background-position-x:81.75%, 11%;}',
'input': 'div {background-position-x:18.25%, 89%;}',
'reversable': true
},
{
'should': 'Should complement percentage with calc: position-x',
'expected': 'div {background-position-x:calc(100%-(30% + 50px)), -webkit-calc(100%-(30% + 50px));}',
'input': 'div {background-position-x:calc(30% + 50px), -webkit-calc(30% + 50px);}',
'reversable': false
},
{
'should': 'Should swap left with right: position-x',
'expected': 'div {background-position-x:right, left;}',
'input': 'div {background-position-x:left, right;}',
'reversable': true
},
{
'should': 'Should keep as is: position-x',
'expected': 'div {background-position-x:100px, 0px;}',
'input': 'div {background-position-x:100px, 0px;}',
'reversable': true
}
],
'Mirrored Properties:': [
{
'should': 'Should mirror property name: border-top-right-radius',
'expected': 'div { border-top-left-radius:15px; }',
'input': 'div { border-top-right-radius:15px; }',
'reversable': true
},
{
'should': 'Should mirror property name: border-bottom-right-radius',
'expected': 'div { border-bottom-left-radius:15px; }',
'input': 'div { border-bottom-right-radius:15px; }',
'reversable': true
},
{
'should': 'Should mirror property name: border-left',
'expected': 'div { border-right:1px solid black; }',
'input': 'div { border-left:1px solid black; }',
'reversable': true
},
{
'should': 'Should mirror property name: border-left-color',
'expected': 'div { border-right-color:black; }',
'input': 'div { border-left-color:black; }',
'reversable': true
},
{
'should': 'Should mirror property name: border-left-style',
'expected': 'div { border-right-style:solid; }',
'input': 'div { border-left-style:solid; }',
'reversable': true
},
{
'should': 'Should mirror property name: border-left-width',
'expected': 'div { border-right-width:1em; }',
'input': 'div { border-left-width:1em; }',
'reversable': true
},
{
'should': 'Should mirror property name: left',
'expected': 'div { right:auto; }',
'input': 'div { left:auto; }',
'reversable': true
},
{
'should': 'Should mirror property name: margin-left',
'expected': 'div { margin-right:2em; }',
'input': 'div { margin-left:2em; }',
'reversable': true
},
{
'should': 'Should mirror property name: padding-left',
'expected': 'div { padding-right:2em; }',
'input': 'div { padding-left:2em; }',
'reversable': true
},
{
'should': 'Should mirror property name: nav-left',
'expected': 'div { nav-right:#b4; }',
'input': 'div { nav-left:#b4; }',
'reversable': true
}
],
'Mirrored Values:': [
{
'should': 'Should mirror property value: clear',
'expected': 'div { clear:right; }',
'input': 'div { clear:left; }',
'reversable': true
},
{
'should': 'Should mirror property value: direction',
'expected': 'div { direction:ltr; }',
'input': 'div { direction:rtl; }',
'reversable': true
},
{
'should': 'Should mirror property value: float',
'expected': 'div { float:right; }',
'input': 'div { float:left; }',
'reversable': true
},
{
'should': 'Should mirror property value: text-align',
'expected': 'div { text-align:right; }',
'input': 'div { text-align:left; }',
'reversable': true
},
{
'should': 'Should mirror property value: cursor nw',
'expected': 'div { cursor:nw-resize; }',
'input': 'div { cursor:ne-resize; }',
'reversable': true
},
{
'should': 'Should mirror property value: cursor sw',
'expected': 'div { cursor:sw-resize; }',
'input': 'div { cursor:se-resize; }',
'reversable': true
},
{
'should': 'Should mirror property value: cursor nesw',
'expected': 'div { cursor:nesw-resize; }',
'input': 'div { cursor:nwse-resize; }',
'reversable': true
},
{
'should': 'Should keep property value as is: cursor ns',
'expected': 'div { cursor:ns-resize; }',
'input': 'div { cursor:ns-resize; }',
'reversable': false
},
{
'should': 'Should swap left,ltr,west in url: cursor',
'expected': '.foo { cursor: url(right.cur), url(rtl.cur), url(east.cur), se-resize, auto }',
'input': '.foo { cursor: url(left.cur), url(ltr.cur), url(west.cur), sw-resize, auto }',
'reversable': true
},
{
'should': 'Should mirror property value: transition',
'expected': '.foo { transition: right .3s ease .1s, left .3s ease .1s, margin-right .3s ease, margin-left .3s ease, padding-right .3s ease, padding-left .3s ease}',
'input': '.foo { transition: left .3s ease .1s, right .3s ease .1s, margin-left .3s ease, margin-right .3s ease, padding-left .3s ease, padding-right .3s ease}',
'reversable': true
},
{
'should': 'Should mirror property value: transition-property',
'expected': '.foo { transition-property: right; }',
'input': '.foo { transition-property: left; }',
'reversable': true
}
],
'Mirrored Values (N Value Syntax):': [
{
'should': 'Should mirror property value: border-radius (4 values)',
'expected': 'div { border-radius: 40.25px 10.5px /*comment*/ 10.75px 40.3px; }',
'input': 'div { border-radius: 10.5px 40.25px /*comment*/ 40.3px 10.75px; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-radius (3 values)',
'expected': 'div { border-radius: 40.75px 10.75px 40.75px 40.3px; }',
'input': 'div { border-radius: 10.75px 40.75px 40.3px; }',
'reversable': false
},
{
'should': 'Should mirror property value: border-radius (2 values)',
'expected': 'div { border-radius: 40.25px 10.75px; }',
'input': 'div { border-radius: 10.75px 40.25px; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-radius (4 values - double)',
'expected': 'div { border-radius: 40.25px 10.75px .5px 40.75px /*comment*/ / /*comment*/ .4em 1em 1em 4.5em; }',
'input': 'div { border-radius: 10.75px 40.25px 40.75px .5px /*comment*/ / /*comment*/ 1em .4em 4.5em 1em; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-radius (3 values - double)',
'expected': 'div { border-radius: .40px 10.5px .40px 40px / 4em 1em 4em 3em; }',
'input': 'div { border-radius: 10.5px .40px 40px / 1em 4em 3em; }',
'reversable': false
},
{
'should': 'Should mirror property value: border-radius (2 values- double)',
'expected': 'div { border-radius: 40px 10px / 2.5em .75em; }',
'input': 'div { border-radius: 10px 40px / .75em 2.5em; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-width',
'expected': 'div { border-width: 1px 4px .3em 2.5em; }',
'input': 'div { border-width: 1px 2.5em .3em 4px; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-width (none length)',
'expected': 'div { border-width: thin medium thick none; }',
'input': 'div { border-width: thin none thick medium; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-style (4 values)',
'expected': 'div { border-style: none dashed dotted solid; }',
'input': 'div { border-style: none solid dotted dashed; }',
'reversable': true
},
{
'should': 'Should mirror property value: border-color (4 values)',
'expected': 'div { border-color: rgba(255, 255, 255, 1) rgb( 0, 0, 0) #000 hsla(0, 100%, 50%, 1); }',
'input': 'div { border-color: rgba(255, 255, 255, 1) hsla(0, 100%, 50%, 1) #000 rgb( 0, 0, 0); }',
'reversable': true
},
{
'should': 'Should not mirror property value: border-color (3 values)',
'expected': 'div { border-color: #000 rgb( 0, 0, 0) hsla(0, 100%, 50%, 1); }',
'input': 'div { border-color: #000 rgb( 0, 0, 0) hsla(0, 100%, 50%, 1); }',
'reversable': false
},
{
'should': 'Should not mirror property value: border-color (2 values)',
'expected': 'div { border-color:rgb( 0, 0, 0) hsla(0, 100%, 50%, 1); }',
'input': 'div { border-color:rgb( 0, 0, 0) hsla(0, 100%, 50%, 1); }',
'reversable': false
},
{
'should': 'Should mirror property value: margin',
'expected': 'div { margin: .1em auto 3.5rem 2px; }',
'input': 'div { margin: .1em 2px 3.5rem auto; }',
'reversable': true
},
{
'should': 'Should mirror property value: padding',
'expected': 'div { padding: 1px 4px .3rem 2.5em; }',
'input': 'div { padding: 1px 2.5em .3rem 4px; }',
'reversable': true
},
{
'should': 'Should mirror property value: box-shadow',
'expected': 'div { box-shadow: -60px -16px rgba(0, 128, 128, 0.98), -10.25px 5px 5px #ff0, inset -0.5em 1em 0 white; }',
'input': 'div { box-shadow: 60px -16px rgba(0, 128, 128, 0.98), 10.25px 5px 5px #ff0, inset 0.5em 1em 0 white; }',
'reversable': true
},
{
'should': 'Should mirror property value: text-shadow',
'expected': 'div { text-shadow: -60px -16px rgba(0, 128, 128, 0.98), -10.25px 5px 5px #ff0, inset -0.5em 1em 0 white; }',
'input': 'div { text-shadow: 60px -16px rgba(0, 128, 128, 0.98), 10.25px 5px 5px #ff0, inset 0.5em 1em 0 white; }',
'reversable': true
},
{
'should': 'Should mirror property value (no digit before the dot): box-shadow, text-shadow',
'expected': 'div { box-shadow: inset -0.5em 1em 0 white; text-shadow: inset -0.5em 1em 0 white; }',
'input': 'div { box-shadow: inset .5em 1em 0 white; text-shadow: inset .5em 1em 0 white; }',
'reversable': false
}
],
'Transform Origin:': [
{
'should': 'Should mirror (x-offset: 0 means 0%)',
'expected': 'div { transform-origin:100%; }',
'input': 'div { transform-origin:0; }',
'reversable': false
},
{
'should': 'Should mirror (x-offset)',
'expected': 'div { transform-origin:90.25%; }',
'input': 'div { transform-origin:9.75%; }',
'reversable': true
},
{
'should': 'Should mirror calc (x-offset)',
'expected': 'div { transform-origin: -moz-calc(100%-(((25%/2) * 10px))) ; }',
'input': 'div { transform-origin: -moz-calc(((25%/2) * 10px)) ; }',
'reversable': false
},
{
'should': 'Should not mirror (x-offset: not percent, not calc)',
'expected': 'div { transform-origin:10.75px; }',
'input': 'div { transform-origin:10.75px; }',
'reversable': false
},
{
'should': 'Should mirror (offset-keyword)',
'expected': 'div { transform-origin:right; }',
'input': 'div { transform-origin:left; }',
'reversable': true
},
{
'should': 'Should mirror (x-offset y-offset: 0 means 0%)',
'expected': 'div { transform-origin:100% 0; }',
'input': 'div { transform-origin:0 0; }',
'reversable': false
},
{
'should': 'Should mirror with y being calc (x-offset y-offset: 0 means 0%)',
'expected': 'div { transform-origin:100% -webkit-calc(15% * (3/2)); }',
'input': 'div { transform-origin:0 -webkit-calc(15% * (3/2)); }',
'reversable': false
},
{
'should': 'Should mirror percent (x-offset y-offset)',
'expected': 'div { transform-origin:30.25% 10%; }',
'input': 'div { transform-origin:69.75% 10%; }',
'reversable': true
},
{
'should': 'Should mirror with x being calc (x-offset y-offset)',
'expected': 'div { transform-origin: -webkit-calc(100%-(15% * (3/2))) 30.25% ; }',
'input': 'div { transform-origin: -webkit-calc(15% * (3/2)) 30.25% ; }',
'reversable': false
},
{
'should': 'Should mirror with y being calc (x-offset y-offset)',
'expected': 'div { transform-origin:30.25% calc(15% * (3/2)); }',
'input': 'div { transform-origin:69.75% calc(15% * (3/2)); }',
'reversable': true
},
{
'should': 'Should mirror (y-offset x-offset-keyword)',
'expected': 'div { transform-origin:70% right; }',
'input': 'div { transform-origin:70% left; }',
'reversable': true
},
{
'should': 'Should mirror with calc (y-offset x-offset-keyword)',
'expected': 'div { transform-origin:-ms-calc(140%/2) right; }',
'input': 'div { transform-origin:-ms-calc(140%/2) left; }',
'reversable': true
},
{
'should': 'Should mirror (x-offset-keyword y-offset)',
'expected': 'div { transform-origin:right 70%; }',
'input': 'div { transform-origin:left 70%; }',
'reversable': true
},
{
'should': 'Should mirror with calc (x-offset-keyword y-offset)',
'expected': 'div { transform-origin:right -moz-calc(((140%/2))); }',
'input': 'div { transform-origin:left -moz-calc(((140%/2))); }',
'reversable': true
},
{
'should': 'Should mirror (y-offset-keyword x-offset)',
'expected': 'div { transform-origin:top 30.25%; }',
'input': 'div { transform-origin:top 69.75%; }',
'reversable': true
},
{
'should': 'Should not mirror with x being calc (y-offset-keyword x-offset)',
'expected': 'div { transform-origin:top calc(100%-(((140%/2)))); }',
'input': 'div { transform-origin:top calc(((140%/2))); }',
'reversable': false
},
{
'should': 'Should mirror (x-offset-keyword y-offset-keyword)',
'expected': 'div { transform-origin:right top; }',
'input': 'div { transform-origin:left top; }',
'reversable': true
},
{
'should': 'Should mirror (y-offset-keyword x-offset-keyword)',
'expected': 'div { transform-origin:top right; }',
'input': 'div { transform-origin:top left; }',
'reversable': true
},
{
'should': 'Should mirror (x-offset y-offset z-offset)',
'expected': 'div { transform-origin:80.25% 30% 10%; }',
'input': 'div { transform-origin:19.75% 30% 10%; }',
'reversable': true
},
{
'should': 'Should mirror with x being calc (x-offset y-offset z-offset)',
'expected': 'div { transform-origin: calc(100%-(25% * 3 + 20px)) 30% 10%; }',
'input': 'div { transform-origin: calc(25% * 3 + 20px) 30% 10%; }',
'reversable': false
},
{
'should': 'Should mirror (y-offset x-offset-keyword z-offset)',
'expected': 'div { transform-origin:20% right 10%; }',
'input': 'div { transform-origin:20% left 10%; }',
'reversable': true
},
{
'should': 'Should mirror (x-offset-keyword y-offset z-offset)',
'expected': 'div { transform-origin:left 20% 10%; }',
'input': 'div { transform-origin:right 20% 10%; }',
'reversable': true
},
{
'should': 'Should mirror (x-offset-keyword y-offset-keyword z-offset)',
'expected': 'div { transform-origin:left bottom 10%; }',
'input': 'div { transform-origin:right bottom 10%; }',
'reversable': true
},
{
'should': 'Should mirror (y-offset-keyword x-offset-keyword z-offset)',
'expected': 'div { transform-origin:bottom left 10%; }',
'input': 'div { transform-origin:bottom right 10%; }',
'reversable': true
}
],
'Transforms:': [
{
'should': 'Should mirror transform : matrix',
'expected': 'div { transform: matrix(2, 0.1, 20.75, 2, 2, 2); }',
'input': 'div { transform: matrix(2, -0.1, -20.75, 2, -2, 2); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): matrix',
'expected': 'div { transform: matrix(2, 0.1, 0.75, 2, 2, 2); }',
'input': 'div { transform: matrix(2, -0.1, -.75, 2, -2, 2); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: matrix',
'expected': 'div { transform: matrix( -moz-calc(((25%/2) * 10px)), calc(-1*(((25%/2) * 10px))), 20.75, 2, 2, 2 ); }',
'input': 'div { transform: matrix( -moz-calc(((25%/2) * 10px)), calc(((25%/2) * 10px)), -20.75, 2, -2, 2 ); }',
'reversable': false
},
{
'should': 'Should mirror transform : matrix3d',
'expected': 'div { transform:matrix3d(0.227114470162179, 0.127248412323519, 0, 0.000811630714323203, 0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, -165, 67, 0, 1); }',
'input': 'div { transform:matrix3d(0.227114470162179, -0.127248412323519, 0, -0.000811630714323203, -0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, 165, 67, 0, 1); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): matrix3d',
'expected': 'div { transform:matrix3d(0.227114470162179, 0.127248412323519, 0, 0.000811630714323203, 0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, -165, 67, 0, 1); }',
'input': 'div { transform:matrix3d(0.227114470162179, -.127248412323519, 0, -0.000811630714323203, -0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, 165, 67, 0, 1); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc : matrix3d',
'expected': 'div { transform:matrix3d(0.227114470162179, 0.127248412323519, 0, 0.000811630714323203, 0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, calc(-1*(((25%/2) * 10px))), 67, 0, 1); }',
'input': 'div { transform:matrix3d(0.227114470162179, -0.127248412323519, 0, -0.000811630714323203, -0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, calc(((25%/2) * 10px)), 67, 0, 1); }',
'reversable': false
},
{
'should': 'Should mirror transform : translate',
'expected': 'div { transform: translate(-10.75px); }',
'input': 'div { transform: translate(10.75px); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): translate',
'expected': 'div { transform: translate(-0.75px); }',
'input': 'div { transform: translate(.75px); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: translate',
'expected': 'div { transform: translate(-moz-calc(-1*(((25%/2) * 10px)))); }',
'input': 'div { transform: translate(-moz-calc(((25%/2) * 10px))); }',
'reversable': false
},
{
'should': 'Should mirror transform : translateX',
'expected': 'div { transform: translateX(-50.25px); }',
'input': 'div { transform: translateX(50.25px); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): translateX',
'expected': 'div { transform: translateX(-0.25px); }',
'input': 'div { transform: translateX(.25px); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc : translateX',
'expected': 'div { transform: translateX(-ms-calc(-1*(((25%/2) * 10px))))); }',
'input': 'div { transform: translateX(-ms-calc(((25%/2) * 10px)))); }',
'reversable': false
},
{
'should': 'Should mirror transform : translate3d',
'expected': 'div { transform: translate3d(-12.75px, 50%, 3em); }',
'input': 'div { transform: translate3d(12.75px, 50%, 3em); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): translate3d',
'expected': 'div { transform: translate3d(-0.75px, 50%, 3em); }',
'input': 'div { transform: translate3d(.75px, 50%, 3em); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: translate3d',
'expected': 'div { transform: translate3d(-webkit-calc(-1*(((25%/2) * 10px))))), 50%, calc(((25%/2) * 10px))))); }',
'input': 'div { transform: translate3d(-webkit-calc(((25%/2) * 10px)))), 50%, calc(((25%/2) * 10px))))); }',
'reversable': false
},
{
'should': 'Should mirror transform : rotate',
'expected': 'div { transform: rotate(-20.75deg); }',
'input': 'div { transform: rotate(20.75deg); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): rotate',
'expected': 'div { transform: rotate(-0.75deg); }',
'input': 'div { transform: rotate(.75deg); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: rotate',
'expected': 'div { transform: rotate(calc(-1*(((25%/2) * 10deg)))); }',
'input': 'div { transform: rotate(calc(((25%/2) * 10deg))); }',
'reversable': false
},
{
'should': 'Should mirror transform : rotate3d',
'expected': 'div { transform: rotate3d(10, -20.15, 10, -45.14deg); }',
'input': 'div { transform: rotate3d(10, 20.15, 10, 45.14deg); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): rotate3d',
'expected': 'div { transform: rotate3d(10, -20, 10, -0.14deg); }',
'input': 'div { transform: rotate3d(10, 20, 10, .14deg); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: rotate3d',
'expected': 'div { transform: rotate3d(10, -20.15, 10, calc(-1*(((25%/2) * 10deg)))); }',
'input': 'div { transform: rotate3d(10, 20.15, 10, calc(((25%/2) * 10deg))); }',
'reversable': false
},
{
'should': 'Should not mirror transform : rotateX',
'expected': 'div { transform: rotateX(45deg); }',
'input': 'div { transform: rotateX(45deg); }',
'reversable': false
},
{
'should': 'Should not mirror transform with calc: rotateX',
'expected': 'div { transform: rotateX(calc(((25%/2) * 10deg))); }',
'input': 'div { transform: rotateX(calc(((25%/2) * 10deg))); }',
'reversable': false
},
{
'should': 'Should not mirror transform : rotateY',
'expected': 'div { transform: rotateY(45deg); }',
'input': 'div { transform: rotateY(45deg); }',
'reversable': false
},
{
'should': 'Should not mirror transform with calc: rotateY',
'expected': 'div { transform: rotateY(calc(((25%/2) * 10deg))); }',
'input': 'div { transform: rotateY(calc(((25%/2) * 10deg))); }',
'reversable': false
},
{
'should': 'Should mirror transform : rotateZ',
'expected': 'div { transform: rotateZ(-45.75deg); }',
'input': 'div { transform: rotateZ(45.75deg); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): rotateZ',
'expected': 'div { transform: rotateZ(-0.75deg); }',
'input': 'div { transform: rotateZ(.75deg); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: rotateZ',
'expected': 'div { transform: rotateZ(-ms-calc(-1*(((25%/2) * 10deg)))); }',
'input': 'div { transform: rotateZ(-ms-calc(((25%/2) * 10deg))); }',
'reversable': false
},
{
'should': 'Should mirror transform : skew',
'expected': 'div { transform: skew(-20.25rad,-30deg); }',
'input': 'div { transform: skew(20.25rad,30deg); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): skew',
'expected': 'div { transform: skew(-0.25rad,-30deg); }',
'input': 'div { transform: skew(.25rad,30deg); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: skew',
'expected': 'div { transform: skew(calc(-1*(((25%/2) * 10rad))),calc(-1*(((25%/2) * 10deg)))); }',
'input': 'div { transform: skew(calc(((25%/2) * 10rad)),calc(((25%/2) * 10deg))); }',
'reversable': false
},
{
'should': 'Should mirror transform : skewX',
'expected': 'div { transform: skewX(-20.75rad); }',
'input': 'div { transform: skewX(20.75rad); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): skewX',
'expected': 'div { transform: skewX(-0.75rad); }',
'input': 'div { transform: skewX(.75rad); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: skewX',
'expected': 'div { transform: skewX(-moz-calc(-1*(((25%/2) * 10rad)))); }',
'input': 'div { transform: skewX(-moz-calc(((25%/2) * 10rad))); }',
'reversable': false
},
{
'should': 'Should mirror transform : skewY',
'expected': 'div { transform: skewY(-10.75grad); }',
'input': 'div { transform: skewY(10.75grad); }',
'reversable': true
},
{
'should': 'Should mirror transform (with no digits before dot): skewY',
'expected': 'div { transform: skewY(-0.75grad); }',
'input': 'div { transform: skewY(.75grad); }',
'reversable': false
},
{
'should': 'Should mirror transform with calc: skewY',
'expected': 'div { transform: skewY(calc(-1*(((25%/2) * 10grad)))); }',
'input': 'div { transform: skewY(calc(((25%/2) * 10grad))); }',
'reversable': false
},
],
'RTLCSS (Options):': [
{
'should': 'Should not rename selectors having directional decl. (default)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; right:0; }',
'input': '.right .rtl .east .bright .ultra .least { display:block; left:0; }',
'reversable': true
},
{
'should': 'Should auto rename selectors having no directional decl. (default)',
'expected': '.left .ltr .west .bright .ultra .least { display:block; }',
'input': '.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': true
},
{
'should': 'Should auto rename selectors having no directional decl. (greedy)',
'expected': '.left .ltr .west .bleft .urtla .lwest { display:block; }',
'input': '.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': true,
'options': { 'greedy': true }
},
{
'should': 'Should not auto rename selectors having no directional decl. (autoRename:false)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; }',
'input': '.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': true,
'options': { 'autoRename': false }
},
{
'should': 'Should not auto rename selectors having no directional decl. (autoRename:false,greedy)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; }',
'input': '.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': true,
'options': { 'autoRename': false, 'greedy': true }
},
{
'should': 'Should not auto rename when rules are flipped via decl directives',
'expected': 'div.right { display:block; font-family: "Droid Sans", Tahoma, "Droid Arabic Kufi"; }',
'input': 'div.right { display:block; font-family: "Droid Sans", Tahoma/*!rtl:append:, "Droid Arabic Kufi"*/; }',
'reversable': false,
'options': { 'autoRename': true }
},
{
'should': 'Should not preserve processing directive. (default)',
'expected': 'div { left:0; }',
'input': '/*rtl:ignore*/div { left:0; }',
'reversable': false
},
{
'should': 'Should preserve processing directive. (preserveDirectives:true)',
'expected': '/*rtl:ignore*/div { left:0; }',
'input': '/*rtl:ignore*/div { left:0; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should swap left with right in url (defult)',
'expected': 'div { background-image: url(rtl/west/right.png); right:0; }',
'input': 'div { background-image: url(ltr/east/left.png); left:0; }',
'reversable': true
},
{
'should': 'Should not swap left with right in url (swapLeftRightInUrl:false)',
'expected': 'div { background-image: url(rtl/west/left.png); right:0; }',
'input': 'div { background-image: url(ltr/east/left.png); left:0; }',
'reversable': true,
'options': { 'swapLeftRightInUrl': false }
},
{
'should': 'Should swap ltr with rtl in url (defult)',
'expected': 'div { background-image: url(rtl/west/right.png); right:0; }',
'input': 'div { background-image: url(ltr/east/left.png); left:0; }',
'reversable': true
},
{
'should': 'Should not swap ltr with rtl in url (swapLtrRtlInUrl:false)',
'expected': 'div { background-image: url(ltr/west/right.png); right:0; }',
'input': 'div { background-image: url(ltr/east/left.png); left:0; }',
'reversable': false,
'options': { 'swapLtrRtlInUrl': false }
},
{
'should': 'Should swap east with west in url (defult)',
'expected': 'div { background-image: url(rtl/west/right.png); right:0; }',
'input': 'div { background-image: url(ltr/east/left.png); left:0; }',
'reversable': true
},
{
'should': 'Should not swap east with west in url (swapWestEastInUrl:false)',
'expected': 'div { background-image: url(rtl/east/right.png); right:0; }',
'input': 'div { background-image: url(ltr/east/left.png); left:0; }',
'reversable': false,
'options': { 'swapWestEastInUrl': false }
},
{
'should': 'Should swap left with right in @import url (swapLeftRightInUrl:true)',
'expected': 'div{display:none;} @import url("right.css");',
'input': 'div{display:none;} @import url("left.css");',
'reversable': true,
'options': { 'swapLeftRightInUrl': true }
},
{
'should': 'Should not swap bright with bleft in @import url (swapLeftRightInUrl:true,greedy:false)',
'expected': 'div{display:none;} @import url("bright.css");',
'input': 'div{display:none;} @import url("bright.css");',
'reversable': true,
'options': { 'swapLeftRightInUrl': true, 'greedy': false }
},
{
'should': 'Should swap bright with bleft in @import url (swapLeftRightInUrl:true,greedy:true)',
'expected': 'div{display:none;} @import url("bleft.css");',
'input': 'div{display:none;} @import url("bright.css");',
'reversable': true,
'options': { 'swapLeftRightInUrl': true, 'greedy': true }
},
{
'should': 'Should swap ltr with rtl in @import url (swapLtrRtlInUrl:true)',
'expected': 'div{display:none;} @import url("rtl.css");',
'input': 'div{display:none;} @import url("ltr.css");',
'reversable': true,
'options': { 'swapLtrRtlInUrl': true }
},
{
'should': 'Should not swap ultra with urtla in @import url (swapLtrRtlInUrl:true,greedy:false)',
'expected': 'div{display:none;} @import url("ultra.css");',
'input': 'div{display:none;} @import url("ultra.css");',
'reversable': true,
'options': { 'swapLtrRtlInUrl': true, 'greedy': false }
},
{
'should': 'Should swap ultra with urtla in @import url (swapLtrRtlInUrl:true,greedy:true)',
'expected': 'div{display:none;} @import url("urtla.css");',
'input': 'div{display:none;} @import url("ultra.css");',
'reversable': true,
'options': { 'swapLtrRtlInUrl': true, 'greedy': true }
},
{
'should': 'Should swap west with east in @import url (swapWestEastInUrl:true)',
'expected': 'div{display:none;} @import url("east.css");',
'input': 'div{display:none;} @import url("west.css");',
'reversable': true,
'options': { 'swapWestEastInUrl': true }
},
{
'should': 'Should not swap western with eastern in @import url (swapWestEastInUrl:true,greedy:false)',
'expected': 'div{display:none;} @import url("western.css");',
'input': 'div{display:none;} @import url("western.css");',
'reversable': true,
'options': { 'swapWestEastInUrl': true, 'greedy': false }
},
{
'should': 'Should swap western with eastern in @import url (swapWestEastInUrl:true,greedy:true)',
'expected': 'div{display:none;} @import url("eastern.css");',
'input': 'div{display:none;} @import url("western.css");',
'reversable': true,
'options': { 'swapWestEastInUrl': true, 'greedy': true }
},
{
'should': 'Should minify (minify:true)',
'expected': 'div{font-family:"Droid Arabic Kufi";padding:10px 5px 5px 10px;color:red;}.div2{display:none;}',
'input': '\n/*comment*/\ndiv\n/*comment*/\n {\n/*comment*/\n font-family:\n/*comment*/\n "Droid Arabic Kufi";\n/*comment*/\n padding:10px 10px 5px 5px;\n/*comment*/\n color:red; \n/*comment*/\n } \n/*comment*/\n .div2{ /*comment*/ \n display:none; /*comment*/ \n /*comment*/}',
'reversable': false,
'options': { 'minify': true }
}
],
'RTLCSS (Directives):': [
{
'should': 'Should auto rename selectors having no directional decl. unless forced to ignore. (default)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; }',
'input': '/*rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false
},
{
'should': 'Should auto rename selectors having no directional decl. unless forced to ignore. (preserveDirectives)',
'expected': '/*rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'input': '/*rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should auto rename selectors having no directional decl. unless forced to ignore. (default, !important comment)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; }',
'input': '/*!rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false
},
{
'should': 'Should auto rename selectors having no directional decl. unless forced to ignore. (preserveDirectives, !important comment)',
'expected': '/*!rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'input': '/*!rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should auto rename selectors having no directional decl. unless forced to ignore. (greedy)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; }',
'input': '/*rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'greedy': true }
},
{
'should': 'Should auto rename selectors having no directional decl. unless forced to ignore. (greedy, !important comment)',
'expected': '.right .rtl .east .bright .ultra .least { display:block; }',
'input': '/*!rtl:ignore*/ .right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'greedy': true }
},
{
'should': 'Should rename selectors when forced. (autoRename:false)',
'expected': '.left .ltr .west .bright .ultra .least { display:block; }',
'input': '/*rtl:rename*/.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'autoRename': false }
},
{
'should': 'Should rename selectors when forced. (autoRename:false, preserveDirectives)',
'expected': '/*rtl:rename*/.left .ltr .west .bright .ultra .least { display:block; }',
'input': '/*rtl:rename*/.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'autoRename': false, 'preserveDirectives': true }
},
{
'should': 'Should rename selectors when forced. (autoRename:false, !important comment)',
'expected': '.left .ltr .west .bright .ultra .least { display:block; }',
'input': '/*!rtl:rename*/.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'autoRename': false }
},
{
'should': 'Should rename selectors when forced. (autoRename:false, preserveDirectives, !important comment)',
'expected': '/*!rtl:rename*/.left .ltr .west .bright .ultra .least { display:block; }',
'input': '/*!rtl:rename*/.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'autoRename': false, 'preserveDirectives': true }
},
{
'should': 'Should rename selectors when forced. (autoRename:false,greedy)',
'expected': '.left .ltr .west .bleft .urtla .lwest { display:block; }',
'input': '/*rtl:rename*/.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'autoRename': false, 'greedy': true }
},
{
'should': 'Should rename selectors when forced. (autoRename:false,greedy, !important comment)',
'expected': '.left .ltr .west .bleft .urtla .lwest { display:block; }',
'input': '/*!rtl:rename*/.right .rtl .east .bright .ultra .least { display:block; }',
'reversable': false,
'options': { 'autoRename': false, 'greedy': true }
},
{
'should': 'Should prepend value. (default)',
'expected': 'div { font-family: "Droid Arabic Kufi", "Droid Sans", Tahoma; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*rtl:prepend:"Droid Arabic Kufi", */; }',
'reversable': false
},
{
'should': 'Should prepend value. (preserveDirectives)',
'expected': 'div { font-family: "Droid Arabic Kufi", "Droid Sans", Tahoma/*rtl:prepend:"Droid Arabic Kufi", */; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*rtl:prepend:"Droid Arabic Kufi", */; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should prepend value (!important comment)',
'expected': 'div { font-family: "Droid Arabic Kufi", "Droid Sans", Tahoma; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*!rtl:prepend:"Droid Arabic Kufi", */; }',
'reversable': false
},
{
'should': 'Should prepend value (preserveDirectives, !important comment)',
'expected': 'div { font-family: "Droid Arabic Kufi", "Droid Sans", Tahoma/*!rtl:prepend:"Droid Arabic Kufi", */; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*!rtl:prepend:"Droid Arabic Kufi", */; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should replace value.',
'expected': 'div { font-family: "Droid Arabic Kufi"; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*rtl:"Droid Arabic Kufi"*/; }',
'reversable': false
},
{
'should': 'Should replace value.(preserveDirectives)',
'expected': 'div { font-family: "Droid Arabic Kufi"/*rtl:"Droid Arabic Kufi"*/; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*rtl:"Droid Arabic Kufi"*/; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should replace value. (!important comment)',
'expected': 'div { font-family: "Droid Arabic Kufi"; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*!rtl:"Droid Arabic Kufi"*/; }',
'reversable': false
},
{
'should': 'Should replace value. (preserveDirectives, !important comment)',
'expected': 'div { font-family: "Droid Arabic Kufi"/*!rtl:"Droid Arabic Kufi"*/; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*!rtl:"Droid Arabic Kufi"*/; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should append value. (default)',
'expected': 'div { font-family: "Droid Sans", Tahoma, "Droid Arabic Kufi"; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*rtl:append:, "Droid Arabic Kufi"*/; }',
'reversable': false
},
{
'should': 'Should append value. (preserveDirectives)',
'expected': 'div { font-family: "Droid Sans", Tahoma/*rtl:append:, "Droid Arabic Kufi"*/, "Droid Arabic Kufi"; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*rtl:append:, "Droid Arabic Kufi"*/; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should append value. (!important comment)',
'expected': 'div { font-family: "Droid Sans", Tahoma, "Droid Arabic Kufi"; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*!rtl:append:, "Droid Arabic Kufi"*/; }',
'reversable': false
},
{
'should': 'Should append value. (preserveDirectives, !important comment)',
'expected': 'div { font-family: "Droid Sans", Tahoma/*!rtl:append:, "Droid Arabic Kufi"*/, "Droid Arabic Kufi"; }',
'input': 'div { font-family: "Droid Sans", Tahoma/*!rtl:append:, "Droid Arabic Kufi"*/; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should insert value. (default)',
'expected': 'div { font-family: "Droid Sans", "Droid Arabic Kufi", Tahoma; }',
'input': 'div { font-family: "Droid Sans"/*rtl:insert:, "Droid Arabic Kufi"*/, Tahoma; }',
'reversable': false
},
{
'should': 'Should insert value. (preserveDirectives)',
'expected': 'div { font-family: "Droid Sans", "Droid Arabic Kufi"/*rtl:insert:, "Droid Arabic Kufi"*/, Tahoma; }',
'input': 'div { font-family: "Droid Sans"/*rtl:insert:, "Droid Arabic Kufi"*/, Tahoma; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should insert value. (!important comment)',
'expected': 'div { font-family: "Droid Sans", "Droid Arabic Kufi", Tahoma; }',
'input': 'div { font-family: "Droid Sans"/*!rtl:insert:, "Droid Arabic Kufi"*/, Tahoma; }',
'reversable': false
},
{
'should': 'Should insert value. (preserveDirectives, !important comment)',
'expected': 'div { font-family: "Droid Sans", "Droid Arabic Kufi"/*!rtl:insert:, "Droid Arabic Kufi"*/, Tahoma; }',
'input': 'div { font-family: "Droid Sans"/*!rtl:insert:, "Droid Arabic Kufi"*/, Tahoma; }',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should ignore flipping - rule level (default)',
'expected': 'div { left:10px; text-align:right;}',
'input': '/*rtl:ignore*/div { left:10px; text-align:right;}',
'reversable': false
},
{
'should': 'Should ignore flipping - rule level (preserveDirectives)',
'expected': '/*rtl:ignore*/div { left:10px; text-align:right;}',
'input': '/*rtl:ignore*/div { left:10px; text-align:right;}',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should ignore flipping - rule level (default, !important comment)',
'expected': 'div { left:10px; text-align:right;}',
'input': '/*!rtl:ignore*/div { left:10px; text-align:right;}',
'reversable': false
},
{
'should': 'Should ignore flipping - rule level (preserveDirectives , !important comment)',
'expected': '/*!rtl:ignore*/div { left:10px; text-align:right;}',
'input': '/*!rtl:ignore*/div { left:10px; text-align:right;}',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should ignore flipping - decl. level (default)',
'expected': 'div { left:10px;text-align:left;}',
'input': 'div { left:10px/*rtl:ignore*/;text-align:right;}',
'reversable': false
},
{
'should': 'Should ignore flipping - decl. level (preserveDirectives)',
'expected': 'div { left:10px/*rtl:ignore*/;text-align:left;}',
'input': 'div { left:10px/*rtl:ignore*/;text-align:right;}',
'reversable': false,
'options': { 'preserveDirectives': true }
},
{
'should': 'Should ignore flipping - decl. level (default, !important comment)',
'expected': 'div { left:10px;text-align:left;}',
'input': 'div { left:10px/*!rtl:ignore*/;text-align:right;}',
'reversable': false
},
{
'should': 'Should ignore flipping - decl. level (preserveDirectives, !important comment)',
'expected': 'div { left:10px/*!rtl:ignore*/;text-align:left;}',
'input': 'div { left:10px/*!rtl:ignore*/;text-align:right;}',
'options': { 'preserveDirectives': true }
}
],
'RTLCSS (String Map):': [
{
'should': 'Should rename "left", "Left", "LEFT" (default). ',
'expected': 'div.right, div.Right, div.RIGHT, div.respectLeft { width:10px;}',
'input': 'div.left, div.Left, div.LEFT, div.respectLeft { width:10px;}',
'reversable': true
},
{
'should': 'Should rename "left", "Left", "LEFT" (greedy). ',
'expected': 'div.right, div.Right, div.RIGHT, div.respectRight { width:10px;}',
'input': 'div.left, div.Left, div.LEFT, div.respectLeft { width:10px;}',
'reversable': true,
'options': {
'greedy': true
}
},
{
'should': 'Should rename "ltr", "Ltr", "LTR" (default). ',
'expected': 'div.rtl, div.Rtl, div.RTL, div.Ultra { width:10px;}',
'input': 'div.ltr, div.Ltr, div.LTR, div.Ultra { width:10px;}',
'reversable': true
},
{
'should': 'Should rename "ltr", "Ltr", "LTR" (greedy). ',
'expected': 'div.rtl, div.Rtl, div.RTL, div.Urtla { width:10px;}',
'input': 'div.ltr, div.Ltr, div.LTR, div.Ultra { width:10px;}',
'reversable': true,
'options': {
'greedy': true
}
},
{
'should': 'Should rename "west", "West", "WEST" (default). ',
'expected': 'div.east, div.East, div.EAST, div.Western { width:10px;}',
'input': 'div.west, div.West, div.WEST, div.Western { width:10px;}',
'reversable': true
},
{
'should': 'Should rename "west", "West", "WEST" (greedy). ',
'expected': 'div.east, div.East, div.EAST, div.Eastern { width:10px;}',
'input': 'div.west, div.West, div.WEST, div.Western { width:10px;}',
'reversable': true,
'options': {
'greedy': true
}
},
{
'should': 'Should rename "prev"/"next"',
'expected': 'div.next, div.Right { width:10px;}',
'input': 'div.prev, div.Left { width:10px;}',
'reversable': true,
'options': { 'stringMap': [{ "search": "prev", "replace": "next", 'options': { scope: 'selector' } }] }
},
{
'should': 'Should swap "prev"/"next" in Url',
'expected': 'div { background-image: url(/content/pix/next.png);}',
'input': 'div { background-image: url(/content/pix/prev.png);}',
'reversable': true,
'options': { 'stringMap': [{ "search": "prev", "replace": "next", 'options': { scope: 'url' } }] }
},
{
'should': 'Should swap "prev"/"next" in Url and Rename in selector',
'expected': 'div.next { display:block }; div.prev { background-image: url(/content/pix/prev.png);}',
'input': 'div.prev { display:block }; div.prev { background-image: url(/content/pix/next.png);}',
'reversable': true,
'options': { 'stringMap': [{ "search": "prev", "replace": "next", 'options': { scope: '*' } }] }
},
{
'should': 'Should rename "previous" to "nextious" (autoRename:true, greedy: true)',
'expected': 'div.nextious{ width:10px;}',
'input': 'div.previous{ width:10px;}',
'reversable': true,
'options': { 'stringMap': [{ "name": "prev-next", "search": "prev", "replace": "next", options: { greedy: true } }] }
},
{
'should': 'Should escape strings used in stringMap',
'expected': '@import url("//a.b/c-rtl.css"); @import url("//a.b/css");',
'input': '@import url("//a.b/c.css"); @import url("//a.b/css");',
'reversable': true,
'options': { 'swapLtrRtlInUrl': false, 'stringMap': [ { name: 'import-rtl-stylesheet', search: [ '.css' ], replace: [ '-rtl.css' ], options: { scope: 'url'} }] }
}
]
};
(function Run() {
for (key in tests) {
var group = tests[key];
describe(key, function () {
for (var i = 0; i < group.length; i++) {
var item = group[i];
'# Background:': require('./data/background.js'),
'# Background Image:': require('./data/background-image.js'),
'# Background Position:': require('./data/background-position.js'),
'# Properties:': require('./data/properties.js'),
'# Values:': require('./data/values.js'),
'# Values (N Value Syntax):': require('./data/values-n-syntax.js'),
'# Transforms:': require('./data/transforms.js'),
'# Transform Origin:': require('./data/transform-origin.js'),
'# RTLCSS (Options):': require('./data/rtlcss-options.js'),
'# RTLCSS (Directives):': require('./data/rtlcss-directives.js'),
'# RTLCSS (String Map):': require('./data/rtlcss-stringMap.js'),
'# RTLCSS (Plugins):': require('./data/rtlcss-plugins.js')
}
var key
for (key in tests) {
var group = tests[key]
describe(key, function () {
for (var i = 0; i < group.length; i++) {
var item = group[i]
;(function (test) {
it(test.should, function (done) {
assert.equal(rtlcss.process(test.input, test.options, test.plugins), test.expected)
done()
})
})(item)
if (item.reversable) {
(function (test) {
it(test.should, function () {
assert.equal(rtlcss.process(test.input, test.options), test.expected);
});
})(item);
if (item.reversable) {
(function (test) {
it(test.should + " <REVERESE>", function () {
assert.equal(rtlcss.process(test.expected, test.options), test.input);
});
})(item);
}
it(test.should + ' <REVERESE>', function (done) {
assert.equal(rtlcss.process(test.expected, test.options, test.plugins), test.input)
done()
})
})(item)
}
});
}
})();
}
})
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc