Skip to content

Commit

Permalink
converter: cache files, understand in-file mixin definitinos
Browse files Browse the repository at this point in the history
  • Loading branch information
glebm committed Aug 18, 2013
1 parent 07f9d01 commit 0891740
Show file tree
Hide file tree
Showing 29 changed files with 839 additions and 228 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ Gemfile.lock
/.bundle
/vendor/cache
/vendor/bundle
tmp/
2 changes: 1 addition & 1 deletion lib/bootstrap-sass/version.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Bootstrap
VERSION = '3.0.0.0'
BOOTSTRAP_SHA = 'fee3f1e733e80bd128736e1b9b403f49b701a6a9'
BOOTSTRAP_SHA = '518488cb4069b4181435873380d0738e8b63bc81'
end
96 changes: 71 additions & 25 deletions tasks/converter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,24 @@ def process
store_version
end

NESTED_MIXINS = {'#gradient' => 'gradient'}
VARARG_MIXINS = %w(transition transition-transform box-shadow)
def process_stylesheet_assets
log_status "Processing stylesheets..."
files = read_files('less', bootstrap_less_files)
@mixins = get_mixin_names files['mixins.less']

# read common mixin definitions from mixins.less
mixins_file = files['mixins.less']
@mixins = get_mixin_names(mixins_file)
NESTED_MIXINS.each do |selector, prefix|
replace_rules(mixins_file, selector) { |rule|
@mixins += get_mixin_names(unwrap_rule_block rule).map { |name| "#{prefix}-#{name}" }
rule
}
end
puts "*** MIXINS #{@mixins}"

# convert each file
files.each do |name, file|
log_processing name
case name
Expand All @@ -61,7 +74,9 @@ def process_stylesheet_assets
file = replace_escaping(file)
file = replace_mixin_definitions(file)
file = replace_mixins(file)
file = flatten_mixins(file, '#gradient', 'gradient')
NESTED_MIXINS.each do |selector, prefix|
file = flatten_mixins(file, selector, prefix)
end
file = varargify_mixin_definitions(file, *VARARG_MIXINS)
file = deinterpolate_vararg_mixins(file)
file = parameterize_mixin_parent_selector file, 'responsive-(in)?visibility'
Expand Down Expand Up @@ -214,7 +229,7 @@ def bootstrap_js_files

def get_mixin_names(file)
mixins = []
file.scan(/\.([\w-]+)\(.*\)\s?{?/) do |mixin|
get_css_selectors(file).join("\n" * 2).scan(/\.([\w-]+)\(.*\)\s?\{?/) do |mixin|
mixins << mixin.first
end
mixins
Expand Down Expand Up @@ -246,7 +261,9 @@ def replace_file_imports(less, target_path = 'bootstrap/')

def replace_all(file, regex, replacement = nil, &block)
log_transform regex, replacement
file.gsub(regex, replacement, &block)
new_file = file.gsub(regex, replacement, &block)
raise "replace_all #{regex}, #{replacement} NO MATCH" if file == new_file
new_file
end

# @mixin a() { tr& { color:white } }
Expand Down Expand Up @@ -318,17 +335,17 @@ def flatten_mixins(file, container, prefix)
# Replaces the following:
# .mixin() -> @include mixin()
# #scope > .mixin() -> @include scope-mixin()
def replace_mixins(less)
def replace_mixins(less, mixins = @mixins + get_mixin_names(less))
mixin_pattern = /(\s+)(([#|\.][\w-]+\s*>\s*)*)\.([\w-]+\(.*\))/

less.gsub(mixin_pattern) do |match|
matches = match.scan(mixin_pattern).flatten
scope = matches[1] || ''
if scope != ''
scope = scope.scan(/[\w-]+/).join('-') + '-'
end
mixin_name = match.scan(/\.([\w-]+)\(.*\)\s?\{?/).first

if mixin_name && @mixins.include?(mixin_name.first)
if mixin_name && mixins.include?("#{scope}#{mixin_name.first}")
"#{matches.first}@include #{scope}#{matches.last}".gsub(/; \$/, ", $")
else
"#{matches.first}@extend .#{scope}#{matches.last.gsub(/\(\)/, '')}"
Expand Down Expand Up @@ -473,23 +490,39 @@ def replace_rules(less, rule_prefix = SELECTOR_RE, options = {})
end

while (rule_start = scan_next(s, rule_start_re))
rule_pos = (s.pos - rule_start.length..close_brace_pos(less, s.pos - 1))
pos = byte_to_str_pos less, s.pos
rule_pos = (pos - rule_start.length..close_brace_pos(less, pos - 1))
less[rule_pos] = yield(less[rule_pos], rule_pos)
end
less
end

# Get a list of all top-level selectors with bodies {}
def get_css_selectors(css)
s = StringScanner.new(css)
selectors = []
while (brace = scan_next(s, RULE_OPEN_BRACE_RE))
pos = byte_to_str_pos(css, s.pos)
def_pos = css_def_pos(css, pos, -1)
sel = css[def_pos.begin..pos - 1]
selectors << sel.dup.strip
s.pos = str_to_byte_pos(css, close_brace_pos(css, pos - 1) + 1)
end
selectors
end

# replace in the top-level selector
# replace_in_selector('a {a: {a: a} } a {}', /a/, 'b') => 'b {a: {a: a} } b {}'
def replace_in_selector(css, pattern, sub)
# scan for selector positions in css
s = StringScanner.new(css)
prev_pos = 0
sel_pos = []
while (brace = scan_next(s, /#{RULE_OPEN_BRACE_RE}/))
sel_pos << (prev_pos .. s.pos - 1)
s.pos = close_brace_pos(css, s.pos - 1) + 1
prev_pos = s.pos
while (brace = scan_next(s, RULE_OPEN_BRACE_RE))
pos = byte_to_str_pos css, s.pos
sel_pos << (prev_pos .. pos - 1)
s.pos = str_to_byte_pos(s.string, close_brace_pos(css, s.pos - 1) + 1)
prev_pos = pos
end
replace_substrings_at(css, sel_pos) { |s| s.gsub(pattern, sub) }
end
Expand All @@ -498,10 +531,10 @@ def replace_in_selector(css, pattern, sub)
sel_chars = '\[\]$\w\-{}#,.:&>@'
SELECTOR_RE = /[#{sel_chars}]+[#{sel_chars}\s]*/
COMMENT_RE = %r((?:^[ \t]*//[^\n]*\n))
RULE_OPEN_BRACE_RE = /(?<!#)\{/
RULE_OPEN_BRACE_RE_REVERSE = /\{(?!#)/
RULE_CLOSE_BRACE_RE = /(?<!\w)\}/
RULE_CLOSE_BRACE_RE_REVERSE = /\}(?!\w)/
RULE_OPEN_BRACE_RE = /(?<![@#\$])\{/
RULE_OPEN_BRACE_RE_REVERSE = /\{(?![@#\$])/
RULE_CLOSE_BRACE_RE = /(?<!\w)\}(?![.'"])/
RULE_CLOSE_BRACE_RE_REVERSE = /(?<![.'"])\}(?!\w)/
BRACE_RE = /#{RULE_OPEN_BRACE_RE}|#{RULE_CLOSE_BRACE_RE}/m
BRACE_RE_REVERSE = /#{RULE_OPEN_BRACE_RE_REVERSE}|#{RULE_CLOSE_BRACE_RE_REVERSE}/m
SCSS_MIXIN_DEF_ARGS_RE = /[\w\-,\s$:]*/
Expand All @@ -515,12 +548,13 @@ def replace_properties(css, &block)
depth = 0
pos = []
while (b = scan_next(s, /#{SELECTOR_RE}#{RULE_OPEN_BRACE_RE}|#{RULE_CLOSE_BRACE_RE}/m))
s_pos = byte_to_str_pos(s.string, s.pos)
depth += (b == '}' ? -1 : +1)
if depth == 1
if b == '}'
prev_pos = s.pos
prev_pos = s_pos
else
pos << (prev_pos .. s.pos - b.length - 1)
pos << (prev_pos .. s_pos - b.length - 1)
end
end
end
Expand All @@ -530,7 +564,7 @@ def replace_properties(css, &block)

# immediate selector of css at pos
def selector_for_pos(css, pos, depth = -1)
css[css_def_pos(css, pos, depth)].strip
css[css_def_pos(css, pos, depth)].dup.strip
end

# get the pos of css def at pos (search backwards)
Expand All @@ -549,7 +583,7 @@ def close_brace_pos(css, from, depth = 0)
break if depth.zero?
end
raise "match not found for {" unless depth.zero?
from + s.pos - 1
from + byte_to_str_pos(s.string, s.pos) - 1
end

# opening brace position from +from+ (search backwards)
Expand All @@ -560,14 +594,21 @@ def open_brace_pos(css, from, depth = 0)
break if depth.zero?
end
raise "matching { brace not found" unless depth.zero?
from - s.pos + 1
from - byte_to_str_pos(s.string, s.pos) + 1
end

# advance scanner to pos after the next match of pattern and return the match
def scan_next(scanner, pattern)
return unless scanner.skip_until(pattern)
scanner.pos -= scanner.matched_size
scanner.scan pattern
return unless scanner.scan_until(pattern)
scanner.matched
end

def byte_to_str_pos(str, pos)
str.byteslice(0, pos).length
end

def str_to_byte_pos(str, pos)
str.slice(0, pos).bytesize
end

# insert substitutions into text at positions (Range or Fixnum)
Expand Down Expand Up @@ -620,7 +661,12 @@ def log_transform(*args)
end

def log_downloading(files, from, cached = false)
puts dark cyan " #{' CACHED ' if cached}GET #{files.length} files from #{from} #{files * ' '}..."
s = " #{' CACHED ' if cached}GET #{files.length} files from #{from} #{files * ' '}..."
if cached
puts dark green s
else
puts dark cyan s
end
end

def log_processing(name)
Expand Down
2 changes: 1 addition & 1 deletion test/compilation_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
class CompilationTest < Test::Unit::TestCase
def test_compilation
path = 'vendor/assets/stylesheets'
%w(bootstrap).each do |file|
%w(bootstrap bootstrap/_theme).each do |file|
engine = Sass::Engine.for_file("#{path}/#{file}.scss", syntax: :scss, load_paths: [path])
assert_nothing_raised do
engine.render
Expand Down
6 changes: 3 additions & 3 deletions vendor/assets/javascripts/bootstrap/dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
clearMenus()

if (!isActive) {
if ('ontouchstart' in document.documentElement) {
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
// if mobile we we use a backdrop because click events don't delegate
$('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus)
}
Expand All @@ -52,9 +52,9 @@
$parent
.toggleClass('open')
.trigger('shown.bs.dropdown')
}

$this.focus()
$this.focus()
}

return false
}
Expand Down
8 changes: 5 additions & 3 deletions vendor/assets/javascripts/bootstrap/modal.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

var Modal = function (element, options) {
this.options = options
this.$element = $(element).on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))
this.$element = $(element)
this.$backdrop =
this.isShown = null

Expand Down Expand Up @@ -54,6 +54,8 @@

this.escape()

this.$element.on('click.dismiss.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this))

this.backdrop(function () {
var transition = $.support.transition && that.$element.hasClass('fade')

Expand All @@ -76,7 +78,7 @@
var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget })

transition ?
that.$element
that.$element.find('.modal-dialog') // wait for modal to slide in
.one($.support.transition.end, function () {
that.$element.focus().trigger(e)
})
Expand Down Expand Up @@ -238,7 +240,7 @@
})

$(document)
.on('shown.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
.on('show.bs.modal', '.modal', function () { $(document.body).addClass('modal-open') })
.on('hidden.bs.modal', '.modal', function () { $(document.body).removeClass('modal-open') })

}(window.jQuery);
14 changes: 9 additions & 5 deletions vendor/assets/javascripts/bootstrap/tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,11 @@

clearTimeout(self.timeout)

self.hoverState = 'in'

if (!self.options.delay || !self.options.delay.show) return self.show()

self.hoverState = 'in'
self.timeout = setTimeout(function () {
self.timeout = setTimeout(function () {
if (self.hoverState == 'in') self.show()
}, self.options.delay.show)
}
Expand All @@ -122,10 +123,11 @@

clearTimeout(self.timeout)

self.hoverState = 'out'

if (!self.options.delay || !self.options.delay.hide) return self.hide()

self.hoverState = 'out'
self.timeout = setTimeout(function () {
self.timeout = setTimeout(function () {
if (self.hoverState == 'out') self.hide()
}, self.options.delay.hide)
}
Expand Down Expand Up @@ -258,7 +260,9 @@
var $tip = this.tip()
var e = $.Event('hide.bs.' + this.type)

function complete() { $tip.detach() }
function complete() {
if (that.hoverState != 'in') $tip.detach()
}

this.$element.trigger(e)

Expand Down
2 changes: 1 addition & 1 deletion vendor/assets/javascripts/bootstrap/transition.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

// http://blog.alexmaccaw.com/css-transitions
$.fn.emulateTransitionEnd = function (duration) {
var called = false, $el = this
var called = false, $el = this
$(this).one($.support.transition.end, function () { called = true })
var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
setTimeout(callback, duration)
Expand Down
23 changes: 13 additions & 10 deletions vendor/assets/stylesheets/bootstrap/_button-groups.scss
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@
border-top-color: #fff;
}
}
.dropup .caret {
.btn-default & {
.dropup {
& .btn-default .caret {
border-bottom-color: $btn-default-color;
}
.btn-primary &,
.btn-success &,
.btn-warning &,
.btn-danger &,
.btn-info & {
border-bottom-color: #fff;
& .btn-primary,
& .btn-success,
& .btn-warning,
& .btn-danger,
& .btn-info {
.caret {
border-bottom-color: #fff;
}
}
}

Expand Down Expand Up @@ -157,11 +159,12 @@
}
// Carets in other button sizes
.btn-lg .caret {
border-width: $caret-width-large;
border-width: $caret-width-large $caret-width-large 0;
border-bottom-width: 0;
}
// Upside down carets for .dropup
.dropup .btn-lg .caret {
border-bottom-width: $caret-width-large;
border-width: 0 $caret-width-large $caret-width-large;
}


Expand Down
3 changes: 2 additions & 1 deletion vendor/assets/stylesheets/bootstrap/_buttons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
&:active,
&.active {
outline: 0;
background-image: none;
@include box-shadow(inset 0 3px 5px rgba(0,0,0,.125));
}

Expand Down Expand Up @@ -130,7 +131,7 @@
@include button-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $border-radius-small);
}
.btn-xs {
padding: 3px 5px;
padding: 1px 5px;
}


Expand Down
Loading

0 comments on commit 0891740

Please sign in to comment.