bootstrap-sass 2.3.2.2 → 3.0.0.0.rc
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bootstrap-sass might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.gitignore +9 -0
- data/.travis.yml +8 -2
- data/CHANGELOG.md +4 -0
- data/CONTRIBUTING.md +12 -4
- data/README.md +87 -74
- data/Rakefile +31 -3
- data/bootstrap-sass.gemspec +14 -2
- data/lib/bootstrap-sass/engine.rb +1 -1
- data/lib/bootstrap-sass/version.rb +4 -0
- data/tasks/converter.rb +829 -0
- data/templates/project/_variables.scss.erb +6 -0
- data/templates/project/manifest.rb +8 -13
- data/templates/project/styles.scss +1 -6
- data/test/compass_test.rb +8 -0
- data/test/compilation_test.rb +1 -1
- data/test/dummy/README.rdoc +3 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +2 -0
- data/test/dummy/app/assets/stylesheets/application.css.sass +1 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/controllers/pages_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/app/views/pages/root.html.slim +11 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +17 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +26 -0
- data/test/dummy/config/environments/production.rb +76 -0
- data/test/dummy/config/environments/test.rb +30 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +18 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +3 -0
- data/test/dummy/config/locales/es.yml +3 -0
- data/test/dummy/config/routes.rb +3 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/log/development.log +0 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/gemfiles/sass_3_2.gemfile +1 -1
- data/test/gemfiles/sass_3_3.gemfile +6 -0
- data/test/pages_test.rb +14 -0
- data/test/support/integration_test.rb +29 -0
- data/test/test_helper.rb +26 -1
- data/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
- data/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +228 -0
- data/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
- data/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
- data/vendor/assets/javascripts/bootstrap.js +12 -13
- data/vendor/assets/javascripts/bootstrap/affix.js +126 -0
- data/vendor/assets/javascripts/bootstrap/alert.js +98 -0
- data/vendor/assets/javascripts/bootstrap/button.js +109 -0
- data/vendor/assets/javascripts/bootstrap/carousel.js +217 -0
- data/vendor/assets/javascripts/bootstrap/collapse.js +179 -0
- data/vendor/assets/javascripts/bootstrap/dropdown.js +154 -0
- data/vendor/assets/javascripts/bootstrap/modal.js +246 -0
- data/vendor/assets/javascripts/bootstrap/popover.js +117 -0
- data/vendor/assets/javascripts/bootstrap/scrollspy.js +158 -0
- data/vendor/assets/javascripts/bootstrap/tab.js +135 -0
- data/vendor/assets/javascripts/bootstrap/tooltip.js +386 -0
- data/vendor/assets/javascripts/bootstrap/transition.js +56 -0
- data/vendor/assets/stylesheets/bootstrap/_alerts.scss +46 -58
- data/vendor/assets/stylesheets/bootstrap/_badges.scss +51 -0
- data/vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss +8 -9
- data/vendor/assets/stylesheets/bootstrap/_button-groups.scss +173 -154
- data/vendor/assets/stylesheets/bootstrap/_buttons.scss +97 -165
- data/vendor/assets/stylesheets/bootstrap/_carousel.scss +116 -65
- data/vendor/assets/stylesheets/bootstrap/_close.scss +11 -8
- data/vendor/assets/stylesheets/bootstrap/_code.scss +16 -21
- data/vendor/assets/stylesheets/bootstrap/_component-animations.scss +10 -3
- data/vendor/assets/stylesheets/bootstrap/_dropdowns.scss +94 -148
- data/vendor/assets/stylesheets/bootstrap/_forms.scss +222 -559
- data/vendor/assets/stylesheets/bootstrap/_glyphicons.scss +232 -0
- data/vendor/assets/stylesheets/bootstrap/_grid.scss +336 -11
- data/vendor/assets/stylesheets/bootstrap/_input-groups.scss +127 -0
- data/vendor/assets/stylesheets/bootstrap/_jumbotron.scss +40 -0
- data/vendor/assets/stylesheets/bootstrap/_labels.scss +58 -0
- data/vendor/assets/stylesheets/bootstrap/_list-group.scss +90 -0
- data/vendor/assets/stylesheets/bootstrap/_media.scss +8 -7
- data/vendor/assets/stylesheets/bootstrap/_mixins.scss +468 -434
- data/vendor/assets/stylesheets/bootstrap/_modals.scss +103 -52
- data/vendor/assets/stylesheets/bootstrap/_navbar.scss +511 -383
- data/vendor/assets/stylesheets/bootstrap/_navs.scss +169 -349
- data/vendor/assets/stylesheets/bootstrap/_normalize.scss +396 -0
- data/vendor/assets/stylesheets/bootstrap/_pager.scss +45 -33
- data/vendor/assets/stylesheets/bootstrap/_pagination.scss +65 -105
- data/vendor/assets/stylesheets/bootstrap/_panels.scss +148 -0
- data/vendor/assets/stylesheets/bootstrap/_popovers.scss +51 -51
- data/vendor/assets/stylesheets/bootstrap/_print.scss +100 -0
- data/vendor/assets/stylesheets/bootstrap/_progress-bars.scss +28 -55
- data/vendor/assets/stylesheets/bootstrap/_responsive-utilities.scss +180 -45
- data/vendor/assets/stylesheets/bootstrap/_scaffolding.scss +101 -24
- data/vendor/assets/stylesheets/bootstrap/_tables.scss +169 -168
- data/vendor/assets/stylesheets/bootstrap/_theme.scss +232 -0
- data/vendor/assets/stylesheets/bootstrap/_thumbnails.scss +11 -33
- data/vendor/assets/stylesheets/bootstrap/_tooltip.scss +45 -20
- data/vendor/assets/stylesheets/bootstrap/_type.scss +101 -110
- data/vendor/assets/stylesheets/bootstrap/_utilities.scss +19 -22
- data/vendor/assets/stylesheets/bootstrap/_variables.scss +498 -179
- data/vendor/assets/stylesheets/bootstrap/_wells.scss +7 -7
- data/vendor/assets/stylesheets/bootstrap/bootstrap.scss +29 -33
- metadata +201 -34
- data/asseturl.patch +0 -15
- data/templates/project/_variables.scss +0 -301
- data/update-bootstrap.sh +0 -25
- data/vendor/assets/images/glyphicons-halflings-white.png +0 -0
- data/vendor/assets/images/glyphicons-halflings.png +0 -0
- data/vendor/assets/javascripts/bootstrap-affix.js +0 -117
- data/vendor/assets/javascripts/bootstrap-alert.js +0 -99
- data/vendor/assets/javascripts/bootstrap-button.js +0 -105
- data/vendor/assets/javascripts/bootstrap-carousel.js +0 -207
- data/vendor/assets/javascripts/bootstrap-collapse.js +0 -167
- data/vendor/assets/javascripts/bootstrap-dropdown.js +0 -169
- data/vendor/assets/javascripts/bootstrap-modal.js +0 -247
- data/vendor/assets/javascripts/bootstrap-popover.js +0 -114
- data/vendor/assets/javascripts/bootstrap-scrollspy.js +0 -162
- data/vendor/assets/javascripts/bootstrap-tab.js +0 -144
- data/vendor/assets/javascripts/bootstrap-tooltip.js +0 -361
- data/vendor/assets/javascripts/bootstrap-transition.js +0 -60
- data/vendor/assets/javascripts/bootstrap-typeahead.js +0 -335
- data/vendor/assets/stylesheets/bootstrap-responsive.scss +0 -1
- data/vendor/assets/stylesheets/bootstrap/_accordion.scss +0 -34
- data/vendor/assets/stylesheets/bootstrap/_hero-unit.scss +0 -25
- data/vendor/assets/stylesheets/bootstrap/_labels-badges.scss +0 -83
- data/vendor/assets/stylesheets/bootstrap/_layouts.scss +0 -16
- data/vendor/assets/stylesheets/bootstrap/_reset.scss +0 -216
- data/vendor/assets/stylesheets/bootstrap/_responsive-1200px-min.scss +0 -28
- data/vendor/assets/stylesheets/bootstrap/_responsive-767px-max.scss +0 -193
- data/vendor/assets/stylesheets/bootstrap/_responsive-768px-979px.scss +0 -19
- data/vendor/assets/stylesheets/bootstrap/_responsive-navbar.scss +0 -189
- data/vendor/assets/stylesheets/bootstrap/_sprites.scss +0 -197
- data/vendor/assets/stylesheets/bootstrap/responsive.scss +0 -48
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aafebd3e6a2ed9f1ee3cdb4ab1a426484735e7af
|
4
|
+
data.tar.gz: 54c98b77cc87298118101f5f323cf9772511097b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72f10b7ae37034721741b38985095bbbf481d3d992beb077ac843815dae2a7d483a39d97770c87f074edf76b129482ae6bd5f5ffb20fec97d4f6d6c66f949d7e
|
7
|
+
data.tar.gz: 4a3cdebd4dbba31327b25ecec5b42e2cf2de2de46f9fa00adb6d098855de7588067c948c8d6d3defb3be08ac2b533e75a8bb72b48e7a68204f7c720fd058513d
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
+
- 2.0.0
|
3
4
|
- 1.9.3
|
4
|
-
- jruby
|
5
|
+
- jruby
|
6
|
+
- rbx-19mode
|
5
7
|
gemfile:
|
8
|
+
- test/gemfiles/sass_3_3.gemfile
|
6
9
|
- test/gemfiles/sass_3_2.gemfile
|
7
|
-
- test/gemfiles/sass_head.gemfile
|
10
|
+
- test/gemfiles/sass_head.gemfile
|
11
|
+
matrix:
|
12
|
+
allow_failures:
|
13
|
+
- gemfile: test/gemfiles/sass_head.gemfile
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 3.0.0.0
|
4
|
+
* Ported rake task from vwall/compass-twitter-bootstrap to convert Bootstrap upstream - *Peter Gumeson*
|
5
|
+
* Moved javascripts from `bootstrap-component.js` to `bootstrap/component.js` - *Peter Gumeson*
|
6
|
+
|
3
7
|
## 2.3.2.2
|
4
8
|
|
5
9
|
* Allow sass-rails `>= 3.2` - *Thomas McDonald*
|
data/CONTRIBUTING.md
CHANGED
@@ -1,4 +1,12 @@
|
|
1
|
-
# Contributing to bootstrap-sass
|
1
|
+
# Contributing to bootstrap-sass
|
2
|
+
|
3
|
+
## Asset Changes
|
4
|
+
|
5
|
+
Any changes to `bootstrap-sass` assets (scss, javascripts, fonts) should be checked against the `convert` rake task.
|
6
|
+
For usage instructions, see the [README](https://github.com/thomas-mcdonald/bootstrap-sass/blob/3/README.md).
|
7
|
+
|
8
|
+
If something is broken in the converter, it's preferable to update the converter along with the asset itself.
|
9
|
+
|
2
10
|
|
3
11
|
## Bugs
|
4
12
|
|
@@ -8,7 +16,7 @@ repository. Good bug reports are extremely helpful - thank you!
|
|
8
16
|
Guidelines for bug reports:
|
9
17
|
|
10
18
|
1. **Does it belong here?** — is this a problem with bootstrap-sass, or
|
11
|
-
it an issue with [
|
19
|
+
it an issue with [twbs/bootstrap](https://github.com/twbs/bootstrap)?
|
12
20
|
We only distribute a direct port and will not modify files if they're not
|
13
21
|
changed upstream.
|
14
22
|
|
@@ -49,7 +57,7 @@ Example:
|
|
49
57
|
|
50
58
|
**We will not accept pull requests that modify the SCSS beyond fixing bugs caused by *our* code!**
|
51
59
|
|
52
|
-
Most pull requests should go to [
|
60
|
+
Most pull requests should go to [twbs/bootstrap](https://github.com/twbs/bootstrap) or [jlong/sass-twitter-bootstrap](https://github.com/jlong/sass-twitter-bootstrap)
|
53
61
|
|
54
62
|
Good pull requests - patches, improvements, new features - are a fantastic
|
55
63
|
help. They should remain focused in scope and avoid containing unrelated
|
@@ -68,4 +76,4 @@ Please **do not** use the issue tracker for personal support requests (use
|
|
68
76
|
Please **do not** derail or troll issues. Keep the
|
69
77
|
discussion on topic and respect the opinions of others.
|
70
78
|
|
71
|
-
*props [html5-boilerplate](https://github.com/h5bp/html5-boilerplate/blob/master/CONTRIBUTING.md)*
|
79
|
+
*props [html5-boilerplate](https://github.com/h5bp/html5-boilerplate/blob/master/CONTRIBUTING.md)*
|
data/README.md
CHANGED
@@ -4,126 +4,139 @@
|
|
4
4
|
|
5
5
|
`bootstrap-sass` is an Sass-powered version of [Bootstrap](http://github.com/twbs/bootstrap), ready to drop right into your Sass powered applications.
|
6
6
|
|
7
|
-
|
7
|
+
## Installation
|
8
8
|
|
9
|
-
|
9
|
+
Please see the appropriate guide for your environment of choice:
|
10
|
+
|
11
|
+
### a. Rails
|
10
12
|
|
11
|
-
|
13
|
+
`bootstrap-sass` is easy to drop into Rails with the asset pipeline.
|
12
14
|
|
13
|
-
In your Gemfile
|
15
|
+
In your Gemfile you need to add the `bootstrap-sass` gem, and ensure that the `sass-rails` gem is present - it is added to new Rails applications by default.
|
14
16
|
|
15
17
|
```ruby
|
16
|
-
gem 'sass-rails', '
|
17
|
-
gem 'bootstrap-sass', '~>
|
18
|
+
gem 'sass-rails', '>= 3.2' # sass-rails needs to be higher than 3.2
|
19
|
+
gem 'bootstrap-sass', '~> 3.0.0.0.rc'
|
18
20
|
```
|
19
21
|
|
20
|
-
`bundle install` and restart your server to make the files available.
|
21
|
-
|
22
|
-
#### CSS
|
22
|
+
`bundle install` and restart your server to make the files available through the pipeline.
|
23
23
|
|
24
|
-
|
24
|
+
### b. Compass (no Rails)
|
25
25
|
|
26
|
-
|
27
|
-
|
26
|
+
Install the gem
|
27
|
+
```console
|
28
|
+
gem install bootstrap-sass --pre
|
28
29
|
```
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
You can include the Bootstrap javascripts through two methods. In this case, Sprocket's `//= require` directives are useful, since there is no better alternative.
|
31
|
+
If you have an existing Compass project:
|
33
32
|
|
34
|
-
|
33
|
+
```ruby
|
34
|
+
# config.rb:
|
35
|
+
require 'bootstrap-sass'
|
36
|
+
```
|
35
37
|
|
36
|
-
```
|
37
|
-
|
38
|
-
//= require bootstrap
|
38
|
+
```console
|
39
|
+
compass install bootstrap
|
39
40
|
```
|
40
41
|
|
41
|
-
|
42
|
+
If you are creating a new Compass project, you can generate it with bootstrap-sass support:
|
42
43
|
|
43
|
-
```
|
44
|
-
|
45
|
-
//= require bootstrap-modal
|
46
|
-
//= require bootstrap-dropdown
|
44
|
+
```console
|
45
|
+
compass create my-new-project -r bootstrap-sass --using bootstrap
|
47
46
|
```
|
48
47
|
|
49
|
-
|
48
|
+
This will create a new Compass project with the following files in it:
|
50
49
|
|
51
|
-
|
50
|
+
* [_variables.scss](/templates/project/_variables.scss.erb) - all of bootstrap variables (override them here).
|
51
|
+
* [styles.scss](/templates/project/styles.scss) - main project SCSS file, import `variables` and `bootstrap`.
|
52
52
|
|
53
|
-
`bootstrap-sass` 2.0 now comes with support for Compass, meaning projects that don't use Rails can get in on the fun Bootstrap web.
|
54
53
|
|
55
|
-
|
54
|
+
## Usage
|
56
55
|
|
57
|
-
|
56
|
+
### CSS / SCSS / SASS
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
Import Bootstrap in an SCSS file (for example, `application.css.scss`) to get all of Bootstrap's styles, mixins and variables! We recommend against using `//= require` directives, since none of your other stylesheets will be [able to access][antirequire] the Bootstrap mixins or variables.
|
59
|
+
|
60
|
+
```css
|
61
|
+
@import "bootstrap";
|
62
62
|
```
|
63
63
|
|
64
|
-
|
64
|
+
You can also include optional bootstrap theme:
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
```css
|
67
|
+
@import "bootstrap/theme";
|
68
|
+
```
|
69
69
|
|
70
|
-
|
70
|
+
### Javascript
|
71
71
|
|
72
|
-
|
72
|
+
We have a helper that includes all Bootstrap javascripts. If you use Rails (or Sprockets separately),
|
73
|
+
put this in your Javascript manifest (usually in `application.js`) to load the files in the [correct order](/vendor/assets/javascripts/bootstrap.js):
|
73
74
|
|
74
|
-
```
|
75
|
-
|
75
|
+
```js
|
76
|
+
// Loads all Bootstrap javascripts
|
77
|
+
//= require bootstrap
|
76
78
|
```
|
77
79
|
|
78
|
-
|
79
|
-
# In config.rb
|
80
|
-
require 'bootstrap-sass'
|
81
|
-
```
|
80
|
+
You can also load individual modules, provided you also require any dependencies. You can check dependencies in the [Bootstrap JS documentation][jsdocs].
|
82
81
|
|
83
|
-
```
|
84
|
-
|
82
|
+
```js
|
83
|
+
//= require bootstrap/scrollspy
|
84
|
+
//= require bootstrap/modal
|
85
|
+
//= require bootstrap/dropdown
|
85
86
|
```
|
86
87
|
|
87
|
-
|
88
|
+
## Development and Contributing
|
88
89
|
|
89
|
-
|
90
|
-
Need to configure a variable or two? Simply define the value of the variable you want to change *before* importing Bootstrap. Sass will respect your existing definition rather than overwriting it with the Bootstrap defaults. A list of customisable variables can be found in the [Bootstrap documentation](http://twbs.github.io/bootstrap/customize/#variables).
|
90
|
+
If you'd like to help with the development of bootstrap-sass itself, read this section.
|
91
91
|
|
92
|
-
|
93
|
-
$btnPrimaryBackground: #f00;
|
94
|
-
@import "bootstrap";
|
95
|
-
```
|
92
|
+
### Upstream Converter
|
96
93
|
|
97
|
-
|
94
|
+
Keeping bootstrap-sass in sync with upstream changes from Bootstrap used to be an error prone and time consuming manual process. With Bootstrap 3 we have introduced a converter that automates this.
|
98
95
|
|
99
|
-
|
96
|
+
**Note: if you're just looking to *use* Bootstrap 3, see the [installation](#installation) section above.**
|
100
97
|
|
101
|
-
|
98
|
+
Upstream changes to the Bootstrap project can now be pulled in using the `convert` rake task.
|
102
99
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
Here's an example run that would pull down the master branch from the main [twbs/bootstrap](https://github.com/twbs/bootstrap) repo:
|
101
|
+
|
102
|
+
rake convert
|
103
|
+
|
104
|
+
This will convert the latest LESS to SASS and update to the latest JS.
|
105
|
+
To convert a specific branch or version, pass the branch name or the commit hash as the first task argument:
|
106
|
+
|
107
|
+
rake convert[e8a1df5f060bf7e6631554648e0abde150aedbe4]
|
108
108
|
|
109
|
-
|
110
|
-
As per the Bootstrap project we don't include the responsive styles by default. `@import "bootstrap-responsive";` to get them.
|
109
|
+
The latest converter script is located [here][converter] and does the following:
|
111
110
|
|
112
|
-
|
113
|
-
|
111
|
+
* Converts upstream bootstrap LESS files to its matching SCSS file.
|
112
|
+
* Copies all upstream JavaScript into `vendor/assets/javascripts/bootstrap`
|
113
|
+
* Generates a javascript manifest at `vendor/assets/javascripts/bootstrap.js`
|
114
|
+
* Copies all upstream font files into `vendor/assets/fonts/bootstrap`
|
115
|
+
* Sets `Bootstrap::BOOTSTRAP_SHA` in [version.rb][version] to the branch sha.
|
114
116
|
|
115
|
-
|
117
|
+
This converter fully converts original LESS to SCSS. Conversion is automatic but requires instructions for certain transformations (see converter output).
|
118
|
+
Please submit GitHub issues tagged with `conversion`.
|
116
119
|
|
117
|
-
|
120
|
+
## Credits
|
118
121
|
|
119
|
-
|
120
|
-
gem 'bootstrap-sass', '~> 2.3.2.2'
|
121
|
-
```
|
122
|
+
bootstrap-sass has a number of major contributors:
|
122
123
|
|
123
|
-
|
124
|
+
<!-- feel free to make these link wherever you wish -->
|
125
|
+
* [Thomas McDonald](https://twitter.com/thomasmcdonald_)
|
126
|
+
* [Tristan Harward](http://www.trisweb.com)
|
127
|
+
* Peter Gumeson
|
128
|
+
* [Gleb Mazovetskiy](https://github.com/glebm)
|
124
129
|
|
125
|
-
|
126
|
-
bootstrap-sass is a project by [Thomas McDonald](https://twitter.com/#!/thomasmcdonald_), with support from [other awesome people](https://github.com/thomas-mcdonald/bootstrap-sass/graphs/contributors).
|
130
|
+
and a [significant number of other contributors][contrib].
|
127
131
|
|
128
132
|
## You're in good company
|
129
|
-
bootstrap-sass is used to build some awesome projects
|
133
|
+
bootstrap-sass is used to build some awesome projects all over the web, including
|
134
|
+
[Diaspora](http://diasporaproject.org/), [rails_admin](https://github.com/sferik/rails_admin),
|
135
|
+
Michael Hartl's [Rails Tutorial](http://railstutorial.org/), [gitlabhq](http://gitlabhq.com/) and
|
136
|
+
[kandan](http://kandanapp.com/).
|
137
|
+
|
138
|
+
[converter]: https://github.com/thomas-mcdonald/bootstrap-sass/blob/3/tasks/converter.rb
|
139
|
+
[version]: https://github.com/thomas-mcdonald/bootstrap-sass/blob/3/lib/bootstrap-sass/version.rb
|
140
|
+
[contrib]: https://github.com/thomas-mcdonald/bootstrap-sass/graphs/contributors
|
141
|
+
[antirequire]: https://github.com/thomas-mcdonald/bootstrap-sass/issues/79#issuecomment-4428595
|
142
|
+
[jsdocs]: http://getbootstrap.com/javascript/#transitions
|
data/Rakefile
CHANGED
@@ -2,7 +2,7 @@ require 'rake/testtask'
|
|
2
2
|
Rake::TestTask.new do |t|
|
3
3
|
t.libs << "test"
|
4
4
|
t.test_files = FileList['test/*_test.rb']
|
5
|
-
t.verbose
|
5
|
+
t.verbose = true
|
6
6
|
end
|
7
7
|
|
8
8
|
desc 'Dumps output to a CSS file for testing'
|
@@ -11,10 +11,38 @@ task :debug do
|
|
11
11
|
require './lib/bootstrap-sass/compass_functions'
|
12
12
|
require './lib/bootstrap-sass/sass_functions'
|
13
13
|
path = './vendor/assets/stylesheets'
|
14
|
-
%w(bootstrap
|
14
|
+
%w(bootstrap).each do |file|
|
15
15
|
engine = Sass::Engine.for_file("#{path}/#{file}.scss", syntax: :scss, load_paths: [path])
|
16
16
|
File.open("./#{file}.css", 'w') { |f| f.write(engine.render) }
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
|
20
|
+
desc 'Convert bootstrap to bootstrap-sass'
|
21
|
+
task :convert, :branch do |t, args|
|
22
|
+
require './tasks/converter'
|
23
|
+
branch = args[:branch]
|
24
|
+
Converter.new(branch).process
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Compile bootstrap-sass to tmp/ (or first arg)'
|
28
|
+
task :compile, :css_path do |t, args|
|
29
|
+
lib_path = File.join(File.dirname(__FILE__), 'lib')
|
30
|
+
$:.unshift(lib_path) unless $:.include?(lib_path)
|
31
|
+
require 'sass'
|
32
|
+
require 'bootstrap-sass/compass_functions'
|
33
|
+
require 'bootstrap-sass/sass_functions'
|
34
|
+
require 'term/ansicolor'
|
35
|
+
|
36
|
+
path = 'vendor/assets/stylesheets'
|
37
|
+
puts Term::ANSIColor.bold "Compiling SCSS in #{path}"
|
38
|
+
%w(bootstrap bootstrap/_theme).each do |file|
|
39
|
+
save_path = "#{args.with_defaults(css_path: 'tmp')[:css_path]}/#{file.sub(/(^|\/)?_+/, '\1').sub('/', '-')}.css"
|
40
|
+
puts Term::ANSIColor.cyan(" #{save_path}") + '...'
|
41
|
+
engine = Sass::Engine.for_file("#{path}/#{file}.scss", syntax: :scss, load_paths: [path])
|
42
|
+
css = engine.render
|
43
|
+
File.mkdir('tmp') unless File.directory?('tmp')
|
44
|
+
File.open(save_path, 'w') { |f| f.write css }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
task default: :test
|
data/bootstrap-sass.gemspec
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'bootstrap-sass/version'
|
4
|
+
|
1
5
|
Gem::Specification.new do |s|
|
2
6
|
s.name = "bootstrap-sass"
|
3
|
-
s.version =
|
7
|
+
s.version = Bootstrap::VERSION
|
4
8
|
s.authors = ["Thomas McDonald"]
|
5
9
|
s.email = 'tom@conceptcoding.co.uk'
|
6
10
|
s.summary = "Twitter's Bootstrap, converted to Sass and ready to drop into Rails or Compass"
|
@@ -8,9 +12,17 @@ Gem::Specification.new do |s|
|
|
8
12
|
s.license = "Apache 2.0"
|
9
13
|
|
10
14
|
s.add_development_dependency 'compass'
|
15
|
+
s.add_development_dependency 'term-ansicolor'
|
11
16
|
s.add_development_dependency 'sass-rails', '>= 3.2'
|
12
17
|
s.add_runtime_dependency 'sass', '~> 3.2'
|
13
18
|
|
19
|
+
|
20
|
+
s.add_development_dependency 'capybara'
|
21
|
+
s.add_development_dependency 'poltergeist'
|
22
|
+
s.add_development_dependency 'tzinfo'
|
23
|
+
s.add_development_dependency 'jquery-rails'
|
24
|
+
s.add_development_dependency 'slim-rails'
|
25
|
+
|
14
26
|
s.files = `git ls-files`.split("\n")
|
15
27
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
16
|
-
end
|
28
|
+
end
|
@@ -2,7 +2,7 @@ module Bootstrap
|
|
2
2
|
module Rails
|
3
3
|
class Engine < ::Rails::Engine
|
4
4
|
initializer "bootstrap-sass.assets.precompile" do |app|
|
5
|
-
app.config.assets.precompile
|
5
|
+
app.config.assets.precompile << %r(bootstrap/glyphicons-halflings-regular\.(?:eot|svg|ttf|woff)$)
|
6
6
|
end
|
7
7
|
end
|
8
8
|
end
|
data/tasks/converter.rb
ADDED
@@ -0,0 +1,829 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# Based on convert script from vwall/compass-twitter-bootstrap gem.
|
3
|
+
# https://github.com/vwall/compass-twitter-bootstrap/blob/master/build/convert.rb
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this work except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License in the LICENSE file, or at:
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'open-uri'
|
18
|
+
require 'json'
|
19
|
+
require 'strscan'
|
20
|
+
require 'forwardable'
|
21
|
+
require 'term/ansicolor'
|
22
|
+
require 'fileutils'
|
23
|
+
|
24
|
+
class Converter
|
25
|
+
extend Forwardable
|
26
|
+
|
27
|
+
GIT_DATA = 'https://api.github.com/repos'
|
28
|
+
GIT_RAW = 'https://raw.github.com'
|
29
|
+
|
30
|
+
def initialize(branch)
|
31
|
+
@repo = 'twbs/bootstrap'
|
32
|
+
@repo_url = "https://github.com/#@repo"
|
33
|
+
@branch = branch || 'master'
|
34
|
+
@branch_sha = get_branch_sha
|
35
|
+
@save_at = { js: 'vendor/assets/javascripts/bootstrap',
|
36
|
+
scss: 'vendor/assets/stylesheets/bootstrap',
|
37
|
+
fonts: 'vendor/assets/fonts/bootstrap' }
|
38
|
+
@save_at.each { |_,v| FileUtils.mkdir_p(v) }
|
39
|
+
@cache_path = 'tmp/converter-cache'
|
40
|
+
@logger = Logger.new(repo: @repo_url, branch: @branch, branch_sha: @branch_sha, save_at: @save_at, cache_path: @cache_path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def_delegators :@logger, :log_status, :log_processing, :log_transform, :log_file_info, :log_processed, :log_http_get_file, :log_http_get_files, :silence_log
|
44
|
+
|
45
|
+
def process
|
46
|
+
process_stylesheet_assets
|
47
|
+
process_javascript_assets
|
48
|
+
process_font_assets
|
49
|
+
store_version
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_font_assets
|
53
|
+
log_status "Processing fonts..."
|
54
|
+
files = read_files('fonts', bootstrap_font_files)
|
55
|
+
save_at = @save_at[:fonts]
|
56
|
+
files.each do |name, content|
|
57
|
+
save_file "#{save_at}/#{name}", content
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
NESTED_MIXINS = {'#gradient' => 'gradient'}
|
62
|
+
VARARG_MIXINS = %w(transition transition-transform box-shadow)
|
63
|
+
def process_stylesheet_assets
|
64
|
+
log_status "Processing stylesheets..."
|
65
|
+
files = read_files('less', bootstrap_less_files)
|
66
|
+
|
67
|
+
# read common mixin definitions (incl. nested mixins) from mixins.less
|
68
|
+
read_shared_mixins! files['mixins.less']
|
69
|
+
|
70
|
+
# convert each file
|
71
|
+
files.each do |name, file|
|
72
|
+
log_processing name
|
73
|
+
# apply common conversions
|
74
|
+
file = convert_to_scss(file)
|
75
|
+
case name
|
76
|
+
when 'mixins.less'
|
77
|
+
NESTED_MIXINS.each do |selector, prefix|
|
78
|
+
file = flatten_mixins(file, selector, prefix)
|
79
|
+
end
|
80
|
+
file = varargify_mixin_definitions(file, *VARARG_MIXINS)
|
81
|
+
file = deinterpolate_vararg_mixins(file)
|
82
|
+
file = parameterize_mixin_parent_selector file, 'responsive-(in)?visibility'
|
83
|
+
file = parameterize_mixin_parent_selector file, 'input-size'
|
84
|
+
file = replace_ms_filters(file)
|
85
|
+
file = replace_all file, /\.\$state/, '.#{$state}'
|
86
|
+
file = replace_all file, /,\s*\.open \.dropdown-toggle& \{(.*?)\}/m,
|
87
|
+
" {\\1}\n .open & { &.dropdown-toggle {\\1} }"
|
88
|
+
when 'responsive-utilities.less'
|
89
|
+
file = apply_mixin_parent_selector(file, '&\.(visible|hidden)')
|
90
|
+
file = apply_mixin_parent_selector(file, '(?<!&)\.(visible|hidden)')
|
91
|
+
file = replace_rules(file, ' @media') { |r| unindent(r, 2) }
|
92
|
+
when 'variables.less'
|
93
|
+
file = insert_default_vars(file)
|
94
|
+
file = replace_all file, /(\$icon-font-path:).*(!default)/, '\1 "bootstrap/" \2'
|
95
|
+
when 'close.less'
|
96
|
+
# extract .close { button& {...} } rule
|
97
|
+
file = extract_nested_rule file, 'button&'
|
98
|
+
when 'modals.less'
|
99
|
+
file = replace_all file, /body&,(.*?)(\{.*?\})/m, "\\1\\2\nbody& \\2"
|
100
|
+
file = extract_nested_rule file, 'body&'
|
101
|
+
when 'dropdowns.less'
|
102
|
+
file = replace_all file, /(\s*)@extend \.pull-right-dropdown-menu;/, "\\1right: 0;\\1left: auto;"
|
103
|
+
when 'forms.less'
|
104
|
+
file = extract_nested_rule file, 'textarea&'
|
105
|
+
file = apply_mixin_parent_selector(file, '\.input-(?:sm|lg)')
|
106
|
+
when 'navbar.less'
|
107
|
+
file = replace_all file, /(\s*)\.navbar-(right|left)\s*\{\s*@extend\s*\.pull-(right|left);\s*/, "\\1.navbar-\\2 {\\1 float: \\2 !important;\\1"
|
108
|
+
when 'tables.less'
|
109
|
+
file = replace_all file, /(@include\s*table-row-variant\()(\w+)/, "\\1'\\2'"
|
110
|
+
when 'list-group.less'
|
111
|
+
file = extract_nested_rule file, 'a&'
|
112
|
+
when 'glyphicons.less'
|
113
|
+
file = replace_rules(file, '@font-face') { |rule|
|
114
|
+
rule = replace_all rule, /(\$icon-font-\w+)/, '#{\1}'
|
115
|
+
replace_all rule, /url\(/, 'font-url('
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
name = name.sub(/\.less$/, '.scss')
|
120
|
+
save_at = @save_at[:scss]
|
121
|
+
path = "#{save_at}/#{'_' unless name == 'bootstrap.scss'}#{name}"
|
122
|
+
save_file(path, file)
|
123
|
+
log_processed File.basename(path)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def store_version
|
128
|
+
path = 'lib/bootstrap-sass/version.rb'
|
129
|
+
content = File.read(path).sub(/BOOTSTRAP_SHA\s*=\s*['"][\w]+['"]/, "BOOTSTRAP_SHA = '#@branch_sha'")
|
130
|
+
File.open(path, 'w') { |f| f.write(content) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def process_javascript_assets
|
134
|
+
log_status "Processing javascripts..."
|
135
|
+
save_at = @save_at[:js]
|
136
|
+
read_files('js', bootstrap_js_files).each do |name, file|
|
137
|
+
save_file("#{save_at}/#{name}", file)
|
138
|
+
end
|
139
|
+
log_processed "#{bootstrap_js_files * ' '}"
|
140
|
+
|
141
|
+
log_status "Updating javascript manifest"
|
142
|
+
content = ''
|
143
|
+
bootstrap_js_files.each do |name|
|
144
|
+
name = name.gsub(/\.js$/, '')
|
145
|
+
content << "//= require bootstrap/#{name}\n"
|
146
|
+
end
|
147
|
+
path = "vendor/assets/javascripts/bootstrap.js"
|
148
|
+
save_file(path, content)
|
149
|
+
log_processed path
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def read_files(path, files)
|
155
|
+
full_path = "#{GIT_RAW}/#@repo/#@branch_sha/#{path}"
|
156
|
+
if (contents = read_cached_files(path, files))
|
157
|
+
log_http_get_files files, full_path, true
|
158
|
+
else
|
159
|
+
log_http_get_files files, full_path, false
|
160
|
+
contents = {}
|
161
|
+
files.map do |name|
|
162
|
+
Thread.start {
|
163
|
+
content = open("#{full_path}/#{name}").read
|
164
|
+
Thread.exclusive { contents[name] = content }
|
165
|
+
}
|
166
|
+
end.each(&:join)
|
167
|
+
write_cached_files path, contents
|
168
|
+
end
|
169
|
+
contents
|
170
|
+
end
|
171
|
+
|
172
|
+
def read_cached_files(path, files)
|
173
|
+
full_path = "#@cache_path/#@branch_sha/#{path}"
|
174
|
+
contents = {}
|
175
|
+
if File.directory?(full_path)
|
176
|
+
files.each do |name|
|
177
|
+
contents[name] = File.read("#{full_path}/#{name}", mode: 'rb') || ''
|
178
|
+
end
|
179
|
+
contents
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def write_cached_files(path, files)
|
184
|
+
full_path = "./#@cache_path/#@branch_sha/#{path}"
|
185
|
+
FileUtils.mkdir_p full_path
|
186
|
+
files.each do |name, content|
|
187
|
+
File.open("#{full_path}/#{name}", 'wb') { |f| f.write content}
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
def get_file(url)
|
193
|
+
cache_path = "./#@cache_path#{URI(url).path}"
|
194
|
+
FileUtils.mkdir_p File.dirname(cache_path)
|
195
|
+
if File.exists?(cache_path)
|
196
|
+
log_http_get_file url, true
|
197
|
+
File.read(cache_path, mode: 'rb')
|
198
|
+
else
|
199
|
+
log_http_get_file url, false
|
200
|
+
content = open(url).read
|
201
|
+
File.open(cache_path, 'wb') { |f| f.write content }
|
202
|
+
content
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# get sha of the branch (= the latest commit)
|
207
|
+
def get_branch_sha
|
208
|
+
cmd = "git ls-remote '#@repo_url' | awk '/#@branch/ {print $1}'"
|
209
|
+
puts cmd
|
210
|
+
@branch_sha ||= %x[#{cmd}].chomp
|
211
|
+
raise 'Could not get branch sha!' unless $?.success?
|
212
|
+
@branch_sha
|
213
|
+
end
|
214
|
+
|
215
|
+
# Get the sha of a dir
|
216
|
+
def get_tree_sha(dir)
|
217
|
+
get_trees['tree'].find { |t| t['path'] == dir }['sha']
|
218
|
+
end
|
219
|
+
|
220
|
+
def get_trees
|
221
|
+
@trees ||= get_json("#{GIT_DATA}/#@repo/git/trees/#@branch_sha")
|
222
|
+
end
|
223
|
+
|
224
|
+
def bootstrap_font_files
|
225
|
+
@bootstrap_font_files ||= begin
|
226
|
+
files = get_json "#{GIT_DATA}/#@repo/git/trees/#{get_tree_sha('fonts')}"
|
227
|
+
files['tree'].select { |f| f['type'] == 'blob' && f['path'] =~ /\.(eot|svg|ttf|woff)$/ }.map { |f| f['path'] }
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
def bootstrap_less_files
|
232
|
+
@bootstrap_less_files ||= begin
|
233
|
+
files = get_json "#{GIT_DATA}/#@repo/git/trees/#{get_tree_sha('less')}"
|
234
|
+
files['tree'].select { |f| f['type'] == 'blob' && f['path'] =~ /\.less$/ }.map { |f| f['path'] }
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def bootstrap_js_files
|
239
|
+
@bootstrap_js_files ||= begin
|
240
|
+
files = get_json "#{GIT_DATA}/#@repo/git/trees/#{get_tree_sha('js')}"
|
241
|
+
files = files['tree'].select { |f| f['type'] == 'blob' && f['path'] =~ /\.js$/ }.map { |f| f['path'] }
|
242
|
+
files.sort_by { |f|
|
243
|
+
case f
|
244
|
+
# tooltip depends on popover and must be loaded earlier
|
245
|
+
when /tooltip/ then 1
|
246
|
+
when /popover/ then 2
|
247
|
+
else
|
248
|
+
0
|
249
|
+
end
|
250
|
+
}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# We need to keep a list of shared mixin names in order to convert the includes correctly
|
255
|
+
# Before doing any processing we read shared mixins from a file
|
256
|
+
# If a mixin is nested, it gets prefixed in the list (e.g. #gradient > .horizontal to 'gradient-horizontal')
|
257
|
+
def read_shared_mixins!(mixins_file)
|
258
|
+
log_status " Reading shared mixins from mixins.less"
|
259
|
+
@shared_mixins = get_mixin_names(mixins_file, silent: true)
|
260
|
+
NESTED_MIXINS.each do |selector, prefix|
|
261
|
+
# we use replace_rules without replacing anything just to use the parsing algorithm
|
262
|
+
replace_rules(mixins_file, selector) { |rule|
|
263
|
+
@shared_mixins += get_mixin_names(unindent(unwrap_rule_block(rule)), silent: true).map { |name| "#{prefix}-#{name}" }
|
264
|
+
rule
|
265
|
+
}
|
266
|
+
end
|
267
|
+
@shared_mixins.sort!
|
268
|
+
log_file_info "shared mixins: #{@shared_mixins * ', '}"
|
269
|
+
@shared_mixins
|
270
|
+
end
|
271
|
+
|
272
|
+
def get_mixin_names(file, opts = {})
|
273
|
+
names = get_css_selectors(file).join("\n" * 2).scan(/^\.([\w-]+)\(#{LESS_MIXIN_DEF_ARGS_RE}\)[ ]*\{/).map(&:first).uniq.sort
|
274
|
+
log_file_info "mixin defs: #{names * ', '}" unless opts[:silent] || names.empty?
|
275
|
+
names
|
276
|
+
end
|
277
|
+
|
278
|
+
def convert_to_scss(file)
|
279
|
+
# mixins may also be defined in the file. get mixin names before doing any processing
|
280
|
+
mixin_names = (@shared_mixins + get_mixin_names(file)).uniq
|
281
|
+
file = replace_vars(file)
|
282
|
+
file = replace_file_imports(file)
|
283
|
+
file = replace_mixin_definitions file
|
284
|
+
file = replace_mixins file, mixin_names
|
285
|
+
# replace_less_extend does not seem to do anything. @glebm
|
286
|
+
file = replace_less_extend(file)
|
287
|
+
file = replace_spin(file)
|
288
|
+
file = replace_image_urls(file)
|
289
|
+
file = replace_image_paths(file)
|
290
|
+
file = replace_escaping(file)
|
291
|
+
file = convert_less_ampersand(file)
|
292
|
+
file = deinterpolate_vararg_mixins(file)
|
293
|
+
file = replace_calculation_semantics(file)
|
294
|
+
file
|
295
|
+
end
|
296
|
+
|
297
|
+
# margin: a -b
|
298
|
+
# LESS: sets 2 values
|
299
|
+
# SASS: sets 1 value (a-b)
|
300
|
+
# This wraps a and -b so they evaluates to 2 values in SASS
|
301
|
+
def replace_calculation_semantics(file)
|
302
|
+
# split_prop_val.call('(@navbar-padding-vertical / 2) -@navbar-padding-horizontal')
|
303
|
+
# #=> ["(navbar-padding-vertical / 2)", "-navbar-padding-horizontal"]
|
304
|
+
split_prop_val = proc { |val|
|
305
|
+
s = CharStringScanner.new(val)
|
306
|
+
r = []
|
307
|
+
buff = ''
|
308
|
+
d = 0
|
309
|
+
prop_char = %r([\$\w\-/\*\+%!])
|
310
|
+
while (token = s.scan_next(/([\)\(]|\s+|#{prop_char}+)/))
|
311
|
+
buff << token
|
312
|
+
case token
|
313
|
+
when '('
|
314
|
+
d += 1
|
315
|
+
when ')'
|
316
|
+
d -= 1
|
317
|
+
if d == 0
|
318
|
+
r << buff
|
319
|
+
buff = ''
|
320
|
+
end
|
321
|
+
when /\s/
|
322
|
+
if d == 0 && !buff.strip.empty?
|
323
|
+
r << buff
|
324
|
+
buff = ''
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
r << buff unless buff.empty?
|
329
|
+
r.map(&:strip)
|
330
|
+
}
|
331
|
+
|
332
|
+
replace_rules file do |rule|
|
333
|
+
replace_properties rule do |props|
|
334
|
+
props.gsub /(?<!\w)([\w-]+):(.*?);/ do |m|
|
335
|
+
prop, vals = $1, split_prop_val.call($2)
|
336
|
+
next m unless vals.length >= 2 && vals.any? { |v| v =~ /^[\+\-]\$/ }
|
337
|
+
transformed = vals.map { |v| v.strip =~ %r(^\(.*\)$) ? v : "(#{v})" }
|
338
|
+
log_transform "property #{prop}: #{transformed * ' '}"
|
339
|
+
"#{prop}: #{transformed * ' '};"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def save_file(path, content, mode='w')
|
346
|
+
File.open(path, mode) { |file| file.write(content) }
|
347
|
+
end
|
348
|
+
|
349
|
+
# @import "file.less" to "#{target_path}file;"
|
350
|
+
def replace_file_imports(less, target_path = 'bootstrap/')
|
351
|
+
less.gsub %r([@\$]import ["|']([\w-]+).less["|'];),
|
352
|
+
%Q(@import "#{target_path}\\1";)
|
353
|
+
end
|
354
|
+
|
355
|
+
def replace_all(file, regex, replacement = nil, &block)
|
356
|
+
log_transform regex, replacement
|
357
|
+
new_file = file.gsub(regex, replacement, &block)
|
358
|
+
raise "replace_all #{regex}, #{replacement} NO MATCH" if file == new_file
|
359
|
+
new_file
|
360
|
+
end
|
361
|
+
|
362
|
+
# @mixin a() { tr& { color:white } }
|
363
|
+
# to:
|
364
|
+
# @mixin a($parent) { tr#{$parent} { color: white } }
|
365
|
+
def parameterize_mixin_parent_selector(file, rule_sel)
|
366
|
+
log_transform rule_sel
|
367
|
+
param = '$parent'
|
368
|
+
replace_rules(file, '^[ \t]*@mixin\s*' + rule_sel) do |mxn_css|
|
369
|
+
mxn_css.sub! /(?=@mixin)/, "// [converter] $parent hack\n"
|
370
|
+
# insert param into mixin def
|
371
|
+
mxn_css.sub!(/(@mixin [\w-]+)\(([\$\w\-,\s]*)\)/) { "#{$1}(#{param}#{', ' if $2 && !$2.empty?}#{$2})" }
|
372
|
+
# wrap properties in #{$parent} { ... }
|
373
|
+
replace_properties(mxn_css) { |props| " \#{#{param}} { #{props.strip} }\n " }
|
374
|
+
# change nested& rules to nested#{$parent}
|
375
|
+
replace_rules(mxn_css, /.*[^\s ]&/) { |rule| replace_in_selector rule, /&/, "\#{#{param}}" }
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
# extracts rule immediately after it's parent, and adjust the selector
|
380
|
+
# .x { textarea& { ... }}
|
381
|
+
# to:
|
382
|
+
# .x { ... }
|
383
|
+
# textarea.x { ... }
|
384
|
+
def extract_nested_rule(file, selector, new_selector = nil)
|
385
|
+
matches = []
|
386
|
+
# first find the rules, and remove them
|
387
|
+
file = replace_rules(file, "\s*#{selector}", comments: true) { |rule, pos, css|
|
388
|
+
matches << [rule, pos]
|
389
|
+
new_selector ||= "#{get_selector(rule).sub(/&$/, '')}#{selector_for_pos(css, pos.begin)}"
|
390
|
+
indent "// [converter] extracted #{get_selector(rule)} to #{new_selector}", indent_width(rule)
|
391
|
+
}
|
392
|
+
log_transform selector, new_selector
|
393
|
+
# replace rule selector with new_selector
|
394
|
+
matches.each do |m|
|
395
|
+
m[0].sub! /(#{COMMENT_RE}*)^(\s*).*?(\s*){/m, "\\1\\2#{new_selector}\\3{"
|
396
|
+
end
|
397
|
+
replace_substrings_at file,
|
398
|
+
matches.map { |_, pos| close_brace_pos(file, pos.begin, 1) + 1 },
|
399
|
+
matches.map { |rule, _| "\n\n" + unindent(rule) }
|
400
|
+
end
|
401
|
+
|
402
|
+
# .visible-sm { @include responsive-visibility() }
|
403
|
+
# to:
|
404
|
+
# @include responsive-visibility('.visible-sm')
|
405
|
+
def apply_mixin_parent_selector(file, rule_sel)
|
406
|
+
log_transform rule_sel
|
407
|
+
replace_rules file, '\s*' + rule_sel, comments: false do |rule, rule_pos, css|
|
408
|
+
body = unwrap_rule_block(rule.dup).strip
|
409
|
+
next rule unless body =~ /^@include \w+/m || body =~ /^@media/ && body =~ /\{\s*@include/
|
410
|
+
rule =~ /(#{COMMENT_RE}*)(#{SELECTOR_RE})\{/
|
411
|
+
cmt, sel = $1, $2.strip
|
412
|
+
# take one up selector chain if this is an &. selector
|
413
|
+
if sel.start_with?('&')
|
414
|
+
parent_sel = selector_for_pos(css, rule_pos.begin)
|
415
|
+
sel = parent_sel + sel[1..-1]
|
416
|
+
end
|
417
|
+
# unwrap, and replace @include
|
418
|
+
unindent unwrap_rule_block(rule).gsub(/(@include [\w-]+)\(([\$\w\-,\s]*)\)/) {
|
419
|
+
"#{cmt}#{$1}('#{sel}'#{', ' if $2 && !$2.empty?}#{$2})"
|
420
|
+
}
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# #gradient > { @mixin horizontal ... }
|
425
|
+
# to:
|
426
|
+
# @mixin gradient-horizontal
|
427
|
+
def flatten_mixins(file, container, prefix)
|
428
|
+
log_transform container, prefix
|
429
|
+
replace_rules file, Regexp.escape(container) do |mixins_css|
|
430
|
+
unindent unwrap_rule_block(mixins_css).gsub(/@mixin\s*([\w-]+)/, "@mixin #{prefix}-\\1")
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
# Replaces the following:
|
435
|
+
# .mixin() -> @include mixin()
|
436
|
+
# #scope > .mixin() -> @include scope-mixin()
|
437
|
+
def replace_mixins(less, mixin_names)
|
438
|
+
mixin_pattern = /(\s+)(([#|\.][\w-]+\s*>\s*)*)\.([\w-]+\(.*\))(?!\s\{)/
|
439
|
+
|
440
|
+
less.gsub(mixin_pattern) do |match|
|
441
|
+
matches = match.scan(mixin_pattern).flatten
|
442
|
+
scope = matches[1] || ''
|
443
|
+
if scope != ''
|
444
|
+
scope = scope.scan(/[\w-]+/).join('-') + '-'
|
445
|
+
end
|
446
|
+
mixin_name = match.scan(/\.([\w-]+)\(.*\)\s?\{?/).first
|
447
|
+
if mixin_name && mixin_names.include?("#{scope}#{mixin_name.first}")
|
448
|
+
"#{matches.first}@include #{scope}#{matches.last}".gsub(/; \$/, ", $").sub(/;\)$/, ')')
|
449
|
+
else
|
450
|
+
"#{matches.first}@extend .#{scope}#{matches.last.gsub(/\(\)/, '')}"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
# change Microsoft filters to SASS calling convention
|
456
|
+
def replace_ms_filters(file)
|
457
|
+
log_transform
|
458
|
+
file.gsub(
|
459
|
+
/filter: e\(%\("progid:DXImageTransform.Microsoft.gradient\(startColorstr='%d', endColorstr='%d', GradientType=(\d)\)",argb\(([\-$\w]+)\),argb\(([\-$\w]+)\)\)\);/,
|
460
|
+
%Q(filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='\#{ie-hex-str(\\2)}', endColorstr='\#{ie-hex-str(\\3)}', GradientType=\\1);)
|
461
|
+
)
|
462
|
+
end
|
463
|
+
|
464
|
+
# unwraps topmost rule block
|
465
|
+
# #sel { a: b; }
|
466
|
+
# to:
|
467
|
+
# a: b;
|
468
|
+
def unwrap_rule_block(css)
|
469
|
+
css[(css =~ RULE_OPEN_BRACE_RE) + 1..-1].sub(/\n?}\s*\z/m, '')
|
470
|
+
end
|
471
|
+
|
472
|
+
def replace_mixin_definitions(less)
|
473
|
+
less.gsub(/^(\s*)\.([\w-]+\(.*\))(\s*\{)/) { |match|
|
474
|
+
"#{$1}@mixin #{$2.tr(';', ',')}#{$3}".sub(/,\)/, ')')
|
475
|
+
}
|
476
|
+
end
|
477
|
+
|
478
|
+
def replace_vars(less)
|
479
|
+
less = less.dup
|
480
|
+
# skip header comment
|
481
|
+
less =~ %r(\A/\*(.*?)\*/)m
|
482
|
+
from = $~ ? $~.to_s.length : 0
|
483
|
+
less[from..-1] = less[from..-1].
|
484
|
+
gsub(/(?!@mixin|@media|@page|@keyframes|@font-face|@-\w)@/, '$').
|
485
|
+
# variables that would be ignored by gsub above: e.g. @page-header-border-color
|
486
|
+
gsub(/@(page[\w-]+)/, '$\1')
|
487
|
+
less
|
488
|
+
end
|
489
|
+
|
490
|
+
# #gradient > .horizontal()
|
491
|
+
# to:
|
492
|
+
# @include .horizontal-gradient()
|
493
|
+
def replace_less_extend(less)
|
494
|
+
less.gsub(/\#(\w+) \> \.([\w-]*)(\(.*\));?/, '@include \1-\2\3;')
|
495
|
+
end
|
496
|
+
|
497
|
+
def replace_spin(less)
|
498
|
+
less.gsub(/(?![\-$@.])spin(?!-)/, 'adjust-hue')
|
499
|
+
end
|
500
|
+
|
501
|
+
def replace_image_urls(less)
|
502
|
+
less.gsub(/background-image: url\("?(.*?)"?\);/) {|s| "background-image: image-url(\"#{$1}\");" }
|
503
|
+
end
|
504
|
+
|
505
|
+
def replace_image_paths(less)
|
506
|
+
less.gsub('../img/', '')
|
507
|
+
end
|
508
|
+
|
509
|
+
def replace_escaping(less)
|
510
|
+
less = less.gsub(/\~"([^"]+)"/, '#{\1}') # Get rid of ~"" escape
|
511
|
+
less.gsub!(/\$\{([^}]+)\}/, '$\1') # Get rid of @{} escape
|
512
|
+
less.gsub!(/"([^"\n]*)(\$[\w\-]+)([^"\n]*)"/, '"\1#{\2}\3"') # interpolate variable in string, e.g. url("$file-1x") => url("#{$file-1x}")
|
513
|
+
less.gsub(/(\W)e\(%\("?([^"]*)"?\)\)/, '\1\2') # Get rid of e(%("")) escape
|
514
|
+
end
|
515
|
+
|
516
|
+
def insert_default_vars(scss)
|
517
|
+
log_transform
|
518
|
+
scss.gsub(/^(\$.+);/, '\1 !default;')
|
519
|
+
end
|
520
|
+
|
521
|
+
# Converts &-
|
522
|
+
def convert_less_ampersand(less)
|
523
|
+
regx = /^\.badge\s*\{[\s\/\w\(\)]+(&{1}-{1})\w.*?^}$/m
|
524
|
+
|
525
|
+
tmp = ''
|
526
|
+
less.scan(/^(\s*&)(-[\w\[\]]+\s*{.+})$/) do |ampersand, css|
|
527
|
+
tmp << ".badge#{css}\n"
|
528
|
+
end
|
529
|
+
|
530
|
+
less.gsub(regx, tmp)
|
531
|
+
end
|
532
|
+
|
533
|
+
# unindent by n spaces
|
534
|
+
def unindent(txt, n = 2)
|
535
|
+
txt.gsub /^[ ]{#{n}}/, ''
|
536
|
+
end
|
537
|
+
|
538
|
+
# indent by n spaces
|
539
|
+
def indent(txt, n = 2)
|
540
|
+
"#{' ' * n}#{txt}"
|
541
|
+
end
|
542
|
+
|
543
|
+
# get indent length from the first line of txt
|
544
|
+
def indent_width(txt)
|
545
|
+
txt.match(/\A\s*/).to_s.length
|
546
|
+
end
|
547
|
+
|
548
|
+
# @mixin transition($transition) {
|
549
|
+
# to:
|
550
|
+
# @mixin transition($transition...) {
|
551
|
+
def varargify_mixin_definitions(scss, *mixins)
|
552
|
+
log_transform *mixins
|
553
|
+
scss = scss.dup
|
554
|
+
mixins.each do |mixin|
|
555
|
+
scss.gsub! /(@mixin\s*#{Regexp.quote(mixin)})\((#{SCSS_MIXIN_DEF_ARGS_RE})\)/, '\1(\2...)'
|
556
|
+
end
|
557
|
+
scss
|
558
|
+
end
|
559
|
+
|
560
|
+
# @include transition(#{border-color ease-in-out .15s, box-shadow ease-in-out .15s})
|
561
|
+
# to
|
562
|
+
# @include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s)
|
563
|
+
def deinterpolate_vararg_mixins(scss)
|
564
|
+
scss = scss.dup
|
565
|
+
VARARG_MIXINS.each do |mixin|
|
566
|
+
if scss.gsub! /(@include\s*#{Regexp.quote(mixin)})\(\s*\#\{([^}]+)\}\s*\)/, '\1(\2)'
|
567
|
+
log_transform mixin
|
568
|
+
end
|
569
|
+
end
|
570
|
+
scss
|
571
|
+
end
|
572
|
+
|
573
|
+
# get full selector for rule_block
|
574
|
+
def get_selector(rule_block)
|
575
|
+
/^\s*(#{SELECTOR_RE}?)\s*\{/.match(rule_block) && $1 && $1.strip
|
576
|
+
end
|
577
|
+
|
578
|
+
# replace CSS rule blocks matching rule_prefix with yield(rule_block, rule_pos)
|
579
|
+
# will also include immediately preceding comments in rule_block
|
580
|
+
#
|
581
|
+
# option :comments -- include immediately preceding comments in rule_block
|
582
|
+
#
|
583
|
+
# replace_rules(".a{ \n .b{} }", '.b') { |rule, pos| ">#{rule}<" } #=> ".a{ \n >.b{}< }"
|
584
|
+
def replace_rules(less, rule_prefix = SELECTOR_RE, options = {}, &block)
|
585
|
+
options = {comments: true}.merge(options || {})
|
586
|
+
less = less.dup
|
587
|
+
s = CharStringScanner.new(less)
|
588
|
+
rule_re = /(?:#{rule_prefix}[^{]*#{RULE_OPEN_BRACE_RE})/
|
589
|
+
if options[:comments]
|
590
|
+
rule_start_re = /(?:#{COMMENT_RE}*)^#{rule_re}/
|
591
|
+
else
|
592
|
+
rule_start_re = /^#{rule_re}/
|
593
|
+
end
|
594
|
+
|
595
|
+
positions = []
|
596
|
+
while (rule_start = s.scan_next(rule_start_re))
|
597
|
+
pos = s.pos
|
598
|
+
positions << (pos - rule_start.length..close_brace_pos(less, pos - 1))
|
599
|
+
end
|
600
|
+
replace_substrings_at(less, positions, &block)
|
601
|
+
less
|
602
|
+
end
|
603
|
+
|
604
|
+
# Get a all top-level selectors (with {)
|
605
|
+
def get_css_selectors(css, opts = {})
|
606
|
+
s = CharStringScanner.new(css)
|
607
|
+
selectors = []
|
608
|
+
while s.scan_next(RULE_OPEN_BRACE_RE)
|
609
|
+
brace_pos = s.pos
|
610
|
+
def_pos = css_def_pos(css, brace_pos+1, -1)
|
611
|
+
sel = css[def_pos.begin..brace_pos - 1].dup
|
612
|
+
sel.strip! if opts[:strip]
|
613
|
+
selectors << sel
|
614
|
+
sel.dup.strip
|
615
|
+
s.pos = close_brace_pos(css, brace_pos, 1) + 1
|
616
|
+
end
|
617
|
+
selectors
|
618
|
+
end
|
619
|
+
|
620
|
+
# replace in the top-level selector
|
621
|
+
# replace_in_selector('a {a: {a: a} } a {}', /a/, 'b') => 'b {a: {a: a} } b {}'
|
622
|
+
def replace_in_selector(css, pattern, sub)
|
623
|
+
# scan for selector positions in css
|
624
|
+
s = CharStringScanner.new(css)
|
625
|
+
prev_pos = 0
|
626
|
+
sel_pos = []
|
627
|
+
while (brace = s.scan_next(RULE_OPEN_BRACE_RE))
|
628
|
+
pos = s.pos
|
629
|
+
sel_pos << (prev_pos .. pos - 1)
|
630
|
+
s.pos = close_brace_pos(css, s.pos - 1) + 1
|
631
|
+
prev_pos = pos
|
632
|
+
end
|
633
|
+
replace_substrings_at(css, sel_pos) { |s| s.gsub(pattern, sub) }
|
634
|
+
end
|
635
|
+
|
636
|
+
|
637
|
+
sel_chars = '\[\]$\w\-{}#,.:&>@'
|
638
|
+
SELECTOR_RE = /[#{sel_chars}]+[#{sel_chars}\s]*/
|
639
|
+
COMMENT_RE = %r((?:^[ \t]*//[^\n]*\n))
|
640
|
+
RULE_OPEN_BRACE_RE = /(?<![@#\$])\{/
|
641
|
+
RULE_OPEN_BRACE_RE_REVERSE = /\{(?![@#\$])/
|
642
|
+
RULE_CLOSE_BRACE_RE = /(?<!\w)\}(?![.'"])/
|
643
|
+
RULE_CLOSE_BRACE_RE_REVERSE = /(?<![.'"])\}(?!\w)/
|
644
|
+
BRACE_RE = /#{RULE_OPEN_BRACE_RE}|#{RULE_CLOSE_BRACE_RE}/m
|
645
|
+
BRACE_RE_REVERSE = /#{RULE_OPEN_BRACE_RE_REVERSE}|#{RULE_CLOSE_BRACE_RE_REVERSE}/m
|
646
|
+
SCSS_MIXIN_DEF_ARGS_RE = /[\w\-,\s$:#%]*/
|
647
|
+
LESS_MIXIN_DEF_ARGS_RE = /[\w\-,;\s@:#%]*/
|
648
|
+
|
649
|
+
# replace first level properties in the css with yields
|
650
|
+
# replace_properties("a { color: white }") { |props| props.gsub 'white', 'red' }
|
651
|
+
def replace_properties(css, &block)
|
652
|
+
s = CharStringScanner.new(css)
|
653
|
+
s.skip_until /#{RULE_OPEN_BRACE_RE}\n?/
|
654
|
+
prev_pos = s.pos
|
655
|
+
depth = 0
|
656
|
+
pos = []
|
657
|
+
while (b = s.scan_next(/#{SELECTOR_RE}#{RULE_OPEN_BRACE_RE}|#{RULE_CLOSE_BRACE_RE}/m))
|
658
|
+
s_pos = s.pos
|
659
|
+
depth += (b == '}' ? -1 : +1)
|
660
|
+
if depth == 1
|
661
|
+
if b == '}'
|
662
|
+
prev_pos = s_pos
|
663
|
+
else
|
664
|
+
pos << (prev_pos .. s_pos - b.length - 1)
|
665
|
+
end
|
666
|
+
end
|
667
|
+
end
|
668
|
+
replace_substrings_at css, pos, &block
|
669
|
+
end
|
670
|
+
|
671
|
+
|
672
|
+
# immediate selector of css at pos
|
673
|
+
def selector_for_pos(css, pos, depth = -1)
|
674
|
+
css[css_def_pos(css, pos, depth)].dup.strip
|
675
|
+
end
|
676
|
+
|
677
|
+
# get the pos of css def at pos (search backwards)
|
678
|
+
def css_def_pos(css, pos, depth = -1)
|
679
|
+
to = open_brace_pos(css, pos, depth)
|
680
|
+
prev_def = to - (css[0..to].reverse.index('}') || to) + 1
|
681
|
+
from = prev_def + 1 + (css[prev_def + 1..-1] =~ %r(^\s*[^\s/]))
|
682
|
+
(from..to - 1)
|
683
|
+
end
|
684
|
+
|
685
|
+
# next matching brace for brace at from
|
686
|
+
def close_brace_pos(css, from, depth = 0)
|
687
|
+
s = CharStringScanner.new(css[from..-1])
|
688
|
+
while (b = s.scan_next(BRACE_RE))
|
689
|
+
depth += (b == '}' ? -1 : +1)
|
690
|
+
break if depth.zero?
|
691
|
+
end
|
692
|
+
raise "match not found for {" unless depth.zero?
|
693
|
+
from + s.pos - 1
|
694
|
+
end
|
695
|
+
|
696
|
+
# opening brace position from +from+ (search backwards)
|
697
|
+
def open_brace_pos(css, from, depth = 0)
|
698
|
+
s = CharStringScanner.new(css[0..from].reverse)
|
699
|
+
while (b = s.scan_next(BRACE_RE_REVERSE))
|
700
|
+
depth += (b == '{' ? +1 : -1)
|
701
|
+
break if depth.zero?
|
702
|
+
end
|
703
|
+
raise "matching { brace not found" unless depth.zero?
|
704
|
+
from - s.pos + 1
|
705
|
+
end
|
706
|
+
|
707
|
+
# insert substitutions into text at positions (Range or Fixnum)
|
708
|
+
# substitutions can be passed as array or as yields from the &block called with |substring, position, text|
|
709
|
+
# position is a range (begin..end)
|
710
|
+
def replace_substrings_at(text, positions, replacements = nil, &block)
|
711
|
+
offset = 0
|
712
|
+
positions.each_with_index do |p, i|
|
713
|
+
p = (p...p) if p.is_a?(Fixnum)
|
714
|
+
from = p.begin + offset
|
715
|
+
to = p.end + offset
|
716
|
+
p = p.exclude_end? ? (from...to) : (from..to)
|
717
|
+
# block returns the substitution, e.g.: { |text, pos| text[pos].upcase }
|
718
|
+
r = replacements ? replacements[i] : block.call(text[p], p, text)
|
719
|
+
text[p] = r
|
720
|
+
# add the change in length to offset
|
721
|
+
offset += r.size - (p.end - p.begin + (p.exclude_end? ? 0 : 1))
|
722
|
+
end
|
723
|
+
text
|
724
|
+
end
|
725
|
+
|
726
|
+
def get_json(url)
|
727
|
+
JSON.parse get_file(url)
|
728
|
+
end
|
729
|
+
|
730
|
+
# regular string scanner works with bytes
|
731
|
+
# this one works with chars and provides #scan_next
|
732
|
+
class CharStringScanner
|
733
|
+
extend Forwardable
|
734
|
+
|
735
|
+
def initialize(*args)
|
736
|
+
@s = StringScanner.new(*args)
|
737
|
+
end
|
738
|
+
|
739
|
+
def_delegators :@s, :scan_until, :skip_until, :string
|
740
|
+
|
741
|
+
# advance scanner to pos after the next match of pattern and return the match
|
742
|
+
def scan_next(pattern)
|
743
|
+
return unless @s.scan_until(pattern)
|
744
|
+
@s.matched
|
745
|
+
end
|
746
|
+
|
747
|
+
def pos
|
748
|
+
byte_to_str_pos @s.pos
|
749
|
+
end
|
750
|
+
|
751
|
+
def pos=(i)
|
752
|
+
@s.pos = str_to_byte_pos i
|
753
|
+
i
|
754
|
+
end
|
755
|
+
|
756
|
+
private
|
757
|
+
|
758
|
+
def byte_to_str_pos(pos)
|
759
|
+
@s.string.byteslice(0, pos).length
|
760
|
+
end
|
761
|
+
|
762
|
+
def str_to_byte_pos(pos)
|
763
|
+
@s.string.slice(0, pos).bytesize
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
class Logger
|
768
|
+
include Term::ANSIColor
|
769
|
+
|
770
|
+
def initialize(env)
|
771
|
+
@env = env
|
772
|
+
puts bold "Convert Bootstrap LESS to SASS"
|
773
|
+
puts " repo : #{env[:repo]}"
|
774
|
+
puts " branch : #{env[:branch]} #{dark "#{env[:repo]}/tree/#{env[:branch_sha]}"}"
|
775
|
+
puts " save to: #{@env[:save_at].to_json}"
|
776
|
+
puts " twbs cache: #{@env[:cache_path]}"
|
777
|
+
puts dark "-" * 60
|
778
|
+
end
|
779
|
+
|
780
|
+
def log_status(status)
|
781
|
+
puts bold status
|
782
|
+
end
|
783
|
+
|
784
|
+
def log_file_info(s)
|
785
|
+
puts " #{magenta s}"
|
786
|
+
end
|
787
|
+
|
788
|
+
def log_transform(*args)
|
789
|
+
puts "#{cyan " #{caller[1][/`.*'/][1..-2].sub(/^block in /, '')}"}#{cyan ": #{args * ', '}" unless args.empty?}"
|
790
|
+
end
|
791
|
+
|
792
|
+
def log_processing(name)
|
793
|
+
puts yellow " #{File.basename(name)}"
|
794
|
+
end
|
795
|
+
|
796
|
+
def log_processed(name)
|
797
|
+
puts green " #{name}"
|
798
|
+
end
|
799
|
+
|
800
|
+
def log_http_get_file(url, cached = false)
|
801
|
+
s = " #{'CACHED ' if cached}GET #{url}..."
|
802
|
+
if cached
|
803
|
+
puts dark green s
|
804
|
+
else
|
805
|
+
puts dark cyan s
|
806
|
+
end
|
807
|
+
end
|
808
|
+
|
809
|
+
def log_http_get_files(files, from, cached = false)
|
810
|
+
s = " #{'CACHED ' if cached}GET #{files.length} files from #{from} #{files * ' '}..."
|
811
|
+
if cached
|
812
|
+
puts dark green s
|
813
|
+
else
|
814
|
+
puts dark cyan s
|
815
|
+
end
|
816
|
+
end
|
817
|
+
|
818
|
+
def puts(*args)
|
819
|
+
STDOUT.puts *args unless @silence
|
820
|
+
end
|
821
|
+
|
822
|
+
def silence_log
|
823
|
+
@silence = true
|
824
|
+
yield
|
825
|
+
ensure
|
826
|
+
@silence = false
|
827
|
+
end
|
828
|
+
end
|
829
|
+
end
|