bootstrap-sass 2.3.2.2 → 3.0.0.0.rc
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.
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
|