namespace dotless.Core.Plugins { using System; using System.Collections.Generic; using System.Linq; using System.Text; using dotless.Core.Parser.Infrastructure.Nodes; using dotless.Core.Parser.Tree; using System.Globalization; using System.Text.RegularExpressions; using System.ComponentModel; [DisplayName("Rtl"), Description("Reverses some css when in rtl mode")] public class RtlPlugin : VisitorPlugin { public RtlPlugin(bool onlyReversePrefixedRules, bool forceRtlTransform) : this() { OnlyReversePrefixedRules = onlyReversePrefixedRules; ForceRtlTransform = forceRtlTransform; } public RtlPlugin() { PropertiesToReverse = new List() { "border-left", "border-right", "border-top-left-radius", "border-top-right-radius", "border-bottom-left-radius", "border-bottom-right-radius", "border-width", "margin", "padding", "float", "right", "left", "text-align" }; } public bool OnlyReversePrefixedRules { get; set; } public bool ForceRtlTransform { get; set; } public IEnumerable PropertiesToReverse { get; set; } public override VisitorPluginType AppliesTo { get { return VisitorPluginType.AfterEvaluation; } } public override void OnPreVisiting(Parser.Infrastructure.Env env) { base.OnPreVisiting(env); bool isRtl = ForceRtlTransform || CultureInfo.CurrentCulture.TextInfo.IsRightToLeft; PrefixesToProcess = new List(); if (!OnlyReversePrefixedRules && isRtl) { foreach (string property in PropertiesToReverse) { PrefixesToProcess.Add(new Prefix() { KeepRule = true, PrefixString = property, RemovePrefix = false, Reverse = true }); } } PrefixesToProcess.Add(new Prefix() { PrefixString = "-rtl-reverse-", RemovePrefix = true, KeepRule = true, Reverse = isRtl }); PrefixesToProcess.Add(new Prefix() { PrefixString = "-ltr-reverse-", RemovePrefix = true, KeepRule = true, Reverse = !isRtl }); PrefixesToProcess.Add(new Prefix() { PrefixString = "-rtl-ltr-", RemovePrefix = true, KeepRule = true, Reverse = false }); PrefixesToProcess.Add(new Prefix() { PrefixString = "-ltr-rtl-", RemovePrefix = true, KeepRule = true, Reverse = false }); PrefixesToProcess.Add(new Prefix() { PrefixString = "-rtl-", RemovePrefix = true, KeepRule = isRtl }); PrefixesToProcess.Add(new Prefix() { PrefixString = "-ltr-", RemovePrefix = true, KeepRule = !isRtl }); } public override Node Execute(Node node, out bool visitDeeper) { Rule rule = node as Rule; if (rule != null) { visitDeeper = false; string ruleName = (rule.Name ?? "").ToLowerInvariant(); foreach (Prefix prefix in PrefixesToProcess) { if (ruleName.StartsWith(prefix.PrefixString)) { if (!prefix.KeepRule) { return null; } if (prefix.RemovePrefix) { rule.Name = rule.Name.Substring(prefix.PrefixString.Length); } if (prefix.Reverse) { if (rule.Name.IndexOf("right", StringComparison.InvariantCultureIgnoreCase) >= 0) { rule.Name = Replace(rule.Name, "right", "left", StringComparison.InvariantCultureIgnoreCase); return rule; } if (rule.Name.IndexOf("left", StringComparison.InvariantCultureIgnoreCase) >= 0) { rule.Name = Replace(rule.Name, "left", "right", StringComparison.InvariantCultureIgnoreCase); return rule; } if (rule.Name.IndexOf("top", StringComparison.InvariantCultureIgnoreCase) >= 0 || rule.Name.IndexOf("bottom", StringComparison.InvariantCultureIgnoreCase) >= 0) { return rule; } ValuesReverserVisitor reverser = new ValuesReverserVisitor(); return reverser.ReverseRule(rule); } else { return rule; } } } } visitDeeper = true; return node; } private string Replace(string haystack, string needle, string replacement, StringComparison comparisonType) { int index = haystack.IndexOf(needle, comparisonType); if (index < 0) { return haystack; } return haystack.Substring(0, index) + replacement + haystack.Substring(index + needle.Length); } private List PrefixesToProcess { get; set; } private class Prefix { public string PrefixString { get; set; } public bool KeepRule { get; set; } public bool Reverse { get; set; } public bool RemovePrefix { get; set; } } private class ValuesReverserVisitor : IVisitor { private StringBuilder _textContent = new StringBuilder(); private List _nodeContent = new List(); public Rule ReverseRule(Rule rule) { rule.Accept(this); // we have the values.. now we can reverse it string content = _textContent.ToString(); string important = ""; var value = rule.Value as Value; if (value != null) { important = value.Important; } bool valueChanged = false; if (_nodeContent.Count > 1) { if (_nodeContent.Count == 4) { Node tmp = _nodeContent[1]; _nodeContent[1] = _nodeContent[3]; _nodeContent[3] = tmp; return new Rule(rule.Name, new Value(new[] { new Expression(_nodeContent)}, important)).ReducedFrom(rule); } } else { if (content == "left") { content = ("right " + important).TrimEnd(); valueChanged = true; } else if (content == "right") { content = ("left " + important).TrimEnd(); valueChanged = true; } else { string[] items = content.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (items.Length == 4) { string temp = items[1]; items[1] = items[3]; items[3] = temp; content = String.Join(" ", items); valueChanged = true; } } if (valueChanged) { return new Rule(rule.Name, new TextNode(content)).ReducedFrom(rule); } } return rule; } #region IVisitor Members public Node Visit(Node node) { TextNode tn = node as TextNode; if (tn != null) { _textContent.Append(tn.Value); _nodeContent.Add(tn); return node; } Number number = node as Number; if (number != null) { _nodeContent.Add(number); return node; } Keyword keyword = node as Keyword; if (keyword != null) { _nodeContent.Add(keyword); _textContent.Append(keyword.Value); return node; } node.Accept(this); return node; } #endregion } } }