diff options
author | Gleb Mazovetskiy <glex.spb@gmail.com> | 2014-10-29 23:49:47 +0300 |
---|---|---|
committer | Gleb Mazovetskiy <glex.spb@gmail.com> | 2014-10-29 23:49:47 +0300 |
commit | 37ea51e9ac2ea1e0f475f998f6df70fabf0996b7 (patch) | |
tree | 8ce3b8b2b581459a140813e329ebe06043e9ac09 | |
parent | 9365af1399b518f1f1d1217e8f9dc56febdd7a93 (diff) | |
parent | f675be933cd4d16e760f3dd8906e721a92973b71 (diff) |
Merge branch 'next'
Conflicts:
README.md
lib/bootstrap-sass.rb
79 files changed, 2082 insertions, 995 deletions
@@ -14,6 +14,6 @@ Gemfile.lock tmp/ test/screenshots/ test/dummy_rails/log/*.log - +test/dummy_rails/public/assets/ .DS_Store node_modules diff --git a/.travis.yml b/.travis.yml index 0223e8c..3087a94 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,12 +1,12 @@ language: ruby rvm: - - 2.1.1 - - 2.0.0 + - 2.1.2 - jruby - rbx-2 gemfile: - test/gemfiles/sass_3_2.gemfile - test/gemfiles/sass_3_3.gemfile + - test/gemfiles/sass_3_4.gemfile - test/gemfiles/sass_head.gemfile before_install: - "npm install" @@ -17,3 +17,9 @@ matrix: - gemfile: test/gemfiles/sass_head.gemfile notifications: slack: heybb:3n88HHilXn76ji9vV4gL819Y +env: + global: + - VERBOSE=1 +script: + - bundle exec rake + - sh test/*.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 5446eca..3c6dbdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## 3.3.0 + +* Improve libsass compatibility +* Support using Bower package with Rails + ## 3.2.0.2 Main bootstrap file is now a partial (_bootstrap.scss), for compatibility with Compass 1+. @@ -17,7 +17,7 @@ Please see the appropriate guide for your environment of choice: 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. ```ruby -gem 'bootstrap-sass', '~> 3.2.0' +gem 'bootstrap-sass', '~> 3.3.0' gem 'sass-rails', '>= 3.2' ``` @@ -33,6 +33,7 @@ gem 'autoprefixer-rails' Import Bootstrap styles in `app/assets/stylesheets/application.css.scss`: ```scss +// "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables" @import "bootstrap-sprockets"; @import "bootstrap"; ``` @@ -57,13 +58,29 @@ Require Bootstrap Javascripts in `app/assets/javascripts/application.js`: #### Bower with Rails -When using [bootstrap-sass Bower package](#c-bower) in Rails, ensure [minimum Sass number precision](#sass-number-precision): +When using [bootstrap-sass Bower package](#c-bower) instead of the gem in Rails, add Bootstrap asset paths: ```ruby -# e.g. config/initializers/sass.rb +# config/application.rb +# bootstrap-sass asset paths +root.join('vendor/assets/bower_components/bootstrap-sass/assets').tap do |path| + config.sass.load_paths << path.join('stylesheets') + config.assets.paths += %w(javascripts fonts images).map(&path.method(:join)) +end +``` + +Then, ensure [minimum Sass number precision](#sass-number-precision): + +```ruby +# config/initializers/sass.rb +# Minimum precision required by bootstrap-sass ::Sass::Script::Number.precision = [10, ::Sass::Script::Number.precision].max ``` +#### Rails 4.x + +Please make sure `sprockets-rails` is at least v2.1.4. + #### Rails 3.2.x Rails 3.2 is [no longer maintained for bugfixes](http://guides.rubyonrails.org/maintenance_policy.html), and you should upgrade as soon as possible. @@ -159,8 +176,8 @@ See also this [example manifest.js](/test/dummy_node_mincer/manifest.js) for min By default all of Bootstrap is imported. You can also import components explicitly. To start with a full list of modules copy -[`bootstrap.scss`](assets/stylesheets/_bootstrap.scss) file into your assets as `bootstrap-custom.scss`. -Then comment out components you do not want from `bootstrap-custom`. +[`_bootstrap.scss`](assets/stylesheets/_bootstrap.scss) file into your assets as `_bootstrap-custom.scss`. +Then comment out components you do not want from `_bootstrap-custom`. In the application Sass file, replace `@import 'bootstrap'` with: ```scss @@ -4,10 +4,9 @@ $:.unshift(lib_path) unless $:.include?(lib_path) load './tasks/bower.rake' require 'rake/testtask' -Rake::TestTask.new do |t| - t.libs << "test" - t.test_files = FileList['test/*_test.rb'] - t.verbose = true +task :test do |t| + $: << File.expand_path('test/') + Dir.glob('./test/**/*_test.rb').each { |file| require file } end desc 'Dumps output to a CSS file for testing' diff --git a/assets/fonts/bootstrap/glyphicons-halflings-regular.svg b/assets/fonts/bootstrap/glyphicons-halflings-regular.svg index e3e2dc7..25691af 100644 --- a/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +++ b/assets/fonts/bootstrap/glyphicons-halflings-regular.svg @@ -226,4 +226,4 @@ <glyph unicode="" d="M100 200h400v-155l-75 -45h350l-75 45v155h400l-270 300h170l-270 300h170l-300 333l-300 -333h170l-270 -300h170z" /> <glyph unicode="" d="M121 700q0 -53 28.5 -97t75.5 -65q-4 -16 -4 -38q0 -74 52.5 -126.5t126.5 -52.5q56 0 100 30v-306l-75 -45h350l-75 45v306q46 -30 100 -30q74 0 126.5 52.5t52.5 126.5q0 24 -9 55q50 32 79.5 83t29.5 112q0 90 -61.5 155.5t-150.5 71.5q-26 89 -99.5 145.5 t-167.5 56.5q-116 0 -197.5 -81.5t-81.5 -197.5q0 -4 1 -11.5t1 -11.5q-14 2 -23 2q-74 0 -126.5 -52.5t-52.5 -126.5z" /> </font> -</defs></svg>
\ No newline at end of file +</defs></svg>
\ No newline at end of file diff --git a/assets/javascripts/bootstrap.js b/assets/javascripts/bootstrap.js index 30409f4..eac0ba4 100644 --- a/assets/javascripts/bootstrap.js +++ b/assets/javascripts/bootstrap.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: affix.js v3.2.0 + * Bootstrap: affix.js v3.3.0 * http://getbootstrap.com/javascript/#affix * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -28,7 +28,7 @@ this.checkPosition() } - Affix.VERSION = '3.2.0' + Affix.VERSION = '3.3.0' Affix.RESET = 'affix affix-top affix-bottom' @@ -37,6 +37,28 @@ target: window } + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && colliderTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') @@ -52,42 +74,40 @@ Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return - var scrollHeight = $(document).height() - var scrollTop = this.$target.scrollTop() - var position = this.$element.offset() + var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom + var scrollHeight = $('body').height() if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) - var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : - offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : - offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) - if (this.affixed === affix) return - if (this.unpin != null) this.$element.css('top', '') + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') - var affixType = 'affix' + (affix ? '-' + affix : '') - var e = $.Event(affixType + '.bs.affix') + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') - this.$element.trigger(e) + this.$element.trigger(e) - if (e.isDefaultPrevented()) return + if (e.isDefaultPrevented()) return - this.affixed = affix - this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null - this.$element - .removeClass(Affix.RESET) - .addClass(affixType) - .trigger($.Event(affixType.replace('affix', 'affixed'))) + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } if (affix == 'bottom') { this.$element.offset({ - top: scrollHeight - this.$element.height() - offsetBottom + top: scrollHeight - height - offsetBottom }) } } @@ -132,8 +152,8 @@ data.offset = data.offset || {} - if (data.offsetBottom) data.offset.bottom = data.offsetBottom - if (data.offsetTop) data.offset.top = data.offsetTop + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop Plugin.call($spy, data) }) @@ -142,7 +162,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: alert.js v3.2.0 + * Bootstrap: alert.js v3.3.0 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -161,7 +181,9 @@ $(el).on('click', dismiss, this.close) } - Alert.VERSION = '3.2.0' + Alert.VERSION = '3.3.0' + + Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) @@ -177,7 +199,7 @@ if (e) e.preventDefault() if (!$parent.length) { - $parent = $this.hasClass('alert') ? $this : $this.parent() + $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) @@ -194,7 +216,7 @@ $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } @@ -235,7 +257,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: button.js v3.2.0 + * Bootstrap: button.js v3.3.0 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -255,7 +277,7 @@ this.isLoading = false } - Button.VERSION = '3.2.0' + Button.VERSION = '3.3.0' Button.DEFAULTS = { loadingText: 'loading...' @@ -271,10 +293,10 @@ if (data.resetText == null) $el.data('resetText', $el[val]()) - $el[val](data[state] == null ? this.options[state] : data[state]) - // push to event loop to allow forms to submit setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) @@ -296,6 +318,8 @@ else $parent.find('.active').removeClass('active') } if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) } if (changed) this.$element.toggleClass('active') @@ -336,17 +360,21 @@ // BUTTON DATA-API // =============== - $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - Plugin.call($btn, 'toggle') - e.preventDefault() - }) + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', e.type == 'focus') + }) }(jQuery); /* ======================================================================== - * Bootstrap: carousel.js v3.2.0 + * Bootstrap: carousel.js v3.3.0 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -361,7 +389,7 @@ // ========================= var Carousel = function (element, options) { - this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) + this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = @@ -370,17 +398,22 @@ this.$active = this.$items = null - this.options.pause == 'hover' && this.$element + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } - Carousel.VERSION = '3.2.0' + Carousel.VERSION = '3.3.0' + + Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', - wrap: true + wrap: true, + keyboard: true } Carousel.prototype.keydown = function (e) { @@ -410,6 +443,13 @@ return this.$items.index(item || this.$active) } + Carousel.prototype.getItemForDirection = function (direction, active) { + var delta = direction == 'prev' ? -1 : 1 + var activeIndex = this.getItemIndex(active) + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) @@ -419,7 +459,7 @@ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { @@ -447,7 +487,7 @@ Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') - var $next = next || $active[type]() + var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var fallback = type == 'next' ? 'first' : 'last' @@ -493,7 +533,7 @@ that.$element.trigger(slidEvent) }, 0) }) - .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') @@ -542,7 +582,7 @@ // CAROUSEL DATA-API // ================= - $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 @@ -558,7 +598,11 @@ } e.preventDefault() - }) + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { @@ -570,7 +614,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: collapse.js v3.2.0 + * Bootstrap: collapse.js v3.3.0 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -587,16 +631,25 @@ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') this.transitioning = null - if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + if (this.options.toggle) this.toggle() } - Collapse.VERSION = '3.2.0' + Collapse.VERSION = '3.3.0' + + Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { - toggle: true + toggle: true, + trigger: '[data-toggle="collapse"]' } Collapse.prototype.dimension = function () { @@ -607,17 +660,21 @@ Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return + var activesData + var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return - var actives = this.$parent && this.$parent.find('> .panel > .in') - if (actives && actives.length) { - var hasData = actives.data('bs.collapse') - if (hasData && hasData.transitioning) return Plugin.call(actives, 'hide') - hasData || actives.data('bs.collapse', null) + activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() @@ -625,6 +682,11 @@ this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) this.transitioning = 1 @@ -643,7 +705,7 @@ this.$element .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { @@ -659,17 +721,21 @@ this.$element .addClass('collapsing') - .removeClass('collapse') - .removeClass('in') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element - .trigger('hidden.bs.collapse') .removeClass('collapsing') .addClass('collapse') + .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) @@ -677,13 +743,40 @@ this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + // COLLAPSE PLUGIN DEFINITION // ========================== @@ -694,7 +787,7 @@ var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) - if (!data && options.toggle && option == 'show') option = !option + if (!data && options.toggle && option == 'show') options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) @@ -719,21 +812,13 @@ // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { - var href var $this = $(this) - var target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 - var $target = $(target) - var data = $target.data('bs.collapse') - var option = data ? 'toggle' : $this.data() - var parent = $this.attr('data-parent') - var $parent = parent && $(parent) - if (!data || !data.transitioning) { - if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') - $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') - } + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) Plugin.call($target, option) }) @@ -741,7 +826,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: dropdown.js v3.2.0 + * Bootstrap: dropdown.js v3.3.0 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -761,7 +846,7 @@ $(element).on('click.bs.dropdown', this.toggle) } - Dropdown.VERSION = '3.2.0' + Dropdown.VERSION = '3.3.0' Dropdown.prototype.toggle = function (e) { var $this = $(this) @@ -784,7 +869,9 @@ if (e.isDefaultPrevented()) return - $this.trigger('focus') + $this + .trigger('focus') + .attr('aria-expanded', 'true') $parent .toggleClass('open') @@ -795,7 +882,7 @@ } Dropdown.prototype.keydown = function (e) { - if (!/(38|40|27)/.test(e.keyCode)) return + if (!/(38|40|27|32)/.test(e.which)) return var $this = $(this) @@ -807,7 +894,7 @@ var $parent = getParent($this) var isActive = $parent.hasClass('open') - if (!isActive || (isActive && e.keyCode == 27)) { + if ((!isActive && e.which != 27) || (isActive && e.which == 27)) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } @@ -817,10 +904,10 @@ if (!$items.length) return - var index = $items.index($items.filter(':focus')) + var index = $items.index(e.target) - if (e.keyCode == 38 && index > 0) index-- // up - if (e.keyCode == 40 && index < $items.length - 1) index++ // down + if (e.which == 38 && index > 0) index-- // up + if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') @@ -830,11 +917,17 @@ if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { - var $parent = getParent($(this)) + var $this = $(this) + var $parent = getParent($this) var relatedTarget = { relatedTarget: this } + if (!$parent.hasClass('open')) return + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) }) } @@ -888,12 +981,14 @@ .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) - .on('keydown.bs.dropdown.data-api', toggle + ', [role="menu"], [role="listbox"]', Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown) }(jQuery); /* ======================================================================== - * Bootstrap: tab.js v3.2.0 + * Bootstrap: tab.js v3.3.0 * http://getbootstrap.com/javascript/#tabs * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -911,7 +1006,9 @@ this.element = $(element) } - Tab.VERSION = '3.2.0' + Tab.VERSION = '3.3.0' + + Tab.TRANSITION_DURATION = 150 Tab.prototype.show = function () { var $this = this.element @@ -925,22 +1022,30 @@ if ($this.parent('li').hasClass('active')) return - var previous = $ul.find('.active:last a')[0] - var e = $.Event('show.bs.tab', { - relatedTarget: previous + var $previous = $ul.find('.active:last a') + var hideEvent = $.Event('hide.bs.tab', { + relatedTarget: $this[0] + }) + var showEvent = $.Event('show.bs.tab', { + relatedTarget: $previous[0] }) - $this.trigger(e) + $previous.trigger(hideEvent) + $this.trigger(showEvent) - if (e.isDefaultPrevented()) return + if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return var $target = $(selector) this.activate($this.closest('li'), $ul) this.activate($target, $target.parent(), function () { + $previous.trigger({ + type: 'hidden.bs.tab', + relatedTarget: $this[0] + }) $this.trigger({ type: 'shown.bs.tab', - relatedTarget: previous + relatedTarget: $previous[0] }) }) } @@ -949,15 +1054,21 @@ var $active = container.find('> .active') var transition = callback && $.support.transition - && $active.hasClass('fade') + && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length) function next() { $active .removeClass('active') .find('> .dropdown-menu > .active') - .removeClass('active') + .removeClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', false) - element.addClass('active') + element + .addClass('active') + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) if (transition) { element[0].offsetWidth // reflow for transition @@ -967,16 +1078,21 @@ } if (element.parent('.dropdown-menu')) { - element.closest('li.dropdown').addClass('active') + element + .closest('li.dropdown') + .addClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) } callback && callback() } - transition ? + $active.length && transition ? $active .one('bsTransitionEnd', next) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') @@ -1014,15 +1130,19 @@ // TAB DATA-API // ============ - $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { + var clickHandler = function (e) { e.preventDefault() Plugin.call($(this), 'show') - }) + } + + $(document) + .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) + .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) }(jQuery); /* ======================================================================== - * Bootstrap: transition.js v3.2.0 + * Bootstrap: transition.js v3.3.0 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -1082,7 +1202,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: scrollspy.js v3.2.0 + * Bootstrap: scrollspy.js v3.3.0 * http://getbootstrap.com/javascript/#scrollspy * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -1113,7 +1233,7 @@ this.process() } - ScrollSpy.VERSION = '3.2.0' + ScrollSpy.VERSION = '3.3.0' ScrollSpy.DEFAULTS = { offset: 10 @@ -1174,8 +1294,9 @@ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } - if (activeTarget && scrollTop <= offsets[0]) { - return activeTarget != (i = targets[0]) && this.activate(i) + if (activeTarget && scrollTop < offsets[0]) { + this.activeTarget = null + return this.clear() } for (i = offsets.length; i--;) { @@ -1189,9 +1310,7 @@ ScrollSpy.prototype.activate = function (target) { this.activeTarget = target - $(this.selector) - .parentsUntil(this.options.target, '.active') - .removeClass('active') + this.clear() var selector = this.selector + '[data-target="' + target + '"],' + @@ -1210,6 +1329,12 @@ active.trigger('activate.bs.scrollspy') } + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + // SCROLLSPY PLUGIN DEFINITION // =========================== @@ -1253,7 +1378,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: modal.js v3.2.0 + * Bootstrap: modal.js v3.3.0 * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -1284,7 +1409,10 @@ } } - Modal.VERSION = '3.2.0' + Modal.VERSION = '3.3.0' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { backdrop: true, @@ -1342,7 +1470,7 @@ .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) - .emulateTransitionEnd(300) : + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } @@ -1358,9 +1486,6 @@ this.isShown = false - this.$body.removeClass('modal-open') - - this.resetScrollbar() this.escape() $(document).off('focusin.bs.modal') @@ -1373,7 +1498,7 @@ $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) - .emulateTransitionEnd(300) : + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } @@ -1389,11 +1514,11 @@ Modal.prototype.escape = function () { if (this.isShown && this.options.keyboard) { - this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { e.which == 27 && this.hide() }, this)) } else if (!this.isShown) { - this.$element.off('keyup.dismiss.bs.modal') + this.$element.off('keydown.dismiss.bs.modal') } } @@ -1401,6 +1526,8 @@ var that = this this.$element.hide() this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } @@ -1418,14 +1545,13 @@ var doAnimate = $.support.transition && animate this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') - .appendTo(this.$body) - - this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { - if (e.target !== e.currentTarget) return - this.options.backdrop == 'static' - ? this.$element[0].focus.call(this.$element[0]) - : this.hide.call(this) - }, this)) + .prependTo(this.$element) + .on('click.dismiss.bs.modal', $.proxy(function (e) { + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus.call(this.$element[0]) + : this.hide.call(this) + }, this)) if (doAnimate) this.$backdrop[0].offsetWidth // force reflow @@ -1436,7 +1562,7 @@ doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { @@ -1449,7 +1575,7 @@ $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { @@ -1458,8 +1584,7 @@ } Modal.prototype.checkScrollbar = function () { - if (document.body.clientWidth >= window.innerWidth) return - this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar() + this.scrollbarWidth = this.measureScrollbar() } Modal.prototype.setScrollbar = function () { @@ -1472,6 +1597,7 @@ } Modal.prototype.measureScrollbar = function () { // thx walsh + if (document.body.clientWidth >= window.innerWidth) return 0 var scrollDiv = document.createElement('div') scrollDiv.className = 'modal-scrollbar-measure' this.$body.append(scrollDiv) @@ -1534,7 +1660,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: tooltip.js v3.2.0 + * Bootstrap: tooltip.js v3.3.0 * http://getbootstrap.com/javascript/#tooltip * Inspired by the original jQuery.tipsy by Jason Frame * ======================================================================== @@ -1560,7 +1686,9 @@ this.init('tooltip', element, options) } - Tooltip.VERSION = '3.2.0' + Tooltip.VERSION = '3.3.0' + + Tooltip.TRANSITION_DURATION = 150 Tooltip.DEFAULTS = { animation: true, @@ -1638,6 +1766,11 @@ var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) + if (self && self.$tip && self.$tip.is(':visible')) { + self.hoverState = 'in' + return + } + if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) @@ -1680,7 +1813,7 @@ if (this.hasContent() && this.enabled) { this.$element.trigger(e) - var inDom = $.contains(document.documentElement, this.$element[0]) + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) if (e.isDefaultPrevented() || !inDom) return var that = this @@ -1716,13 +1849,13 @@ if (autoPlace) { var orgPlacement = placement - var $parent = this.$element.parent() - var parentDim = this.getPosition($parent) + var $container = this.options.container ? $(this.options.container) : this.$element.parent() + var containerDim = this.getPosition($container) - placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' : - placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' : - placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' : - placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' : + placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' : placement $tip @@ -1735,14 +1868,17 @@ this.applyPlacement(calculatedOffset, placement) var complete = function () { + var prevHoverState = that.hoverState that.$element.trigger('shown.bs.' + that.type) that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) } $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } @@ -1789,16 +1925,18 @@ if (delta.left) offset.left += delta.left else offset.top += delta.top - var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight - var arrowPosition = delta.left ? 'left' : 'top' - var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight' + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' $tip.offset(offset) - this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) } - Tooltip.prototype.replaceArrow = function (delta, dimension, position) { - this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '') + Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) { + this.arrow() + .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isHorizontal ? 'top' : 'left', '') } Tooltip.prototype.setContent = function () { @@ -1809,16 +1947,17 @@ $tip.removeClass('fade in top bottom left right') } - Tooltip.prototype.hide = function () { + Tooltip.prototype.hide = function (callback) { var that = this var $tip = this.tip() var e = $.Event('hide.bs.' + this.type) - this.$element.removeAttr('aria-describedby') - function complete() { if (that.hoverState != 'in') $tip.detach() - that.$element.trigger('hidden.bs.' + that.type) + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + callback && callback() } this.$element.trigger(e) @@ -1830,7 +1969,7 @@ $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null @@ -1851,13 +1990,20 @@ Tooltip.prototype.getPosition = function ($element) { $element = $element || this.$element + var el = $element[0] var isBody = el.tagName == 'BODY' - return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, { - scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(), - width: isBody ? $(window).width() : $element.outerWidth(), - height: isBody ? $(window).height() : $element.outerHeight() - }, isBody ? { top: 0, left: 0 } : $element.offset()) + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) } Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { @@ -1921,14 +2067,6 @@ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) } - Tooltip.prototype.validate = function () { - if (!this.$element[0].parentNode) { - this.hide() - this.$element = null - this.options = null - } - } - Tooltip.prototype.enable = function () { this.enabled = true } @@ -1955,8 +2093,11 @@ } Tooltip.prototype.destroy = function () { + var that = this clearTimeout(this.timeout) - this.hide().$element.off('.' + this.type).removeData('bs.' + this.type) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + }) } @@ -1965,12 +2106,18 @@ function Plugin(option) { return this.each(function () { - var $this = $(this) - var data = $this.data('bs.tooltip') - var options = typeof option == 'object' && option + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + var selector = options && options.selector if (!data && option == 'destroy') return - if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (selector) { + if (!data) $this.data('bs.tooltip', (data = {})) + if (!data[selector]) data[selector] = new Tooltip(this, options) + } else { + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + } if (typeof option == 'string') data[option]() }) } @@ -1992,7 +2139,7 @@ }(jQuery); /* ======================================================================== - * Bootstrap: popover.js v3.2.0 + * Bootstrap: popover.js v3.3.0 * http://getbootstrap.com/javascript/#popovers * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -2012,7 +2159,7 @@ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') - Popover.VERSION = '3.2.0' + Popover.VERSION = '3.3.0' Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { placement: 'right', @@ -2039,7 +2186,7 @@ var content = this.getContent() $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) - $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events + $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ](content) @@ -2079,12 +2226,18 @@ function Plugin(option) { return this.each(function () { - var $this = $(this) - var data = $this.data('bs.popover') - var options = typeof option == 'object' && option + var $this = $(this) + var data = $this.data('bs.popover') + var options = typeof option == 'object' && option + var selector = options && options.selector if (!data && option == 'destroy') return - if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + if (selector) { + if (!data) $this.data('bs.popover', (data = {})) + if (!data[selector]) data[selector] = new Popover(this, options) + } else { + if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + } if (typeof option == 'string') data[option]() }) } diff --git a/assets/javascripts/bootstrap/affix.js b/assets/javascripts/bootstrap/affix.js index 7d404eb..e4efa20 100644 --- a/assets/javascripts/bootstrap/affix.js +++ b/assets/javascripts/bootstrap/affix.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: affix.js v3.2.0 + * Bootstrap: affix.js v3.3.0 * http://getbootstrap.com/javascript/#affix * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -28,7 +28,7 @@ this.checkPosition() } - Affix.VERSION = '3.2.0' + Affix.VERSION = '3.3.0' Affix.RESET = 'affix affix-top affix-bottom' @@ -37,6 +37,28 @@ target: window } + Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { + var scrollTop = this.$target.scrollTop() + var position = this.$element.offset() + var targetHeight = this.$target.height() + + if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false + + if (this.affixed == 'bottom') { + if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' + return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' + } + + var initializing = this.affixed == null + var colliderTop = initializing ? scrollTop : position.top + var colliderHeight = initializing ? targetHeight : height + + if (offsetTop != null && colliderTop <= offsetTop) return 'top' + if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' + + return false + } + Affix.prototype.getPinnedOffset = function () { if (this.pinnedOffset) return this.pinnedOffset this.$element.removeClass(Affix.RESET).addClass('affix') @@ -52,42 +74,40 @@ Affix.prototype.checkPosition = function () { if (!this.$element.is(':visible')) return - var scrollHeight = $(document).height() - var scrollTop = this.$target.scrollTop() - var position = this.$element.offset() + var height = this.$element.height() var offset = this.options.offset var offsetTop = offset.top var offsetBottom = offset.bottom + var scrollHeight = $('body').height() if (typeof offset != 'object') offsetBottom = offsetTop = offset if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) - var affix = this.unpin != null && (scrollTop + this.unpin <= position.top) ? false : - offsetBottom != null && (position.top + this.$element.height() >= scrollHeight - offsetBottom) ? 'bottom' : - offsetTop != null && (scrollTop <= offsetTop) ? 'top' : false + var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) - if (this.affixed === affix) return - if (this.unpin != null) this.$element.css('top', '') + if (this.affixed != affix) { + if (this.unpin != null) this.$element.css('top', '') - var affixType = 'affix' + (affix ? '-' + affix : '') - var e = $.Event(affixType + '.bs.affix') + var affixType = 'affix' + (affix ? '-' + affix : '') + var e = $.Event(affixType + '.bs.affix') - this.$element.trigger(e) + this.$element.trigger(e) - if (e.isDefaultPrevented()) return + if (e.isDefaultPrevented()) return - this.affixed = affix - this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null + this.affixed = affix + this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null - this.$element - .removeClass(Affix.RESET) - .addClass(affixType) - .trigger($.Event(affixType.replace('affix', 'affixed'))) + this.$element + .removeClass(Affix.RESET) + .addClass(affixType) + .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') + } if (affix == 'bottom') { this.$element.offset({ - top: scrollHeight - this.$element.height() - offsetBottom + top: scrollHeight - height - offsetBottom }) } } @@ -132,8 +152,8 @@ data.offset = data.offset || {} - if (data.offsetBottom) data.offset.bottom = data.offsetBottom - if (data.offsetTop) data.offset.top = data.offsetTop + if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom + if (data.offsetTop != null) data.offset.top = data.offsetTop Plugin.call($spy, data) }) diff --git a/assets/javascripts/bootstrap/alert.js b/assets/javascripts/bootstrap/alert.js index 0efd92c..d47d85f 100644 --- a/assets/javascripts/bootstrap/alert.js +++ b/assets/javascripts/bootstrap/alert.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: alert.js v3.2.0 + * Bootstrap: alert.js v3.3.0 * http://getbootstrap.com/javascript/#alerts * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -18,7 +18,9 @@ $(el).on('click', dismiss, this.close) } - Alert.VERSION = '3.2.0' + Alert.VERSION = '3.3.0' + + Alert.TRANSITION_DURATION = 150 Alert.prototype.close = function (e) { var $this = $(this) @@ -34,7 +36,7 @@ if (e) e.preventDefault() if (!$parent.length) { - $parent = $this.hasClass('alert') ? $this : $this.parent() + $parent = $this.closest('.alert') } $parent.trigger(e = $.Event('close.bs.alert')) @@ -51,7 +53,7 @@ $.support.transition && $parent.hasClass('fade') ? $parent .one('bsTransitionEnd', removeElement) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Alert.TRANSITION_DURATION) : removeElement() } diff --git a/assets/javascripts/bootstrap/button.js b/assets/javascripts/bootstrap/button.js index dc3164f..2be0234 100644 --- a/assets/javascripts/bootstrap/button.js +++ b/assets/javascripts/bootstrap/button.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: button.js v3.2.0 + * Bootstrap: button.js v3.3.0 * http://getbootstrap.com/javascript/#buttons * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -19,7 +19,7 @@ this.isLoading = false } - Button.VERSION = '3.2.0' + Button.VERSION = '3.3.0' Button.DEFAULTS = { loadingText: 'loading...' @@ -35,10 +35,10 @@ if (data.resetText == null) $el.data('resetText', $el[val]()) - $el[val](data[state] == null ? this.options[state] : data[state]) - // push to event loop to allow forms to submit setTimeout($.proxy(function () { + $el[val](data[state] == null ? this.options[state] : data[state]) + if (state == 'loadingText') { this.isLoading = true $el.addClass(d).attr(d, d) @@ -60,6 +60,8 @@ else $parent.find('.active').removeClass('active') } if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') + } else { + this.$element.attr('aria-pressed', !this.$element.hasClass('active')) } if (changed) this.$element.toggleClass('active') @@ -100,11 +102,15 @@ // BUTTON DATA-API // =============== - $(document).on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - Plugin.call($btn, 'toggle') - e.preventDefault() - }) + $(document) + .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + Plugin.call($btn, 'toggle') + e.preventDefault() + }) + .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { + $(e.target).closest('.btn').toggleClass('focus', e.type == 'focus') + }) }(jQuery); diff --git a/assets/javascripts/bootstrap/carousel.js b/assets/javascripts/bootstrap/carousel.js index b7da1ba..4e4e477 100644 --- a/assets/javascripts/bootstrap/carousel.js +++ b/assets/javascripts/bootstrap/carousel.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: carousel.js v3.2.0 + * Bootstrap: carousel.js v3.3.0 * http://getbootstrap.com/javascript/#carousel * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -14,7 +14,7 @@ // ========================= var Carousel = function (element, options) { - this.$element = $(element).on('keydown.bs.carousel', $.proxy(this.keydown, this)) + this.$element = $(element) this.$indicators = this.$element.find('.carousel-indicators') this.options = options this.paused = @@ -23,17 +23,22 @@ this.$active = this.$items = null - this.options.pause == 'hover' && this.$element + this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) + + this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) } - Carousel.VERSION = '3.2.0' + Carousel.VERSION = '3.3.0' + + Carousel.TRANSITION_DURATION = 600 Carousel.DEFAULTS = { interval: 5000, pause: 'hover', - wrap: true + wrap: true, + keyboard: true } Carousel.prototype.keydown = function (e) { @@ -63,6 +68,13 @@ return this.$items.index(item || this.$active) } + Carousel.prototype.getItemForDirection = function (direction, active) { + var delta = direction == 'prev' ? -1 : 1 + var activeIndex = this.getItemIndex(active) + var itemIndex = (activeIndex + delta) % this.$items.length + return this.$items.eq(itemIndex) + } + Carousel.prototype.to = function (pos) { var that = this var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) @@ -72,7 +84,7 @@ if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" if (activeIndex == pos) return this.pause().cycle() - return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) + return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) } Carousel.prototype.pause = function (e) { @@ -100,7 +112,7 @@ Carousel.prototype.slide = function (type, next) { var $active = this.$element.find('.item.active') - var $next = next || $active[type]() + var $next = next || this.getItemForDirection(type, $active) var isCycling = this.interval var direction = type == 'next' ? 'left' : 'right' var fallback = type == 'next' ? 'first' : 'last' @@ -146,7 +158,7 @@ that.$element.trigger(slidEvent) }, 0) }) - .emulateTransitionEnd($active.css('transition-duration').slice(0, -1) * 1000) + .emulateTransitionEnd(Carousel.TRANSITION_DURATION) } else { $active.removeClass('active') $next.addClass('active') @@ -195,7 +207,7 @@ // CAROUSEL DATA-API // ================= - $(document).on('click.bs.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var clickHandler = function (e) { var href var $this = $(this) var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 @@ -211,7 +223,11 @@ } e.preventDefault() - }) + } + + $(document) + .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) + .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) $(window).on('load', function () { $('[data-ride="carousel"]').each(function () { diff --git a/assets/javascripts/bootstrap/collapse.js b/assets/javascripts/bootstrap/collapse.js index e4e6d79..4f85964 100644 --- a/assets/javascripts/bootstrap/collapse.js +++ b/assets/javascripts/bootstrap/collapse.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: collapse.js v3.2.0 + * Bootstrap: collapse.js v3.3.0 * http://getbootstrap.com/javascript/#collapse * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -16,16 +16,25 @@ var Collapse = function (element, options) { this.$element = $(element) this.options = $.extend({}, Collapse.DEFAULTS, options) + this.$trigger = $(this.options.trigger).filter('[href="#' + element.id + '"], [data-target="#' + element.id + '"]') this.transitioning = null - if (this.options.parent) this.$parent = $(this.options.parent) + if (this.options.parent) { + this.$parent = this.getParent() + } else { + this.addAriaAndCollapsedClass(this.$element, this.$trigger) + } + if (this.options.toggle) this.toggle() } - Collapse.VERSION = '3.2.0' + Collapse.VERSION = '3.3.0' + + Collapse.TRANSITION_DURATION = 350 Collapse.DEFAULTS = { - toggle: true + toggle: true, + trigger: '[data-toggle="collapse"]' } Collapse.prototype.dimension = function () { @@ -36,17 +45,21 @@ Collapse.prototype.show = function () { if (this.transitioning || this.$element.hasClass('in')) return + var activesData + var actives = this.$parent && this.$parent.find('> .panel').children('.in, .collapsing') + + if (actives && actives.length) { + activesData = actives.data('bs.collapse') + if (activesData && activesData.transitioning) return + } + var startEvent = $.Event('show.bs.collapse') this.$element.trigger(startEvent) if (startEvent.isDefaultPrevented()) return - var actives = this.$parent && this.$parent.find('> .panel > .in') - if (actives && actives.length) { - var hasData = actives.data('bs.collapse') - if (hasData && hasData.transitioning) return Plugin.call(actives, 'hide') - hasData || actives.data('bs.collapse', null) + activesData || actives.data('bs.collapse', null) } var dimension = this.dimension() @@ -54,6 +67,11 @@ this.$element .removeClass('collapse') .addClass('collapsing')[dimension](0) + .attr('aria-expanded', true) + + this.$trigger + .removeClass('collapsed') + .attr('aria-expanded', true) this.transitioning = 1 @@ -72,7 +90,7 @@ this.$element .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize]) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) } Collapse.prototype.hide = function () { @@ -88,17 +106,21 @@ this.$element .addClass('collapsing') - .removeClass('collapse') - .removeClass('in') + .removeClass('collapse in') + .attr('aria-expanded', false) + + this.$trigger + .addClass('collapsed') + .attr('aria-expanded', false) this.transitioning = 1 var complete = function () { this.transitioning = 0 this.$element - .trigger('hidden.bs.collapse') .removeClass('collapsing') .addClass('collapse') + .trigger('hidden.bs.collapse') } if (!$.support.transition) return complete.call(this) @@ -106,13 +128,40 @@ this.$element [dimension](0) .one('bsTransitionEnd', $.proxy(complete, this)) - .emulateTransitionEnd(350) + .emulateTransitionEnd(Collapse.TRANSITION_DURATION) } Collapse.prototype.toggle = function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() } + Collapse.prototype.getParent = function () { + return $(this.options.parent) + .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') + .each($.proxy(function (i, element) { + var $element = $(element) + this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) + }, this)) + .end() + } + + Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { + var isOpen = $element.hasClass('in') + + $element.attr('aria-expanded', isOpen) + $trigger + .toggleClass('collapsed', !isOpen) + .attr('aria-expanded', isOpen) + } + + function getTargetFromTrigger($trigger) { + var href + var target = $trigger.attr('data-target') + || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 + + return $(target) + } + // COLLAPSE PLUGIN DEFINITION // ========================== @@ -123,7 +172,7 @@ var data = $this.data('bs.collapse') var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) - if (!data && options.toggle && option == 'show') option = !option + if (!data && options.toggle && option == 'show') options.toggle = false if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) @@ -148,21 +197,13 @@ // ================= $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { - var href var $this = $(this) - var target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 - var $target = $(target) - var data = $target.data('bs.collapse') - var option = data ? 'toggle' : $this.data() - var parent = $this.attr('data-parent') - var $parent = parent && $(parent) - if (!data || !data.transitioning) { - if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed') - $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed') - } + if (!$this.attr('data-target')) e.preventDefault() + + var $target = getTargetFromTrigger($this) + var data = $target.data('bs.collapse') + var option = data ? 'toggle' : $.extend({}, $this.data(), { trigger: this }) Plugin.call($target, option) }) diff --git a/assets/javascripts/bootstrap/dropdown.js b/assets/javascripts/bootstrap/dropdown.js index 88f118c..ae192e5 100644 --- a/assets/javascripts/bootstrap/dropdown.js +++ b/assets/javascripts/bootstrap/dropdown.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: dropdown.js v3.2.0 + * Bootstrap: dropdown.js v3.3.0 * http://getbootstrap.com/javascript/#dropdowns * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -19,7 +19,7 @@ $(element).on('click.bs.dropdown', this.toggle) } - Dropdown.VERSION = '3.2.0' + Dropdown.VERSION = '3.3.0' Dropdown.prototype.toggle = function (e) { var $this = $(this) @@ -42,7 +42,9 @@ if (e.isDefaultPrevented()) return - $this.trigger('focus') + $this + .trigger('focus') + .attr('aria-expanded', 'true') $parent .toggleClass('open') @@ -53,7 +55,7 @@ } Dropdown.prototype.keydown = function (e) { - if (!/(38|40|27)/.test(e.keyCode)) return + if (!/(38|40|27|32)/.test(e.which)) return var $this = $(this) @@ -65,7 +67,7 @@ var $parent = getParent($this) var isActive = $parent.hasClass('open') - if (!isActive || (isActive && e.keyCode == 27)) { + if ((!isActive && e.which != 27) || (isActive && e.which == 27)) { if (e.which == 27) $parent.find(toggle).trigger('focus') return $this.trigger('click') } @@ -75,10 +77,10 @@ if (!$items.length) return - var index = $items.index($items.filter(':focus')) + var index = $items.index(e.target) - if (e.keyCode == 38 && index > 0) index-- // up - if (e.keyCode == 40 && index < $items.length - 1) index++ // down + if (e.which == 38 && index > 0) index-- // up + if (e.which == 40 && index < $items.length - 1) index++ // down if (!~index) index = 0 $items.eq(index).trigger('focus') @@ -88,11 +90,17 @@ if (e && e.which === 3) return $(backdrop).remove() $(toggle).each(function () { - var $parent = getParent($(this)) + var $this = $(this) + var $parent = getParent($this) var relatedTarget = { relatedTarget: this } + if (!$parent.hasClass('open')) return + $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) + if (e.isDefaultPrevented()) return + + $this.attr('aria-expanded', 'false') $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) }) } @@ -146,6 +154,8 @@ .on('click.bs.dropdown.data-api', clearMenus) .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) - .on('keydown.bs.dropdown.data-api', toggle + ', [role="menu"], [role="listbox"]', Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown) + .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown) }(jQuery); diff --git a/assets/javascripts/bootstrap/modal.js b/assets/javascripts/bootstrap/modal.js index 29eedf1..8b0e269 100644 --- a/assets/javascripts/bootstrap/modal.js +++ b/assets/javascripts/bootstrap/modal.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: modal.js v3.2.0 + * Bootstrap: modal.js v3.3.0 * http://getbootstrap.com/javascript/#modals * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -30,7 +30,10 @@ } } - Modal.VERSION = '3.2.0' + Modal.VERSION = '3.3.0' + + Modal.TRANSITION_DURATION = 300 + Modal.BACKDROP_TRANSITION_DURATION = 150 Modal.DEFAULTS = { backdrop: true, @@ -88,7 +91,7 @@ .one('bsTransitionEnd', function () { that.$element.trigger('focus').trigger(e) }) - .emulateTransitionEnd(300) : + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : that.$element.trigger('focus').trigger(e) }) } @@ -104,9 +107,6 @@ this.isShown = false - this.$body.removeClass('modal-open') - - this.resetScrollbar() this.escape() $(document).off('focusin.bs.modal') @@ -119,7 +119,7 @@ $.support.transition && this.$element.hasClass('fade') ? this.$element .one('bsTransitionEnd', $.proxy(this.hideModal, this)) - .emulateTransitionEnd(300) : + .emulateTransitionEnd(Modal.TRANSITION_DURATION) : this.hideModal() } @@ -135,11 +135,11 @@ Modal.prototype.escape = function () { if (this.isShown && this.options.keyboard) { - this.$element.on('keyup.dismiss.bs.modal', $.proxy(function (e) { + this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { e.which == 27 && this.hide() }, this)) } else if (!this.isShown) { - this.$element.off('keyup.dismiss.bs.modal') + this.$element.off('keydown.dismiss.bs.modal') } } @@ -147,6 +147,8 @@ var that = this this.$element.hide() this.backdrop(function () { + that.$body.removeClass('modal-open') + that.resetScrollbar() that.$element.trigger('hidden.bs.modal') }) } @@ -164,14 +166,13 @@ var doAnimate = $.support.transition && animate this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') - .appendTo(this.$body) - - this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { - if (e.target !== e.currentTarget) return - this.options.backdrop == 'static' - ? this.$element[0].focus.call(this.$element[0]) - : this.hide.call(this) - }, this)) + .prependTo(this.$element) + .on('click.dismiss.bs.modal', $.proxy(function (e) { + if (e.target !== e.currentTarget) return + this.options.backdrop == 'static' + ? this.$element[0].focus.call(this.$element[0]) + : this.hide.call(this) + }, this)) if (doAnimate) this.$backdrop[0].offsetWidth // force reflow @@ -182,7 +183,7 @@ doAnimate ? this.$backdrop .one('bsTransitionEnd', callback) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callback() } else if (!this.isShown && this.$backdrop) { @@ -195,7 +196,7 @@ $.support.transition && this.$element.hasClass('fade') ? this.$backdrop .one('bsTransitionEnd', callbackRemove) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : callbackRemove() } else if (callback) { @@ -204,8 +205,7 @@ } Modal.prototype.checkScrollbar = function () { - if (document.body.clientWidth >= window.innerWidth) return - this.scrollbarWidth = this.scrollbarWidth || this.measureScrollbar() + this.scrollbarWidth = this.measureScrollbar() } Modal.prototype.setScrollbar = function () { @@ -218,6 +218,7 @@ } Modal.prototype.measureScrollbar = function () { // thx walsh + if (document.body.clientWidth >= window.innerWidth) return 0 var scrollDiv = document.createElement('div') scrollDiv.className = 'modal-scrollbar-measure' this.$body.append(scrollDiv) diff --git a/assets/javascripts/bootstrap/popover.js b/assets/javascripts/bootstrap/popover.js index 825e1b3..3548059 100644 --- a/assets/javascripts/bootstrap/popover.js +++ b/assets/javascripts/bootstrap/popover.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: popover.js v3.2.0 + * Bootstrap: popover.js v3.3.0 * http://getbootstrap.com/javascript/#popovers * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -19,7 +19,7 @@ if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') - Popover.VERSION = '3.2.0' + Popover.VERSION = '3.3.0' Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { placement: 'right', @@ -46,7 +46,7 @@ var content = this.getContent() $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) - $tip.find('.popover-content').empty()[ // we use append for html objects to maintain js events + $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' ](content) @@ -86,12 +86,18 @@ function Plugin(option) { return this.each(function () { - var $this = $(this) - var data = $this.data('bs.popover') - var options = typeof option == 'object' && option + var $this = $(this) + var data = $this.data('bs.popover') + var options = typeof option == 'object' && option + var selector = options && options.selector if (!data && option == 'destroy') return - if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + if (selector) { + if (!data) $this.data('bs.popover', (data = {})) + if (!data[selector]) data[selector] = new Popover(this, options) + } else { + if (!data) $this.data('bs.popover', (data = new Popover(this, options))) + } if (typeof option == 'string') data[option]() }) } diff --git a/assets/javascripts/bootstrap/scrollspy.js b/assets/javascripts/bootstrap/scrollspy.js index db23787..973021a 100644 --- a/assets/javascripts/bootstrap/scrollspy.js +++ b/assets/javascripts/bootstrap/scrollspy.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: scrollspy.js v3.2.0 + * Bootstrap: scrollspy.js v3.3.0 * http://getbootstrap.com/javascript/#scrollspy * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -30,7 +30,7 @@ this.process() } - ScrollSpy.VERSION = '3.2.0' + ScrollSpy.VERSION = '3.3.0' ScrollSpy.DEFAULTS = { offset: 10 @@ -91,8 +91,9 @@ return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) } - if (activeTarget && scrollTop <= offsets[0]) { - return activeTarget != (i = targets[0]) && this.activate(i) + if (activeTarget && scrollTop < offsets[0]) { + this.activeTarget = null + return this.clear() } for (i = offsets.length; i--;) { @@ -106,9 +107,7 @@ ScrollSpy.prototype.activate = function (target) { this.activeTarget = target - $(this.selector) - .parentsUntil(this.options.target, '.active') - .removeClass('active') + this.clear() var selector = this.selector + '[data-target="' + target + '"],' + @@ -127,6 +126,12 @@ active.trigger('activate.bs.scrollspy') } + ScrollSpy.prototype.clear = function () { + $(this.selector) + .parentsUntil(this.options.target, '.active') + .removeClass('active') + } + // SCROLLSPY PLUGIN DEFINITION // =========================== diff --git a/assets/javascripts/bootstrap/tab.js b/assets/javascripts/bootstrap/tab.js index c0e1e46..7a68169 100644 --- a/assets/javascripts/bootstrap/tab.js +++ b/assets/javascripts/bootstrap/tab.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: tab.js v3.2.0 + * Bootstrap: tab.js v3.3.0 * http://getbootstrap.com/javascript/#tabs * ======================================================================== * Copyright 2011-2014 Twitter, Inc. @@ -17,7 +17,9 @@ this.element = $(element) } - Tab.VERSION = '3.2.0' + Tab.VERSION = '3.3.0' + + Tab.TRANSITION_DURATION = 150 Tab.prototype.show = function () { var $this = this.element @@ -31,22 +33,30 @@ if ($this.parent('li').hasClass('active')) return - var previous = $ul.find('.active:last a')[0] - var e = $.Event('show.bs.tab', { - relatedTarget: previous + var $previous = $ul.find('.active:last a') + var hideEvent = $.Event('hide.bs.tab', { + relatedTarget: $this[0] + }) + var showEvent = $.Event('show.bs.tab', { + relatedTarget: $previous[0] }) - $this.trigger(e) + $previous.trigger(hideEvent) + $this.trigger(showEvent) - if (e.isDefaultPrevented()) return + if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return var $target = $(selector) this.activate($this.closest('li'), $ul) this.activate($target, $target.parent(), function () { + $previous.trigger({ + type: 'hidden.bs.tab', + relatedTarget: $this[0] + }) $this.trigger({ type: 'shown.bs.tab', - relatedTarget: previous + relatedTarget: $previous[0] }) }) } @@ -55,15 +65,21 @@ var $active = container.find('> .active') var transition = callback && $.support.transition - && $active.hasClass('fade') + && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length) function next() { $active .removeClass('active') .find('> .dropdown-menu > .active') - .removeClass('active') + .removeClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', false) - element.addClass('active') + element + .addClass('active') + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) if (transition) { element[0].offsetWidth // reflow for transition @@ -73,16 +89,21 @@ } if (element.parent('.dropdown-menu')) { - element.closest('li.dropdown').addClass('active') + element + .closest('li.dropdown') + .addClass('active') + .end() + .find('[data-toggle="tab"]') + .attr('aria-expanded', true) } callback && callback() } - transition ? + $active.length && transition ? $active .one('bsTransitionEnd', next) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tab.TRANSITION_DURATION) : next() $active.removeClass('in') @@ -120,9 +141,13 @@ // TAB DATA-API // ============ - $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) { + var clickHandler = function (e) { e.preventDefault() Plugin.call($(this), 'show') - }) + } + + $(document) + .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) + .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) }(jQuery); diff --git a/assets/javascripts/bootstrap/tooltip.js b/assets/javascripts/bootstrap/tooltip.js index 9cdb6c9..68163c1 100644 --- a/assets/javascripts/bootstrap/tooltip.js +++ b/assets/javascripts/bootstrap/tooltip.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: tooltip.js v3.2.0 + * Bootstrap: tooltip.js v3.3.0 * http://getbootstrap.com/javascript/#tooltip * Inspired by the original jQuery.tipsy by Jason Frame * ======================================================================== @@ -25,7 +25,9 @@ this.init('tooltip', element, options) } - Tooltip.VERSION = '3.2.0' + Tooltip.VERSION = '3.3.0' + + Tooltip.TRANSITION_DURATION = 150 Tooltip.DEFAULTS = { animation: true, @@ -103,6 +105,11 @@ var self = obj instanceof this.constructor ? obj : $(obj.currentTarget).data('bs.' + this.type) + if (self && self.$tip && self.$tip.is(':visible')) { + self.hoverState = 'in' + return + } + if (!self) { self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) $(obj.currentTarget).data('bs.' + this.type, self) @@ -145,7 +152,7 @@ if (this.hasContent() && this.enabled) { this.$element.trigger(e) - var inDom = $.contains(document.documentElement, this.$element[0]) + var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) if (e.isDefaultPrevented() || !inDom) return var that = this @@ -181,13 +188,13 @@ if (autoPlace) { var orgPlacement = placement - var $parent = this.$element.parent() - var parentDim = this.getPosition($parent) + var $container = this.options.container ? $(this.options.container) : this.$element.parent() + var containerDim = this.getPosition($container) - placement = placement == 'bottom' && pos.top + pos.height + actualHeight - parentDim.scroll > parentDim.height ? 'top' : - placement == 'top' && pos.top - parentDim.scroll - actualHeight < 0 ? 'bottom' : - placement == 'right' && pos.right + actualWidth > parentDim.width ? 'left' : - placement == 'left' && pos.left - actualWidth < parentDim.left ? 'right' : + placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' : + placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' : + placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' : + placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' : placement $tip @@ -200,14 +207,17 @@ this.applyPlacement(calculatedOffset, placement) var complete = function () { + var prevHoverState = that.hoverState that.$element.trigger('shown.bs.' + that.type) that.hoverState = null + + if (prevHoverState == 'out') that.leave(that) } $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() } } @@ -254,16 +264,18 @@ if (delta.left) offset.left += delta.left else offset.top += delta.top - var arrowDelta = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight - var arrowPosition = delta.left ? 'left' : 'top' - var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight' + var isVertical = /top|bottom/.test(placement) + var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight + var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' $tip.offset(offset) - this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition) + this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) } - Tooltip.prototype.replaceArrow = function (delta, dimension, position) { - this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '') + Tooltip.prototype.replaceArrow = function (delta, dimension, isHorizontal) { + this.arrow() + .css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') + .css(isHorizontal ? 'top' : 'left', '') } Tooltip.prototype.setContent = function () { @@ -274,16 +286,17 @@ $tip.removeClass('fade in top bottom left right') } - Tooltip.prototype.hide = function () { + Tooltip.prototype.hide = function (callback) { var that = this var $tip = this.tip() var e = $.Event('hide.bs.' + this.type) - this.$element.removeAttr('aria-describedby') - function complete() { if (that.hoverState != 'in') $tip.detach() - that.$element.trigger('hidden.bs.' + that.type) + that.$element + .removeAttr('aria-describedby') + .trigger('hidden.bs.' + that.type) + callback && callback() } this.$element.trigger(e) @@ -295,7 +308,7 @@ $.support.transition && this.$tip.hasClass('fade') ? $tip .one('bsTransitionEnd', complete) - .emulateTransitionEnd(150) : + .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : complete() this.hoverState = null @@ -316,13 +329,20 @@ Tooltip.prototype.getPosition = function ($element) { $element = $element || this.$element + var el = $element[0] var isBody = el.tagName == 'BODY' - return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, { - scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(), - width: isBody ? $(window).width() : $element.outerWidth(), - height: isBody ? $(window).height() : $element.outerHeight() - }, isBody ? { top: 0, left: 0 } : $element.offset()) + + var elRect = el.getBoundingClientRect() + if (elRect.width == null) { + // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 + elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) + } + var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() + var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } + var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null + + return $.extend({}, elRect, scroll, outerDims, elOffset) } Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { @@ -386,14 +406,6 @@ return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) } - Tooltip.prototype.validate = function () { - if (!this.$element[0].parentNode) { - this.hide() - this.$element = null - this.options = null - } - } - Tooltip.prototype.enable = function () { this.enabled = true } @@ -420,8 +432,11 @@ } Tooltip.prototype.destroy = function () { + var that = this clearTimeout(this.timeout) - this.hide().$element.off('.' + this.type).removeData('bs.' + this.type) + this.hide(function () { + that.$element.off('.' + that.type).removeData('bs.' + that.type) + }) } @@ -430,12 +445,18 @@ function Plugin(option) { return this.each(function () { - var $this = $(this) - var data = $this.data('bs.tooltip') - var options = typeof option == 'object' && option + var $this = $(this) + var data = $this.data('bs.tooltip') + var options = typeof option == 'object' && option + var selector = options && options.selector if (!data && option == 'destroy') return - if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + if (selector) { + if (!data) $this.data('bs.tooltip', (data = {})) + if (!data[selector]) data[selector] = new Tooltip(this, options) + } else { + if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) + } if (typeof option == 'string') data[option]() }) } diff --git a/assets/javascripts/bootstrap/transition.js b/assets/javascripts/bootstrap/transition.js index 83f85bf..8199383 100644 --- a/assets/javascripts/bootstrap/transition.js +++ b/assets/javascripts/bootstrap/transition.js @@ -1,5 +1,5 @@ /* ======================================================================== - * Bootstrap: transition.js v3.2.0 + * Bootstrap: transition.js v3.3.0 * http://getbootstrap.com/javascript/#transitions * ======================================================================== * Copyright 2011-2014 Twitter, Inc. diff --git a/assets/stylesheets/bootstrap/_button-groups.scss b/assets/stylesheets/bootstrap/_button-groups.scss index 63ccd92..9bfa085 100644 --- a/assets/stylesheets/bootstrap/_button-groups.scss +++ b/assets/stylesheets/bootstrap/_button-groups.scss @@ -38,7 +38,7 @@ // Optional: Group multiple button groups together for a toolbar .btn-toolbar { margin-left: -5px; // Offset the first child's margin - @include clearfix(); + @include clearfix; .btn-group, .input-group { @@ -156,7 +156,7 @@ // Clear floats so dropdown menus can be properly placed > .btn-group { - @include clearfix(); + @include clearfix; > .btn { float: none; } @@ -198,7 +198,6 @@ } - // Justified button groups // ---------------------- @@ -226,15 +225,23 @@ // Checkbox and radio options // // In order to support the browser's form validation feedback, powered by the -// `required` attribute, we have to "hide" the inputs via `opacity`. We cannot -// use `display: none;` or `visibility: hidden;` as that also hides the popover. +// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use +// `display: none;` or `visibility: hidden;` as that also hides the popover. +// Simply visually hiding the inputs via `opacity` would leave them clickable in +// certain cases which is prevented by using `clip` and `pointer-events`. // This way, we ensure a DOM element is visible to position the popover from. // -// See https://github.com/twbs/bootstrap/pull/12794 for more. +// See https://github.com/twbs/bootstrap/pull/12794 and +// https://github.com/twbs/bootstrap/pull/14559 for more information. -[data-toggle="buttons"] > .btn > input[type="radio"], -[data-toggle="buttons"] > .btn > input[type="checkbox"] { - position: absolute; - z-index: -1; - @include opacity(0); +[data-toggle="buttons"] { + > .btn, + > .btn-group > .btn { + input[type="radio"], + input[type="checkbox"] { + position: absolute; + clip: rect(0,0,0,0); + pointer-events: none; + } + } } diff --git a/assets/stylesheets/bootstrap/_buttons.scss b/assets/stylesheets/bootstrap/_buttons.scss index dae6ee3..37bf259 100644 --- a/assets/stylesheets/bootstrap/_buttons.scss +++ b/assets/stylesheets/bootstrap/_buttons.scss @@ -12,6 +12,7 @@ font-weight: $btn-font-weight; text-align: center; vertical-align: middle; + touch-action: manipulation; cursor: pointer; background-image: none; // Reset unusual Firefox-on-Android default style; see https://github.com/necolas/normalize.css/issues/214 border: 1px solid transparent; @@ -22,13 +23,15 @@ &, &:active, &.active { - &:focus { - @include tab-focus(); + &:focus, + &.focus { + @include tab-focus; } } &:hover, - &:focus { + &:focus, + &.focus { color: $btn-default-color; text-decoration: none; } @@ -43,7 +46,7 @@ &.disabled, &[disabled], fieldset[disabled] & { - cursor: not-allowed; + cursor: $cursor-disabled; pointer-events: none; // Future-proof disabling of clicks @include opacity(.65); @include box-shadow(none); @@ -85,11 +88,11 @@ .btn-link { color: $link-color; font-weight: normal; - cursor: pointer; border-radius: 0; &, &:active, + &.active, &[disabled], fieldset[disabled] & { background-color: transparent; diff --git a/assets/stylesheets/bootstrap/_carousel.scss b/assets/stylesheets/bootstrap/_carousel.scss index e9e2f7c..49db83f 100644 --- a/assets/stylesheets/bootstrap/_carousel.scss +++ b/assets/stylesheets/bootstrap/_carousel.scss @@ -21,9 +21,33 @@ // Account for jankitude on images > img, > a > img { - @include img-responsive(); + @include img-responsive; line-height: 1; } + + // WebKit CSS3 transforms for supported devices + @media all and (transform-3d), (-webkit-transform-3d) { + transition: transform .6s ease-in-out; + backface-visibility: hidden; + perspective: 1000; + + &.next, + &.active.right { + transform: translate3d(100%, 0, 0); + left: 0; + } + &.prev, + &.active.left { + transform: translate3d(-100%, 0, 0); + left: 0; + } + &.next.left, + &.prev.right, + &.active { + transform: translate3d(0, 0, 0); + left: 0; + } + } } > .active, diff --git a/assets/stylesheets/bootstrap/_code.scss b/assets/stylesheets/bootstrap/_code.scss index 8d83b14..caa5f06 100644 --- a/assets/stylesheets/bootstrap/_code.scss +++ b/assets/stylesheets/bootstrap/_code.scss @@ -32,6 +32,7 @@ kbd { kbd { padding: 0; font-size: 100%; + font-weight: bold; box-shadow: none; } } diff --git a/assets/stylesheets/bootstrap/_component-animations.scss b/assets/stylesheets/bootstrap/_component-animations.scss index 8c3fd07..1f76b8c 100644 --- a/assets/stylesheets/bootstrap/_component-animations.scss +++ b/assets/stylesheets/bootstrap/_component-animations.scss @@ -17,8 +17,9 @@ .collapse { display: none; + visibility: hidden; - &.in { display: block; } + &.in { display: block; visibility: visible; } // [converter] extracted tr&.in to tr.collapse.in // [converter] extracted tbody&.in to tbody.collapse.in } @@ -31,5 +32,7 @@ tbody.collapse.in { display: table-row-group; } position: relative; height: 0; overflow: hidden; - @include transition(height .35s ease); + @include transition-property(height, visibility); + @include transition-duration(.35s); + @include transition-timing-function(ease); } diff --git a/assets/stylesheets/bootstrap/_dropdowns.scss b/assets/stylesheets/bootstrap/_dropdowns.scss index eb74be5..c7256e1 100644 --- a/assets/stylesheets/bootstrap/_dropdowns.scss +++ b/assets/stylesheets/bootstrap/_dropdowns.scss @@ -103,16 +103,15 @@ &:focus { color: $dropdown-link-disabled-color; } -} -// Nuke hover/focus effects -.dropdown-menu > .disabled > a { + + // Nuke hover/focus effects &:hover, &:focus { text-decoration: none; background-color: transparent; background-image: none; // Remove CSS gradient - @include reset-filter(); - cursor: not-allowed; + @include reset-filter; + cursor: $cursor-disabled; } } @@ -212,4 +211,3 @@ } } } - diff --git a/assets/stylesheets/bootstrap/_forms.scss b/assets/stylesheets/bootstrap/_forms.scss index 74bb67d..4e694b3 100644 --- a/assets/stylesheets/bootstrap/_forms.scss +++ b/assets/stylesheets/bootstrap/_forms.scss @@ -77,7 +77,7 @@ select[size] { input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { - @include tab-focus(); + @include tab-focus; } // Adjust output element @@ -128,10 +128,10 @@ output { @include transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); // Customize the `:focus` state to imitate native WebKit styles. - @include form-control-focus(); + @include form-control-focus; // Placeholder - @include placeholder(); + @include placeholder; // Disabled and read-only inputs // @@ -141,7 +141,7 @@ output { &[disabled], &[readonly], fieldset[disabled] & { - cursor: not-allowed; + cursor: $cursor-disabled; background-color: $input-bg-disabled; opacity: 1; // iOS fix for unreadable disabled content } @@ -185,9 +185,26 @@ input[type="month"] { &.input-sm { line-height: $input-height-small; + line-height: $line-height-small #{\0}; } &.input-lg { line-height: $input-height-large; + line-height: $line-height-large #{\0}; + } +} + +// IE 11 hack to reverse the iOS temporal input hack. +_:-ms-fullscreen, :root input[type="date"], +_:-ms-fullscreen, :root input[type="time"], +_:-ms-fullscreen, :root input[type="datetime-local"], +_:-ms-fullscreen, :root input[type="month"] { + line-height: $line-height-base; + + &.input-sm { + line-height: $line-height-small; + } + &.input-lg { + line-height: $line-height-large; } } @@ -210,11 +227,11 @@ input[type="month"] { .checkbox { position: relative; display: block; - min-height: $line-height-computed; // clear the floating input if there is no label text margin-top: 10px; margin-bottom: 10px; label { + min-height: $line-height-computed; // Ensure the input doesn't jump when there is no text padding-left: 20px; margin-bottom: 0; font-weight: normal; @@ -260,7 +277,7 @@ input[type="checkbox"] { &[disabled], &.disabled, fieldset[disabled] & { - cursor: not-allowed; + cursor: $cursor-disabled; } } // These classes are used directly on <label>s @@ -268,7 +285,7 @@ input[type="checkbox"] { .checkbox-inline { &.disabled, fieldset[disabled] & { - cursor: not-allowed; + cursor: $cursor-disabled; } } // These classes are used on elements with <label> descendants @@ -277,7 +294,7 @@ input[type="checkbox"] { &.disabled, fieldset[disabled] & { label { - cursor: not-allowed; + cursor: $cursor-disabled; } } } @@ -308,9 +325,9 @@ input[type="checkbox"] { // Build on `.form-control` with modifier classes to decrease or increase the // height and font-size of form controls. -@include input-size('.input-sm', $input-height-small, $padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $border-radius-small); +@include input-size('.input-sm, .form-group-sm .form-control', $input-height-small, $padding-small-vertical, $padding-small-horizontal, $font-size-small, $line-height-small, $input-border-radius-small); -@include input-size('.input-lg', $input-height-large, $padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $border-radius-large); +@include input-size('.input-lg, .form-group-lg .form-control', $input-height-large, $padding-large-vertical, $padding-large-horizontal, $font-size-large, $line-height-large, $input-border-radius-large); // Form control feedback states @@ -329,7 +346,7 @@ input[type="checkbox"] { // Feedback icon (requires .glyphicon classes) .form-control-feedback { position: absolute; - top: ($line-height-computed + 5); // Height of the `label` and its margin + top: 0; right: 0; z-index: 2; // Ensure icon is above input groups display: block; @@ -337,6 +354,7 @@ input[type="checkbox"] { height: $input-height-base; line-height: $input-height-base; text-align: center; + pointer-events: none; } .input-lg + .form-control-feedback { width: $input-height-large; @@ -360,10 +378,15 @@ input[type="checkbox"] { @include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg); } +// Reposition feedback icon if input has visible label above +.has-feedback label { -// Reposition feedback icon if label is hidden with "screenreader only" state -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; + & ~ .form-control-feedback { + top: ($line-height-computed + 5); // Height of the `label` and its margin + } + &.sr-only ~ .form-control-feedback { + top: 0; + } } @@ -380,7 +403,6 @@ input[type="checkbox"] { } - // Inline forms // // Make forms appear inline(-block) by adding the `.form-inline` class. Inline @@ -392,7 +414,8 @@ input[type="checkbox"] { // // Heads up! This is mixin-ed into `.navbar-form` in navbars.less. -.form-inline { +// [converter] extracted from `.form-inline` for libsass compatibility +@mixin form-inline { // Kick in the inline @media (min-width: $screen-sm-min) { @@ -410,6 +433,11 @@ input[type="checkbox"] { vertical-align: middle; } + // Make static controls behave like regular ones + .form-control-static { + display: inline-block; + } + .input-group { display: inline-table; vertical-align: middle; @@ -451,15 +479,17 @@ input[type="checkbox"] { margin-left: 0; } - // Validation states - // - // Reposition the icon because it's now within a grid column and columns have - // `position: relative;` on them. Also accounts for the grid gutter padding. + // Re-override the feedback icon. .has-feedback .form-control-feedback { top: 0; } } } +// [converter] extracted as `@mixin form-inline` for libsass compatibility +.form-inline { + @include form-inline; +} + // Horizontal forms @@ -489,7 +519,7 @@ input[type="checkbox"] { // Make form groups behave like rows .form-group { - @include make-row(); + @include make-row; } // Reset spacing and right align labels, but scope to media queries so that @@ -507,7 +537,6 @@ input[type="checkbox"] { // Reposition the icon because it's now within a grid column and columns have // `position: relative;` on them. Also accounts for the grid gutter padding. .has-feedback .form-control-feedback { - top: 0; right: ($grid-gutter-width / 2); } @@ -521,9 +550,6 @@ input[type="checkbox"] { padding-top: (($padding-large-vertical * $line-height-large) + 1); } } - .form-control { - @extend .input-lg; - } } .form-group-sm { @media (min-width: $screen-sm-min) { @@ -531,8 +557,5 @@ input[type="checkbox"] { padding-top: ($padding-small-vertical + 1); } } - .form-control { - @extend .input-sm; - } } } diff --git a/assets/stylesheets/bootstrap/_glyphicons.scss b/assets/stylesheets/bootstrap/_glyphicons.scss index 0f6ad34..5486eaa 100644 --- a/assets/stylesheets/bootstrap/_glyphicons.scss +++ b/assets/stylesheets/bootstrap/_glyphicons.scss @@ -1,7 +1,3 @@ -//= depend_on "bootstrap/glyphicons-halflings-regular.eot" -//= depend_on "bootstrap/glyphicons-halflings-regular.svg" -//= depend_on "bootstrap/glyphicons-halflings-regular.ttf" -//= depend_on "bootstrap/glyphicons-halflings-regular.woff" // // Glyphicons for Bootstrap // @@ -37,7 +33,8 @@ // Individual icons .glyphicon-asterisk { &:before { content: "\2a"; } } .glyphicon-plus { &:before { content: "\2b"; } } -.glyphicon-euro { &:before { content: "\20ac"; } } +.glyphicon-euro, +.glyphicon-eur { &:before { content: "\20ac"; } } .glyphicon-minus { &:before { content: "\2212"; } } .glyphicon-cloud { &:before { content: "\2601"; } } .glyphicon-envelope { &:before { content: "\2709"; } } diff --git a/assets/stylesheets/bootstrap/_grid.scss b/assets/stylesheets/bootstrap/_grid.scss index f71f8b9..b15ca27 100644 --- a/assets/stylesheets/bootstrap/_grid.scss +++ b/assets/stylesheets/bootstrap/_grid.scss @@ -8,7 +8,7 @@ // Set the container width, and override it for fixed navbars in media queries. .container { - @include container-fixed(); + @include container-fixed; @media (min-width: $screen-sm-min) { width: $container-sm; @@ -28,7 +28,7 @@ // width for fluid, full width layouts. .container-fluid { - @include container-fixed(); + @include container-fixed; } @@ -37,7 +37,7 @@ // Rows contain and clear the floats of your columns. .row { - @include make-row(); + @include make-row; } @@ -45,7 +45,7 @@ // // Common styles for small and large grid columns -@include make-grid-columns(); +@include make-grid-columns; // Extra small grid diff --git a/assets/stylesheets/bootstrap/_jumbotron.scss b/assets/stylesheets/bootstrap/_jumbotron.scss index 75efe64..f386213 100644 --- a/assets/stylesheets/bootstrap/_jumbotron.scss +++ b/assets/stylesheets/bootstrap/_jumbotron.scss @@ -4,7 +4,7 @@ .jumbotron { - padding: $jumbotron-padding; + padding: $jumbotron-padding ($jumbotron-padding / 2); margin-bottom: $jumbotron-padding; color: $jumbotron-color; background-color: $jumbotron-bg; @@ -23,7 +23,8 @@ border-top-color: darken($jumbotron-bg, 10%); } - .container & { + .container &, + .container-fluid & { border-radius: $border-radius-large; // Only round corners at higher resolutions if contained in a container } @@ -32,8 +33,7 @@ } @media screen and (min-width: $screen-sm-min) { - padding-top: ($jumbotron-padding * 1.6); - padding-bottom: ($jumbotron-padding * 1.6); + padding: ($jumbotron-padding * 1.6) 0; .container & { padding-left: ($jumbotron-padding * 2); diff --git a/assets/stylesheets/bootstrap/_list-group.scss b/assets/stylesheets/bootstrap/_list-group.scss index 36c297c..14912b1 100644 --- a/assets/stylesheets/bootstrap/_list-group.scss +++ b/assets/stylesheets/bootstrap/_list-group.scss @@ -74,6 +74,7 @@ a.list-group-item { &.disabled:focus { background-color: $list-group-disabled-bg; color: $list-group-disabled-color; + cursor: $cursor-disabled; // Force color to inherit for custom content .list-group-item-heading { diff --git a/assets/stylesheets/bootstrap/_media.scss b/assets/stylesheets/bootstrap/_media.scss index 5ad22cd..292e98d 100644 --- a/assets/stylesheets/bootstrap/_media.scss +++ b/assets/stylesheets/bootstrap/_media.scss @@ -1,54 +1,45 @@ -// Media objects -// Source: http://stubbornella.org/content/?p=497 -// -------------------------------------------------- +.media { + // Proper spacing between instances of .media + margin-top: 15px; + + &:first-child { + margin-top: 0; + } +} +.media-right, +.media > .pull-right { + padding-left: 10px; +} -// Common styles -// ------------------------- +.media-left, +.media > .pull-left { + padding-right: 10px; +} -// Clear the floats -.media, +.media-left, +.media-right, .media-body { - overflow: hidden; - zoom: 1; + display: table-cell; + vertical-align: top; } -// Proper spacing between instances of .media -.media, -.media .media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; +.media-middle { + vertical-align: middle; } -// For images and videos, set to block -.media-object { - display: block; +.media-bottom { + vertical-align: bottom; } // Reset margins on headings for tighter default spacing .media-heading { - margin: 0 0 5px; -} - - -// Media image alignment -// ------------------------- - -.media { - > .pull-left { - margin-right: 10px; - } - > .pull-right { - margin-left: 10px; - } + margin-top: 0; + margin-bottom: 5px; } - // Media list variation -// ------------------------- - +// // Undo default ul/ol styles .media-list { padding-left: 0; diff --git a/assets/stylesheets/bootstrap/_modals.scss b/assets/stylesheets/bootstrap/_modals.scss index 785ca5c..fea4f36 100644 --- a/assets/stylesheets/bootstrap/_modals.scss +++ b/assets/stylesheets/bootstrap/_modals.scss @@ -30,10 +30,10 @@ // When fading in the modal, animate it to slide down &.fade .modal-dialog { - @include translate3d(0, -25%, 0); + @include translate(0, -25%); @include transition-transform(0.3s ease-out); } - &.in .modal-dialog { @include translate3d(0, 0, 0) } + &.in .modal-dialog { @include translate(0, 0) } } .modal-open .modal { overflow-x: hidden; @@ -67,7 +67,6 @@ right: 0; bottom: 0; left: 0; - z-index: $zindex-modal-background; background-color: $modal-backdrop-bg; // Fade for backdrop &.fade { @include opacity(0); } @@ -104,7 +103,7 @@ padding: $modal-inner-padding; text-align: right; // right align buttons border-top: 1px solid $modal-footer-border-color; - @include clearfix(); // clear it in case folks use .pull-* classes on buttons + @include clearfix; // clear it in case folks use .pull-* classes on buttons // Properly space out buttons .btn + .btn { diff --git a/assets/stylesheets/bootstrap/_navbar.scss b/assets/stylesheets/bootstrap/_navbar.scss index 3b94d6f..8737ad3 100644 --- a/assets/stylesheets/bootstrap/_navbar.scss +++ b/assets/stylesheets/bootstrap/_navbar.scss @@ -15,7 +15,7 @@ border: 1px solid transparent; // Prevent floats from breaking the navbar - @include clearfix(); + @include clearfix; @media (min-width: $grid-float-breakpoint) { border-radius: $navbar-border-radius; @@ -29,7 +29,7 @@ // styling of responsive aspects. .navbar-header { - @include clearfix(); + @include clearfix; @media (min-width: $grid-float-breakpoint) { float: left; @@ -53,7 +53,7 @@ padding-left: $navbar-padding-horizontal; border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255,255,255,.1); - @include clearfix(); + @include clearfix; -webkit-overflow-scrolling: touch; &.in { @@ -67,6 +67,7 @@ &.collapse { display: block !important; + visibility: visible !important; height: auto !important; padding-bottom: 0; // Override default setting overflow: visible !important; @@ -92,7 +93,7 @@ .navbar-collapse { max-height: $navbar-collapse-max-height; - @media (max-width: $screen-xs-min) and (orientation: landscape) { + @media (max-device-width: $screen-xs-min) and (orientation: landscape) { max-height: 200px; } } @@ -141,7 +142,6 @@ right: 0; left: 0; z-index: $zindex-navbar-fixed; - @include translate3d(0, 0, 0); // Undo the rounded corners @media (min-width: $grid-float-breakpoint) { @@ -173,6 +173,10 @@ text-decoration: none; } + > img { + display: block; + } + @media (min-width: $grid-float-breakpoint) { .navbar > .container &, .navbar > .container-fluid & { @@ -271,26 +275,6 @@ padding-bottom: $navbar-padding-vertical; } } - - &.navbar-right:last-child { - margin-right: -$navbar-padding-horizontal; - } - } -} - - -// Component alignment -// -// Repurpose the pull utilities as their own navbar utilities to avoid specificity -// issues with parents and chaining. Only do this when the navbar is uncollapsed -// though so that navbar contents properly stack and align in mobile. - -@media (min-width: $grid-float-breakpoint) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; } } @@ -310,11 +294,15 @@ @include box-shadow($shadow); // Mixin behavior for optimum display - @extend .form-inline; + @include form-inline; .form-group { @media (max-width: $grid-float-breakpoint-max) { margin-bottom: 5px; + + &:last-child { + margin-bottom: 0; + } } } @@ -330,11 +318,6 @@ padding-top: 0; padding-bottom: 0; @include box-shadow(none); - - // Outdent the form if last child to line up with content down the page - &.navbar-right:last-child { - margin-right: -$navbar-padding-horizontal; - } } } @@ -379,14 +362,33 @@ float: left; margin-left: $navbar-padding-horizontal; margin-right: $navbar-padding-horizontal; + } +} + + +// Component alignment +// +// Repurpose the pull utilities as their own navbar utilities to avoid specificity +// issues with parents and chaining. Only do this when the navbar is uncollapsed +// though so that navbar contents properly stack and align in mobile. +// +// Declared after the navbar components to ensure more specificity on the margins. - // Outdent the form if last child to line up with content down the page - &.navbar-right:last-child { +@media (min-width: $grid-float-breakpoint) { + .navbar-left { + float: left !important; + } + .navbar-right { + float: right !important; + margin-right: -$navbar-padding-horizontal; + + ~ .navbar-right { margin-right: 0; } } } + // Alternate navbars // -------------------------------------------------- diff --git a/assets/stylesheets/bootstrap/_navs.scss b/assets/stylesheets/bootstrap/_navs.scss index 9016873..ce0b917 100644 --- a/assets/stylesheets/bootstrap/_navs.scss +++ b/assets/stylesheets/bootstrap/_navs.scss @@ -10,7 +10,7 @@ margin-bottom: 0; padding-left: 0; // Override default ul/ol list-style: none; - @include clearfix(); + @include clearfix; > li { position: relative; @@ -36,7 +36,7 @@ color: $nav-disabled-link-hover-color; text-decoration: none; background-color: transparent; - cursor: not-allowed; + cursor: $cursor-disabled; } } } @@ -57,7 +57,7 @@ // we missed it. We don't currently support this anywhere, but in the interest // of maintaining backward compatibility in case you use it, it's deprecated. .nav-divider { - @include nav-divider(); + @include nav-divider; } // Prevent IE8 from misplacing imgs @@ -223,9 +223,11 @@ .tab-content { > .tab-pane { display: none; + visibility: hidden; } > .active { display: block; + visibility: visible; } } diff --git a/assets/stylesheets/bootstrap/_normalize.scss b/assets/stylesheets/bootstrap/_normalize.scss index ce04b6a..62a085a 100644 --- a/assets/stylesheets/bootstrap/_normalize.scss +++ b/assets/stylesheets/bootstrap/_normalize.scss @@ -1,4 +1,4 @@ -/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ // // 1. Set default font family to sans-serif. @@ -25,7 +25,8 @@ body { // // Correct `block` display not defined for any HTML5 element in IE 8/9. -// Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. +// Correct `block` display not defined for `details` or `summary` in IE 10/11 +// and Firefox. // Correct `block` display not defined for `main` in IE 11. // @@ -38,6 +39,7 @@ footer, header, hgroup, main, +menu, nav, section, summary { @@ -85,7 +87,7 @@ template { // a { - background: transparent; + background-color: transparent; } // diff --git a/assets/stylesheets/bootstrap/_pager.scss b/assets/stylesheets/bootstrap/_pager.scss index 6531fe6..c234217 100644 --- a/assets/stylesheets/bootstrap/_pager.scss +++ b/assets/stylesheets/bootstrap/_pager.scss @@ -8,7 +8,7 @@ margin: $line-height-computed 0; list-style: none; text-align: center; - @include clearfix(); + @include clearfix; li { display: inline; > a, @@ -48,8 +48,7 @@ > span { color: $pager-disabled-color; background-color: $pager-bg; - cursor: not-allowed; + cursor: $cursor-disabled; } } - } diff --git a/assets/stylesheets/bootstrap/_pagination.scss b/assets/stylesheets/bootstrap/_pagination.scss index 44c1222..323ec72 100644 --- a/assets/stylesheets/bootstrap/_pagination.scss +++ b/assets/stylesheets/bootstrap/_pagination.scss @@ -69,7 +69,7 @@ color: $pagination-disabled-color; background-color: $pagination-disabled-bg; border-color: $pagination-disabled-border; - cursor: not-allowed; + cursor: $cursor-disabled; } } } diff --git a/assets/stylesheets/bootstrap/_panels.scss b/assets/stylesheets/bootstrap/_panels.scss index 57cdcbc..6c323d9 100644 --- a/assets/stylesheets/bootstrap/_panels.scss +++ b/assets/stylesheets/bootstrap/_panels.scss @@ -15,7 +15,7 @@ // Panel contents .panel-body { padding: $panel-body-padding; - @include clearfix(); + @include clearfix; } // Optional heading @@ -56,7 +56,8 @@ // any kind of custom content between the two. .panel { - > .list-group { + > .list-group, + > .panel-collapse > .list-group { margin-bottom: 0; .list-group-item { @@ -100,6 +101,11 @@ > .table-responsive > .table, > .panel-collapse > .table { margin-bottom: 0; + + caption { + padding-left: $panel-body-padding; + padding-right: $panel-body-padding; + } } // Add border top radius for first one > .table:first-child, @@ -109,6 +115,9 @@ > thead:first-child, > tbody:first-child { > tr:first-child { + border-top-left-radius: ($panel-border-radius - 1); + border-top-right-radius: ($panel-border-radius - 1); + td:first-child, th:first-child { border-top-left-radius: ($panel-border-radius - 1); @@ -128,6 +137,9 @@ > tbody:last-child, > tfoot:last-child { > tr:last-child { + border-bottom-left-radius: ($panel-border-radius - 1); + border-bottom-right-radius: ($panel-border-radius - 1); + td:first-child, th:first-child { border-bottom-left-radius: ($panel-border-radius - 1); @@ -140,7 +152,9 @@ } } > .panel-body + .table, - > .panel-body + .table-responsive { + > .panel-body + .table-responsive, + > .table + .panel-body, + > .table-responsive + .panel-body { border-top: 1px solid $table-border-color; } > .table > tbody:first-child > tr:first-child th, @@ -202,6 +216,7 @@ .panel { margin-bottom: 0; border-radius: $panel-border-radius; + + .panel { margin-top: 5px; } @@ -209,10 +224,13 @@ .panel-heading { border-bottom: 0; - + .panel-collapse > .panel-body { + + + .panel-collapse > .panel-body, + + .panel-collapse > .list-group { border-top: 1px solid $panel-inner-border; } } + .panel-footer { border-top: 0; + .panel-collapse .panel-body { diff --git a/assets/stylesheets/bootstrap/_popovers.scss b/assets/stylesheets/bootstrap/_popovers.scss index 1cf27ae..9e28c3c 100644 --- a/assets/stylesheets/bootstrap/_popovers.scss +++ b/assets/stylesheets/bootstrap/_popovers.scss @@ -11,7 +11,11 @@ display: none; max-width: $popover-max-width; padding: 1px; - text-align: left; // Reset given new insertion method + // Reset font and text propertes given new insertion method + font-size: $font-size-base; + font-weight: normal; + line-height: $line-height-base; + text-align: left; background-color: $popover-bg; background-clip: padding-box; border: 1px solid $popover-fallback-border-color; @@ -33,8 +37,6 @@ margin: 0; // reset heading margin padding: 8px 14px; font-size: $font-size-base; - font-weight: normal; - line-height: 18px; background-color: $popover-title-bg; border-bottom: 1px solid darken($popover-title-bg, 5%); border-radius: ($border-radius-large - 1) ($border-radius-large - 1) 0 0; @@ -129,5 +131,4 @@ bottom: -$popover-arrow-width; } } - } diff --git a/assets/stylesheets/bootstrap/_print.scss b/assets/stylesheets/bootstrap/_print.scss index 3655d03..94ca58f 100644 --- a/assets/stylesheets/bootstrap/_print.scss +++ b/assets/stylesheets/bootstrap/_print.scss @@ -1,101 +1,107 @@ -// -// Basic print styles -// -------------------------------------------------- -// Source: https://github.com/h5bp/html5-boilerplate/blob/master/css/main.css +/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ + +// ========================================================================== +// Print styles. +// Inlined to avoid the additional HTTP request: h5bp.com/r +// ========================================================================== @media print { + *, + *:before, + *:after { + background: transparent !important; + color: #000 !important; // Black prints faster: h5bp.com/s + box-shadow: none !important; + text-shadow: none !important; + } + + a, + a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + // Don't show links that are fragment identifiers, + // or use the `javascript:` pseudo protocol + a[href^="#"]:after, + a[href^="javascript:"]:after { + content: ""; + } + + pre, + blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; // h5bp.com/t + } + + tr, + img { + page-break-inside: avoid; + } - * { - text-shadow: none !important; - color: #000 !important; // Black prints faster: h5bp.com/s - background: transparent !important; - box-shadow: none !important; - } - - a, - a:visited { - text-decoration: underline; - } - - a[href]:after { - content: " (" attr(href) ")"; - } - - abbr[title]:after { - content: " (" attr(title) ")"; - } - - // Don't show links for images, or javascript/internal links - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - - thead { - display: table-header-group; // h5bp.com/t - } - - tr, - img { - page-break-inside: avoid; - } - - img { - max-width: 100% !important; - } - - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - - h2, - h3 { - page-break-after: avoid; - } - - // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 - // Once fixed, we can just straight up remove this. - select { - background: #fff !important; - } - - // Bootstrap components - .navbar { - display: none; - } - .table { - td, - th { - background-color: #fff !important; - } - } - .btn, - .dropup > .btn { - > .caret { - border-top-color: #000 !important; - } - } - .label { - border: 1px solid #000; - } - - .table { - border-collapse: collapse !important; - } - .table-bordered { - th, - td { - border: 1px solid #ddd !important; - } - } + img { + max-width: 100% !important; + } + + p, + h2, + h3 { + orphans: 3; + widows: 3; + } + + h2, + h3 { + page-break-after: avoid; + } + + // Bootstrap specific changes start + // + // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 + // Once fixed, we can just straight up remove this. + select { + background: #fff !important; + } + + // Bootstrap components + .navbar { + display: none; + } + .btn, + .dropup > .btn { + > .caret { + border-top-color: #000 !important; + } + } + .label { + border: 1px solid #000; + } + + .table { + border-collapse: collapse !important; + + td, + th { + background-color: #fff !important; + } + } + .table-bordered { + th, + td { + border: 1px solid #ddd !important; + } + } + // Bootstrap specific changes end } diff --git a/assets/stylesheets/bootstrap/_progress-bars.scss b/assets/stylesheets/bootstrap/_progress-bars.scss index 6395ac2..343df63 100644 --- a/assets/stylesheets/bootstrap/_progress-bars.scss +++ b/assets/stylesheets/bootstrap/_progress-bars.scss @@ -19,7 +19,6 @@ } - // Bar itself // ------------------------- @@ -29,7 +28,7 @@ height: $line-height-computed; margin-bottom: $line-height-computed; background-color: $progress-bg; - border-radius: $border-radius-base; + border-radius: $progress-border-radius; @include box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); } @@ -54,7 +53,7 @@ // `.progress-bar`. .progress-striped .progress-bar, .progress-bar-striped { - @include gradient-striped(); + @include gradient-striped; background-size: 40px 40px; } @@ -67,23 +66,6 @@ @include animation(progress-bar-stripes 2s linear infinite); } -// Account for lower percentages -.progress-bar { - &[aria-valuenow="1"], - &[aria-valuenow="2"] { - min-width: 30px; - } - - &[aria-valuenow="0"] { - color: $gray-light; - min-width: 30px; - background-color: transparent; - background-image: none; - box-shadow: none; - } -} - - // Variations // ------------------------- diff --git a/assets/stylesheets/bootstrap/_responsive-embed.scss b/assets/stylesheets/bootstrap/_responsive-embed.scss index a884d49..c1fa8f8 100644 --- a/assets/stylesheets/bootstrap/_responsive-embed.scss +++ b/assets/stylesheets/bootstrap/_responsive-embed.scss @@ -12,7 +12,8 @@ .embed-responsive-item, iframe, embed, - object { + object, + video { position: absolute; top: 0; left: 0; diff --git a/assets/stylesheets/bootstrap/_scaffolding.scss b/assets/stylesheets/bootstrap/_scaffolding.scss index 8cea4cd..626e5eb 100644 --- a/assets/stylesheets/bootstrap/_scaffolding.scss +++ b/assets/stylesheets/bootstrap/_scaffolding.scss @@ -52,11 +52,11 @@ a { &:hover, &:focus { color: $link-hover-color; - text-decoration: underline; + text-decoration: $link-hover-decoration; } &:focus { - @include tab-focus(); + @include tab-focus; } } @@ -79,7 +79,7 @@ img { // Responsive images (ensure images don't scale beyond their parents) .img-responsive { - @include img-responsive(); + @include img-responsive; } // Rounded corners diff --git a/assets/stylesheets/bootstrap/_tables.scss b/assets/stylesheets/bootstrap/_tables.scss index 7c5d5bb..a575ff1 100644 --- a/assets/stylesheets/bootstrap/_tables.scss +++ b/assets/stylesheets/bootstrap/_tables.scss @@ -6,6 +6,12 @@ table { background-color: $table-bg; } +caption { + padding-top: $table-cell-padding; + padding-bottom: $table-cell-padding; + color: $text-muted; + text-align: left; +} th { text-align: left; } @@ -106,10 +112,7 @@ th { .table-striped { > tbody > tr:nth-child(odd) { - > td, - > th { - background-color: $table-bg-accent; - } + background-color: $table-bg-accent; } } @@ -120,10 +123,7 @@ th { .table-hover { > tbody > tr:hover { - > td, - > th { - background-color: $table-bg-hover; - } + background-color: $table-bg-hover; } } @@ -169,14 +169,15 @@ table { // will display normally. .table-responsive { + overflow-x: auto; + min-height: 0.01%; // Workaround for IE9 bug (see https://github.com/twbs/bootstrap/issues/14837) + @media screen and (max-width: $screen-xs-max) { width: 100%; margin-bottom: ($line-height-computed * 0.75); overflow-y: hidden; - overflow-x: auto; -ms-overflow-style: -ms-autohiding-scrollbar; border: 1px solid $table-border-color; - -webkit-overflow-scrolling: touch; // Tighten up spacing > .table { diff --git a/assets/stylesheets/bootstrap/_theme.scss b/assets/stylesheets/bootstrap/_theme.scss index 00386a2..05eb32a 100644 --- a/assets/stylesheets/bootstrap/_theme.scss +++ b/assets/stylesheets/bootstrap/_theme.scss @@ -7,7 +7,6 @@ @import "mixins"; - // // Buttons // -------------------------------------------------- @@ -28,12 +27,16 @@ &.active { @include box-shadow(inset 0 3px 5px rgba(0,0,0,.125)); } + + .badge { + text-shadow: none; + } } // Mixin for generating new styles @mixin btn-styles($btn-color: #555) { @include gradient-vertical($start-color: $btn-color, $end-color: darken($btn-color, 12%)); - @include reset-filter(); // Disable gradients for IE9 because filter bleeds through rounded corners + @include reset-filter; // Disable gradients for IE9 because filter bleeds through rounded corners background-repeat: repeat-x; border-color: darken($btn-color, 14%); @@ -74,7 +77,6 @@ .btn-danger { @include btn-styles($btn-danger-bg); } - // // Images // -------------------------------------------------- @@ -85,7 +87,6 @@ } - // // Dropdowns // -------------------------------------------------- @@ -103,7 +104,6 @@ } - // // Navbar // -------------------------------------------------- @@ -111,13 +111,14 @@ // Default navbar .navbar-default { @include gradient-vertical($start-color: lighten($navbar-default-bg, 10%), $end-color: $navbar-default-bg); - @include reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered + @include reset-filter; // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered border-radius: $navbar-border-radius; $shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 5px rgba(0,0,0,.075); @include box-shadow($shadow); + .navbar-nav > .open > a, .navbar-nav > .active > a { - @include gradient-vertical($start-color: darken($navbar-default-bg, 5%), $end-color: darken($navbar-default-bg, 2%)); + @include gradient-vertical($start-color: darken($navbar-default-link-active-bg, 5%), $end-color: darken($navbar-default-link-active-bg, 2%)); @include box-shadow(inset 0 3px 9px rgba(0,0,0,.075)); } } @@ -129,10 +130,11 @@ // Inverted navbar .navbar-inverse { @include gradient-vertical($start-color: lighten($navbar-inverse-bg, 10%), $end-color: $navbar-inverse-bg); - @include reset-filter(); // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered + @include reset-filter; // Remove gradient in IE<10 to fix bug where dropdowns don't get triggered + .navbar-nav > .open > a, .navbar-nav > .active > a { - @include gradient-vertical($start-color: $navbar-inverse-bg, $end-color: lighten($navbar-inverse-bg, 2.5%)); + @include gradient-vertical($start-color: $navbar-inverse-link-active-bg, $end-color: lighten($navbar-inverse-link-active-bg, 2.5%)); @include box-shadow(inset 0 3px 9px rgba(0,0,0,.25)); } @@ -150,7 +152,6 @@ } - // // Alerts // -------------------------------------------------- @@ -175,7 +176,6 @@ .alert-danger { @include alert-styles($alert-danger-bg); } - // // Progress bars // -------------------------------------------------- @@ -200,7 +200,7 @@ // Reset the striped class because our mixins don't do multiple gradients and // the above custom styles override the new `.progress-bar-striped` in v3.2.0. .progress-bar-striped { - @include gradient-striped(); + @include gradient-striped; } @@ -218,8 +218,11 @@ text-shadow: 0 -1px 0 darken($list-group-active-bg, 10%); @include gradient-vertical($start-color: $list-group-active-bg, $end-color: darken($list-group-active-bg, 7.5%)); border-color: darken($list-group-active-border, 7.5%); -} + .badge { + text-shadow: none; + } +} // @@ -245,7 +248,6 @@ .panel-danger > .panel-heading { @include panel-heading-styles($panel-danger-heading-bg); } - // // Wells // -------------------------------------------------- diff --git a/assets/stylesheets/bootstrap/_thumbnails.scss b/assets/stylesheets/bootstrap/_thumbnails.scss index 3d5ed86..da0e1e7 100644 --- a/assets/stylesheets/bootstrap/_thumbnails.scss +++ b/assets/stylesheets/bootstrap/_thumbnails.scss @@ -12,11 +12,11 @@ background-color: $thumbnail-bg; border: 1px solid $thumbnail-border; border-radius: $thumbnail-border-radius; - @include transition(all .2s ease-in-out); + @include transition(border .2s ease-in-out); > img, a > img { - @include img-responsive(); + @include img-responsive; margin-left: auto; margin-right: auto; } diff --git a/assets/stylesheets/bootstrap/_type.scss b/assets/stylesheets/bootstrap/_type.scss index 3f21b21..62d685c 100644 --- a/assets/stylesheets/bootstrap/_type.scss +++ b/assets/stylesheets/bootstrap/_type.scss @@ -80,11 +80,6 @@ small, font-size: floor((100% * $font-size-small / $font-size-base)); } -// Undo browser default styling -cite { - font-style: normal; -} - mark, .mark { background-color: $state-warning-bg; @@ -163,15 +158,20 @@ ol { // List options -// Unstyled keeps list items block level, just removes default browser padding and list-style -.list-unstyled { +// [converter] extracted from `.list-unstyled` for libsass compatibility +@mixin list-unstyled { padding-left: 0; list-style: none; } +// [converter] extracted as `@mixin list-unstyled` for libsass compatibility +.list-unstyled { + @include list-unstyled; +} + // Inline turns list items into inline-block .list-inline { - @extend .list-unstyled; + @include list-unstyled; margin-left: -5px; > li { @@ -204,7 +204,7 @@ dd { .dl-horizontal { dd { - @include clearfix(); // Clear the floated `dt` if an empty `dd` is present + @include clearfix; // Clear the floated `dt` if an empty `dd` is present } @media (min-width: $grid-float-breakpoint) { @@ -213,7 +213,7 @@ dd { width: ($dl-horizontal-offset - 20); clear: left; text-align: right; - @include text-overflow(); + @include text-overflow; } dd { margin-left: $dl-horizontal-offset; @@ -290,12 +290,6 @@ blockquote.pull-right { } } -// Quotes -blockquote:before, -blockquote:after { - content: ""; -} - // Addresses address { margin-bottom: $line-height-computed; diff --git a/assets/stylesheets/bootstrap/_utilities.scss b/assets/stylesheets/bootstrap/_utilities.scss index 3ad5f2e..386c524 100644 --- a/assets/stylesheets/bootstrap/_utilities.scss +++ b/assets/stylesheets/bootstrap/_utilities.scss @@ -7,10 +7,10 @@ // ------------------------- .clearfix { - @include clearfix(); + @include clearfix; } .center-block { - @include center-block(); + @include center-block; } .pull-right { float: right !important; @@ -34,7 +34,7 @@ visibility: hidden; } .text-hide { - @include text-hide(); + @include text-hide; } @@ -53,5 +53,4 @@ .affix { position: fixed; - @include translate3d(0, 0, 0); } diff --git a/assets/stylesheets/bootstrap/_variables.scss b/assets/stylesheets/bootstrap/_variables.scss index 2d17e0c..b4fba43 100644 --- a/assets/stylesheets/bootstrap/_variables.scss +++ b/assets/stylesheets/bootstrap/_variables.scss @@ -12,11 +12,12 @@ $bootstrap-sass-asset-helper: (twbs-font-path("") != unquote('twbs-font-path("") // //## Gray and brand colors for use across Bootstrap. -$gray-darker: lighten(#000, 13.5%) !default; // #222 -$gray-dark: lighten(#000, 20%) !default; // #333 -$gray: lighten(#000, 33.5%) !default; // #555 -$gray-light: lighten(#000, 46.7%) !default; // #777 -$gray-lighter: lighten(#000, 93.5%) !default; // #eee +$gray-base: #000 !default; +$gray-darker: lighten($gray-base, 13.5%) !default; // #222 +$gray-dark: lighten($gray-base, 20%) !default; // #333 +$gray: lighten($gray-base, 33.5%) !default; // #555 +$gray-light: lighten($gray-base, 46.7%) !default; // #777 +$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee $brand-primary: #428bca !default; $brand-success: #5cb85c !default; @@ -38,6 +39,8 @@ $text-color: $gray-dark !default; $link-color: $brand-primary !default; //** Link hover color set via `darken()` function. $link-hover-color: darken($link-color, 15%) !default; +//** Link hover decoration. +$link-hover-decoration: underline !default; //== Typography @@ -189,13 +192,20 @@ $input-bg-disabled: $gray-lighter !default; $input-color: $gray !default; //** `<input>` border color $input-border: #ccc !default; -//** `<input>` border radius + +// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4 +//** Default `.form-control` border radius $input-border-radius: $border-radius-base !default; +//** Large `.form-control` border radius +$input-border-radius-large: $border-radius-large !default; +//** Small `.form-control` border radius +$input-border-radius-small: $border-radius-small !default; + //** Border color for inputs on focus $input-border-focus: #66afe9 !default; //** Placeholder text color -$input-color-placeholder: $gray-light !default; +$input-color-placeholder: #999 !default; //** Default `.form-control` height $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default; @@ -212,6 +222,9 @@ $input-group-addon-bg: $gray-lighter !default; //** Border color for textual input addons $input-group-addon-border-color: $input-border !default; +//** Disabled cursor for form controls and buttons. +$cursor-disabled: not-allowed !default; + //== Dropdowns // @@ -260,8 +273,7 @@ $zindex-dropdown: 1000 !default; $zindex-popover: 1060 !default; $zindex-tooltip: 1070 !default; $zindex-navbar-fixed: 1030 !default; -$zindex-modal-background: 1040 !default; -$zindex-modal: 1050 !default; +$zindex-modal: 1040 !default; //== Media queries breakpoints @@ -323,17 +335,17 @@ $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default; //## Define the maximum width of `.container` for different screen sizes. // Small screen / tablet -$container-tablet: ((720px + $grid-gutter-width)) !default; +$container-tablet: (720px + $grid-gutter-width) !default; //** For `$screen-sm-min` and up. $container-sm: $container-tablet !default; // Medium screen / desktop -$container-desktop: ((940px + $grid-gutter-width)) !default; +$container-desktop: (940px + $grid-gutter-width) !default; //** For `$screen-md-min` and up. $container-md: $container-desktop !default; // Large screen / wide desktop -$container-large-desktop: ((1140px + $grid-gutter-width)) !default; +$container-large-desktop: (1140px + $grid-gutter-width) !default; //** For `$screen-lg-min` and up. $container-lg: $container-large-desktop !default; @@ -376,12 +388,12 @@ $navbar-default-toggle-border-color: #ddd !default; // Inverted navbar // Reset inverted navbar basics -$navbar-inverse-color: $gray-light !default; +$navbar-inverse-color: lighten($gray-light, 15%) !default; $navbar-inverse-bg: #222 !default; $navbar-inverse-border: darken($navbar-inverse-bg, 10%) !default; // Inverted navbar links -$navbar-inverse-link-color: $gray-light !default; +$navbar-inverse-link-color: lighten($gray-light, 15%) !default; $navbar-inverse-link-hover-color: #fff !default; $navbar-inverse-link-hover-bg: transparent !default; $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color !default; @@ -411,8 +423,6 @@ $nav-link-hover-bg: $gray-lighter !default; $nav-disabled-link-color: $gray-light !default; $nav-disabled-link-hover-color: $gray-light !default; -$nav-open-link-hover-color: #fff !default; - //== Tabs $nav-tabs-border-color: #ddd !default; @@ -537,7 +547,7 @@ $popover-title-bg: darken($popover-bg, 3%) !default; //** Popover arrow width $popover-arrow-width: 10px !default; //** Popover arrow color -$popover-arrow-color: #fff !default; +$popover-arrow-color: $popover-bg !default; //** Popover outer arrow width $popover-arrow-outer-width: ($popover-arrow-width + 1) !default; @@ -636,6 +646,8 @@ $alert-danger-border: $state-danger-border !default; $progress-bg: #f5f5f5 !default; //** Progress bar text color $progress-bar-color: #fff !default; +//** Variable for setting rounded corners on progress bar. +$progress-border-radius: $border-radius-base !default; //** Default progress bar color $progress-bar-bg: $brand-primary !default; @@ -850,5 +862,3 @@ $page-header-border-color: $gray-lighter !default; $dl-horizontal-offset: $component-offset-horizontal !default; //** Horizontal line color. $hr-border: $gray-lighter !default; - - diff --git a/assets/stylesheets/bootstrap/mixins/_buttons.scss b/assets/stylesheets/bootstrap/mixins/_buttons.scss index 58ad13e..74a4ffc 100644 --- a/assets/stylesheets/bootstrap/mixins/_buttons.scss +++ b/assets/stylesheets/bootstrap/mixins/_buttons.scss @@ -10,6 +10,7 @@ &:hover, &:focus, + &.focus, &:active, &.active, .open > &.dropdown-toggle { @@ -28,6 +29,7 @@ &, &:hover, &:focus, + &.focus, &:active, &.active { background-color: $background; diff --git a/assets/stylesheets/bootstrap/mixins/_forms.scss b/assets/stylesheets/bootstrap/mixins/_forms.scss index ff72f0e..277aa5f 100644 --- a/assets/stylesheets/bootstrap/mixins/_forms.scss +++ b/assets/stylesheets/bootstrap/mixins/_forms.scss @@ -10,7 +10,11 @@ .radio, .checkbox, .radio-inline, - .checkbox-inline { + .checkbox-inline, + &.radio label, + &.checkbox label, + &.radio-inline label, + &.checkbox-inline label { color: $text-color; } // Set the border and box shadow on specific inputs to match diff --git a/assets/stylesheets/bootstrap/mixins/_grid.scss b/assets/stylesheets/bootstrap/mixins/_grid.scss index 1601f6a..ae16433 100644 --- a/assets/stylesheets/bootstrap/mixins/_grid.scss +++ b/assets/stylesheets/bootstrap/mixins/_grid.scss @@ -8,14 +8,14 @@ margin-left: auto; padding-left: ($gutter / 2); padding-right: ($gutter / 2); - @include clearfix(); + @include clearfix; } // Creates a wrapper for a series of columns @mixin make-row($gutter: $grid-gutter-width) { margin-left: ($gutter / -2); margin-right: ($gutter / -2); - @include clearfix(); + @include clearfix; } // Generate the extra small columns diff --git a/assets/stylesheets/bootstrap/mixins/_hide-text.scss b/assets/stylesheets/bootstrap/mixins/_hide-text.scss index 5e06972..45db638 100644 --- a/assets/stylesheets/bootstrap/mixins/_hide-text.scss +++ b/assets/stylesheets/bootstrap/mixins/_hide-text.scss @@ -17,5 +17,5 @@ // New mixin to use as of v3.0.1 @mixin text-hide() { - @include hide-text(); + @include hide-text; } diff --git a/assets/stylesheets/bootstrap/mixins/_image.scss b/assets/stylesheets/bootstrap/mixins/_image.scss index 57d60a3..c8dcf5e 100644 --- a/assets/stylesheets/bootstrap/mixins/_image.scss +++ b/assets/stylesheets/bootstrap/mixins/_image.scss @@ -8,7 +8,6 @@ // Keep images from scaling beyond the width of their parents. @mixin img-responsive($display: block) { display: $display; - width: 100% \9; // Force IE10 and below to size SVG images correctly max-width: 100%; // Part 1: Set a maximum relative to the parent height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching } diff --git a/assets/stylesheets/bootstrap/mixins/_labels.scss b/assets/stylesheets/bootstrap/mixins/_labels.scss index d99a653..eda6dfd 100644 --- a/assets/stylesheets/bootstrap/mixins/_labels.scss +++ b/assets/stylesheets/bootstrap/mixins/_labels.scss @@ -2,7 +2,7 @@ @mixin label-variant($color) { background-color: $color; - + &[href] { &:hover, &:focus { diff --git a/assets/stylesheets/bootstrap/mixins/_progress-bar.scss b/assets/stylesheets/bootstrap/mixins/_progress-bar.scss index 3275ea3..90a62af 100644 --- a/assets/stylesheets/bootstrap/mixins/_progress-bar.scss +++ b/assets/stylesheets/bootstrap/mixins/_progress-bar.scss @@ -5,6 +5,6 @@ // Deprecated parent class requirement as of v3.2.0 .progress-striped & { - @include gradient-striped(); + @include gradient-striped; } } diff --git a/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss b/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss index f91f576..a6b9ef4 100644 --- a/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss +++ b/assets/stylesheets/bootstrap/mixins/_vendor-prefixes.scss @@ -99,9 +99,12 @@ // Placeholder text @mixin placeholder($color: $input-color-placeholder) { - &::-moz-placeholder { color: $color; // Firefox - opacity: 1; } // See https://github.com/twbs/bootstrap/pull/11526 - &:-ms-input-placeholder { color: $color; } // Internet Explorer 10+ + // Firefox + &::-moz-placeholder { + color: $color; + opacity: 1; // See https://github.com/twbs/bootstrap/pull/11526 + } + &:-ms-input-placeholder { color: $color; } // Internet Explorer 10+ &::-webkit-input-placeholder { color: $color; } // Safari and Chrome } diff --git a/lib/bootstrap-sass.rb b/lib/bootstrap-sass.rb index 019e284..2c6c1bc 100644 --- a/lib/bootstrap-sass.rb +++ b/lib/bootstrap-sass.rb @@ -4,7 +4,6 @@ module Bootstrap def load! register_compass_extension if compass? register_rails_engine if rails? - configure_sass end @@ -56,6 +55,7 @@ module Bootstrap def register_compass_extension ::Compass::Frameworks.register( 'bootstrap', + :version => Bootstrap::VERSION, :path => gem_path, :stylesheets_directory => stylesheets_path, :templates_directory => File.join(gem_path, 'templates') diff --git a/lib/bootstrap-sass/engine.rb b/lib/bootstrap-sass/engine.rb index bd82137..2ebe261 100644 --- a/lib/bootstrap-sass/engine.rb +++ b/lib/bootstrap-sass/engine.rb @@ -3,7 +3,7 @@ module Bootstrap class Engine < ::Rails::Engine initializer 'bootstrap-sass.assets.precompile' do |app| %w(stylesheets javascripts fonts images).each do |sub| - app.config.assets.paths << root.join('assets', sub) + app.config.assets.paths << root.join('assets', sub).to_s end app.config.assets.precompile << %r(bootstrap/glyphicons-halflings-regular\.(?:eot|svg|ttf|woff)$) end diff --git a/lib/bootstrap-sass/version.rb b/lib/bootstrap-sass/version.rb index dc78dde..89be577 100644 --- a/lib/bootstrap-sass/version.rb +++ b/lib/bootstrap-sass/version.rb @@ -1,4 +1,4 @@ module Bootstrap - VERSION = '3.2.0.2' - BOOTSTRAP_SHA = 'c068162161154a4b85110ea1e7dd3d7897ce2b72' + VERSION = '3.3.0.0' + BOOTSTRAP_SHA = '16dbdbd7a2c6cfa3be4e5dcc52249e577c02c84a' end diff --git a/package.json b/package.json index 6085dd9..27e1bf2 100644 --- a/package.json +++ b/package.json @@ -23,8 +23,8 @@ "url": "https://github.com/twbs/bootstrap-sass/issues" }, "devDependencies": { - "node-sass": "*", - "mincer": "*", - "ejs": "*" + "node-sass": "~1.0", + "mincer": "~1.1", + "ejs": "~1.0" } } diff --git a/tasks/converter/less_conversion.rb b/tasks/converter/less_conversion.rb index a903aec..0d46ce9 100644 --- a/tasks/converter/less_conversion.rb +++ b/tasks/converter/less_conversion.rb @@ -22,7 +22,7 @@ class Converter # match any brace that opens or closes a properties body 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 - # valid + # valid characters in mixin definitions SCSS_MIXIN_DEF_ARGS_RE = /[\w\-,\s$:#%()]*/ LESS_MIXIN_DEF_ARGS_RE = /[\w\-,;.\s@:#%()]*/ @@ -31,27 +31,34 @@ class Converter # These mixins will get vararg definitions in SCSS (not supported by LESS): VARARG_MIXINS = %w( - scale transition transition-duration transition-property transition-transform box-shadow - ) + scale transition transition-duration transition-property transition-transform box-shadow + ) + + # A list of classes that will be extracted into mixins + # Only the top-level selectors of form .CLASS { ... } are extracted. CLASS must not be used in any other rule definition. + # This is a work-around for libsass @extend issues + CLASSES_TO_MIXINS = %w( + list-unstyled form-inline + ) # Convert a snippet of bootstrap LESS to Scss def convert_less(less) - load_shared less = convert_to_scss(less) less = yield(less) if block_given? less end - def load_shared + def shared_mixins @shared_mixins ||= begin log_status ' Reading shared mixins from mixins.less' - read_mixins read_files('less', bootstrap_less_files.grep(/mixins\//)).values.join("\n"), nested: NESTED_MIXINS + CLASSES_TO_MIXINS + read_mixins(read_files('less', bootstrap_less_files.grep(/mixins\//)).values.join("\n"), + nested: NESTED_MIXINS) end end def process_stylesheet_assets log_status 'Processing stylesheets...' - files = read_files('less', bootstrap_less_files) + files = read_files('less', bootstrap_less_files) save_to = @save_to[:scss] log_status ' Converting LESS files to Scss:' @@ -94,7 +101,7 @@ class Converter // NB: in Sass 3.3 there is a native function: function-exists(twbs-font-path) $bootstrap-sass-asset-helper: #{sass_fn_exists('twbs-font-path')} !default; SCSS - file = replace_all file, %r{(\$icon-font-path): \s*"(.*)" (!default);}, "\n" + unindent(<<-SCSS, 14) + file = replace_all file, %r{(\$icon-font-path): \s*"(.*)" (!default);}, "\n" + unindent(<<-SCSS, 14) // [converter] Asset helpers such as Sprockets and Node.js Mincer do not resolve relative paths \\1: if($bootstrap-sass-asset-helper, "bootstrap/", "\\2bootstrap/") \\3; SCSS @@ -114,7 +121,6 @@ class Converter when 'thumbnails.less', 'labels.less', 'badges.less' file = extract_nested_rule file, 'a&' when 'glyphicons.less' - file = bootstrap_font_files.map { |p| %Q(//= depend_on "bootstrap/#{File.basename(p)}") } * "\n" + "\n" + file file = replace_rules(file, '@font-face') { |rule| rule = replace_all rule, /(\$icon-font(?:-\w+)+)/, '#{\1}' replace_asset_url rule, :font @@ -136,6 +142,11 @@ class Converter main_to = File.expand_path("#{save_to}/../_bootstrap.scss") save_file main_to, File.read(main_from).gsub(/ "/, ' "bootstrap/') File.delete(main_from) + + # generate variables template + save_file 'templates/project/_bootstrap-variables.sass', + "// Override Bootstrap variables here (defaults from bootstrap-sass v<%= Bootstrap::VERSION %>):\n\n" + + File.read("#{save_to}/_variables.scss").gsub(/^(?=\$)/, '// ').gsub(/ !default;/, '') end def bootstrap_less_files @@ -147,10 +158,11 @@ class Converter # apply general less to scss conversion def convert_to_scss(file) # get local mixin names before converting the definitions - mixins = @shared_mixins + read_mixins(file) + mixins = shared_mixins + read_mixins(file) file = replace_vars(file) file = replace_mixin_definitions(file) file = replace_mixins(file, mixins) + file = extract_mixins_from_selectors(file, CLASSES_TO_MIXINS.inject({}) { |h, cl| h.update(".#{cl}" => cl) }) file = replace_spin(file) file = replace_fadein(file) file = replace_image_urls(file) @@ -371,7 +383,7 @@ SASS sel = parent_sel + sel[1..-1] end # unwrap, and replace @include - unindent unwrap_rule_block(rule).gsub(/(@include [\w-]+)\(([\$\w\-,\s]*)\)/) { + unindent unwrap_rule_block(rule).gsub(/(@include [\w-]+)\(?([\$\w\-,\s]*)\)?/) { args = $2 "#{cmt}#{$1}('#{sel.gsub(/\s+/, ' ')}'#{', ' if args && !args.empty?}#{args})" } @@ -388,6 +400,25 @@ SASS end end + # .btn { ... } -> @mixin btn { ... }; .btn { @include btn } + def extract_mixins_from_selectors(file, selectors_to_mixins) + selectors_to_mixins.each do |selector, mixin| + file = replace_rules file, Regexp.escape(selector), prefix: false do |selector_css| + log_transform "#{selector} { ... } -> @mixin #{mixin} { ... }; #{selector} { @include #{mixin} } ", from: 'extract_mixins_from_selectors' + <<-SCSS +// [converter] extracted from `#{selector}` for libsass compatibility +@mixin #{mixin} {#{unwrap_rule_block(selector_css)} +} +// [converter] extracted as `@mixin #{mixin}` for libsass compatibility +#{selector} { + @include #{mixin}; +} + SCSS + end + end + file + end + # @include and @extend from LESS: # .mixin() -> @include mixin() # #scope > .mixin() -> @include scope-mixin() @@ -396,14 +427,11 @@ SASS mixin_pattern = /(\s+)(([#|\.][\w-]+\s*>\s*)*)\.([\w-]+\(.*\))(?!\s\{)/ less = less.gsub(mixin_pattern) do |match| - matches = match.scan(mixin_pattern).flatten - scope = matches[1] || '' - if scope != '' - scope = scope.scan(/[\w-]+/).join('-') + '-' - end + matches = match.scan(mixin_pattern).flatten + scope = matches[1] && matches[1] != '' ? matches[1].scan(/[\w-]+/).join('-') + '-' : '' mixin_name = match.scan(/\.([\w-]+)\(.*\)\s?\{?/).first if mixin_name && mixin_names.include?("#{scope}#{mixin_name.first}") - "#{matches.first}@include #{scope}#{matches.last}".gsub(/; \$/, ", $").sub(/;\)$/, ')') + "#{matches.first}@include #{scope}#{matches.last.gsub(/;\s*\$/, ', $').sub(/;\)$/, ')').sub(/\(\)$/, '')}" else "#{matches.first}@extend .#{scope}#{matches.last.gsub(/\(\)/, '')}" end @@ -414,7 +442,7 @@ SASS selector =~ /\.([\w-]+)/ mixin = $1 if mixin && mixin_names.include?(mixin) - "@include #{mixin}()" + "@include #{mixin}" else "@extend #{selector}" end @@ -548,16 +576,20 @@ SASS # option :comments -- include immediately preceding comments in rule_block # # replace_rules(".a{ \n .b{} }", '.b') { |rule, pos| ">#{rule}<" } #=> ".a{ \n >.b{}< }" - def replace_rules(less, rule_prefix = SELECTOR_RE, options = {}, &block) - options = {comments: true}.merge(options || {}) - less = less.dup - s = CharStringScanner.new(less) - rule_re = /(?:#{rule_prefix}[#{SELECTOR_CHAR})=(\s]*?#{RULE_OPEN_BRACE_RE})/ - if options[:comments] - rule_start_re = /(?:#{COMMENT_RE}*)^#{rule_re}/ - else - rule_start_re = /^#{rule_re}/ - end + def replace_rules(less, selector = SELECTOR_RE, options = {}, &block) + options = {prefix: true, comments: true}.merge(options || {}) + less = less.dup + s = CharStringScanner.new(less) + rule_re = if options[:prefix] + /(?:#{selector}[#{SELECTOR_CHAR})=(\s]*?#{RULE_OPEN_BRACE_RE})/ + else + /#{selector}[\s]*#{RULE_OPEN_BRACE_RE}/ + end + rule_start_re = if options[:comments] + /(?:#{COMMENT_RE}*)^#{rule_re}/ + else + /^#{rule_re}/ + end positions = [] while (rule_start = s.scan_next(rule_start_re)) diff --git a/tasks/converter/network.rb b/tasks/converter/network.rb index a5ec956..848778a 100644 --- a/tasks/converter/network.rb +++ b/tasks/converter/network.rb @@ -1,3 +1,4 @@ +require 'shellwords' class Converter module Network protected @@ -58,12 +59,17 @@ class Converter # get sha of the branch (= the latest commit) def get_branch_sha - return @branch if @branch =~ /\A[0-9a-f]+\z/ - cmd = "git ls-remote 'https://github.com/#@repo' | awk '/#@branch/ {print $1}'" - log cmd - @branch_sha ||= %x[#{cmd}].chomp - raise 'Could not get branch sha!' unless $?.success? - @branch_sha + @branch_sha ||= begin + if @branch + "\n" == %x[git rev-parse #@branch] + @branch + else + cmd = "git ls-remote #{Shellwords.escape "https://github.com/#@repo"} #@branch" + log cmd + result = %x[#{cmd}] + raise 'Could not get branch sha!' unless $?.success? && !result.empty? + result.split(/\s+/).first + end + end end # Get the sha of a dir diff --git a/templates/project/_bootstrap-variables.sass b/templates/project/_bootstrap-variables.sass new file mode 100644 index 0000000..917a457 --- /dev/null +++ b/templates/project/_bootstrap-variables.sass @@ -0,0 +1,866 @@ +// Override Bootstrap variables here (defaults from bootstrap-sass v<%= Bootstrap::VERSION %>): + +// When true, asset path helpers are used, otherwise the regular CSS `url()` is used. +// When there no function is defined, `fn('')` is parsed as string that equals the right hand side +// NB: in Sass 3.3 there is a native function: function-exists(twbs-font-path) +// $bootstrap-sass-asset-helper: (twbs-font-path("") != unquote('twbs-font-path("")')) + +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +// $gray-base: #000 +// $gray-darker: lighten($gray-base, 13.5%) // #222 +// $gray-dark: lighten($gray-base, 20%) // #333 +// $gray: lighten($gray-base, 33.5%) // #555 +// $gray-light: lighten($gray-base, 46.7%) // #777 +// $gray-lighter: lighten($gray-base, 93.5%) // #eee + +// $brand-primary: #428bca +// $brand-success: #5cb85c +// $brand-info: #5bc0de +// $brand-warning: #f0ad4e +// $brand-danger: #d9534f + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for `<body>`. +// $body-bg: #fff +//** Global text color on `<body>`. +// $text-color: $gray-dark + +//** Global textual link color. +// $link-color: $brand-primary +//** Link hover color set via `darken()` function. +// $link-hover-color: darken($link-color, 15%) +//** Link hover decoration. +// $link-hover-decoration: underline + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +// $font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif +// $font-family-serif: Georgia, "Times New Roman", Times, serif +//** Default monospace fonts for `<code>`, `<kbd>`, and `<pre>`. +// $font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace +// $font-family-base: $font-family-sans-serif + +// $font-size-base: 14px +// $font-size-large: ceil(($font-size-base * 1.25)) // ~18px +// $font-size-small: ceil(($font-size-base * 0.85)) // ~12px + +// $font-size-h1: floor(($font-size-base * 2.6)) // ~36px +// $font-size-h2: floor(($font-size-base * 2.15)) // ~30px +// $font-size-h3: ceil(($font-size-base * 1.7)) // ~24px +// $font-size-h4: ceil(($font-size-base * 1.25)) // ~18px +// $font-size-h5: $font-size-base +// $font-size-h6: ceil(($font-size-base * 0.85)) // ~12px + +//** Unit-less `line-height` for use in components like buttons. +// $line-height-base: 1.428571429 // 20/14 +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc. +// $line-height-computed: floor(($font-size-base * $line-height-base)) // ~20px + +//** By default, this inherits from the `<body>`. +// $headings-font-family: inherit +// $headings-font-weight: 500 +// $headings-line-height: 1.1 +// $headings-color: inherit + + +//== Iconography +// +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower. + +//** Load fonts from this directory. + +// [converter] Asset helpers such as Sprockets and Node.js Mincer do not resolve relative paths +// $icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") + +//** File name for all font files. +// $icon-font-name: "glyphicons-halflings-regular" +//** Element ID within SVG icon file. +// $icon-font-svg-id: "glyphicons_halflingsregular" + + +//== Components +// +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start). + +// $padding-base-vertical: 6px +// $padding-base-horizontal: 12px + +// $padding-large-vertical: 10px +// $padding-large-horizontal: 16px + +// $padding-small-vertical: 5px +// $padding-small-horizontal: 10px + +// $padding-xs-vertical: 1px +// $padding-xs-horizontal: 5px + +// $line-height-large: 1.33 +// $line-height-small: 1.5 + +// $border-radius-base: 4px +// $border-radius-large: 6px +// $border-radius-small: 3px + +//** Global color for active items (e.g., navs or dropdowns). +// $component-active-color: #fff +//** Global background color for active items (e.g., navs or dropdowns). +// $component-active-bg: $brand-primary + +//** Width of the `border` for generating carets that indicator dropdowns. +// $caret-width-base: 4px +//** Carets increase slightly in size for larger components. +// $caret-width-large: 5px + + +//== Tables +// +//## Customizes the `.table` component with basic values, each used across all table variations. + +//** Padding for `<th>`s and `<td>`s. +// $table-cell-padding: 8px +//** Padding for cells in `.table-condensed`. +// $table-condensed-cell-padding: 5px + +//** Default background color used for all tables. +// $table-bg: transparent +//** Background color used for `.table-striped`. +// $table-bg-accent: #f9f9f9 +//** Background color used for `.table-hover`. +// $table-bg-hover: #f5f5f5 +// $table-bg-active: $table-bg-hover + +//** Border color for table and cell borders. +// $table-border-color: #ddd + + +//== Buttons +// +//## For each of Bootstrap's buttons, define text, background and border color. + +// $btn-font-weight: normal + +// $btn-default-color: #333 +// $btn-default-bg: #fff +// $btn-default-border: #ccc + +// $btn-primary-color: #fff +// $btn-primary-bg: $brand-primary +// $btn-primary-border: darken($btn-primary-bg, 5%) + +// $btn-success-color: #fff +// $btn-success-bg: $brand-success +// $btn-success-border: darken($btn-success-bg, 5%) + +// $btn-info-color: #fff +// $btn-info-bg: $brand-info +// $btn-info-border: darken($btn-info-bg, 5%) + +// $btn-warning-color: #fff +// $btn-warning-bg: $brand-warning +// $btn-warning-border: darken($btn-warning-bg, 5%) + +// $btn-danger-color: #fff +// $btn-danger-bg: $brand-danger +// $btn-danger-border: darken($btn-danger-bg, 5%) + +// $btn-link-disabled-color: $gray-light + + +//== Forms +// +//## + +//** `<input>` background color +// $input-bg: #fff +//** `<input disabled>` background color +// $input-bg-disabled: $gray-lighter + +//** Text color for `<input>`s +// $input-color: $gray +//** `<input>` border color +// $input-border: #ccc + +// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4 +//** Default `.form-control` border radius +// $input-border-radius: $border-radius-base +//** Large `.form-control` border radius +// $input-border-radius-large: $border-radius-large +//** Small `.form-control` border radius +// $input-border-radius-small: $border-radius-small + +//** Border color for inputs on focus +// $input-border-focus: #66afe9 + +//** Placeholder text color +// $input-color-placeholder: #999 + +//** Default `.form-control` height +// $input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) +//** Large `.form-control` height +// $input-height-large: (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) +//** Small `.form-control` height +// $input-height-small: (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) + +// $legend-color: $gray-dark +// $legend-border-color: #e5e5e5 + +//** Background color for textual input addons +// $input-group-addon-bg: $gray-lighter +//** Border color for textual input addons +// $input-group-addon-border-color: $input-border + +//** Disabled cursor for form controls and buttons. +// $cursor-disabled: not-allowed + + +//== Dropdowns +// +//## Dropdown menu container and contents. + +//** Background for the dropdown menu. +// $dropdown-bg: #fff +//** Dropdown menu `border-color`. +// $dropdown-border: rgba(0,0,0,.15) +//** Dropdown menu `border-color` **for IE8**. +// $dropdown-fallback-border: #ccc +//** Divider color for between dropdown items. +// $dropdown-divider-bg: #e5e5e5 + +//** Dropdown link text color. +// $dropdown-link-color: $gray-dark +//** Hover color for dropdown links. +// $dropdown-link-hover-color: darken($gray-dark, 5%) +//** Hover background for dropdown links. +// $dropdown-link-hover-bg: #f5f5f5 + +//** Active dropdown menu item text color. +// $dropdown-link-active-color: $component-active-color +//** Active dropdown menu item background color. +// $dropdown-link-active-bg: $component-active-bg + +//** Disabled dropdown menu item background color. +// $dropdown-link-disabled-color: $gray-light + +//** Text color for headers within dropdown menus. +// $dropdown-header-color: $gray-light + +//** Deprecated `$dropdown-caret-color` as of v3.1.0 +// $dropdown-caret-color: #000 + + +//-- Z-index master list +// +// Warning: Avoid customizing these values. They're used for a bird's eye view +// of components dependent on the z-axis and are designed to all work together. +// +// Note: These variables are not generated into the Customizer. + +// $zindex-navbar: 1000 +// $zindex-dropdown: 1000 +// $zindex-popover: 1060 +// $zindex-tooltip: 1070 +// $zindex-navbar-fixed: 1030 +// $zindex-modal: 1040 + + +//== Media queries breakpoints +// +//## Define the breakpoints at which your layout will change, adapting to different screen sizes. + +// Extra small screen / phone +//** Deprecated `$screen-xs` as of v3.0.1 +// $screen-xs: 480px +//** Deprecated `$screen-xs-min` as of v3.2.0 +// $screen-xs-min: $screen-xs +//** Deprecated `$screen-phone` as of v3.0.1 +// $screen-phone: $screen-xs-min + +// Small screen / tablet +//** Deprecated `$screen-sm` as of v3.0.1 +// $screen-sm: 768px +// $screen-sm-min: $screen-sm +//** Deprecated `$screen-tablet` as of v3.0.1 +// $screen-tablet: $screen-sm-min + +// Medium screen / desktop +//** Deprecated `$screen-md` as of v3.0.1 +// $screen-md: 992px +// $screen-md-min: $screen-md +//** Deprecated `$screen-desktop` as of v3.0.1 +// $screen-desktop: $screen-md-min + +// Large screen / wide desktop +//** Deprecated `$screen-lg` as of v3.0.1 +// $screen-lg: 1200px +// $screen-lg-min: $screen-lg +//** Deprecated `$screen-lg-desktop` as of v3.0.1 +// $screen-lg-desktop: $screen-lg-min + +// So media queries don't overlap when required, provide a maximum +// $screen-xs-max: ($screen-sm-min - 1) +// $screen-sm-max: ($screen-md-min - 1) +// $screen-md-max: ($screen-lg-min - 1) + + +//== Grid system +// +//## Define your custom responsive grid. + +//** Number of columns in the grid. +// $grid-columns: 12 +//** Padding between columns. Gets divided in half for the left and right. +// $grid-gutter-width: 30px +// Navbar collapse +//** Point at which the navbar becomes uncollapsed. +// $grid-float-breakpoint: $screen-sm-min +//** Point at which the navbar begins collapsing. +// $grid-float-breakpoint-max: ($grid-float-breakpoint - 1) + + +//== Container sizes +// +//## Define the maximum width of `.container` for different screen sizes. + +// Small screen / tablet +// $container-tablet: (720px + $grid-gutter-width) +//** For `$screen-sm-min` and up. +// $container-sm: $container-tablet + +// Medium screen / desktop +// $container-desktop: (940px + $grid-gutter-width) +//** For `$screen-md-min` and up. +// $container-md: $container-desktop + +// Large screen / wide desktop +// $container-large-desktop: (1140px + $grid-gutter-width) +//** For `$screen-lg-min` and up. +// $container-lg: $container-large-desktop + + +//== Navbar +// +//## + +// Basics of a navbar +// $navbar-height: 50px +// $navbar-margin-bottom: $line-height-computed +// $navbar-border-radius: $border-radius-base +// $navbar-padding-horizontal: floor(($grid-gutter-width / 2)) +// $navbar-padding-vertical: (($navbar-height - $line-height-computed) / 2) +// $navbar-collapse-max-height: 340px + +// $navbar-default-color: #777 +// $navbar-default-bg: #f8f8f8 +// $navbar-default-border: darken($navbar-default-bg, 6.5%) + +// Navbar links +// $navbar-default-link-color: #777 +// $navbar-default-link-hover-color: #333 +// $navbar-default-link-hover-bg: transparent +// $navbar-default-link-active-color: #555 +// $navbar-default-link-active-bg: darken($navbar-default-bg, 6.5%) +// $navbar-default-link-disabled-color: #ccc +// $navbar-default-link-disabled-bg: transparent + +// Navbar brand label +// $navbar-default-brand-color: $navbar-default-link-color +// $navbar-default-brand-hover-color: darken($navbar-default-brand-color, 10%) +// $navbar-default-brand-hover-bg: transparent + +// Navbar toggle +// $navbar-default-toggle-hover-bg: #ddd +// $navbar-default-toggle-icon-bar-bg: #888 +// $navbar-default-toggle-border-color: #ddd + + +// Inverted navbar +// Reset inverted navbar basics +// $navbar-inverse-color: lighten($gray-light, 15%) +// $navbar-inverse-bg: #222 +// $navbar-inverse-border: darken($navbar-inverse-bg, 10%) + +// Inverted navbar links +// $navbar-inverse-link-color: lighten($gray-light, 15%) +// $navbar-inverse-link-hover-color: #fff +// $navbar-inverse-link-hover-bg: transparent +// $navbar-inverse-link-active-color: $navbar-inverse-link-hover-color +// $navbar-inverse-link-active-bg: darken($navbar-inverse-bg, 10%) +// $navbar-inverse-link-disabled-color: #444 +// $navbar-inverse-link-disabled-bg: transparent + +// Inverted navbar brand label +// $navbar-inverse-brand-color: $navbar-inverse-link-color +// $navbar-inverse-brand-hover-color: #fff +// $navbar-inverse-brand-hover-bg: transparent + +// Inverted navbar toggle +// $navbar-inverse-toggle-hover-bg: #333 +// $navbar-inverse-toggle-icon-bar-bg: #fff +// $navbar-inverse-toggle-border-color: #333 + + +//== Navs +// +//## + +//=== Shared nav styles +// $nav-link-padding: 10px 15px +// $nav-link-hover-bg: $gray-lighter + +// $nav-disabled-link-color: $gray-light +// $nav-disabled-link-hover-color: $gray-light + +//== Tabs +// $nav-tabs-border-color: #ddd + +// $nav-tabs-link-hover-border-color: $gray-lighter + +// $nav-tabs-active-link-hover-bg: $body-bg +// $nav-tabs-active-link-hover-color: $gray +// $nav-tabs-active-link-hover-border-color: #ddd + +// $nav-tabs-justified-link-border-color: #ddd +// $nav-tabs-justified-active-link-border-color: $body-bg + +//== Pills +// $nav-pills-border-radius: $border-radius-base +// $nav-pills-active-link-hover-bg: $component-active-bg +// $nav-pills-active-link-hover-color: $component-active-color + + +//== Pagination +// +//## + +// $pagination-color: $link-color +// $pagination-bg: #fff +// $pagination-border: #ddd + +// $pagination-hover-color: $link-hover-color +// $pagination-hover-bg: $gray-lighter +// $pagination-hover-border: #ddd + +// $pagination-active-color: #fff +// $pagination-active-bg: $brand-primary +// $pagination-active-border: $brand-primary + +// $pagination-disabled-color: $gray-light +// $pagination-disabled-bg: #fff +// $pagination-disabled-border: #ddd + + +//== Pager +// +//## + +// $pager-bg: $pagination-bg +// $pager-border: $pagination-border +// $pager-border-radius: 15px + +// $pager-hover-bg: $pagination-hover-bg + +// $pager-active-bg: $pagination-active-bg +// $pager-active-color: $pagination-active-color + +// $pager-disabled-color: $pagination-disabled-color + + +//== Jumbotron +// +//## + +// $jumbotron-padding: 30px +// $jumbotron-color: inherit +// $jumbotron-bg: $gray-lighter +// $jumbotron-heading-color: inherit +// $jumbotron-font-size: ceil(($font-size-base * 1.5)) + + +//== Form states and alerts +// +//## Define colors for form feedback states and, by default, alerts. + +// $state-success-text: #3c763d +// $state-success-bg: #dff0d8 +// $state-success-border: darken(adjust-hue($state-success-bg, -10), 5%) + +// $state-info-text: #31708f +// $state-info-bg: #d9edf7 +// $state-info-border: darken(adjust-hue($state-info-bg, -10), 7%) + +// $state-warning-text: #8a6d3b +// $state-warning-bg: #fcf8e3 +// $state-warning-border: darken(adjust-hue($state-warning-bg, -10), 5%) + +// $state-danger-text: #a94442 +// $state-danger-bg: #f2dede +// $state-danger-border: darken(adjust-hue($state-danger-bg, -10), 5%) + + +//== Tooltips +// +//## + +//** Tooltip max width +// $tooltip-max-width: 200px +//** Tooltip text color +// $tooltip-color: #fff +//** Tooltip background color +// $tooltip-bg: #000 +// $tooltip-opacity: .9 + +//** Tooltip arrow width +// $tooltip-arrow-width: 5px +//** Tooltip arrow color +// $tooltip-arrow-color: $tooltip-bg + + +//== Popovers +// +//## + +//** Popover body background color +// $popover-bg: #fff +//** Popover maximum width +// $popover-max-width: 276px +//** Popover border color +// $popover-border-color: rgba(0,0,0,.2) +//** Popover fallback border color +// $popover-fallback-border-color: #ccc + +//** Popover title background color +// $popover-title-bg: darken($popover-bg, 3%) + +//** Popover arrow width +// $popover-arrow-width: 10px +//** Popover arrow color +// $popover-arrow-color: $popover-bg + +//** Popover outer arrow width +// $popover-arrow-outer-width: ($popover-arrow-width + 1) +//** Popover outer arrow color +// $popover-arrow-outer-color: fade_in($popover-border-color, 0.05) +//** Popover outer arrow fallback color +// $popover-arrow-outer-fallback-color: darken($popover-fallback-border-color, 20%) + + +//== Labels +// +//## + +//** Default label background color +// $label-default-bg: $gray-light +//** Primary label background color +// $label-primary-bg: $brand-primary +//** Success label background color +// $label-success-bg: $brand-success +//** Info label background color +// $label-info-bg: $brand-info +//** Warning label background color +// $label-warning-bg: $brand-warning +//** Danger label background color +// $label-danger-bg: $brand-danger + +//** Default label text color +// $label-color: #fff +//** Default text color of a linked label +// $label-link-hover-color: #fff + + +//== Modals +// +//## + +//** Padding applied to the modal body +// $modal-inner-padding: 15px + +//** Padding applied to the modal title +// $modal-title-padding: 15px +//** Modal title line-height +// $modal-title-line-height: $line-height-base + +//** Background color of modal content area +// $modal-content-bg: #fff +//** Modal content border color +// $modal-content-border-color: rgba(0,0,0,.2) +//** Modal content border color **for IE8** +// $modal-content-fallback-border-color: #999 + +//** Modal backdrop background color +// $modal-backdrop-bg: #000 +//** Modal backdrop opacity +// $modal-backdrop-opacity: .5 +//** Modal header border color +// $modal-header-border-color: #e5e5e5 +//** Modal footer border color +// $modal-footer-border-color: $modal-header-border-color + +// $modal-lg: 900px +// $modal-md: 600px +// $modal-sm: 300px + + +//== Alerts +// +//## Define alert colors, border radius, and padding. + +// $alert-padding: 15px +// $alert-border-radius: $border-radius-base +// $alert-link-font-weight: bold + +// $alert-success-bg: $state-success-bg +// $alert-success-text: $state-success-text +// $alert-success-border: $state-success-border + +// $alert-info-bg: $state-info-bg +// $alert-info-text: $state-info-text +// $alert-info-border: $state-info-border + +// $alert-warning-bg: $state-warning-bg +// $alert-warning-text: $state-warning-text +// $alert-warning-border: $state-warning-border + +// $alert-danger-bg: $state-danger-bg +// $alert-danger-text: $state-danger-text +// $alert-danger-border: $state-danger-border + + +//== Progress bars +// +//## + +//** Background color of the whole progress component +// $progress-bg: #f5f5f5 +//** Progress bar text color +// $progress-bar-color: #fff +//** Variable for setting rounded corners on progress bar. +// $progress-border-radius: $border-radius-base + +//** Default progress bar color +// $progress-bar-bg: $brand-primary +//** Success progress bar color +// $progress-bar-success-bg: $brand-success +//** Warning progress bar color +// $progress-bar-warning-bg: $brand-warning +//** Danger progress bar color +// $progress-bar-danger-bg: $brand-danger +//** Info progress bar color +// $progress-bar-info-bg: $brand-info + + +//== List group +// +//## + +//** Background color on `.list-group-item` +// $list-group-bg: #fff +//** `.list-group-item` border color +// $list-group-border: #ddd +//** List group border radius +// $list-group-border-radius: $border-radius-base + +//** Background color of single list items on hover +// $list-group-hover-bg: #f5f5f5 +//** Text color of active list items +// $list-group-active-color: $component-active-color +//** Background color of active list items +// $list-group-active-bg: $component-active-bg +//** Border color of active list elements +// $list-group-active-border: $list-group-active-bg +//** Text color for content within active list items +// $list-group-active-text-color: lighten($list-group-active-bg, 40%) + +//** Text color of disabled list items +// $list-group-disabled-color: $gray-light +//** Background color of disabled list items +// $list-group-disabled-bg: $gray-lighter +//** Text color for content within disabled list items +// $list-group-disabled-text-color: $list-group-disabled-color + +// $list-group-link-color: #555 +// $list-group-link-hover-color: $list-group-link-color +// $list-group-link-heading-color: #333 + + +//== Panels +// +//## + +// $panel-bg: #fff +// $panel-body-padding: 15px +// $panel-heading-padding: 10px 15px +// $panel-footer-padding: $panel-heading-padding +// $panel-border-radius: $border-radius-base + +//** Border color for elements within panels +// $panel-inner-border: #ddd +// $panel-footer-bg: #f5f5f5 + +// $panel-default-text: $gray-dark +// $panel-default-border: #ddd +// $panel-default-heading-bg: #f5f5f5 + +// $panel-primary-text: #fff +// $panel-primary-border: $brand-primary +// $panel-primary-heading-bg: $brand-primary + +// $panel-success-text: $state-success-text +// $panel-success-border: $state-success-border +// $panel-success-heading-bg: $state-success-bg + +// $panel-info-text: $state-info-text +// $panel-info-border: $state-info-border +// $panel-info-heading-bg: $state-info-bg + +// $panel-warning-text: $state-warning-text +// $panel-warning-border: $state-warning-border +// $panel-warning-heading-bg: $state-warning-bg + +// $panel-danger-text: $state-danger-text +// $panel-danger-border: $state-danger-border +// $panel-danger-heading-bg: $state-danger-bg + + +//== Thumbnails +// +//## + +//** Padding around the thumbnail image +// $thumbnail-padding: 4px +//** Thumbnail background color +// $thumbnail-bg: $body-bg +//** Thumbnail border color +// $thumbnail-border: #ddd +//** Thumbnail border radius +// $thumbnail-border-radius: $border-radius-base + +//** Custom text color for thumbnail captions +// $thumbnail-caption-color: $text-color +//** Padding around the thumbnail caption +// $thumbnail-caption-padding: 9px + + +//== Wells +// +//## + +// $well-bg: #f5f5f5 +// $well-border: darken($well-bg, 7%) + + +//== Badges +// +//## + +// $badge-color: #fff +//** Linked badge text color on hover +// $badge-link-hover-color: #fff +// $badge-bg: $gray-light + +//** Badge text color in active nav link +// $badge-active-color: $link-color +//** Badge background color in active nav link +// $badge-active-bg: #fff + +// $badge-font-weight: bold +// $badge-line-height: 1 +// $badge-border-radius: 10px + + +//== Breadcrumbs +// +//## + +// $breadcrumb-padding-vertical: 8px +// $breadcrumb-padding-horizontal: 15px +//** Breadcrumb background color +// $breadcrumb-bg: #f5f5f5 +//** Breadcrumb text color +// $breadcrumb-color: #ccc +//** Text color of current page in the breadcrumb +// $breadcrumb-active-color: $gray-light +//** Textual separator for between breadcrumb elements +// $breadcrumb-separator: "/" + + +//== Carousel +// +//## + +// $carousel-text-shadow: 0 1px 2px rgba(0,0,0,.6) + +// $carousel-control-color: #fff +// $carousel-control-width: 15% +// $carousel-control-opacity: .5 +// $carousel-control-font-size: 20px + +// $carousel-indicator-active-bg: #fff +// $carousel-indicator-border-color: #fff + +// $carousel-caption-color: #fff + + +//== Close +// +//## + +// $close-font-weight: bold +// $close-color: #000 +// $close-text-shadow: 0 1px 0 #fff + + +//== Code +// +//## + +// $code-color: #c7254e +// $code-bg: #f9f2f4 + +// $kbd-color: #fff +// $kbd-bg: #333 + +// $pre-bg: #f5f5f5 +// $pre-color: $gray-dark +// $pre-border-color: #ccc +// $pre-scrollable-max-height: 340px + + +//== Type +// +//## + +//** Horizontal offset for forms and lists. +// $component-offset-horizontal: 180px +//** Text muted color +// $text-muted: $gray-light +//** Abbreviations and acronyms border color +// $abbr-border-color: $gray-light +//** Headings small color +// $headings-small-color: $gray-light +//** Blockquote small color +// $blockquote-small-color: $gray-light +//** Blockquote font size +// $blockquote-font-size: ($font-size-base * 1.25) +//** Blockquote border color +// $blockquote-border-color: $gray-lighter +//** Page header border color +// $page-header-border-color: $gray-lighter +//** Width of horizontal description list titles +// $dl-horizontal-offset: $component-offset-horizontal +//** Horizontal line color. +// $hr-border: $gray-lighter diff --git a/templates/project/_bootstrap-variables.sass.erb b/templates/project/_bootstrap-variables.sass.erb deleted file mode 100644 index d5265d3..0000000 --- a/templates/project/_bootstrap-variables.sass.erb +++ /dev/null @@ -1,4 +0,0 @@ -<% require 'bootstrap-sass/version' %> -// Override Bootstrap variables here (defaults from bootstrap-sass v<%= Bootstrap::VERSION %>): - -<%= File.read(@template[:options][:bs_variables_path]).gsub(/^(?=\$)/, '// ').gsub(/ !default;/, '') %> diff --git a/templates/project/manifest.rb b/templates/project/manifest.rb index 20d6b63..93b4fac 100644 --- a/templates/project/manifest.rb +++ b/templates/project/manifest.rb @@ -3,24 +3,18 @@ description 'Bootstrap for Sass' # Stylesheet importing bootstrap stylesheet 'styles.sass' -manifest = Pathname.new(File.dirname(__FILE__)) -assets = File.expand_path('../../assets', manifest) - -# Provide variables files -bs_stylesheets = "#{assets}/stylesheets/bootstrap" -stylesheet '_bootstrap-variables.sass.erb', - :erb => true, - :to => '_bootstrap-variables.sass', - :bs_variables_path => File.expand_path("#{bs_stylesheets}/_variables.scss", manifest) +# Bootstrap variable overrides file +stylesheet '_bootstrap-variables.sass', :to => '_bootstrap-variables.sass' # Copy JS and fonts +manifest = Pathname.new(File.dirname(__FILE__)) +assets = File.expand_path('../../assets', manifest) {:javascript => 'javascripts', :font => 'fonts' }.each do |method, dir| root = Pathname.new(assets).join(dir) Dir.glob root.join('**', '*.*') do |path| path = Pathname.new(path) - send method, path.relative_path_from(manifest).to_s, - :to => path.relative_path_from(root).to_s + send method, path.relative_path_from(manifest).to_s, :to => path.relative_path_from(root).to_s end end diff --git a/test/dummy_rails/bin/bundle b/test/dummy_rails/bin/bundle deleted file mode 100755 index 66e9889..0000000 --- a/test/dummy_rails/bin/bundle +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -load Gem.bin_path('bundler', 'bundle') diff --git a/test/dummy_rails/bin/rails b/test/dummy_rails/bin/rails deleted file mode 100755 index 728cd85..0000000 --- a/test/dummy_rails/bin/rails +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -APP_PATH = File.expand_path('../../config/application', __FILE__) -require_relative '../config/boot' -require 'rails/commands' diff --git a/test/dummy_rails/bin/rake b/test/dummy_rails/bin/rake deleted file mode 100755 index 1724048..0000000 --- a/test/dummy_rails/bin/rake +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env ruby -require_relative '../config/boot' -require 'rake' -Rake.application.run diff --git a/test/dummy_rails/db/test.sqlite3 b/test/dummy_rails/db/test.sqlite3 deleted file mode 100644 index e69de29..0000000 --- a/test/dummy_rails/db/test.sqlite3 +++ /dev/null diff --git a/test/dummy_rails/lib/assets/.keep b/test/dummy_rails/lib/assets/.keep deleted file mode 100644 index e69de29..0000000 --- a/test/dummy_rails/lib/assets/.keep +++ /dev/null diff --git a/test/dummy_rails/log/production.log b/test/dummy_rails/log/production.log deleted file mode 100644 index e69de29..0000000 --- a/test/dummy_rails/log/production.log +++ /dev/null diff --git a/test/dummy_rails/public/404.html b/test/dummy_rails/public/404.html deleted file mode 100644 index a0daa0c..0000000 --- a/test/dummy_rails/public/404.html +++ /dev/null @@ -1,58 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>The page you were looking for doesn't exist (404)</title> - <style> - body { - background-color: #EFEFEF; - color: #2E2F30; - text-align: center; - font-family: arial, sans-serif; - } - - div.dialog { - width: 25em; - margin: 4em auto 0 auto; - border: 1px solid #CCC; - border-right-color: #999; - border-left-color: #999; - border-bottom-color: #BBB; - border-top: #B00100 solid 4px; - border-top-left-radius: 9px; - border-top-right-radius: 9px; - background-color: white; - padding: 7px 4em 0 4em; - } - - h1 { - font-size: 100%; - color: #730E15; - line-height: 1.5em; - } - - body > p { - width: 33em; - margin: 0 auto 1em; - padding: 1em 0; - background-color: #F7F7F7; - border: 1px solid #CCC; - border-right-color: #999; - border-bottom-color: #999; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-top-color: #DADADA; - color: #666; - box-shadow:0 3px 8px rgba(50, 50, 50, 0.17); - } - </style> -</head> - -<body> - <!-- This file lives in public/404.html --> - <div class="dialog"> - <h1>The page you were looking for doesn't exist.</h1> - <p>You may have mistyped the address or the page may have moved.</p> - </div> - <p>If you are the application owner check the logs for more information.</p> -</body> -</html> diff --git a/test/dummy_rails/public/422.html b/test/dummy_rails/public/422.html deleted file mode 100644 index fbb4b84..0000000 --- a/test/dummy_rails/public/422.html +++ /dev/null @@ -1,58 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>The change you wanted was rejected (422)</title> - <style> - body { - background-color: #EFEFEF; - color: #2E2F30; - text-align: center; - font-family: arial, sans-serif; - } - - div.dialog { - width: 25em; - margin: 4em auto 0 auto; - border: 1px solid #CCC; - border-right-color: #999; - border-left-color: #999; - border-bottom-color: #BBB; - border-top: #B00100 solid 4px; - border-top-left-radius: 9px; - border-top-right-radius: 9px; - background-color: white; - padding: 7px 4em 0 4em; - } - - h1 { - font-size: 100%; - color: #730E15; - line-height: 1.5em; - } - - body > p { - width: 33em; - margin: 0 auto 1em; - padding: 1em 0; - background-color: #F7F7F7; - border: 1px solid #CCC; - border-right-color: #999; - border-bottom-color: #999; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-top-color: #DADADA; - color: #666; - box-shadow:0 3px 8px rgba(50, 50, 50, 0.17); - } - </style> -</head> - -<body> - <!-- This file lives in public/422.html --> - <div class="dialog"> - <h1>The change you wanted was rejected.</h1> - <p>Maybe you tried to change something you didn't have access to.</p> - </div> - <p>If you are the application owner check the logs for more information.</p> -</body> -</html> diff --git a/test/dummy_rails/public/500.html b/test/dummy_rails/public/500.html deleted file mode 100644 index e9052d3..0000000 --- a/test/dummy_rails/public/500.html +++ /dev/null @@ -1,57 +0,0 @@ -<!DOCTYPE html> -<html> -<head> - <title>We're sorry, but something went wrong (500)</title> - <style> - body { - background-color: #EFEFEF; - color: #2E2F30; - text-align: center; - font-family: arial, sans-serif; - } - - div.dialog { - width: 25em; - margin: 4em auto 0 auto; - border: 1px solid #CCC; - border-right-color: #999; - border-left-color: #999; - border-bottom-color: #BBB; - border-top: #B00100 solid 4px; - border-top-left-radius: 9px; - border-top-right-radius: 9px; - background-color: white; - padding: 7px 4em 0 4em; - } - - h1 { - font-size: 100%; - color: #730E15; - line-height: 1.5em; - } - - body > p { - width: 33em; - margin: 0 auto 1em; - padding: 1em 0; - background-color: #F7F7F7; - border: 1px solid #CCC; - border-right-color: #999; - border-bottom-color: #999; - border-bottom-left-radius: 4px; - border-bottom-right-radius: 4px; - border-top-color: #DADADA; - color: #666; - box-shadow:0 3px 8px rgba(50, 50, 50, 0.17); - } - </style> -</head> - -<body> - <!-- This file lives in public/500.html --> - <div class="dialog"> - <h1>We're sorry, but something went wrong.</h1> - </div> - <p>If you are the application owner check the logs for more information.</p> -</body> -</html> diff --git a/test/dummy_rails/public/favicon.ico b/test/dummy_rails/public/favicon.ico deleted file mode 100644 index e69de29..0000000 --- a/test/dummy_rails/public/favicon.ico +++ /dev/null diff --git a/test/node_sass_compile_test.sh b/test/node_sass_compile_test.sh new file mode 100755 index 0000000..df6314a --- /dev/null +++ b/test/node_sass_compile_test.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +# Test compilation with node-sass binary + +mkdir -p tmp/node-sass +node-sass assets/stylesheets/bootstrap -o tmp/node-sass/bootstrap.css && \ +node-sass assets/stylesheets/bootstrap/theme -o tmp/node-sass/bootstrap-theme.css || \ +(echo "node-sass compilation failed" && exit 1) diff --git a/test/node_sass_test.rb b/test/node_sass_test.rb deleted file mode 100644 index d1c68ec..0000000 --- a/test/node_sass_test.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'test_helper' -require 'fileutils' - -class NodeSassTest < Minitest::Test - def test_node_sass_compilation - path = 'assets/stylesheets' - %w(bootstrap bootstrap/_theme).each do |file| - FileUtils.mkdir_p 'tmp/node-sass' - command = "node-sass #{path}/#{file} -o tmp/node-sass/#{File.basename file}.css" - success = silence_stderr_if !ENV['VERBOSE'] do - system command - end - assert success, 'node-sass compilation failed' - end - end -end diff --git a/test/support/integration_test.rb b/test/support/integration_test.rb deleted file mode 100644 index b49bb3a..0000000 --- a/test/support/integration_test.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'capybara' -module IntegrationTest - include Capybara::DSL - - def setup - super - %x[rm -rf test/dummy_rails/tmp/cache] - end - - def teardown - super - Capybara.reset_sessions! - Capybara.use_default_driver - end - - def screenshot! - screenshot_dir = File.expand_path('../../tmp/', File.dirname(__FILE__)) - page.driver.render(File.join(screenshot_dir, "#{name}.png"), :full => true) - source = page.evaluate_script("document.getElementsByTagName('html')[0].outerHTML") rescue nil - File.open(File.join(screenshot_dir, "#{name}.html"), 'w') { |f| f.write(source) } if source - end -end |