mirror-mirror 0.0.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|