rtlize-2 1.0.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +167 -0
- data/Rakefile +29 -0
- data/bin/rtlize +8 -0
- data/lib/rtlize/declaration.rb +178 -0
- data/lib/rtlize/exec.rb +70 -0
- data/lib/rtlize/helpers.rb +5 -0
- data/lib/rtlize/railtie.rb +22 -0
- data/lib/rtlize/rtl_processor.rb +24 -0
- data/lib/rtlize/rtlizer.rb +126 -0
- data/lib/rtlize/version.rb +3 -0
- data/lib/rtlize.rb +6 -0
- data/lib/tasks/rtlize_tasks.rake +4 -0
- metadata +112 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 884272fe01dab544c72d86df4ea20244d451288d8ae579b9bae1c70cffb8bff6
|
|
4
|
+
data.tar.gz: e0d12af5c26c6d21810549cb2f63b91a4b5b272c49b266f95bee1de5373ca216
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9be72cfe6fc05eab6b89c6116cd1522f8fb0e0bd623716fb4a2aa621f5e898ba3426ff9700ccc62ef3d2c30f0d5909aadc18ec0e86bcaa6467f02e22038ce403
|
|
7
|
+
data.tar.gz: '00778d679fda8ef300be52b36304e11b08cd80d36315681f86b89ef409f3a12a78f10ac27849f9f24e7add26e59ffea511b43cafcf3fa9f4d323f130cbc0e2d9'
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright 2014 Marwan Al Jubeh
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# RTLize
|
|
2
|
+
|
|
3
|
+
[](https://github.com/fadynaffa3/RTLize/actions)
|
|
4
|
+
|
|
5
|
+
RTLize allows you to write your stylesheets for left-to-right (LTR) layouts and have them automatically work for right-to-left (RTL) layouts as well. It does this by mirroring all the properties and values to their RTL equivalent.
|
|
6
|
+
|
|
7
|
+
RTLize doesn't depend on any other gem and doesn't tie you down to Rails or any other framework. However, it does work with Rails & Sprockets out of the box, and you can hook it up manually if you use a different stack.
|
|
8
|
+
|
|
9
|
+
## Requirements
|
|
10
|
+
|
|
11
|
+
- Ruby >= 4.0.0
|
|
12
|
+
- Rails >= 7.0 (optional — works standalone too)
|
|
13
|
+
- Sprockets 4.x (when used with Rails)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Add it to your Gemfile:
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
gem 'rtlize'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then run:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
bundle install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Using RTLize from the Command Line
|
|
30
|
+
|
|
31
|
+
Install the gem:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
gem install rtlize
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then use the `rtlize` command to flip your LTR stylesheets to RTL (or vice versa):
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
rtlize application.css application.rtl.css
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For help:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
rtlize -h
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Using RTLize with Rails
|
|
50
|
+
|
|
51
|
+
### 1. Create symlinks for your RTL stylesheets
|
|
52
|
+
|
|
53
|
+
Create symbolic links of your stylesheets with `.css` replaced by `.rtl.css`:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
ln -s sheet1.css sheet1.rtl.css
|
|
57
|
+
ln -s sheet2.css.scss sheet2.rtl.css.scss
|
|
58
|
+
ln -s sheet3.css.erb sheet3.rtl.css.erb
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 2. Declare them in your Sprockets manifest
|
|
62
|
+
|
|
63
|
+
In `app/assets/config/manifest.js`, explicitly link your RTL assets:
|
|
64
|
+
|
|
65
|
+
```js
|
|
66
|
+
//= link application.css
|
|
67
|
+
//= link application.rtl.css
|
|
68
|
+
//= link sheet2.css
|
|
69
|
+
//= link sheet2.rtl.css
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3. Set the `dir` attribute on your HTML element
|
|
73
|
+
|
|
74
|
+
```erb
|
|
75
|
+
<html lang="<%= I18n.locale %>" dir="<%= Rtlize.dir %>">
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`Rtlize.dir` returns `'rtl'` when the current locale is in the RTL locales list (`:ar`, `:fa`, `:he`, `:ur` by default), and `'ltr'` otherwise.
|
|
79
|
+
|
|
80
|
+
Now when you request `sheet1.rtl.css`, you receive an RTLized version of `sheet1.css`.
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
In `config/application.rb` or an initializer:
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
# Default: "[dir=rtl]"
|
|
88
|
+
config.rtlize.rtl_selector = "[dir=rtl]"
|
|
89
|
+
|
|
90
|
+
# Default: [:ar, :fa, :he, :ur]
|
|
91
|
+
config.rtlize.rtl_locales = [:ar, :fa, :he, :ur]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Manually Overriding CSS
|
|
95
|
+
|
|
96
|
+
RTLize won't transform any CSS rules whose selector contains the `rtl_selector`. Use this to write RTL-specific overrides:
|
|
97
|
+
|
|
98
|
+
```css
|
|
99
|
+
/* This will be transformed: margin becomes "1px 4px 3px 2px" */
|
|
100
|
+
.class-1 {
|
|
101
|
+
margin: 1px 2px 3px 4px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* These will NOT be transformed — they already target RTL */
|
|
105
|
+
[dir=rtl] .class-2 { margin-right: 2px; }
|
|
106
|
+
[dir="rtl"] .class-3 { margin-right: 3px; }
|
|
107
|
+
[dir='rtl'].class-4 { margin-right: 4px; }
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Preventing Sections from Being Transformed
|
|
111
|
+
|
|
112
|
+
Use the `no-rtl` directive to exclude blocks of CSS:
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
/* This will be transformed */
|
|
116
|
+
.top-level-class .child-1 {
|
|
117
|
+
margin-left: 1px;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/*!= begin(no-rtl) */
|
|
121
|
+
/* This will NOT be transformed */
|
|
122
|
+
.top-level-class .child-2 {
|
|
123
|
+
float: left;
|
|
124
|
+
}
|
|
125
|
+
/*!= end(no-rtl) */
|
|
126
|
+
|
|
127
|
+
/* This will be transformed again */
|
|
128
|
+
.another-class span {
|
|
129
|
+
padding-right: 5px;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
> **Note:** The `!` in the comment is required so CSS compressors don't strip the directive before RTLize sees it. In SCSS/Sass you may need `!!` (e.g. `/*!!= begin(no-rtl) */`). Only use these directives at the top level of your CSS — not inside nested rules.
|
|
134
|
+
|
|
135
|
+
## Rails Admin
|
|
136
|
+
|
|
137
|
+
RTLize works with [rails_admin](https://github.com/railsadminteam/rails_admin). Add a symlink for the rails_admin stylesheet and link it in your manifest:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
ln -s rails_admin/application.css rails_admin/application.rtl.css
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
In `app/assets/config/manifest.js`:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
//= link rails_admin/application.rtl.css
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Then in your rails_admin layout or initializer, switch the stylesheet based on locale:
|
|
150
|
+
|
|
151
|
+
```erb
|
|
152
|
+
<html dir="<%= Rtlize.dir %>">
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Versioning
|
|
156
|
+
|
|
157
|
+
This gem follows [Semantic Versioning](https://semver.org).
|
|
158
|
+
|
|
159
|
+
## Credits
|
|
160
|
+
|
|
161
|
+
Maintained by [Fady Naffa](https://github.com/fadynaffa3).
|
|
162
|
+
|
|
163
|
+
Originally inspired by [Dustin Diaz's R2](https://github.com/ded/R2).
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
RTLize is released under the [MIT License](MIT-LICENSE).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
Bundler::GemHelper.install_tasks
|
|
12
|
+
|
|
13
|
+
require 'rake/testtask'
|
|
14
|
+
|
|
15
|
+
Rake::TestTask.new(:test) do |t|
|
|
16
|
+
t.libs << 'lib'
|
|
17
|
+
t.libs << 'test'
|
|
18
|
+
t.pattern = 'test/**/*_test.rb'
|
|
19
|
+
t.verbose = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
task :default => :test
|
|
24
|
+
|
|
25
|
+
desc "Delete the latest tag"
|
|
26
|
+
task :delete_tag do
|
|
27
|
+
system "git tag -d v#{Rtlize::VERSION}"
|
|
28
|
+
system "git push origin :refs/tags/v#{Rtlize::VERSION}"
|
|
29
|
+
end
|
data/bin/rtlize
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
module Rtlize
|
|
2
|
+
class Declaration
|
|
3
|
+
@property_map = {
|
|
4
|
+
'margin-left' => 'margin-right',
|
|
5
|
+
'margin-right' => 'margin-left',
|
|
6
|
+
|
|
7
|
+
'padding-left' => 'padding-right',
|
|
8
|
+
'padding-right' => 'padding-left',
|
|
9
|
+
|
|
10
|
+
'border-left' => 'border-right',
|
|
11
|
+
'border-right' => 'border-left',
|
|
12
|
+
|
|
13
|
+
'border-left-width' => 'border-right-width',
|
|
14
|
+
'border-right-width' => 'border-left-width',
|
|
15
|
+
|
|
16
|
+
'border-left-style' => 'border-right-style',
|
|
17
|
+
'border-right-style' => 'border-left-style',
|
|
18
|
+
|
|
19
|
+
'border-left-color' => 'border-right-color',
|
|
20
|
+
'border-right-color' => 'border-left-color',
|
|
21
|
+
|
|
22
|
+
'border-bottom-right-radius' => 'border-bottom-left-radius',
|
|
23
|
+
'border-bottom-left-radius' => 'border-bottom-right-radius',
|
|
24
|
+
'-webkit-border-bottom-right-radius' => '-webkit-border-bottom-left-radius',
|
|
25
|
+
'-webkit-border-bottom-left-radius' => '-webkit-border-bottom-right-radius',
|
|
26
|
+
'-moz-border-radius-bottomright' => '-moz-border-radius-bottomleft',
|
|
27
|
+
'-moz-border-radius-bottomleft' => '-moz-border-radius-bottomright',
|
|
28
|
+
|
|
29
|
+
'border-top-right-radius' => 'border-top-left-radius',
|
|
30
|
+
'border-top-left-radius' => 'border-top-right-radius',
|
|
31
|
+
'-webkit-border-top-right-radius' => '-webkit-border-top-left-radius',
|
|
32
|
+
'-webkit-border-top-left-radius' => '-webkit-border-top-right-radius',
|
|
33
|
+
'-moz-border-radius-topright' => '-moz-border-radius-topleft',
|
|
34
|
+
'-moz-border-radius-topleft' => '-moz-border-radius-topright',
|
|
35
|
+
|
|
36
|
+
'left' => 'right',
|
|
37
|
+
'right' => 'left',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@value_map = {
|
|
41
|
+
'border-color' => :quad,
|
|
42
|
+
'border-style' => :quad,
|
|
43
|
+
'border-width' => :quad,
|
|
44
|
+
'padding' => :quad,
|
|
45
|
+
'margin' => :quad,
|
|
46
|
+
'clip' => :rect,
|
|
47
|
+
'cursor' => :cursor,
|
|
48
|
+
'text-align' => :rtltr,
|
|
49
|
+
'float' => :rtltr,
|
|
50
|
+
'clear' => :rtltr,
|
|
51
|
+
'direction' => :direction,
|
|
52
|
+
'border-radius' => :quad_radius,
|
|
53
|
+
'-webkit-border-radius' => :quad_radius,
|
|
54
|
+
'-moz-border-radius' => :quad_radius,
|
|
55
|
+
'box-shadow' => :shadow,
|
|
56
|
+
'-webkit-box-shadow' => :shadow,
|
|
57
|
+
'-moz-box-shadow' => :shadow,
|
|
58
|
+
'text-shadow' => :shadow,
|
|
59
|
+
'-webkit-text-shadow' => :shadow,
|
|
60
|
+
'-moz-text-shadow' => :shadow,
|
|
61
|
+
'rotation' => :deg,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
class << self
|
|
65
|
+
def transform(property, value)
|
|
66
|
+
# Get the property, without comments or spaces, to be able to find it.
|
|
67
|
+
property_name = property.strip.split(' ').last.gsub(/^[*_]/, '')
|
|
68
|
+
if @property_map[property_name]
|
|
69
|
+
property = property.sub(property_name, @property_map[property_name])
|
|
70
|
+
elsif @value_map[property_name]
|
|
71
|
+
clean_value = value.sub(/;$/, '').sub(/\\9/, '').sub(/!\s*important/, '').strip
|
|
72
|
+
value = value.sub(clean_value, self.send(@value_map[property_name], clean_value))
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
property + ':' + value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def transform_multiple(declarations)
|
|
79
|
+
declarations.split(/(;)(?!base64)/).map do |declaration|
|
|
80
|
+
m = declaration.match(/([^:]+):(.+)/m)
|
|
81
|
+
|
|
82
|
+
if m
|
|
83
|
+
property, value = m[1..2]
|
|
84
|
+
transform(property, value)
|
|
85
|
+
else
|
|
86
|
+
declaration
|
|
87
|
+
end
|
|
88
|
+
end.join
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def rtltr(v)
|
|
92
|
+
v == 'left' ? 'right' : v == 'right' ? 'left' : v
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def direction(v)
|
|
96
|
+
v == 'ltr' ? 'rtl' : v == 'rtl' ? 'ltr' : v
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def rect(v)
|
|
100
|
+
if v.match(/rect\([^)]*\)/)
|
|
101
|
+
v.gsub(/\([^)]*\)/) do |m|
|
|
102
|
+
parts = m.gsub(/[()]/, '').split(',').map(&:strip)
|
|
103
|
+
if parts.size == 1
|
|
104
|
+
# Using backwards compatible syntax
|
|
105
|
+
parts = m.gsub(/[()]/, '').split(/\s+/).map(&:strip)
|
|
106
|
+
"(#{parts[0]} #{parts[3]} #{parts[2]} #{parts[1]})"
|
|
107
|
+
else
|
|
108
|
+
"(#{parts[0]}, #{parts[3]}, #{parts[2]}, #{parts[1]})"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
v
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def quad(v)
|
|
117
|
+
# 1px 2px 3px 4px => 1px 4px 3px 2px
|
|
118
|
+
m = v.split(/\s+/)
|
|
119
|
+
m.length == 4 && !v.include?('rgba(') ? [m[0], m[3], m[2], m[1]].join(' ') : v
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def quad_radius(v)
|
|
123
|
+
# top-left, top-right, bottom-right, bottom-left
|
|
124
|
+
# when bottom-left is omitted, it takes the value of top-right
|
|
125
|
+
# when bottom-right is omitted, it takes the value of top-left
|
|
126
|
+
# when top-right is omitted, it takes the value of top-left
|
|
127
|
+
m = v.split(/\s+/)
|
|
128
|
+
case m.length
|
|
129
|
+
when 4 then [m[1], m[0], m[3], m[2]].join(' ')
|
|
130
|
+
when 3 then [m[1], m[0], m[1], m[2]].join(' ')
|
|
131
|
+
when 2 then [m[1], m[0]].join(' ')
|
|
132
|
+
else v
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def cursor(v)
|
|
137
|
+
if v.match(/^[ns]?e-resize$/)
|
|
138
|
+
v.gsub(/e-resize/, 'w-resize')
|
|
139
|
+
elsif v.match(/^[ns]?w-resize$/)
|
|
140
|
+
v.gsub(/w-resize/, 'e-resize')
|
|
141
|
+
else
|
|
142
|
+
v
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def shadow(v)
|
|
147
|
+
found = false
|
|
148
|
+
v.gsub(/rgba\([^)]*\)|,|#[0-9A-Fa-f]*|[-0-9.px]+/) do |m|
|
|
149
|
+
if m == ","
|
|
150
|
+
# this property can take several comma-seperated values, we account for that, and transform each one correctly.
|
|
151
|
+
found = false
|
|
152
|
+
m
|
|
153
|
+
elsif m.match(/rgba\([^)]*\)|#[0-9A-Fa-f]*/) || found
|
|
154
|
+
m
|
|
155
|
+
else
|
|
156
|
+
found = true
|
|
157
|
+
if m.to_f.zero?
|
|
158
|
+
m
|
|
159
|
+
else
|
|
160
|
+
m.chars.first == '-' ? m[1..-1] : '-' + m
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def deg(v)
|
|
167
|
+
if v == '0'
|
|
168
|
+
v
|
|
169
|
+
else
|
|
170
|
+
old_angle = v.to_f
|
|
171
|
+
new_angle = 360 - old_angle
|
|
172
|
+
new_angle = new_angle.to_i if new_angle == new_angle.to_i # If it's an integer, write it without a decimal part.
|
|
173
|
+
v.gsub(/[0-9.]+/, new_angle.to_s)
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
data/lib/rtlize/exec.rb
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'optparse'
|
|
2
|
+
|
|
3
|
+
module Rtlize
|
|
4
|
+
class Exec
|
|
5
|
+
def initialize(args)
|
|
6
|
+
@args = args
|
|
7
|
+
@input = $stdin
|
|
8
|
+
@output = $stdout
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def setup_option_parser
|
|
12
|
+
@option_parser = OptionParser.new do |opts|
|
|
13
|
+
opts.banner = <<-END
|
|
14
|
+
Usage: rtlize [options | source_file [target_file]]
|
|
15
|
+
|
|
16
|
+
Description:
|
|
17
|
+
|
|
18
|
+
The rtlize utility reads CSS from the source_file, or the standard input (stdin) if no source_file is specified,
|
|
19
|
+
and transforms it to target right-to-left (RTL) layouts instead of left-to-right (LTR) layouts, or vice versa.
|
|
20
|
+
|
|
21
|
+
The transformed CSS will then be written to the target_file, or the standard output (stdout) if no target_file is specified.
|
|
22
|
+
|
|
23
|
+
Options:
|
|
24
|
+
END
|
|
25
|
+
|
|
26
|
+
opts.on_tail("-h", "-?", "--help", "Show this message") do
|
|
27
|
+
puts opts
|
|
28
|
+
exit
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
opts.on_tail("-v", "--version", "Print version") do
|
|
32
|
+
puts("RTLize #{Rtlize::VERSION}")
|
|
33
|
+
exit
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def parse_options
|
|
39
|
+
setup_option_parser
|
|
40
|
+
@option_parser.parse!(@args)
|
|
41
|
+
|
|
42
|
+
if @args.length > 2
|
|
43
|
+
puts @option_parser
|
|
44
|
+
exit 1
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@input = File.open(@args[0], 'r') if @args.length > 0
|
|
48
|
+
@output = File.open(@args[1], 'w') if @args.length > 1
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def parse!
|
|
52
|
+
parse_options
|
|
53
|
+
rtlize_input
|
|
54
|
+
exit 0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def rtlize_input
|
|
58
|
+
if @input.tty?
|
|
59
|
+
puts "Warning: Reading from standard input. Use Ctrl-D to indicate EOF."
|
|
60
|
+
end
|
|
61
|
+
input = @input.read
|
|
62
|
+
@input.close
|
|
63
|
+
|
|
64
|
+
output = Rtlize::RTLizer.transform(input)
|
|
65
|
+
|
|
66
|
+
@output.write(output)
|
|
67
|
+
@output.close
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rtlize/helpers'
|
|
2
|
+
require 'rtlize/rtl_processor'
|
|
3
|
+
|
|
4
|
+
module Rtlize
|
|
5
|
+
class Railtie < ::Rails::Railtie
|
|
6
|
+
config.rtlize = ActiveSupport::OrderedOptions.new
|
|
7
|
+
config.rtlize.rtl_selector = Rtlize.rtl_selector
|
|
8
|
+
config.rtlize.rtl_locales = Rtlize.rtl_locales
|
|
9
|
+
|
|
10
|
+
initializer "rtlize.railtie", :after => "sprockets.environment" do |app|
|
|
11
|
+
if defined?(Sprockets)
|
|
12
|
+
# Sprockets 4+ (Rails 7+) uses a callable with call(input) -> { data: }
|
|
13
|
+
# Sprockets 3 used register_postprocessor with a class having render(context, locals)
|
|
14
|
+
# We support Sprockets 4+ only.
|
|
15
|
+
Sprockets.register_postprocessor 'text/css', Rtlize::RtlProcessor
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Rtlize.rtl_selector = config.rtlize.rtl_selector
|
|
19
|
+
Rtlize.rtl_locales = config.rtlize.rtl_locales
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Rtlize
|
|
2
|
+
class RtlProcessor
|
|
3
|
+
ALLOWED_EXTENSIONS = %w[css scss sass].freeze
|
|
4
|
+
|
|
5
|
+
def self.call(input)
|
|
6
|
+
filename = input[:filename]
|
|
7
|
+
source = input[:data]
|
|
8
|
+
context = input[:environment].context_class.new(input)
|
|
9
|
+
|
|
10
|
+
result = run(filename, source, context)
|
|
11
|
+
context.metadata.merge(data: result)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.run(filename, source, context)
|
|
15
|
+
basename = File.basename(filename)
|
|
16
|
+
extension = basename.split('.').last
|
|
17
|
+
if ALLOWED_EXTENSIONS.include?(extension) && context.logical_path.to_s.match?(/\.rtl/i)
|
|
18
|
+
Rtlize::RTLizer.transform(source)
|
|
19
|
+
else
|
|
20
|
+
source
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
require 'rtlize/declaration'
|
|
2
|
+
|
|
3
|
+
module Rtlize
|
|
4
|
+
def self.rtl_selector
|
|
5
|
+
@@rtl_selector ||= "[dir=rtl]"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.rtl_selector=(selector)
|
|
9
|
+
@@rtl_selector = selector
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.rtl_locales
|
|
13
|
+
@@rtl_locales ||= [:ar, :fa, :he, :ur]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.rtl_locales=(locales)
|
|
17
|
+
@@rtl_locales = locales
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class RTLizer
|
|
21
|
+
class << self
|
|
22
|
+
def simple_block_regexp
|
|
23
|
+
%r{
|
|
24
|
+
( [^\{\}]+ ) \{
|
|
25
|
+
( [^\{\}]+ )
|
|
26
|
+
\}
|
|
27
|
+
}x
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def block_regexp
|
|
31
|
+
%r{
|
|
32
|
+
[^\{\}]+ \{
|
|
33
|
+
(?:
|
|
34
|
+
(?:
|
|
35
|
+
[^\{\}]+ \{
|
|
36
|
+
[^\}]*
|
|
37
|
+
\} [^\{\}]*
|
|
38
|
+
)*
|
|
39
|
+
|
|
|
40
|
+
[^\{\}]+
|
|
41
|
+
)
|
|
42
|
+
\}
|
|
43
|
+
}x
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def rule_regexp
|
|
47
|
+
# Break the blocks' rules into their selector & declaration parts
|
|
48
|
+
%r{
|
|
49
|
+
( [^\{\}]+ ) \{
|
|
50
|
+
(
|
|
51
|
+
(?:
|
|
52
|
+
[^\{\}]+ \{
|
|
53
|
+
[^\}]*
|
|
54
|
+
\} [^\{\}]*
|
|
55
|
+
)*
|
|
56
|
+
)
|
|
57
|
+
\}
|
|
58
|
+
|
|
|
59
|
+
( [^\{\}]+ ) \{
|
|
60
|
+
( [^\{\}]+ )
|
|
61
|
+
\}
|
|
62
|
+
}x
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def should_rtlize_selector?(selector)
|
|
66
|
+
selector = selector.gsub(/\/\*[\s\S]+?\*\//, '') # Remove comments
|
|
67
|
+
selector = selector.gsub(/['"]/, '') # Remove quote characters
|
|
68
|
+
|
|
69
|
+
rtl_selector = Rtlize.rtl_selector.gsub(/['"]/, '') # Remove quote characters
|
|
70
|
+
|
|
71
|
+
rtl_selector_regexp = /(^|\b|\s)#{Regexp.escape(rtl_selector)}($|\b|\s)/
|
|
72
|
+
!selector.match(rtl_selector_regexp)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def update_no_invert(selector)
|
|
76
|
+
# The CSS comment must start with "!" in order to be considered as important by the CSS compressor
|
|
77
|
+
# otherwise, it will be removed by the asset pipeline before reaching this processor.
|
|
78
|
+
if selector.match(/\/\*!= begin\(no-rtl\) \*\//)
|
|
79
|
+
@no_invert = true
|
|
80
|
+
elsif selector.match(/\/\*!= end\(no-rtl\) \*\//)
|
|
81
|
+
@no_invert = false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def transform(css)
|
|
86
|
+
@no_invert = false
|
|
87
|
+
|
|
88
|
+
css.gsub(block_regexp) do |block|
|
|
89
|
+
block_selector_regexp = %r{ ^ [^\{\}]+ }x
|
|
90
|
+
block_selector = block.match(block_selector_regexp).to_s
|
|
91
|
+
next if block_selector.length == 0
|
|
92
|
+
|
|
93
|
+
block.gsub(rule_regexp) do |rule|
|
|
94
|
+
parts = rule.match(rule_regexp)
|
|
95
|
+
if parts[1].nil?
|
|
96
|
+
# Simple block
|
|
97
|
+
selector, declarations = parts[3,4]
|
|
98
|
+
transform_simple_block(selector, declarations)
|
|
99
|
+
else
|
|
100
|
+
# Nested blocks
|
|
101
|
+
selector, declarations = parts[1,2]
|
|
102
|
+
selector + '{' + transform_nested_blocks(declarations) + '}'
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def transform_simple_block(selector, declarations)
|
|
109
|
+
update_no_invert(selector)
|
|
110
|
+
if !@no_invert && should_rtlize_selector?(selector)
|
|
111
|
+
selector + '{' + Rtlize::Declaration.transform_multiple(declarations) + '}'
|
|
112
|
+
else
|
|
113
|
+
selector + '{' + declarations + '}'
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def transform_nested_blocks(blocks)
|
|
118
|
+
blocks.gsub(simple_block_regexp) do |block|
|
|
119
|
+
parts = block.match(simple_block_regexp)
|
|
120
|
+
selector, declarations = parts[1,2]
|
|
121
|
+
transform_simple_block(selector, declarations)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
data/lib/rtlize.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rtlize-2
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Fady Naffa
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 7.0.0
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 7.0.0
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: sprockets-rails
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: sqlite3
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '1.4'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '1.4'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: simplecov
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
description: |
|
|
69
|
+
RTLize allows you to write your stylesheets for left-to-right (LTR) layouts
|
|
70
|
+
and have them automatically work for right-to-left (RTL) layouts as well.
|
|
71
|
+
email:
|
|
72
|
+
- fadynaffa3@gmail.com
|
|
73
|
+
executables:
|
|
74
|
+
- rtlize
|
|
75
|
+
extensions: []
|
|
76
|
+
extra_rdoc_files: []
|
|
77
|
+
files:
|
|
78
|
+
- MIT-LICENSE
|
|
79
|
+
- README.md
|
|
80
|
+
- Rakefile
|
|
81
|
+
- bin/rtlize
|
|
82
|
+
- lib/rtlize.rb
|
|
83
|
+
- lib/rtlize/declaration.rb
|
|
84
|
+
- lib/rtlize/exec.rb
|
|
85
|
+
- lib/rtlize/helpers.rb
|
|
86
|
+
- lib/rtlize/railtie.rb
|
|
87
|
+
- lib/rtlize/rtl_processor.rb
|
|
88
|
+
- lib/rtlize/rtlizer.rb
|
|
89
|
+
- lib/rtlize/version.rb
|
|
90
|
+
- lib/tasks/rtlize_tasks.rake
|
|
91
|
+
homepage: https://github.com/fadynaffa3/RTLize
|
|
92
|
+
licenses:
|
|
93
|
+
- MIT
|
|
94
|
+
metadata: {}
|
|
95
|
+
rdoc_options: []
|
|
96
|
+
require_paths:
|
|
97
|
+
- lib
|
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 4.0.0
|
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '0'
|
|
108
|
+
requirements: []
|
|
109
|
+
rubygems_version: 4.0.10
|
|
110
|
+
specification_version: 4
|
|
111
|
+
summary: Automatic CSS layout switcher (from LTR to RTL)
|
|
112
|
+
test_files: []
|