mirror-mirror 0.0.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/mirror-mirror/activate.rb +3 -0
- data/lib/mirror-mirror/mirror_visitor.rb +195 -0
- data/lib/mirror-mirror/sass_functions.rb +5 -0
- data/lib/mirror-mirror/sass_patch.rb +62 -0
- data/lib/mirror-mirror/transformation/background_position.rb +114 -0
- data/lib/mirror-mirror/transformation/base.rb +18 -0
- data/lib/mirror-mirror/transformation/border_radius.rb +39 -0
- data/lib/mirror-mirror/transformation/box_shadow.rb +32 -0
- data/lib/mirror-mirror/transformation/cursor.rb +17 -0
- data/lib/mirror-mirror/transformation/direction.rb +18 -0
- data/lib/mirror-mirror/transformation/directional_property.rb +13 -0
- data/lib/mirror-mirror/transformation/float.rb +18 -0
- data/lib/mirror-mirror/transformation/linear_gradient.rb +84 -0
- data/lib/mirror-mirror/transformation/property_transformation.rb +69 -0
- data/lib/mirror-mirror/transformation/quad_shorthand.rb +25 -0
- data/lib/mirror-mirror/transformation/text_align.rb +19 -0
- data/lib/mirror-mirror/transformation.rb +12 -0
- data/lib/mirror-mirror/version.rb +1 -1
- data/lib/mirror-mirror.rb +25 -0
- data/stylesheets/mirror-mirror.scss +68 -0
- metadata +34 -6
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/mirror-mirror.gemspec +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14586345e6e0083164bf05e65d57bc096676491e
|
4
|
+
data.tar.gz: 38813d5fea803d5c7b36e1277fd74e9950d22687
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82e25a14ddac18c555010a536d94ab023eec33f7bc041e2b04a064773667c3bc2a74cad3f946ddafa3694cea3632b5320c1735eed5c7bc3192a7eb2ef974b676
|
7
|
+
data.tar.gz: 2bd76bcc6790fea53366a0c315ed6f8e9c4d5eb4549a7d3ff2fadcf366f82d54bf0b77edbff64659dc977a1180db65899abed05760f01a000b782fed195c8f25
|
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'mirror-mirror/transformation'
|
2
|
+
|
3
|
+
module MirrorMirror
|
4
|
+
class MirrorVisitor < Sass::Tree::Visitors::Base
|
5
|
+
def initialize
|
6
|
+
@in_meta_directive = false
|
7
|
+
@nested = false
|
8
|
+
@enabled = false
|
9
|
+
@inline = false
|
10
|
+
@options = {}
|
11
|
+
@property_transformers = Transformation::PropertyTransformation.instances
|
12
|
+
@debug = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def visit_root(root_node)
|
16
|
+
# XXX set up defaults from options
|
17
|
+
@options = root_node.options
|
18
|
+
@enabled = true if @options[:custom] && @options[:custom][:mirror_mirror] == true
|
19
|
+
root_node.children = yield
|
20
|
+
root_node
|
21
|
+
end
|
22
|
+
|
23
|
+
def visit(node)
|
24
|
+
method = "visit_#{node_name node}"
|
25
|
+
if self.respond_to?(method, true)
|
26
|
+
self.send(method, node) {node.children = visit_children(node)}
|
27
|
+
else
|
28
|
+
node.children = visit_children(node)
|
29
|
+
node
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def visit_children(parent)
|
34
|
+
r = super
|
35
|
+
r.flatten!
|
36
|
+
r.compact!
|
37
|
+
r
|
38
|
+
end
|
39
|
+
|
40
|
+
def visit_rule(rule_node)
|
41
|
+
puts "RULE: #{rule_node.resolved_rules}" if @debug
|
42
|
+
@nested = true;
|
43
|
+
yield
|
44
|
+
@nested = false;
|
45
|
+
rule_node
|
46
|
+
end
|
47
|
+
|
48
|
+
def visit_prop(prop_node)
|
49
|
+
if @in_meta_directive && prop_node.resolved_name == "enabled"
|
50
|
+
@enabled = prop_node.resolved_value == "true"
|
51
|
+
#if @nested
|
52
|
+
#raise Sass::SyntaxError, "Cannot #{@enabled ? 'enable' : 'disable'} mirror flipping in a nested context"
|
53
|
+
#end
|
54
|
+
puts @enabled ? "ENABLED" : "DISABLED" if @debug
|
55
|
+
return prop_node
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "PROPERTY: #{prop_node.resolved_name}" if @debug
|
59
|
+
unless @enabled
|
60
|
+
return prop_node
|
61
|
+
end
|
62
|
+
|
63
|
+
prop_name = prop_node.resolved_name.downcase
|
64
|
+
value = prop_node.resolved_value
|
65
|
+
prefix, prop_name = $1, $2 if prop_name =~ /^(\*|_|#|-moz-|-ms-|-webkit-|-o-)(.*)$/
|
66
|
+
original_name = prop_name
|
67
|
+
original_value = value
|
68
|
+
important = nil
|
69
|
+
|
70
|
+
if value =~ /^(.*[^ ])( *!important *)$/
|
71
|
+
value, important = $1, $2
|
72
|
+
end
|
73
|
+
|
74
|
+
property_transformers(prop_name, :name).each do |t|
|
75
|
+
prop_name = t.transform_name(prop_name)
|
76
|
+
end
|
77
|
+
|
78
|
+
property_transformers(original_name, :value).each do |t|
|
79
|
+
value = t.transform_value(original_name, value)
|
80
|
+
end
|
81
|
+
|
82
|
+
expression = nil
|
83
|
+
begin
|
84
|
+
property_transformers(original_name, :expression).each do |t|
|
85
|
+
expression ||= parsed_value(value, prop_node.line, original_name.size)
|
86
|
+
expression = t.transform_expression(original_name, expression)
|
87
|
+
end
|
88
|
+
rescue Sass::SyntaxError => e
|
89
|
+
Sass.logger.warn("Syntax error during mirror flip of `#{original_name}: #{original_value}`: #{e.message}")
|
90
|
+
return prop_node
|
91
|
+
end
|
92
|
+
begin
|
93
|
+
value = literalize(expression).to_s(@options) if expression
|
94
|
+
rescue => e
|
95
|
+
Sass.logger.warn "Failed to stringify the transformation of `#{original_name}: #{original_value}`: #{e.message} (#{expression.class.name})"
|
96
|
+
Sass.logger.warn(e.backtrace.join("\n"))
|
97
|
+
value = original_value
|
98
|
+
end
|
99
|
+
|
100
|
+
if @debug && prop_node.resolved_name != "#{prefix}#{prop_name}"
|
101
|
+
puts "CHANGED name FROM #{prop_node.resolved_name} TO #{prefix}#{prop_name}"
|
102
|
+
end
|
103
|
+
if @debug && prop_node.resolved_value != value
|
104
|
+
puts "CHANGED value FROM #{prop_node.resolved_value} TO #{value}"
|
105
|
+
end
|
106
|
+
prop_node.resolved_name = "#{prefix}#{prop_name}"
|
107
|
+
prop_node.resolved_value = "#{value}#{important}"
|
108
|
+
prop_node
|
109
|
+
end
|
110
|
+
|
111
|
+
def visit_directive(directive_node)
|
112
|
+
if directive_node.resolved_value.strip == "@-mirror-mirror"
|
113
|
+
@in_meta_directive = true
|
114
|
+
begin
|
115
|
+
yield
|
116
|
+
ensure
|
117
|
+
@in_meta_directive = false
|
118
|
+
end
|
119
|
+
nil # remove this node from the output
|
120
|
+
elsif directive_node.resolved_value.strip == "@-mirror-mirror no-flip"
|
121
|
+
old_enabled = @enabled
|
122
|
+
@enabled = false
|
123
|
+
begin
|
124
|
+
yield
|
125
|
+
directive_node.children
|
126
|
+
ensure
|
127
|
+
@enabled = old_enabled
|
128
|
+
end
|
129
|
+
elsif directive_node.resolved_value.strip == "@-mirror-mirror inline-flip"
|
130
|
+
old_inline = @inline
|
131
|
+
old_enabled = @enabled
|
132
|
+
@inline = true
|
133
|
+
@enabled = true
|
134
|
+
begin
|
135
|
+
yield
|
136
|
+
directive_node.children
|
137
|
+
ensure
|
138
|
+
@enabled = old_enabled
|
139
|
+
@inline = old_inline
|
140
|
+
end
|
141
|
+
else
|
142
|
+
yield
|
143
|
+
directive_node
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
protected
|
148
|
+
|
149
|
+
def property_transformers(name, type)
|
150
|
+
@propert_transformers_by_property ||= {}
|
151
|
+
@propert_transformers_by_property[name] ||= {}
|
152
|
+
@propert_transformers_by_property[name][type] ||= begin
|
153
|
+
@property_transformers.select {|t| t.send(:"transform_#{type}?", name)}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def parsed_value(value, line, offset)
|
158
|
+
slashify(Sass::Script::Parser.new(value, line, offset).parse)
|
159
|
+
end
|
160
|
+
|
161
|
+
def literalize(expression)
|
162
|
+
case expression
|
163
|
+
when Sass::Script::List
|
164
|
+
expression.value.map!{|v| literalize(v) }
|
165
|
+
expression
|
166
|
+
when Sass::Script::Funcall
|
167
|
+
expression.send(:to_literal, expression.args.map {|a| literalize(a) })
|
168
|
+
else
|
169
|
+
expression
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def slashify(expression)
|
174
|
+
result = _slashify(expression)
|
175
|
+
if result.is_a?(Array)
|
176
|
+
result = Sass::Script::List.new(result, :space)
|
177
|
+
result.options = expression.options
|
178
|
+
end
|
179
|
+
result
|
180
|
+
end
|
181
|
+
|
182
|
+
def _slashify(expression)
|
183
|
+
if expression.is_a?(Sass::Script::List)
|
184
|
+
expression.value.map! {|v| _slashify(v) }
|
185
|
+
expression.value.flatten!
|
186
|
+
elsif expression.is_a?(Sass::Script::Operation)
|
187
|
+
operator = Sass::Script::String.new(Sass::Script::Lexer::OPERATORS_REVERSE[expression.operator])
|
188
|
+
operator.options = expression.options
|
189
|
+
return [_slashify(expression.operand1), operator, _slashify(expression.operand2)]
|
190
|
+
end
|
191
|
+
expression
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'mirror-mirror/mirror_visitor'
|
2
|
+
|
3
|
+
module MirrorMirror
|
4
|
+
# Sass should provide hooks for getting access to the AST a various stages of transformation.
|
5
|
+
# So that we don't have to monkeypatch it.
|
6
|
+
module SassPatch
|
7
|
+
def self.included(base)
|
8
|
+
if base.respond_to?(:css_tree, true)
|
9
|
+
# Sass >= 3.3
|
10
|
+
base.send(:alias_method, :css_tree_without_mirror_mirror, :css_tree)
|
11
|
+
base.send(:alias_method, :css_tree, :css_tree_with_mirror_mirror)
|
12
|
+
else
|
13
|
+
# Sass < 3.3
|
14
|
+
base.send(:alias_method, :render_without_mirror_mirror, :render)
|
15
|
+
base.send(:alias_method, :render, :render_with_mirror_mirror)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def css_tree_with_mirror_mirror
|
20
|
+
root = css_tree_without_mirror_mirror
|
21
|
+
MirrorVisitor.visit(root)
|
22
|
+
root
|
23
|
+
end
|
24
|
+
|
25
|
+
def render_with_mirror_mirror
|
26
|
+
Sass::Tree::Visitors::CheckNesting.visit(self)
|
27
|
+
result = Sass::Tree::Visitors::Perform.visit(self)
|
28
|
+
Sass::Tree::Visitors::CheckNesting.visit(result) # Check again to validate mixins
|
29
|
+
result, extends = Sass::Tree::Visitors::Cssize.visit(result)
|
30
|
+
Sass::Tree::Visitors::Extend.visit(result, extends)
|
31
|
+
MirrorVisitor.visit(result)
|
32
|
+
result.to_s
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module CssizePatch
|
37
|
+
def visit_directive(directive)
|
38
|
+
if directive.resolved_value.start_with?("@-mirror-mirror")
|
39
|
+
did_bubble = bubble(directive)
|
40
|
+
yield unless did_bubble
|
41
|
+
directive
|
42
|
+
else
|
43
|
+
yield
|
44
|
+
directive
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module DirectiveNodePatch
|
50
|
+
def self.included(base)
|
51
|
+
base.send(:attr_accessor, :group_end)
|
52
|
+
end
|
53
|
+
|
54
|
+
def bubbles?
|
55
|
+
if resolved_value
|
56
|
+
resolved_value.start_with?("@-mirror-mirror")
|
57
|
+
else
|
58
|
+
value.first.start_with?("@-mirror-mirror")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class BackgroundPosition < PropertyTransformation
|
3
|
+
def transform_expression?(name)
|
4
|
+
name == "background" || name == "background-position" || name == "background-position-x"
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_expression(name, expression)
|
8
|
+
for_each_value(expression) do |e|
|
9
|
+
if name == "background"
|
10
|
+
transform_background(e)
|
11
|
+
else
|
12
|
+
transform_background_position(e)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def transform_background(expression)
|
18
|
+
unless expression.is_a?(Sass::Script::List)
|
19
|
+
expression = opts(expression.options) do
|
20
|
+
Sass::Script::List.new([expression], :space)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
positions = []
|
24
|
+
position_indices = []
|
25
|
+
expression.value.each_with_index do |v, i|
|
26
|
+
if legal_as_background_position?(v)
|
27
|
+
positions << v
|
28
|
+
position_indices << i
|
29
|
+
else
|
30
|
+
break if positions.any?
|
31
|
+
next
|
32
|
+
end
|
33
|
+
positions
|
34
|
+
end
|
35
|
+
return expression unless positions.any?
|
36
|
+
if positions.size > 2
|
37
|
+
require 'pry'
|
38
|
+
binding.pry
|
39
|
+
Sass.logger.warn("Line #{expression.line}: malformed background: #{expression.to_sass}")
|
40
|
+
return expression
|
41
|
+
end
|
42
|
+
# tranform the background position elements and replace the position
|
43
|
+
# section of the shorthand with it
|
44
|
+
transformed = transform_background_position(Sass::Script::List.new(positions, :space))
|
45
|
+
position_indices << position_indices.first if position_indices.size < 2
|
46
|
+
expression.value[Range.new(*position_indices)] = transformed.value
|
47
|
+
expression
|
48
|
+
end
|
49
|
+
|
50
|
+
def transform_background_position(expression)
|
51
|
+
unless expression.is_a?(Sass::Script::List)
|
52
|
+
expression = opts(expression.options) do
|
53
|
+
Sass::Script::List.new([expression], :space)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
if expression.value.size > 2
|
57
|
+
# positions with offsets
|
58
|
+
expression.value.each_with_index do |e, i|
|
59
|
+
next if i % 2 == 1
|
60
|
+
expression.value[i] = flip_it(e, i / 2)
|
61
|
+
end
|
62
|
+
else
|
63
|
+
# positions
|
64
|
+
expression.value.each_with_index do |e, i|
|
65
|
+
expression.value[i] = flip_it(e, i)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
expression
|
69
|
+
end
|
70
|
+
|
71
|
+
ONE_HUNDRED_PERCENT = Sass::Script::Number.new(100, ["%"], [])
|
72
|
+
|
73
|
+
def flip_it(e, position)
|
74
|
+
return e unless e.respond_to?(:value)
|
75
|
+
case e
|
76
|
+
when Sass::Script::Number
|
77
|
+
if position == 0 && e.value == 0
|
78
|
+
opts(e.options) { ONE_HUNDRED_PERCENT.dup }
|
79
|
+
elsif position == 0 && e.numerator_units.first == "%"
|
80
|
+
opts(e.options) { ONE_HUNDRED_PERCENT.minus(e) }
|
81
|
+
elsif position == 0
|
82
|
+
Sass.logger.warn("Line #{e.line}: Cannot flip background position: #{e}")
|
83
|
+
e
|
84
|
+
else
|
85
|
+
e
|
86
|
+
end
|
87
|
+
when Sass::Script::String
|
88
|
+
case e.value
|
89
|
+
when "right"
|
90
|
+
e.value.replace("left")
|
91
|
+
when "left"
|
92
|
+
e.value.replace("right")
|
93
|
+
end
|
94
|
+
e
|
95
|
+
else
|
96
|
+
e
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
POSITION_KEYWORDS = Set.new(%w(top bottom left right center))
|
101
|
+
|
102
|
+
def legal_as_background_position?(e)
|
103
|
+
case e
|
104
|
+
when Sass::Script::String
|
105
|
+
POSITION_KEYWORDS.include?(e.value)
|
106
|
+
when Sass::Script::Number
|
107
|
+
true
|
108
|
+
else
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module MirrorMirror::Transformation
|
4
|
+
class Base
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def self.inherited(subclass)
|
8
|
+
super
|
9
|
+
@subclasses ||= []
|
10
|
+
@subclasses << subclass
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.instances
|
14
|
+
@subclasses.map {|c| c.instance }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class BorderRadius < PropertyTransformation
|
3
|
+
def transform_value?(name)
|
4
|
+
name == "border-radius"
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_value(name, value)
|
8
|
+
values = value.split(/ +/)
|
9
|
+
h_radii = []
|
10
|
+
v_radii = []
|
11
|
+
has_separator = false
|
12
|
+
values.each do |v|
|
13
|
+
if v == "/"
|
14
|
+
has_separator = true
|
15
|
+
next
|
16
|
+
end
|
17
|
+
if has_separator
|
18
|
+
v_radii << v
|
19
|
+
else
|
20
|
+
h_radii << v
|
21
|
+
end
|
22
|
+
end
|
23
|
+
flip_corners!(h_radii)
|
24
|
+
flip_corners!(v_radii)
|
25
|
+
values[0...h_radii.size] = h_radii
|
26
|
+
if v_radii.any?
|
27
|
+
values[(h_radii.size+1)..(h_radii.size+v_radii.size)] = v_radii
|
28
|
+
end
|
29
|
+
values.join(" ")
|
30
|
+
end
|
31
|
+
|
32
|
+
def flip_corners!(list)
|
33
|
+
list[0], list[1] = list[1], list[0] if list.size > 1
|
34
|
+
list[2], list[3] = list[3], list[2] if list.size > 3
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class BoxShadow < PropertyTransformation
|
3
|
+
def transform_expression?(name)
|
4
|
+
name == "box-shadow"
|
5
|
+
end
|
6
|
+
|
7
|
+
NEGATIVE_ONE = Sass::Script::Number.new(-1)
|
8
|
+
|
9
|
+
def transform_expression(name, expression)
|
10
|
+
for_each_value(expression) do |e|
|
11
|
+
case e
|
12
|
+
when Sass::Script::List
|
13
|
+
if e.value[0].is_a?(Sass::Script::Number)
|
14
|
+
e.value[0] = flip_it(e.value[0])
|
15
|
+
end
|
16
|
+
when Sass::Script::Number
|
17
|
+
e = flip_it(e)
|
18
|
+
end
|
19
|
+
e
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def flip_it(expression)
|
24
|
+
opts(expression.options) do
|
25
|
+
expression.times(NEGATIVE_ONE)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class Cursor < PropertyTransformation
|
3
|
+
def transform_value?(name)
|
4
|
+
name == "cursor"
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_value(name, value)
|
8
|
+
if value =~ /(n|s)?(e|w)-resize/
|
9
|
+
"#{$1}#{$2 == "e" ? "w" : "e"}-resize"
|
10
|
+
else
|
11
|
+
value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class Direction < PropertyTransformation
|
3
|
+
def transform_value?(name)
|
4
|
+
name == "direction"
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_value(name, value)
|
8
|
+
case value
|
9
|
+
when "ltr"
|
10
|
+
"rtl"
|
11
|
+
when "rtl"
|
12
|
+
"ltr"
|
13
|
+
else
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class DirectionalProperty < PropertyTransformation
|
3
|
+
def transform_name?(name)
|
4
|
+
name =~ /left|right/
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_name(name)
|
8
|
+
name =~ /^(.*)(left|right)(.*)$/
|
9
|
+
"#{$1}#{$2 == "left" ? "right" : "left"}#{$3}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class Float < PropertyTransformation
|
3
|
+
def transform_value?(name)
|
4
|
+
name == "float" || name == "clear"
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_value(name, value)
|
8
|
+
case value
|
9
|
+
when "left"
|
10
|
+
"right"
|
11
|
+
when "right"
|
12
|
+
"left"
|
13
|
+
else
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'set'
|
2
|
+
module MirrorMirror::Transformation
|
3
|
+
class LinearGradient < PropertyTransformation
|
4
|
+
IMAGE_PROPERTIES = Set[
|
5
|
+
'background',
|
6
|
+
'background-image',
|
7
|
+
'border-image',
|
8
|
+
'list-style-image',
|
9
|
+
'list-style',
|
10
|
+
'content'
|
11
|
+
]
|
12
|
+
def transform_expression?(name)
|
13
|
+
IMAGE_PROPERTIES.include?(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def transform_expression(name, expression)
|
17
|
+
for_each_value(expression) do |e|
|
18
|
+
if grad = find_linear_gradient(e)
|
19
|
+
if direction?(grad.args[0])
|
20
|
+
grad.args[0] = flip(grad.args[0])
|
21
|
+
end
|
22
|
+
e
|
23
|
+
else
|
24
|
+
e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def find_linear_gradient(expression)
|
30
|
+
case expression
|
31
|
+
when Sass::Script::List
|
32
|
+
expression.value.each do |v|
|
33
|
+
grad = find_linear_gradient(v)
|
34
|
+
return grad if grad
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
when Sass::Script::Funcall
|
38
|
+
expression if expression.name =~ /^(-(moz|webkit|o|ms)-)?(repeating-)?linear-gradient$/
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def degree?(e)
|
43
|
+
e.is_a?(Sass::Script::Number) && e.numerator_units.first == "deg"
|
44
|
+
end
|
45
|
+
|
46
|
+
DIRECTIONAL_KEYWORDS = Set.new(%w(to top left right bottom))
|
47
|
+
def directional_keywords?(e)
|
48
|
+
(e.is_a?(Sass::Script::List) && DIRECTIONAL_KEYWORDS.include?(e.value.first.value)) ||
|
49
|
+
DIRECTIONAL_KEYWORDS.include?(e.value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def direction?(e)
|
53
|
+
degree?(e) || directional_keywords?(e)
|
54
|
+
end
|
55
|
+
|
56
|
+
def color_stop?(e)
|
57
|
+
e.is_a?(Sass::Script::Color) ||
|
58
|
+
(e.is_a?(Sass::Script::List) && e.value.first.is_a?(Sass::Script::Color))
|
59
|
+
end
|
60
|
+
|
61
|
+
S360 = Sass::Script::Number.new(360)
|
62
|
+
def flip(direction)
|
63
|
+
if degree?(direction)
|
64
|
+
opts(direction.options) { S360.minus(direction).mod(S360) }
|
65
|
+
else
|
66
|
+
case direction
|
67
|
+
when Sass::Script::List
|
68
|
+
direction.value.map!{|v| flip(v) }
|
69
|
+
direction
|
70
|
+
when Sass::Script::String
|
71
|
+
if direction.value == "left"
|
72
|
+
opts(direction.options) { Sass::Script::String.new("right") }
|
73
|
+
elsif direction.value == "right"
|
74
|
+
opts(direction.options) { Sass::Script::String.new("left") }
|
75
|
+
else
|
76
|
+
direction
|
77
|
+
end
|
78
|
+
else
|
79
|
+
direction
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class PropertyTransformation < Base
|
3
|
+
# The singleton instances, one for each transformation class
|
4
|
+
def self.instances
|
5
|
+
super.
|
6
|
+
select {|i| i.is_a?(PropertyTransformation) && i.class != PropertyTransformation}
|
7
|
+
end
|
8
|
+
|
9
|
+
# @param name [String] The property name
|
10
|
+
# @return [Boolean]
|
11
|
+
def transform_name?(name)
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param name [String] The property name
|
16
|
+
def transform_name(name)
|
17
|
+
name
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param name [String] The property name
|
21
|
+
# @return [Boolean]
|
22
|
+
def transform_value?(name)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param name [String] The property name
|
27
|
+
# @return [Boolean]
|
28
|
+
def transform_expression?(name)
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
# @param property [String] The property whose value is being transformed.
|
33
|
+
# Any prefix/hacks should be removed from the property name.
|
34
|
+
# @param value [String] The value being transformed.
|
35
|
+
# @return [String] A new value for the property
|
36
|
+
def transform_value(property, value)
|
37
|
+
value
|
38
|
+
end
|
39
|
+
|
40
|
+
# It is safe for this method to mutate the expression that is passed to it.
|
41
|
+
#
|
42
|
+
# @param property [String] The property whose value is being transformed.
|
43
|
+
# Any prefix/hacks should be removed from the property name.
|
44
|
+
# @param expression [Sass::Script::Node] The value being transformed.
|
45
|
+
# @return [Sass::Script::Node] A new expression for the property
|
46
|
+
def transform_expression(property, expression)
|
47
|
+
return expression
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def opts(options)
|
53
|
+
node = yield
|
54
|
+
node.options = options
|
55
|
+
node
|
56
|
+
end
|
57
|
+
|
58
|
+
def for_each_value(expression)
|
59
|
+
if expression.is_a?(Sass::Script::List) && expression.separator == :comma
|
60
|
+
expression.value.map! {|e| yield(e) }
|
61
|
+
expression
|
62
|
+
else
|
63
|
+
yield(expression)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'set'
|
2
|
+
module MirrorMirror::Transformation
|
3
|
+
class QuadShorthand < PropertyTransformation
|
4
|
+
SHORTHAND_PROPERTIES = Set[
|
5
|
+
'border-color',
|
6
|
+
'border-width',
|
7
|
+
'border-style',
|
8
|
+
'margin',
|
9
|
+
'outline',
|
10
|
+
'padding'
|
11
|
+
]
|
12
|
+
def transform_expression?(name)
|
13
|
+
SHORTHAND_PROPERTIES.include?(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def transform_expression(name, expression)
|
17
|
+
if expression.is_a?(Sass::Script::List) &&
|
18
|
+
expression.separator == :space &&
|
19
|
+
expression.value.size == 4
|
20
|
+
expression.value[1], expression.value[3] = expression.value[3], expression.value[1]
|
21
|
+
end
|
22
|
+
expression
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MirrorMirror::Transformation
|
2
|
+
class TextAlign < PropertyTransformation
|
3
|
+
def transform_value?(name)
|
4
|
+
name == "text-align"
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform_value(name, value)
|
8
|
+
case value
|
9
|
+
when "left"
|
10
|
+
"right"
|
11
|
+
when "right"
|
12
|
+
"left"
|
13
|
+
else
|
14
|
+
value
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'mirror-mirror/transformation/base'
|
2
|
+
require 'mirror-mirror/transformation/property_transformation'
|
3
|
+
require 'mirror-mirror/transformation/cursor'
|
4
|
+
require 'mirror-mirror/transformation/directional_property'
|
5
|
+
require 'mirror-mirror/transformation/direction'
|
6
|
+
require 'mirror-mirror/transformation/float'
|
7
|
+
require 'mirror-mirror/transformation/text_align'
|
8
|
+
require 'mirror-mirror/transformation/background_position'
|
9
|
+
require 'mirror-mirror/transformation/quad_shorthand'
|
10
|
+
require 'mirror-mirror/transformation/box_shadow'
|
11
|
+
require 'mirror-mirror/transformation/border_radius'
|
12
|
+
require 'mirror-mirror/transformation/linear_gradient'
|
data/lib/mirror-mirror.rb
CHANGED
@@ -1,5 +1,30 @@
|
|
1
|
+
require 'sass'
|
1
2
|
require "mirror-mirror/version"
|
3
|
+
require "mirror-mirror/sass_patch"
|
2
4
|
|
3
5
|
module MirrorMirror
|
6
|
+
class << self
|
7
|
+
def activate!
|
8
|
+
require 'mirror-mirror/sass_functions'
|
9
|
+
add_to_load_path!
|
10
|
+
Sass::Tree::RootNode.send(:include, SassPatch)
|
11
|
+
Sass::Tree::Visitors::Cssize.send(:include, CssizePatch)
|
12
|
+
Sass::Tree::DirectiveNode.send(:include, DirectiveNodePatch)
|
13
|
+
end
|
4
14
|
|
15
|
+
def add_to_load_path!
|
16
|
+
stylesheets_path = File.expand_path(File.join(File.dirname(__FILE__), '..', 'stylesheets'))
|
17
|
+
begin
|
18
|
+
require 'compass'
|
19
|
+
Compass::Frameworks.register('mirror-mirror', :stylesheets_directory => stylesheets_path)
|
20
|
+
rescue LoadError
|
21
|
+
# compass not found, register on the Sass path via the environment.
|
22
|
+
if ENV.has_key?("SASS_PATH")
|
23
|
+
ENV["SASS_PATH"] = ENV["SASS_PATH"] + File::PATH_SEPARATOR + stylesheets_path
|
24
|
+
else
|
25
|
+
ENV["SASS_PATH"] = stylesheets_path
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
5
30
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
$mirror-mirror-enabled: mirror-mirror-enabled-by-default() !default;
|
2
|
+
$mirror-mirror-noflip: false !default;
|
3
|
+
$mirror-mirror-default-inline-context: "*[dir=rtl]" !default;
|
4
|
+
$mirror-mirror-inline-contexts: ();
|
5
|
+
|
6
|
+
@function mm-car($list) {
|
7
|
+
$new-list: ();
|
8
|
+
@for $i from 1 to length($list) {
|
9
|
+
$new-list: append($new-list, nth($list, $i));
|
10
|
+
}
|
11
|
+
@return $new-list;
|
12
|
+
}
|
13
|
+
|
14
|
+
@mixin enable-mirror-flip {
|
15
|
+
@if $mirror-mirror-noflip {
|
16
|
+
@warn "Enabled flipping in a no-flip. Ignoring.";
|
17
|
+
} @else {
|
18
|
+
$mirror-mirror-enabled: true;
|
19
|
+
@-mirror-mirror {
|
20
|
+
enabled: true;
|
21
|
+
@content;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
@mixin disable-mirror-flip {
|
27
|
+
@if $mirror-mirror-noflip {
|
28
|
+
@warn "Disabled flipping in a no-flip. Ignoring.";
|
29
|
+
} @else {
|
30
|
+
$mirror-mirror-enabled: false;
|
31
|
+
@-mirror-mirror {
|
32
|
+
enabled: false;
|
33
|
+
@content;
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
@mixin no-flip {
|
39
|
+
@if $mirror-mirror-noflip or not $mirror-mirror-enabled {
|
40
|
+
@content;
|
41
|
+
} @else {
|
42
|
+
$mirror-mirror-noflip: true;
|
43
|
+
@-mirror-mirror no-flip {
|
44
|
+
@content;
|
45
|
+
}
|
46
|
+
$mirror-mirror-noflip: false;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
|
50
|
+
@mixin inline-flip($selector-context: $mirror-mirror-default-inline-context,
|
51
|
+
$prepend-context: false) {
|
52
|
+
@if $mirror-mirror-enabled {
|
53
|
+
// Do nothing if we're in a flipped context already.
|
54
|
+
@content;
|
55
|
+
} @else {
|
56
|
+
@content;
|
57
|
+
@-mirror-mirror inline-flip {
|
58
|
+
@if not index($mirror-mirror-inline-contexts, $selector-context) {
|
59
|
+
$mirror-mirror-inline-contexts: append($mirror-mirror-inline-contexts, $selector-context);
|
60
|
+
#{$selector-context} #{if($prepend-context, '&', null)} {
|
61
|
+
@content;
|
62
|
+
}
|
63
|
+
$mirror-mirror-inline-contexts: mm-car($mirror-mirror-inline-contexts);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mirror-mirror
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Eppstein
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sass
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -46,13 +60,28 @@ extensions: []
|
|
46
60
|
extra_rdoc_files: []
|
47
61
|
files:
|
48
62
|
- .gitignore
|
49
|
-
- Gemfile
|
50
63
|
- LICENSE.txt
|
51
64
|
- README.md
|
52
|
-
- Rakefile
|
53
65
|
- lib/mirror-mirror.rb
|
66
|
+
- lib/mirror-mirror/activate.rb
|
67
|
+
- lib/mirror-mirror/mirror_visitor.rb
|
68
|
+
- lib/mirror-mirror/sass_functions.rb
|
69
|
+
- lib/mirror-mirror/sass_patch.rb
|
70
|
+
- lib/mirror-mirror/transformation.rb
|
71
|
+
- lib/mirror-mirror/transformation/background_position.rb
|
72
|
+
- lib/mirror-mirror/transformation/base.rb
|
73
|
+
- lib/mirror-mirror/transformation/border_radius.rb
|
74
|
+
- lib/mirror-mirror/transformation/box_shadow.rb
|
75
|
+
- lib/mirror-mirror/transformation/cursor.rb
|
76
|
+
- lib/mirror-mirror/transformation/direction.rb
|
77
|
+
- lib/mirror-mirror/transformation/directional_property.rb
|
78
|
+
- lib/mirror-mirror/transformation/float.rb
|
79
|
+
- lib/mirror-mirror/transformation/linear_gradient.rb
|
80
|
+
- lib/mirror-mirror/transformation/property_transformation.rb
|
81
|
+
- lib/mirror-mirror/transformation/quad_shorthand.rb
|
82
|
+
- lib/mirror-mirror/transformation/text_align.rb
|
54
83
|
- lib/mirror-mirror/version.rb
|
55
|
-
- mirror-mirror.
|
84
|
+
- stylesheets/mirror-mirror.scss
|
56
85
|
homepage: ''
|
57
86
|
licenses:
|
58
87
|
- MIT
|
@@ -78,4 +107,3 @@ signing_key:
|
|
78
107
|
specification_version: 4
|
79
108
|
summary: Bidirectional CSS transformation utility for Sass.
|
80
109
|
test_files: []
|
81
|
-
has_rdoc:
|
data/Gemfile
DELETED
data/Rakefile
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
data/mirror-mirror.gemspec
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'mirror-mirror/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "mirror-mirror"
|
8
|
-
spec.version = MirrorMirror::VERSION
|
9
|
-
spec.authors = ["Chris Eppstein"]
|
10
|
-
spec.email = ["chris@eppsteins.net"]
|
11
|
-
spec.description = %q{Flip the design of your website along the vertical axis.}
|
12
|
-
spec.summary = %q{Bidirectional CSS transformation utility for Sass.}
|
13
|
-
spec.homepage = ""
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files`.split($/)
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
-
spec.add_development_dependency "rake"
|
23
|
-
end
|