diff --git a/config/assets.yml b/config/assets.yml index 7ba2110d6..3aae5c5df 100644 --- a/config/assets.yml +++ b/config/assets.yml @@ -15,6 +15,7 @@ javascripts: - public/javascripts/vendor/jquery.hotkeys.js - public/javascripts/vendor/jquery.autoresize.min.js - public/javascripts/vendor/jquery-ui-1.8.9.custom.min.js + - public/javascripts/vendor/jquery-debounce.js - public/javascripts/vendor/jquery.expander.js - public/javascripts/vendor/timeago.js - public/javascripts/vendor/Mustache.js @@ -38,6 +39,7 @@ javascripts: - public/javascripts/widgets/direction-detector.js - public/javascripts/widgets/notifications.js - public/javascripts/widgets/flash-messages.js + - public/javascripts/widgets/back-to-top.js - public/javascripts/widgets/stream.js - public/javascripts/widgets/stream-element.js - public/javascripts/widgets/comment-stream.js diff --git a/public/javascripts/pages/aspects-index.js b/public/javascripts/pages/aspects-index.js index 2438b9ad8..c39fc7ffa 100644 --- a/public/javascripts/pages/aspects-index.js +++ b/public/javascripts/pages/aspects-index.js @@ -5,6 +5,7 @@ Diaspora.Pages.AspectsIndex = function() { self.stream = self.instantiate("Stream", document.find("#aspect_stream_container")); self.header = self.instantiate("Header", document.find("header")); + self.backToTop = self.instantiate("BackToTop", document.find("#back-to-top")); self.hoverCard = self.instantiate("HoverCard", document.find("#hovercard")); self.infiniteScroll = self.instantiate("InfiniteScroll"); self.timeAgo = self.instantiate("TimeAgo", "abbr.timeago"); diff --git a/public/javascripts/vendor/jquery-debounce.js b/public/javascripts/vendor/jquery-debounce.js new file mode 100644 index 000000000..453882d5a --- /dev/null +++ b/public/javascripts/vendor/jquery-debounce.js @@ -0,0 +1,21 @@ + (function($) { + function debounce(callback, delay) { + var self = this, timeout, _arguments; + return function() { + _arguments = Array.prototype.slice.call(arguments, 0), + timeout = clearTimeout(timeout, _arguments), + timeout = setTimeout(function() { + callback.apply(self, _arguments); + timeout = 0; + }, delay); + + return this; + }; + } + + $.extend($.fn, { + debounce: function(event, callback, delay) { + this.bind(event, debounce.apply(this, [callback, delay])); + } + }); +})(jQuery); \ No newline at end of file diff --git a/public/javascripts/view.js b/public/javascripts/view.js index 11477239b..405fb551c 100644 --- a/public/javascripts/view.js +++ b/public/javascripts/view.js @@ -25,19 +25,6 @@ var View = { $('#main_stream label').inFieldLabels(); }); - /*scroll to top */ - var back_to_top = jQuery('#back-to-top'); - jQuery(window).scroll(function(){ - if(jQuery(window).scrollTop() > 1000){ - // show back to top - back_to_top.stop().animate({opacity: .5}); - } - else{ - // hide back to top - back_to_top.stop().animate({opacity: 0}); - } - }); - /* "Toggling" the search input */ $(this.search.selector) .blur(this.search.blur) diff --git a/public/javascripts/widgets/back-to-top.js b/public/javascripts/widgets/back-to-top.js new file mode 100644 index 000000000..429166926 --- /dev/null +++ b/public/javascripts/widgets/back-to-top.js @@ -0,0 +1,32 @@ +(function() { + var BackToTop = function() { + var self = this; + + this.subscribe("widget/ready", function(evt, button) { + $.extend(self, { + button: button, + body: $(document.body), + window: $(window) + }); + + self.button.click(self.backToTop); + self.window.debounce("scroll", self.toggleVisibility, 250); + }); + + this.backToTop = function(evt) { + evt.preventDefault(); + + self.body.animate({scrollTop: 0}); + }; + + this.toggleVisibility = function() { + self.button.animate({ + opacity: (self.body.scrollTop() > 1000) + ? 0.5 + : 0 + }); + }; + }; + + Diaspora.Widgets.BackToTop = BackToTop; +})(); \ No newline at end of file diff --git a/public/javascripts/widgets/stream-element.js b/public/javascripts/widgets/stream-element.js index f5d95b00e..9da3596e5 100644 --- a/public/javascripts/widgets/stream-element.js +++ b/public/javascripts/widgets/stream-element.js @@ -24,6 +24,4 @@ }; Diaspora.Widgets.StreamElement = StreamElement; -})(); - - +})(); \ No newline at end of file diff --git a/spec/controllers/aspects_controller_spec.rb b/spec/controllers/aspects_controller_spec.rb index 1207756ed..27e05734f 100644 --- a/spec/controllers/aspects_controller_spec.rb +++ b/spec/controllers/aspects_controller_spec.rb @@ -73,7 +73,7 @@ describe AspectsController do save_fixture(html_for("body"), "aspects_index_with_posts") end - it 'generates a jasmine fixture with a followed tag' do + it 'generates a jasmine fixture with a followed tag', :fixture => true do @tag = ActsAsTaggableOn::Tag.create!(:name => "partytimeexcellent") TagFollowing.create!(:tag => @tag, :user => alice) get :index diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml index 2f2af8f03..29c820c2f 100644 --- a/spec/javascripts/support/jasmine.yml +++ b/spec/javascripts/support/jasmine.yml @@ -16,7 +16,8 @@ src_files: - public/javascripts/vendor/jquery.tipsy.js - public/javascripts/jquery.infieldlabel-custom.js - public/javascripts/vendor/jquery.autoresize.min.js - - public/javascripts/vendor/jquery.expander.js + - public/javascripts/vendor/jquery.expander.js] + - public/javascripts/vendor/jquery-debounce.js - public/javascripts/vendor/Mustache.js - public/javascripts/vendor/charCount.js - public/javascripts/vendor/timeago.js diff --git a/spec/javascripts/widgets/back-to-top-spec.js b/spec/javascripts/widgets/back-to-top-spec.js new file mode 100644 index 000000000..cadfa99a8 --- /dev/null +++ b/spec/javascripts/widgets/back-to-top-spec.js @@ -0,0 +1,66 @@ +describe("Diaspora.Widgets.BackToTop", function() { + var backToTop; + beforeEach(function() { + spec.loadFixture("aspects_index"); + backToTop = Diaspora.BaseWidget.instantiate("BackToTop", $("#back-to-top")); + $.fx.off = true; + }); + + describe("integration", function() { + beforeEach(function() { + backToTop = new Diaspora.Widgets.BackToTop(); + + spyOn(backToTop, "backToTop"); + spyOn(backToTop, "toggleVisibility"); + + backToTop.publish("widget/ready", [$("#back-to-top")]); + }); + + it("calls backToTop when the button is clicked", function() { + backToTop.button.click(); + + expect(backToTop.backToTop).toHaveBeenCalled(); + }); + + it("calls toggleVisibility after a delay", function() { + jasmine.Clock.useMock(); + + backToTop.window.trigger("scroll"); + + expect(backToTop.toggleVisibility).not.toHaveBeenCalled(); + + jasmine.Clock.tick(5000); + + expect(backToTop.toggleVisibility).toHaveBeenCalled(); + }); + + }); + + describe("backToTop", function() { + it("animates scrollTop to 0", function() { + backToTop.backToTop($.Event()); + + expect($("body").scrollTop()).toEqual(0); + }); + }); + + describe("toggleVisibility", function() { + it("animates the button's opacity based on where the user is scrolled", function() { + var spy = spyOn(backToTop.body, "scrollTop").andReturn(999); + + backToTop.toggleVisibility(); + + expect(backToTop.button.css("opacity")).toEqual("0"); + + spy.andReturn(1001); + + backToTop.toggleVisibility(); + + expect(backToTop.button.css("opacity")).toEqual("0.5"); + }); + }); + + afterEach(function() { + $.fx.off = false; + }); +}); \ No newline at end of file