diff --git a/.gitignore b/.gitignore index b3a86b50..9f541587 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /db/*.sqlite3-journal # Ignore all logfiles and tempfiles. +logfile log/* /log/*.log /tmp @@ -19,6 +20,9 @@ log/* # Ignore dotenv .env +# Ignore generated javascript translations +/public/javascripts/translations.js + # Ignore other things *swp *~ diff --git a/Gemfile b/Gemfile index fdd484db..d25f3ae4 100644 --- a/Gemfile +++ b/Gemfile @@ -17,10 +17,13 @@ gem 'devise', '~> 4.2.0' gem 'geokit', '~> 1.10.0' gem 'haml', '~> 4.0.7' gem 'hashie', '~> 3.5.1' +gem 'i18n-js', '>= 3.0.0.rc13' gem 'jquery-rails', '~> 3.1.4' +gem 'jquery-ui-rails' gem 'puma', '~> 3.7.0' gem 'rails_admin', '~> 1.1.0' gem 'rails_admin_enum4', '~> 0.1.3' +gem 'react-rails' gem 'sass-rails', '~> 5.0.6' gem 'validates_formatting_of', '~> 0.9.0' @@ -43,6 +46,7 @@ group :development, :test do gem 'dotenv-rails', '~> 2.2.0' gem 'faker', '~> 1.7.1' gem 'factory_girl_rails', '~> 4.8.0' + gem 'jasmine-rails' gem 'pry-rails', '~> 0.3.4' gem 'rspec-rails', '~> 3.5.2' end diff --git a/Gemfile.lock b/Gemfile.lock index 25168c6e..f893e4aa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,10 @@ GEM arel (5.0.1.20140414130214) autoprefixer-rails (6.7.2) execjs + babel-source (5.8.35) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) + execjs (~> 2.0) bcrypt (3.1.11) bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) @@ -92,6 +96,7 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.4) + connection_pool (2.2.0) database_cleaner (1.5.3) devise (4.2.0) bcrypt (~> 3.0) @@ -143,6 +148,14 @@ GEM tilt hashie (3.5.1) i18n (0.8.0) + i18n-js (3.0.0.rc13) + i18n (~> 0.6, >= 0.6.6) + jasmine-core (2.4.1) + jasmine-rails (0.12.6) + jasmine-core (>= 1.3, < 3.0) + phantomjs (>= 1.9) + railties (>= 3.2.0) + sprockets-rails jquery-rails (3.1.4) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) @@ -182,6 +195,7 @@ GEM shellany (~> 0.0) orm_adapter (0.5.0) pg (0.19.0) + phantomjs (2.1.1.0) poltergeist (1.13.0) capybara (~> 2.1) cliver (~> 0.3.1) @@ -245,6 +259,13 @@ GEM rb-fsevent (0.9.8) rb-inotify (0.9.8) ffi (>= 0.5.0) + react-rails (1.8.0) + babel-transpiler (>= 0.7.0) + coffee-script-source (~> 1.8) + connection_pool + execjs + railties (>= 3.2) + tilt remotipart (1.3.1) responders (1.1.2) railties (>= 3.2, < 4.2) @@ -352,7 +373,10 @@ DEPENDENCIES guard-rspec (~> 4.7.3) haml (~> 4.0.7) hashie (~> 3.5.1) + i18n-js (>= 3.0.0.rc13) + jasmine-rails jquery-rails (~> 3.1.4) + jquery-ui-rails launchy (~> 2.4.3) libnotify nokogiri (= 1.7.0.1) @@ -366,6 +390,7 @@ DEPENDENCIES rails_12factor (~> 0.0.3) rails_admin (~> 1.1.0) rails_admin_enum4 (~> 0.1.3) + react-rails rspec-rails (~> 3.5.2) sandi_meter (~> 1.2.0) sass-rails (~> 5.0.6) diff --git a/adopt-a-tree.sublime-project b/adopt-a-tree.sublime-project new file mode 100644 index 00000000..24db3031 --- /dev/null +++ b/adopt-a-tree.sublime-project @@ -0,0 +1,8 @@ +{ + "folders": + [ + { + "path": "." + } + ] +} diff --git a/app/assets/javascripts/adopta.js.erb b/app/assets/javascripts/adopta.js.erb new file mode 100644 index 00000000..0bc711f2 --- /dev/null +++ b/app/assets/javascripts/adopta.js.erb @@ -0,0 +1,5 @@ +AdoptA = { + green_marker_image_path: '<%= image_path 'markers/marker-green.png' %>', + yellow_marker_image_path: '<%= image_path 'markers/marker-yellow.png' %>', + shadow_marker_image_path: '<%= image_path 'markers/shadow.png' %>' +} diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 5bc2e1c8..1650691e 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -9,5 +9,11 @@ // // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details // about supported directives. -// +//= require jquery +//= require jquery-ui +//= require react +//= require react_ujs +//= require i18n +//= require i18n/translations +//= require components //= require_tree . diff --git a/app/assets/javascripts/bootstrap.js b/app/assets/javascripts/bootstrap.js index ca868671..44109f62 100644 --- a/app/assets/javascripts/bootstrap.js +++ b/app/assets/javascripts/bootstrap.js @@ -1,8 +1,8 @@ /* =================================================== - * bootstrap-transition.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#transitions + * bootstrap-transition.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#transitions * =================================================== - * Copyright 2012 Twitter, Inc. + * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,42 +17,51 @@ * limitations under the License. * ========================================================== */ -!function( $ ) { - $(function () { +!function ($) { + + "use strict"; // jshint ;_; - "use strict" - /* CSS TRANSITION SUPPORT (https://gist.github.com/373874) - * ======================================================= */ + /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) + * ======================================================= */ + + $(function () { $.support.transition = (function () { - var thisBody = document.body || document.documentElement - , thisStyle = thisBody.style - , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined - - return support && { - end: (function () { - var transitionEnd = "TransitionEnd" - if ( $.browser.webkit ) { - transitionEnd = "webkitTransitionEnd" - } else if ( $.browser.mozilla ) { - transitionEnd = "transitionend" - } else if ( $.browser.opera ) { - transitionEnd = "oTransitionEnd" + + var transitionEnd = (function () { + + var el = document.createElement('bootstrap') + , transEndEventNames = { + 'WebkitTransition' : 'webkitTransitionEnd' + , 'MozTransition' : 'transitionend' + , 'OTransition' : 'oTransitionEnd otransitionend' + , 'transition' : 'transitionend' + } + , name + + for (name in transEndEventNames){ + if (el.style[name] !== undefined) { + return transEndEventNames[name] } - return transitionEnd - }()) + } + + }()) + + return transitionEnd && { + end: transitionEnd } + })() }) -}( window.jQuery );/* ========================================================== - * bootstrap-alert.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#alerts +}(window.jQuery);/* ========================================================== + * bootstrap-alert.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#alerts * ========================================================== - * Copyright 2012 Twitter, Inc. + * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -68,61 +77,59 @@ * ========================================================== */ -!function( $ ){ +!function ($) { + + "use strict"; // jshint ;_; - "use strict" /* ALERT CLASS DEFINITION * ====================== */ var dismiss = '[data-dismiss="alert"]' - , Alert = function ( el ) { + , Alert = function (el) { $(el).on('click', dismiss, this.close) } - Alert.prototype = { + Alert.prototype.close = function (e) { + var $this = $(this) + , selector = $this.attr('data-target') + , $parent - constructor: Alert - - , close: function ( e ) { - var $this = $(this) - , selector = $this.attr('data-target') - , $parent + if (!selector) { + selector = $this.attr('href') + selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 + } - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } + $parent = $(selector) - $parent = $(selector) - $parent.trigger('close') + e && e.preventDefault() - e && e.preventDefault() + $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) - $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) + $parent.trigger(e = $.Event('close')) - $parent - .trigger('close') - .removeClass('in') + if (e.isDefaultPrevented()) return - function removeElement() { - $parent - .trigger('closed') - .remove() - } + $parent.removeClass('in') - $.support.transition && $parent.hasClass('fade') ? - $parent.on($.support.transition.end, removeElement) : - removeElement() + function removeElement() { + $parent + .trigger('closed') + .remove() } + $.support.transition && $parent.hasClass('fade') ? + $parent.on($.support.transition.end, removeElement) : + removeElement() } /* ALERT PLUGIN DEFINITION * ======================= */ - $.fn.alert = function ( option ) { + var old = $.fn.alert + + $.fn.alert = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('alert') @@ -134,18 +141,25 @@ $.fn.alert.Constructor = Alert + /* ALERT NO CONFLICT + * ================= */ + + $.fn.alert.noConflict = function () { + $.fn.alert = old + return this + } + + /* ALERT DATA-API * ============== */ - $(function () { - $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) - }) + $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) -}( window.jQuery );/* ============================================================ - * bootstrap-button.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#buttons +}(window.jQuery);/* ============================================================ + * bootstrap-button.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#buttons * ============================================================ - * Copyright 2012 Twitter, Inc. + * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -160,58 +174,56 @@ * limitations under the License. * ============================================================ */ -!function( $ ){ - "use strict" +!function ($) { + + "use strict"; // jshint ;_; + /* BUTTON PUBLIC CLASS DEFINITION * ============================== */ - var Button = function ( element, options ) { + var Button = function (element, options) { this.$element = $(element) this.options = $.extend({}, $.fn.button.defaults, options) } - Button.prototype = { - - constructor: Button - - , setState: function ( state ) { - var d = 'disabled' - , $el = this.$element - , data = $el.data() - , val = $el.is('input') ? 'val' : 'html' + Button.prototype.setState = function (state) { + var d = 'disabled' + , $el = this.$element + , data = $el.data() + , val = $el.is('input') ? 'val' : 'html' - state = state + 'Text' - data.resetText || $el.data('resetText', $el[val]()) + state = state + 'Text' + data.resetText || $el.data('resetText', $el[val]()) - $el[val](data[state] || this.options[state]) + $el[val](data[state] || this.options[state]) - // push to event loop to allow forms to submit - setTimeout(function () { - state == 'loadingText' ? - $el.addClass(d).attr(d, d) : - $el.removeClass(d).removeAttr(d) - }, 0) - } + // push to event loop to allow forms to submit + setTimeout(function () { + state == 'loadingText' ? + $el.addClass(d).attr(d, d) : + $el.removeClass(d).removeAttr(d) + }, 0) + } - , toggle: function () { - var $parent = this.$element.parent('[data-toggle="buttons-radio"]') + Button.prototype.toggle = function () { + var $parent = this.$element.closest('[data-toggle="buttons-radio"]') - $parent && $parent - .find('.active') - .removeClass('active') - - this.$element.toggleClass('active') - } + $parent && $parent + .find('.active') + .removeClass('active') + this.$element.toggleClass('active') } /* BUTTON PLUGIN DEFINITION * ======================== */ - $.fn.button = function ( option ) { + var old = $.fn.button + + $.fn.button = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('button') @@ -229,22 +241,29 @@ $.fn.button.Constructor = Button + /* BUTTON NO CONFLICT + * ================== */ + + $.fn.button.noConflict = function () { + $.fn.button = old + return this + } + + /* BUTTON DATA-API * =============== */ - $(function () { - $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { - var $btn = $(e.target) - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') - $btn.button('toggle') - }) + $(document).on('click.button.data-api', '[data-toggle^=button]', function (e) { + var $btn = $(e.target) + if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') + $btn.button('toggle') }) -}( window.jQuery );/* ========================================================== - * bootstrap-carousel.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#carousel +}(window.jQuery);/* ========================================================== + * bootstrap-carousel.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#carousel * ========================================================== - * Copyright 2012 Twitter, Inc. + * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -260,17 +279,18 @@ * ========================================================== */ -!function( $ ){ +!function ($) { + + "use strict"; // jshint ;_; - "use strict" /* CAROUSEL CLASS DEFINITION * ========================= */ var Carousel = function (element, options) { this.$element = $(element) - this.options = $.extend({}, $.fn.carousel.defaults, options) - this.options.slide && this.slide(this.options.slide) + this.$indicators = this.$element.find('.carousel-indicators') + this.options = options this.options.pause == 'hover' && this.$element .on('mouseenter', $.proxy(this.pause, this)) .on('mouseleave', $.proxy(this.cycle, this)) @@ -278,18 +298,26 @@ Carousel.prototype = { - cycle: function () { - this.interval = setInterval($.proxy(this.next, this), this.options.interval) + cycle: function (e) { + if (!e) this.paused = false + if (this.interval) clearInterval(this.interval); + this.options.interval + && !this.paused + && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) return this } + , getActiveIndex: function () { + this.$active = this.$element.find('.item.active') + this.$items = this.$active.parent().children() + return this.$items.index(this.$active) + } + , to: function (pos) { - var $active = this.$element.find('.active') - , children = $active.parent().children() - , activePos = children.index($active) + var activeIndex = this.getActiveIndex() , that = this - if (pos > (children.length - 1) || pos < 0) return + if (pos > (this.$items.length - 1) || pos < 0) return if (this.sliding) { return this.$element.one('slid', function () { @@ -297,14 +325,19 @@ }) } - if (activePos == pos) { + if (activeIndex == pos) { return this.pause().cycle() } - return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos])) + return this.slide(pos > activeIndex ? 'next' : 'prev', $(this.$items[pos])) } - , pause: function () { + , pause: function (e) { + if (!e) this.paused = true + if (this.$element.find('.next, .prev').length && $.support.transition.end) { + this.$element.trigger($.support.transition.end) + this.cycle(true) + } clearInterval(this.interval) this.interval = null return this @@ -321,12 +354,13 @@ } , slide: function (type, next) { - var $active = this.$element.find('.active') + var $active = this.$element.find('.item.active') , $next = next || $active[type]() , isCycling = this.interval , direction = type == 'next' ? 'left' : 'right' , fallback = type == 'next' ? 'first' : 'last' , that = this + , e this.sliding = true @@ -334,26 +368,41 @@ $next = $next.length ? $next : this.$element.find('.item')[fallback]() + e = $.Event('slide', { + relatedTarget: $next[0] + , direction: direction + }) + if ($next.hasClass('active')) return - if (!$.support.transition && this.$element.hasClass('slide')) { - this.$element.trigger('slide') - $active.removeClass('active') - $next.addClass('active') - this.sliding = false - this.$element.trigger('slid') - } else { + if (this.$indicators.length) { + this.$indicators.find('.active').removeClass('active') + this.$element.one('slid', function () { + var $nextIndicator = $(that.$indicators.children()[that.getActiveIndex()]) + $nextIndicator && $nextIndicator.addClass('active') + }) + } + + if ($.support.transition && this.$element.hasClass('slide')) { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return $next.addClass(type) $next[0].offsetWidth // force reflow $active.addClass(direction) $next.addClass(direction) - this.$element.trigger('slide') this.$element.one($.support.transition.end, function () { $next.removeClass([type, direction].join(' ')).addClass('active') $active.removeClass(['active', direction].join(' ')) that.sliding = false setTimeout(function () { that.$element.trigger('slid') }, 0) }) + } else { + this.$element.trigger(e) + if (e.isDefaultPrevented()) return + $active.removeClass('active') + $next.addClass('active') + this.sliding = false + this.$element.trigger('slid') } isCycling && this.cycle() @@ -367,15 +416,18 @@ /* CAROUSEL PLUGIN DEFINITION * ========================== */ - $.fn.carousel = function ( option ) { + var old = $.fn.carousel + + $.fn.carousel = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('carousel') - , options = typeof option == 'object' && option + , options = $.extend({}, $.fn.carousel.defaults, typeof option == 'object' && option) + , action = typeof option == 'string' ? option : options.slide if (!data) $this.data('carousel', (data = new Carousel(this, options))) if (typeof option == 'number') data.to(option) - else if (typeof option == 'string' || (option = options.slide)) data[option]() - else data.cycle() + else if (action) data[action]() + else if (options.interval) data.pause().cycle() }) } @@ -387,24 +439,37 @@ $.fn.carousel.Constructor = Carousel + /* CAROUSEL NO CONFLICT + * ==================== */ + + $.fn.carousel.noConflict = function () { + $.fn.carousel = old + return this + } + /* CAROUSEL DATA-API * ================= */ - $(function () { - $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) { - var $this = $(this), href - , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 - , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data()) - $target.carousel(options) - e.preventDefault() - }) + $(document).on('click.carousel.data-api', '[data-slide], [data-slide-to]', function (e) { + var $this = $(this), href + , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7 + , options = $.extend({}, $target.data(), $this.data()) + , slideIndex + + $target.carousel(options) + + if (slideIndex = $this.attr('data-slide-to')) { + $target.data('carousel').pause().to(slideIndex).cycle() + } + + e.preventDefault() }) -}( window.jQuery );/* ============================================================= - * bootstrap-collapse.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#collapse +}(window.jQuery);/* ============================================================= + * bootstrap-collapse.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#collapse * ============================================================= - * Copyright 2012 Twitter, Inc. + * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -419,16 +484,21 @@ * limitations under the License. * ============================================================ */ -!function( $ ){ - "use strict" +!function ($) { + + "use strict"; // jshint ;_; + + + /* COLLAPSE PUBLIC CLASS DEFINITION + * ================================ */ - var Collapse = function ( element, options ) { - this.$element = $(element) + var Collapse = function (element, options) { + this.$element = $(element) this.options = $.extend({}, $.fn.collapse.defaults, options) - if (this.options["parent"]) { - this.$parent = $(this.options["parent"]) + if (this.options.parent) { + this.$parent = $(this.options.parent) } this.options.toggle && this.toggle() @@ -444,31 +514,39 @@ } , show: function () { - var dimension = this.dimension() - , scroll = $.camelCase(['scroll', dimension].join('-')) - , actives = this.$parent && this.$parent.find('.in') + var dimension + , scroll + , actives , hasData + if (this.transitioning || this.$element.hasClass('in')) return + + dimension = this.dimension() + scroll = $.camelCase(['scroll', dimension].join('-')) + actives = this.$parent && this.$parent.find('> .accordion-group > .in') + if (actives && actives.length) { hasData = actives.data('collapse') + if (hasData && hasData.transitioning) return actives.collapse('hide') hasData || actives.data('collapse', null) } this.$element[dimension](0) - this.transition('addClass', 'show', 'shown') - this.$element[dimension](this.$element[0][scroll]) - + this.transition('addClass', $.Event('show'), 'shown') + $.support.transition && this.$element[dimension](this.$element[0][scroll]) } , hide: function () { - var dimension = this.dimension() + var dimension + if (this.transitioning || !this.$element.hasClass('in')) return + dimension = this.dimension() this.reset(this.$element[dimension]()) - this.transition('removeClass', 'hide', 'hidden') + this.transition('removeClass', $.Event('hide'), 'hidden') this.$element[dimension](0) } - , reset: function ( size ) { + , reset: function (size) { var dimension = this.dimension() this.$element @@ -476,41 +554,49 @@ [dimension](size || 'auto') [0].offsetWidth - this.$element[size ? 'addClass' : 'removeClass']('collapse') + this.$element[size !== null ? 'addClass' : 'removeClass']('collapse') return this } - , transition: function ( method, startEvent, completeEvent ) { + , transition: function (method, startEvent, completeEvent) { var that = this , complete = function () { - if (startEvent == 'show') that.reset() + if (startEvent.type == 'show') that.reset() + that.transitioning = 0 that.$element.trigger(completeEvent) } - this.$element - .trigger(startEvent) - [method]('in') + this.$element.trigger(startEvent) + + if (startEvent.isDefaultPrevented()) return + + this.transitioning = 1 + + this.$element[method]('in') $.support.transition && this.$element.hasClass('collapse') ? this.$element.one($.support.transition.end, complete) : complete() - } + } , toggle: function () { this[this.$element.hasClass('in') ? 'hide' : 'show']() - } + } } - /* COLLAPSIBLE PLUGIN DEFINITION - * ============================== */ - $.fn.collapse = function ( option ) { + /* COLLAPSE PLUGIN DEFINITION + * ========================== */ + + var old = $.fn.collapse + + $.fn.collapse = function (option) { return this.each(function () { var $this = $(this) , data = $this.data('collapse') - , options = typeof option == 'object' && option + , options = $.extend({}, $.fn.collapse.defaults, $this.data(), typeof option == 'object' && option) if (!data) $this.data('collapse', (data = new Collapse(this, options))) if (typeof option == 'string') data[option]() }) @@ -523,25 +609,33 @@ $.fn.collapse.Constructor = Collapse - /* COLLAPSIBLE DATA-API + /* COLLAPSE NO CONFLICT * ==================== */ - $(function () { - $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) { - var $this = $(this), href - , target = $this.attr('data-target') - || e.preventDefault() - || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 - , option = $(target).data('collapse') ? 'toggle' : $this.data() - $(target).collapse(option) - }) + $.fn.collapse.noConflict = function () { + $.fn.collapse = old + return this + } + + + /* COLLAPSE DATA-API + * ================= */ + + $(document).on('click.collapse.data-api', '[data-toggle=collapse]', function (e) { + var $this = $(this), href + , target = $this.attr('data-target') + || e.preventDefault() + || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7 + , option = $(target).data('collapse') ? 'toggle' : $this.data() + $this[$(target).hasClass('in') ? 'addClass' : 'removeClass']('collapsed') + $(target).collapse(option) }) -}( window.jQuery );/* ============================================================ - * bootstrap-dropdown.js v2.0.2 - * http://twitter.github.com/bootstrap/javascript.html#dropdowns +}(window.jQuery);/* ============================================================ + * bootstrap-dropdown.js v2.3.2 + * http://getbootstrap.com/2.3.2/javascript.html#dropdowns * ============================================================ - * Copyright 2012 Twitter, Inc. + * Copyright 2013 Twitter, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -557,15 +651,16 @@ * ============================================================ */ -!function( $ ){ +!function ($) { + + "use strict"; // jshint ;_; - "use strict" /* DROPDOWN CLASS DEFINITION * ========================= */ - var toggle = '[data-toggle="dropdown"]' - , Dropdown = function ( element ) { + var toggle = '[data-toggle=dropdown]' + , Dropdown = function (element) { var $el = $(element).on('click.dropdown.data-api', this.toggle) $('html').on('click.dropdown.data-api', function () { $el.parent().removeClass('open') @@ -576,39 +671,105 @@ constructor: Dropdown - , toggle: function ( e ) { + , toggle: function (e) { var $this = $(this) - , selector = $this.attr('data-target') , $parent , isActive - if (!selector) { - selector = $this.attr('href') - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 - } + if ($this.is('.disabled, :disabled')) return - $parent = $(selector) - $parent.length || ($parent = $this.parent()) + $parent = getParent($this) isActive = $parent.hasClass('open') clearMenus() - !isActive && $parent.toggleClass('open') + + if (!isActive) { + if ('ontouchstart' in document.documentElement) { + // if mobile we we use a backdrop because click events don't delegate + $('