diff --git a/public/javascripts/rails.js b/public/javascripts/rails.js index 080fbac5b..2adc48f29 100644 --- a/public/javascripts/rails.js +++ b/public/javascripts/rails.js @@ -1,3 +1,23 @@ +/* Clear form plugin - called using $("elem").clearForm(); */ +$.fn.clearForm = function() { + return this.each(function() { + var type = this.type, tag = this.tagName.toLowerCase(); + if (tag == 'form') + return $(':input',this).clearForm(); + if (type == 'text' || type == 'password' || tag == 'textarea') + this.value = ''; + else if (type == 'checkbox' || type == 'radio') + this.checked = false; + else if (tag == 'select') + this.selectedIndex = -1; + else if (this.name == 'photos[]') + this.value = ''; + $(this).blur(); + }); +}; + + + jQuery(function ($) { var csrf_token = $('meta[name=csrf-token]').attr('content'), csrf_param = $('meta[name=csrf-param]').attr('content'); @@ -76,8 +96,8 @@ jQuery(function ($) { $('form[data-remote]').live('submit', function (e) { $(this).callRemote(); e.preventDefault(); - $(this).clearForm(); - $(this).focusout(); + $(this).clearForm(); + $(this).focusout(); }); $('a[data-remote],input[data-remote]').live('click', function (e) { diff --git a/public/javascripts/stream.js b/public/javascripts/stream.js index 6cab70efe..175db54b6 100644 --- a/public/javascripts/stream.js +++ b/public/javascripts/stream.js @@ -15,6 +15,12 @@ var Stream = { $(this).closest("form").children(".comment_box").attr("rows", 1); }); + $stream.delegate("textarea.comment_box", "keydown", function(e){ + if (e.keyCode === 13) { + $(this).closest("form").submit(); + } + }); + $stream.delegate("textarea.comment_box", "focus", function(evt) { var commentBox = $(this); commentBox.attr("rows", 2) diff --git a/public/javascripts/view.js b/public/javascripts/view.js index f5197ad7e..68ac59146 100644 --- a/public/javascripts/view.js +++ b/public/javascripts/view.js @@ -2,127 +2,198 @@ * licensed under the Affero General Public License version 3 or later. See * the COPYRIGHT file. */ +var View = { + initialize: function() { + /* Buttons */ + $("input[type='submit']").addClass("button"); -$(document).ready(function(){ - $('#debug_info').click(function() { - $('#debug_more').toggle('fast'); - }); + /* Tooltips */ + this.tooltips.bindAll(); - $("label").inFieldLabels(); + /* Animate flashes */ + this.flashes.animate(); - $('#flash_notice, #flash_error, #flash_alert').animate({ - top: 0 - }).delay(2000).animate({ - top: -100 - }, $(this).remove()); + /* In field labels */ + $("label").inFieldLabels(); + + /* Focus aspect name on fancybox */ + $(this.addAspectButton.selector) + .click(this.addAspectButton.click); - //buttons////// - $(".add_aspect_button," + - ".manage_aspect_contacts_button," + - ".add_photo_button," + - ".remove_person_button," + - ".question_mark").fancybox({ 'titleShow': false , 'hideOnOverlayClick' : false }); + /* Showing debug messages */ + $(this.debug.selector) + .click(this.debug.click); - $("input[type='submit']").addClass("button"); + /* "Toggling" the search input */ + $(this.search.selector) + .blur(this.search.blur) + .focus(this.search.focus); - // focus aspect name on fancybox - $(".add_aspect_button").click( function(){ - $("#aspect_name").focus(); - }); + /* Getting started animation */ + $(this.gettingStarted.selector) + .live("click", this.gettingStarted.click); - $("#q").focus( - function() { - $(this).addClass('active'); + /* Submitting the status message form when the user hits enter */ + $(this.publisher.selector) + .keyup(this.publisher.keyup); + + /* User menu */ + $(this.userMenu.selector) + .click(this.userMenu.click); + + /* Sending a request message */ + $(this.newRequest.selector) + .live("submit", this.newRequest.submit); + + /* Button fancyboxes */ + $(this.fancyBoxButtons.selectors.join(", ")) + .fancybox({ + 'titleShow': false, + 'hideOnOverlayClick' : false + }); + + /* Webfinger form ajaxy loading */ + $(this.webFingerForm.selector) + .submit(this.webFingerForm.submit); + + $(document.body) + .click(this.userMenu.removeFocus); + }, + + addAspectButton: { + click: function() { + $("#aspect_name").focus(); + }, + selector: ".add_aspect_button" + }, + + fancyBoxButtons: { + selectors: [ + ".add_aspect_button", + ".manage_aspect_contacts_button", + ".invite_user_button", + ".add_photo_button", + ".remove_person_button", + ".question_mark" + ] + }, + + debug: { + click: function() { + $("#debug_more").toggle("fast"); + }, + selector: "#debug_info" + }, + + flashes: { + animate: function() { + var $this = $(View.flashes.selector); + $this.animate({ + top: 0 + }).delay(2000).animate({ + top: -100 + }, $this.remove()) + }, + selector: "#flash_notice, #flash_error, #flash_alert" + + }, + + gettingStarted: { + click: function() { + var $this = $(this); + $this.animate({ + left: parseInt($this.css("left"), 30) === 0 ? -$this.outerWidth() : 0 + }, function() { + $this.css("left", "1000px"); + }); + }, + selector: ".getting_started_box" + }, + + newRequest: { + submit: function() { + $(this).hide().parent().find(".message").removeClass("hidden"); + }, + selector: ".new_request" + }, + + publisher: { + keyup: function(e) { + if(e.keyCode === 13) { + $(this).closest("form").submit(); + } + }, + selector: "#publisher textarea" + }, + + search: { + blur: function() { + $(this).removeClass("active"); + }, + focus: function() { + $(this).addClass("active"); + }, + selector: "#q" + }, + + tooltips: { + addAspect: { + bind: function() { + $(".add_aspect_button", "#aspect_nav").tipsy({ + gravity:"w" + }); + } + }, + + avatars: { + bind: function() { + $("img.avatar").tipsy({ + live: true + }); + } + }, + + whatIsThis: { + bind: function() { + $(".what_is_this").tipsy({ + live: true, + delayIn: 400 + }); + } + }, + + bindAll: function() { + for(var element in this) { + if(element !== "bindAll") { + this[element].bind(); + } + }; } - ); + }, - $('.new_request').live("submit", function(){ - var foo = $(this).parent(); - $(this).hide(); - foo.find('.message').removeClass('hidden'); - }); + userMenu: { + click: function() { + $(this).toggleClass("active"); + }, + removeFocus: function(evt) { + var $target = $(evt.target); + if(!$target.closest("#user_menu").length) { + $(View.userMenu.selector).removeClass("active"); + } + }, + selector: "#user_menu" + }, - - $("#q").blur( - function() { - $(this).removeClass('active'); - } - ); - - $("#publisher").find("textarea").keydown( function(e) { - if (e.keyCode === 13) { - $(this).closest("form").submit(); - } - }); - - $(".stream").delegate("textarea.comment_box", "keydown", function(e){ - if (e.keyCode === 13) { - $(this).closest("form").submit(); - } - }); - - $("#user_menu").click( function(){ - $(this).toggleClass("active"); - }); - - $('body').click( function(event){ - var target = $(event.target); - if(!target.closest('#user_menu').length){ - $("#user_menu").removeClass("active"); - }; - if(!target.closest('.reshare_pane').length){ - $(".reshare_button").removeClass("active"); - $(".reshare_box").hide(); - }; - }); - - $("img", "#left_pane").tipsy({live:true}); - $(".add_aspect_button", "#aspect_nav").tipsy({gravity:'w'}); - $(".person img", ".dropzone").tipsy({live:true}); - $(".avatar", ".aspects").tipsy({live:true}); - $(".what_is_this").tipsy({live:true,delayIn:400}); - - $('.webfinger_form').submit(function(evt){ - form = $(evt.currentTarget); - form.siblings('#loader').show(); - $('#request_result li:first').hide(); - }); - - // hotkeys - $(window).bind('keyup', 'ctrl+f', function(){ - $("#q").focus(); - }); - - $(window).bind('keyup', 'ctrl+e', function(){ - EditPane.toggle(); - }); - -});//end document ready - - -//Called with $(selector).clearForm() -$.fn.clearForm = function() { - return this.each(function() { - var type = this.type, tag = this.tagName.toLowerCase(); - if (tag == 'form') - return $(':input',this).clearForm(); - if (type == 'text' || type == 'password' || tag == 'textarea') - this.value = ''; - else if (type == 'checkbox' || type == 'radio') - this.checked = false; - else if (tag == 'select') - this.selectedIndex = -1; - else if (this.name == 'photos[]') - this.value = ''; - $(this).blur(); - }); + webFingerForm: { + submit: function(evt) { + $(evt.currentTarget).siblings("#loader").show(); + $("#request_result li:first").hide(); + }, + selector: ".webfinger_form" + } }; -$(".getting_started_box").live("click",function(evt){ - $(this).animate({ - left: parseInt($(this).css('left'),30) == 0 ? - -$(this).outerWidth() : - 0 - },function(evt){ $(this).css('left', '1000px')}); +$(function() { + /* Make sure this refers to View, not the document */ + View.initialize.apply(View); }); - diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml index 1b5cb5a34..5620aea1a 100644 --- a/spec/javascripts/support/jasmine.yml +++ b/spec/javascripts/support/jasmine.yml @@ -14,14 +14,16 @@ src_files: - public/javascripts/vendor/jquery144.js - public/javascripts/vendor/jquery-ui-1.8.6.custom.min.js - public/javascripts/vendor/jquery.tipsy.js - - public/javascripts/validation.js + - public/javascripts/vendor/jquery.infieldlabel.js + - public/javascripts/vendor/fancybox/jquery.fancybox-1.3.1.pack.js - public/javascripts/diaspora.js - public/javascripts/mobile.js - public/javascripts/aspect-edit.js - public/javascripts/aspect-contacts.js - public/javascripts/web-socket-receiver.js + - public/javascripts/view.js - public/javascripts/stream.js - + - public/javascripts/validation.js # stylesheets # # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. diff --git a/spec/javascripts/view-spec.js b/spec/javascripts/view-spec.js new file mode 100644 index 000000000..5c079487c --- /dev/null +++ b/spec/javascripts/view-spec.js @@ -0,0 +1,236 @@ +describe("View", function() { + it("is the object that helps the UI", function() { + expect(typeof View === "object").toBeTruthy(); + }); + + describe("initialize", function() { + it("is called on DOM ready", function() { + spyOn(View, "initialize"); + $(View.initialize); + expect(View.initialize).toHaveBeenCalled(); + }); + }); + + describe("fancyBoxButtons", function() { + describe("selectors", function() { + it("is an array of all the selectors that will have fancybox attached", function() { + expect(typeof View.fancyBoxButtons.selectors === "object").toBeTruthy(); + expect($.isArray(View.fancyBoxButtons.selectors)).toBeTruthy(); + }); + }); + }); + + describe("debug", function() { + describe("click", function() { + beforeEach(function() { + $("#jasmine_content").html( + '
' + + '
DEBUG INFO
' + + '' + + '
' + ); + }); + + it("is called when the user clicks an element matching the selector", function() { + spyOn(View.debug, "click"); + View.initialize(); + $(View.debug.selector).click(); + expect(View.debug.click).toHaveBeenCalled(); + setTimeout(function() { + expect($(View.debug.selector).css("display")).toEqual("block"); + }, 500); + }); + }); + }); + + describe("flashes", function() { + describe("animate", function() { + beforeEach(function() { + $("#jasmine_content").html( + '
' + + 'flash! flash! flash!' + + '
' + ); + }); + + it("is called when the DOM is ready", function() { + spyOn(View.flashes, "animate").andCallThrough(); + View.initialize(); + expect(View.flashes.animate).toHaveBeenCalled(); + }); + }); + }); + + describe("newRequest", function() { + beforeEach(function() { + $("#jasmine_content").html( + '
' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '
' + ); + }); + + describe("submit", function() { + it("is called when the user submits the form", function() { + spyOn(View.newRequest, "submit").andCallThrough(); + View.initialize(); + $(View.newRequest.selector).submit(function(evt) { evt.preventDefault(); }); + $(View.newRequest.selector).trigger("submit"); + expect(View.newRequest.submit).toHaveBeenCalled(); + expect($(View.newRequest.selector + " .message").css("display")).toEqual("block"); + }); + }); + }); + + describe("publisher", function() { + beforeEach(function() { + $("#jasmine_content").html( + '
' + + '
' + + '' + + '
' + + '
' + ); + }); + + describe("keyup", function() { + it("is called when the user types", function() { + spyOn(View.publisher, "keyup"); + View.initialize(); + $(View.publisher.selector).trigger("keyup"); + expect(View.publisher.keyup).toHaveBeenCalled(); + }); + + it("submits the form if the user hits enter while the textarea is focused", function() { + spyOn($.fn, "submit"); + View.initialize(); + $(View.publisher.selector).focus(); + var event = $.Event("keyup"); + event.keyCode = 13; + $(View.publisher.selector).trigger(event); + expect($.fn.submit).toHaveBeenCalled(); + }); + }); + }); + + describe("search", function() { + beforeEach(function() { + $("#jasmine_content").html( + '' + ); + }); + describe("focus", function() { + it("adds the class 'active' when the user focuses the text field", function() { + View.initialize(); + $(View.search.selector).focus(); + expect($(View.search.selector)).toHaveClass("active"); + }); + }); + describe("blur", function() { + it("removes the class 'active' when the user blurs the text field", function() { + View.initialize(); + $(View.search.selector).focus().blur(); + expect($(View.search.selector)).not.toHaveClass("active"); + }); + }); + }); + + describe("tooltips", function() { + describe("bindAll", function() { + //Someone shorten this plz <3 + it("enumerates through the tooltips object, called the method 'bind' on any sibling that is not the bindAll method", function() { + spyOn($, "noop"); + View.initialize(); + View.tooltips.myToolTip = { bind: $.noop }; + View.tooltips.bindAll(); + expect($.noop).toHaveBeenCalled(); + }); + }); + }); + + describe("userMenu", function() { + beforeEach(function() { + $("#jasmine_content").html( + '' + ); + }); + describe("click", function() { + it("adds the class 'active' when the user clicks the ul", function() { + View.initialize(); + $(View.userMenu.selector).click(); + expect($(View.userMenu.selector)).toHaveClass("active"); + }); + }); + describe("removeFocus", function() { + it("removes the class 'active' if the user clicks anywhere that isnt the userMenu", function() { + View.initialize(); + $(View.userMenu.selector).click(); + expect($(View.userMenu.selector)).toHaveClass("active"); + var event = $.Event("click"); + event.target = document.body; + $(document.body).trigger(event); + expect($(View.userMenu.selector)).not.toHaveClass("active"); + }); + }); + }); + + describe("webFingerForm", function() { + beforeEach(function() { + $("#jasmine_content").html( + '
' + + '

' + + 'Add a new contact' + + '

' + + '
' + + '' + + '' + + '
' + + + '' + + '' + + '
' + ); + + // Prevent the form from being submitted + $(View.webFingerForm.selector).submit(function(evt) { evt.preventDefault(); }); + }); + describe("submit", function() { + it("shows the ajax loader after the user submits the form", function() { + View.initialize(); + $(View.webFingerForm.selector).submit(); + expect($(View.webFingerForm.selector).siblings("#loader").css("display")).toEqual("block"); + }); + + it("hides the first list item in the result ul after the user submits the form", function() { + View.initialize(); + $(View.webFingerForm.selector).submit(); + expect($("#request_result li:first").css("display")).toEqual("none"); + }); + }); + }); +});