diff --git a/app/assets/javascripts/app/views/publisher/aspects_selector.js b/app/assets/javascripts/app/views/publisher/aspects_selector.js new file mode 100644 index 0000000000000000000000000000000000000000..ecb2c576433f43c8e06c05cfd7aae322a5979569 --- /dev/null +++ b/app/assets/javascripts/app/views/publisher/aspects_selector.js @@ -0,0 +1,62 @@ +/* Copyright (c) 2010-2012, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + +(function(){ + // mixin-object, used in conjunction with the publisher to provide the + // functionality for selecting aspects + app.views.PublisherAspectsSelector = { + + // event handler for aspect selection + toggleAspect: function(evt) { + var el = $(evt.target); + var btn = el.parent('.dropdown').find('.button'); + + // visually toggle the aspect selection + if( el.is('.radio') ) { + AspectsDropdown.toggleRadio(el); + } else { + AspectsDropdown.toggleCheckbox(el); + } + + // update the selection summary + AspectsDropdown.updateNumber( + el.closest(".dropdown_list"), + null, + el.parent().find('li.selected').length, + '' + ); + + this._updateSelectedAspectIds(); + }, + + // take care of the form fields that will indicate the selected aspects + _updateSelectedAspectIds: function() { + var self = this; + + // remove previous selection + this.$('input[name="aspect_ids[]"]').remove(); + + // create fields for current selection + this.$('.dropdown .dropdown_list li.selected').each(function() { + var el = $(this); + var aspectId = el.data('aspect_id'); + + self._addHiddenAspectInput(aspectId); + + // close the dropdown when a radio item was selected + if( el.is('.radio') ) { + el.closest('.dropdown').removeClass('active'); + } + }); + }, + + _addHiddenAspectInput: function(id) { + var uid = _.uniqueId('aspect_ids_'); + this.$('.content_creation form').append( + '<input id="'+uid+'" name="aspect_ids[]" type="hidden" value="'+id+'">' + ); + } + }; +})(); \ No newline at end of file diff --git a/app/assets/javascripts/app/views/publisher/getting_started.js b/app/assets/javascripts/app/views/publisher/getting_started.js new file mode 100644 index 0000000000000000000000000000000000000000..815f05585473d4c03506063c2eb4f52f1ecc3ad9 --- /dev/null +++ b/app/assets/javascripts/app/views/publisher/getting_started.js @@ -0,0 +1,65 @@ +/* Copyright (c) 2010-2012, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + +(function(){ + // mixin-object, used in conjunction with the publisher to provide the + // functionality for displaying 'getting-started' information + app.views.PublisherGettingStarted = { + + // initiate all the popover message boxes + triggerGettingStarted: function() { + this._addPopover(this.el_input, { + trigger: 'manual', + offset: 30, + id: 'first_message_explain', + placement: 'right', + html: true + }, 600); + this._addPopover(this.$('.dropdown'), { + trigger: 'manual', + offset: 10, + id: 'message_visibility_explain', + placement: 'bottom', + html: true + }, 1000); + this._addPopover($('#gs-shim'), { + trigger: 'manual', + offset: -5, + id: 'stream_explain', + placement: 'left', + html: true + }, 1400); + + // hide some popovers when a post is created + this.$('.button.creation').click(function() { + this.$('.dropdown').popover('hide'); + this.el_input.popover('hide'); + }); + }, + + _addPopover: function(el, opts, timeout) { + el.popover(opts); + el.click(function() { + el.popover('hide'); + }); + + // show the popover after the given timeout + setTimeout(function() { + el.popover('show'); + + // disable 'getting started' when the last popover is closed + var popup = el.data('popover').$tip[0]; + var close = $(popup).find('.close'); + + close.click(function() { + if( $('.popover').length==1 ) { + $.get('/getting_started_completed'); + } + el.popover('hide'); + }); + }, timeout); + } + }; +})(); \ No newline at end of file diff --git a/app/assets/javascripts/app/views/publisher/services.js b/app/assets/javascripts/app/views/publisher/services.js new file mode 100644 index 0000000000000000000000000000000000000000..f8f250093e13e62030fcca77b2222dfc9f1feecd --- /dev/null +++ b/app/assets/javascripts/app/views/publisher/services.js @@ -0,0 +1,51 @@ +/* Copyright (c) 2010-2012, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + +(function(){ + // mixin-object, used in conjunction with the publisher to provide the + // functionality for selecting services for cross-posting + app.views.PublisherServices = { + + // visually toggle the icon and kick-off all other actions for cross-posting + toggleService: function(evt) { + var el = $(evt.target); + var provider = el.attr('id'); + + el.toggleClass("dim"); + + this._createCounter(); + this._toggleServiceField(provider); + }, + + // keep track of character count + _createCounter: function() { + // remove obsolete counter + this.$('.counter').remove(); + + // create new counter + var min = 40000; + var a = this.$('.service_icon:not(.dim)'); + if(a.length > 0){ + $.each(a, function(index, value){ + var num = parseInt($(value).attr('maxchar')); + if (min > num) { min = num; } + }); + this.el_input.charCount({allowed: min, warning: min/10 }); + } + }, + + // add or remove the input containing the selected service + _toggleServiceField: function(provider) { + var hidden_field = this.$('input[name="services[]"][value="'+provider+'"]'); + if(hidden_field.length > 0){ + hidden_field.remove(); + } else { + var uid = _.uniqueId('services_'); + this.$(".content_creation form").append( + '<input id="'+uid+'" name="services[]" type="hidden" value="'+provider+'">'); + } + } + }; +})(); \ No newline at end of file diff --git a/app/assets/javascripts/app/views/publisher_view.js b/app/assets/javascripts/app/views/publisher_view.js index 24b61ff7f8fbea814b484a7a9fae22b8259b607f..c8c952f24331744a7d81876941e8de051180048e 100644 --- a/app/assets/javascripts/app/views/publisher_view.js +++ b/app/assets/javascripts/app/views/publisher_view.js @@ -1,19 +1,57 @@ -//this file is the scary no-no-zone bad-touch of our backbone code. -//after re-writing/eliminating the existing Publisher let's re-write -//this with PANACHE! <333 Dennis +/* Copyright (c) 2010-2012, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + +//= require ./publisher/services +//= require ./publisher/aspects_selector +//= require ./publisher/getting_started + +app.views.Publisher = Backbone.View.extend(_.extend( + app.views.PublisherServices, + app.views.PublisherAspectsSelector, + app.views.PublisherGettingStarted, { -app.views.Publisher = Backbone.View.extend({ - el : "#publisher", events : { "focus textarea" : "open", "click #hide_publisher" : "clear", - "submit form" : "createStatusMessage" + "submit form" : "createStatusMessage", + "click .service_icon": "toggleService", + "textchange #status_message_fake_text": "handleTextchange", + "click .dropdown .dropdown_list li": "toggleAspect" }, initialize : function(){ - this.collection = this.collection //takes a Posts collection + // init shortcut references to the various elements + this.el_input = this.$('#status_message_fake_text'); + this.el_hiddenInput = this.$('#status_message_text'); + this.el_wrapper = this.$('#publisher_textarea_wrapper'); + this.el_submit = this.$('input[type=submit]'); + this.el_photozone = this.$('#photodropzone'); + + // init mentions plugin + Mentions.initialize(this.el_input); + + // init autoresize plugin + this.el_input.autoResize({ 'extraSpace' : 10, 'maxHeight' : Infinity }); + + // sync textarea content + if( this.el_hiddenInput.val() == "" ) { + this.el_hiddenInput.val( this.el_input.val() ); + } + + // hide close button, in case publisher is standalone + // (e.g. bookmarklet, mentions popup) + if( this.options.standalone ) { + this.$('#hide_publisher').hide(); + } + + // this has to be here, otherwise for some reason the callback for the + // textchange event won't be called in Backbone... + this.el_input.bind('textchange', $.noop); + return this; }, @@ -49,36 +87,72 @@ app.views.Publisher = Backbone.View.extend({ }, clear : function() { - this.$('textarea').val(""); - this.$('#publisher_textarea_wrapper').removeClass("with_attachments"); + // clear text(s) + this.el_input.val(''); + this.el_hiddenInput.val(''); + + // remove mentions + this.el_input.mentionsInput('reset'); // remove photos - this.$("#photodropzone").find('li').remove(); + this.el_photozone.find('li').remove(); this.$("input[name='photos[]']").remove(); + this.el_wrapper.removeClass("with_attachments"); // close publishing area (CSS) this.close(); - Publisher.clear() + // disable submitting + this.checkSubmitAvailability(); return this; }, open : function() { - $(this.el).removeClass('closed'); - this.$("#publisher_textarea_wrapper").addClass('active'); + // visually 'open' the publisher + this.$el.removeClass('closed'); + this.el_wrapper.addClass('active'); + + // fetch contacts for mentioning + Mentions.fetchContacts(); return this; }, close : function() { $(this.el).addClass("closed"); - this.$("#publisher_textarea_wrapper").removeClass("active"); - this.$("textarea").css('height', ''); + this.el_wrapper.removeClass("active"); + this.el_input.css('height', ''); return this; + }, + + checkSubmitAvailability: function() { + if( this._submittable() ) { + this.el_submit.removeAttr('disabled'); + } else { + this.el_submit.attr('disabled','disabled'); + } + }, + + // determine submit availability + _submittable: function() { + var onlyWhitespaces = ($.trim(this.el_input.val()) === ''), + isPhotoAttached = (this.el_photozone.children().length > 0); + + return (!onlyWhitespaces || isPhotoAttached); + }, + + handleTextchange: function() { + var self = this; + + this.checkSubmitAvailability(); + this.el_input.mentionsInput("val", function(value){ + self.el_hiddenInput.val(value); + }); } -}); + +})); // jQuery helper for serializing a <form> into JSON $.fn.serializeObject = function() diff --git a/app/assets/javascripts/aspects-dropdown.js b/app/assets/javascripts/aspects-dropdown.js index 61f035d17a630487e1c2193801b2b21d3b58bd32..50ee48fab0f1296a2e165fb90934706a89943afd 100644 --- a/app/assets/javascripts/aspects-dropdown.js +++ b/app/assets/javascripts/aspects-dropdown.js @@ -1,4 +1,4 @@ -// Copyright (c) 2010-2011, Diaspora Inc. This file is +// Copyright (c) 2010-2012, Diaspora Inc. This file is // licensed under the Affero General Public License version 3 or later. See // the COPYRIGHT file. diff --git a/app/assets/javascripts/home.js b/app/assets/javascripts/home.js index 7eb2db8743210b85343a131a898cf374d39168a0..e53601da111a71450248a75204411d4c32905af3 100644 --- a/app/assets/javascripts/home.js +++ b/app/assets/javascripts/home.js @@ -2,7 +2,7 @@ * licensed under the Affero General Public License version 3 or later. See * the COPYRIGHT file. */ -//= require publisher + //= require jquery.textchange //= require aspect-edit-pane //= require fileuploader-custom \ No newline at end of file diff --git a/app/assets/javascripts/publisher.js b/app/assets/javascripts/publisher.js deleted file mode 100644 index 40eeb458024de798c2c2d3db2b7b379d6ec5d22c..0000000000000000000000000000000000000000 --- a/app/assets/javascripts/publisher.js +++ /dev/null @@ -1,221 +0,0 @@ -/* Copyright (c) 2010-2011, Diaspora Inc. This file is - * licensed under the Affero General Public License version 3 or later. See - * the COPYRIGHT file. - */ - -//TODO: make this a widget -var Publisher = { - bookmarklet : false, - - form: function(){ - return Publisher.cachedForm = Publisher.cachedForm || $('#publisher'); - }, - - input: function(){ - return Publisher.cachedInput = Publisher.cachedInput || Publisher.form().find('#status_message_fake_text'); - }, - - wrapper: function(){ - return Publisher.cachedWrapper = Publisher.cachedWrapper || Publisher.form().find('#publisher_textarea_wrapper'); - }, - - hiddenInput: function(){ - return Publisher.cachedHiddenInput= Publisher.cachedHiddenInput || Publisher.form().find('#status_message_text'); - }, - - submit: function(){ - return Publisher.cachedSubmit = Publisher.cachedSubmit || Publisher.form().find("input[type='submit']"); - }, - - determineSubmitAvailability: function(){ - var onlyWhitespaces = ($.trim(Publisher.input().val()) === ''), - isSubmitDisabled = Publisher.submit().attr('disabled'), - isPhotoAttached = ($("#photodropzone").children().length > 0); - - if ((onlyWhitespaces && !isPhotoAttached) && !isSubmitDisabled) { - Publisher.submit().attr('disabled', 'disabled'); - } else if ((!onlyWhitespaces || isPhotoAttached) && isSubmitDisabled) { - Publisher.submit().removeAttr('disabled'); - } - }, - - clear: function(){ - $("#photodropzone").find('li').remove(); - Publisher.input().mentionsInput("reset"); - Publisher.wrapper().removeClass("with_attachments"); - Publisher.hiddenInput().val(''); - Publisher.determineSubmitAvailability() - }, - - bindServiceIcons: function(){ - $(".service_icon").bind("click", function(evt){ - $(this).toggleClass("dim"); - Publisher.toggleServiceField($(this)); - }); - }, - - toggleServiceField: function(service){ - Publisher.createCounter(service); - - var provider = service.attr('id'); - var hidden_field = $('#publisher [name="services[]"][value="'+provider+'"]'); - if(hidden_field.length > 0){ - hidden_field.remove(); - } else { - $("#publisher .content_creation form").append( - '<input id="services_" name="services[]" type="hidden" value="'+provider+'">'); - } - }, - - isPublicPost: function(){ - return $('#publisher [name="aspect_ids[]"]').first().val() == "public"; - }, - - isToAllAspects: function(){ - return $('#publisher [name="aspect_ids[]"]').first().val() == "all_aspects"; - }, - - selectedAspectIds: function() { - var aspects = $('#publisher [name="aspect_ids[]"]'); - var aspectIds = []; - aspects.each(function() { aspectIds.push( parseInt($(this).attr('value'))); }); - return aspectIds; - }, - - removeRadioSelection: function(hiddenFields){ - $.each(hiddenFields, function(index, value){ - var el = $(value); - - if(el.val() == "all_aspects" || el.val() == "public") { - el.remove(); - } - }); - }, - - toggleAspectIds: function(li) { - var aspectId = li.attr('data-aspect_id'), - hiddenFields = $('#publisher [name="aspect_ids[]"]'), - appendId = function(){ - $("#publisher .content_creation form").append( - '<input id="aspect_ids_" name="aspect_ids[]" type="hidden" value="'+aspectId+'">'); - }; - - if(li.hasClass('radio')){ - $.each(hiddenFields, function(index, value){ - $(value).remove(); - }); - appendId(); - - // close dropdown after selecting a binary option - li.closest('.dropdown').removeClass('active'); - - } else { - var hiddenField = $('#publisher [name="aspect_ids[]"][value="'+aspectId+'"]'); - - // remove all radio selections - Publisher.removeRadioSelection(hiddenFields); - - if(hiddenField.length > 0){ - hiddenField.remove(); - } else { - appendId(); - } - } - }, - - createCounter: function(service){ - var counter = $("#publisher .counter"); - counter.remove(); - - var min = 40000; - var a = $('.service_icon:not(.dim)'); - if(a.length > 0){ - $.each(a, function(index, value){ - var num = parseInt($(value).attr('maxchar')); - if (min > num) { min = num; } - }); - $('#status_message_fake_text').charCount({allowed: min, warning: min/10 }); - } - }, - - bindAspectToggles: function() { - $('#publisher .dropdown .dropdown_list li').bind("click", function(evt){ - var li = $(this), - button = li.parent('.dropdown').find('.button'); - - if(li.hasClass('radio')){ - AspectsDropdown.toggleRadio(li); - } else { - AspectsDropdown.toggleCheckbox(li); - } - - AspectsDropdown.updateNumber(li.closest(".dropdown_list"), null, li.parent().find('li.selected').length, ''); - - Publisher.toggleAspectIds(li); - }); - }, - - textChange : function(){ - Publisher.determineSubmitAvailability(); - Publisher.input().mentionsInput("val", function(value) { - Publisher.hiddenInput().val(value); - }); - }, - - triggerGettingStarted: function(){ - Publisher.setUpPopovers("#publisher .dropdown", {trigger: 'manual', offset: 10, id: "message_visibility_explain", placement:'bottom', html:true}, 1000); - Publisher.setUpPopovers("#publisher #status_message_fake_text", {trigger: 'manual', placement: 'right', offset: 30, id: "first_message_explain", html:true}, 600); - Publisher.setUpPopovers("#gs-shim", {trigger: 'manual', placement: 'left', id:"stream_explain", offset: -5, html:true}, 1400); - - $("#publisher .button.creation").bind("click", function(){ - $("#publisher .dropdown").popover("hide"); - $("#publisher #status_message_fake_text").popover("hide"); - }); - }, - - setUpPopovers: function(selector, options, timeout){ - var selection = $(selector); - selection.popover(options); - selection.bind("click", function(){$(this).popover("hide")}); - - setTimeout(function(){ - selection.popover("show"); - - var popup = selection.data('popover').$tip[0], - closeIcon = $(popup).find(".close"); - - closeIcon.bind("click",function(){ - if($(".popover").length == 1){ - $.get("/getting_started_completed"); - }; - selection.popover("hide"); - }); - }, timeout); - }, - - initialize: function() { - Publisher.cachedForm = Publisher.cachedSubmit = - Publisher.cachedInput = Publisher.cachedHiddenInput = false; - - Publisher.bindServiceIcons(); - Publisher.bindAspectToggles(); - - Mentions.initialize(Publisher.input()); - - Publisher.input().bind("focus", function(){ - Mentions.fetchContacts(); - }) - - if(Publisher.hiddenInput().val() === "") { - Publisher.hiddenInput().val(Publisher.input().val()); - } - - Publisher.input().autoResize({ 'extraSpace' : 10, 'maxHeight' : Infinity }); - Publisher.input().bind('textchange', Publisher.textChange); - } -}; - -$(document).ready(function() { - Publisher.initialize(); - Diaspora.page.subscribe("stream/reloaded", Publisher.initialize); -}); diff --git a/app/views/photos/_new_photo.haml b/app/views/photos/_new_photo.haml index b5f418f2f0b7d02a37de44baeb405ffa8c4cd577..b4aec04b3d69d284bac98ccb1b8433513835349e 100644 --- a/app/views/photos/_new_photo.haml +++ b/app/views/photos/_new_photo.haml @@ -29,7 +29,7 @@ $('#file-upload').addClass("loading"); $('#publisher').find("input[type='submit']").attr('disabled','disabled'); - Publisher.wrapper().addClass("with_attachments"); + app.publisher.el_wrapper.addClass("with_attachments"); $('#photodropzone').append( "<li class='publisher_photo loading' style='position:relative;'>" + "#{escape_javascript(image_tag('ajax-loader2.gif'))}" + @@ -43,7 +43,7 @@ url = responseJSON.data.photo.unprocessed_image.url, currentPlaceholder = $('li.loading').first(); - Publisher.wrapper().addClass("with_attachments"); + app.publisher.el_wrapper.addClass("with_attachments"); $('#new_status_message').append("<input type='hidden' value='" + id + "' name='photos[]' />"); // replace image placeholders @@ -70,7 +70,7 @@ photo.fadeOut(400, function(){ photo.remove(); if ( $('.publisher_photo').length == 0){ - Publisher.wrapper().removeClass("with_attachments"); + app.publisher.el_wrapper.removeClass("with_attachments"); } }); } diff --git a/app/views/shared/_publisher.html.haml b/app/views/shared/_publisher.html.haml index 4c8d86094b1cf76088bd3e0e2f493720a2d17be1..2de6bcdb5870686803d387f8487225666c3e166b 100644 --- a/app/views/shared/_publisher.html.haml +++ b/app/views/shared/_publisher.html.haml @@ -5,7 +5,7 @@ -if publisher_explain :javascript $(document).ready(function() { - Publisher.triggerGettingStarted(); + if( app.publisher ) app.publisher.triggerGettingStarted(); }); #publisher{:class => ((aspect == :profile || publisher_open) ? "mention_popup" : "closed")} diff --git a/app/views/status_messages/bookmarklet.html.haml b/app/views/status_messages/bookmarklet.html.haml index b1e7e2c366f7faf63e2a6e7fb245b28dc7c46519..992da8686a4c42e0c2ba57509cfb77b82e9749db 100644 --- a/app/views/status_messages/bookmarklet.html.haml +++ b/app/views/status_messages/bookmarklet.html.haml @@ -9,8 +9,9 @@ = render :partial => 'shared/publisher', :locals => { :aspect => :profile, :selected_aspects => @aspects, :aspect_ids => @aspect_ids } :javascript - Publisher.bookmarklet = true; - app.publisher = new app.views.Publisher(); + app.publisher = new app.views.Publisher({ + standalone: true + }); var contents = "#{escape_javascript params[:title]} - #{escape_javascript params[:url]}"; var notes = "#{escape_javascript params[:notes]}"; diff --git a/app/views/status_messages/new.html.haml b/app/views/status_messages/new.html.haml index 53185ce1161051317b1b3018cf16b45ee8688cc3..fb87afa891585329c24574554112e8a4b132e2ec 100644 --- a/app/views/status_messages/new.html.haml +++ b/app/views/status_messages/new.html.haml @@ -1,13 +1,8 @@ -# Copyright (c) 2010-2011, Diaspora Inc. This file is -# licensed under the Affero General Public License version 3 or later. See -# the COPYRIGHT file. -= javascript_include_tag 'jquery.textchange.js', "publisher.js" -:javascript - $(function() { - $("#publisher").bind('ajax:success', function(){ location.reload(); }); - Publisher.bookmarklet = true; - }); += javascript_include_tag :home #new_status_message_pane .span-15.last @@ -17,3 +12,10 @@ = render :partial => 'shared/publisher', :locals => { :aspect => @aspect, :aspect_ids => @aspect_ids, :selected_aspects => @aspects_with_person, :person => @person} +:javascript + $(function() { + app.publisher = new app.views.Publisher({ + standalone: true + }); + $("#publisher").bind('ajax:success', function(){ location.reload(); }); + }); \ No newline at end of file diff --git a/features/mentions_from_profile_page.feature b/features/mentions_from_profile_page.feature index a3b86c3d7cd186f0827ff8a76266a64c5e180898..be8e5d113bb9f0a66daee03ed3e7e95236eb09d1 100644 --- a/features/mentions_from_profile_page.feature +++ b/features/mentions_from_profile_page.feature @@ -24,8 +24,7 @@ Feature: mentioning a contact from their profile page Scenario: mentioning while posting to all aspects Given I am on "alice@alice.alice"'s page And I have turned off jQuery effects - And I click "Mention" button - And I expand the publisher in the modal window + And I want to mention her from the profile And I append "I am eating a yogurt" to the publisher And I press "Share" in the modal window And I wait for the ajax to finish @@ -42,9 +41,7 @@ Feature: mentioning a contact from their profile page Scenario: mentioning while posting to just one aspect Given I am on "alice@alice.alice"'s page And I have turned off jQuery effects - And I click "Mention" button - And I wait for the ajax to finish - And I expand the publisher in the modal window + And I want to mention her from the profile And I append "I am eating a yogurt" to the publisher And I press the aspect dropdown in the modal window And I toggle the aspect "NotPostingThingsHere" in the modal window diff --git a/features/step_definitions/custom_web_steps.rb b/features/step_definitions/custom_web_steps.rb index 67b95e7eb872ec6334ceb7f6b2cada9292f52bf0..6942655d0ca3862547ef0a1bd2bc2bc95a0408b4 100644 --- a/features/step_definitions/custom_web_steps.rb +++ b/features/step_definitions/custom_web_steps.rb @@ -46,10 +46,18 @@ end When /^I append "([^"]*)" to the publisher$/ do |stuff| elem = find('#status_message_fake_text') - elem.native.send_keys ' ' + stuff + elem.native.send_keys(' ' + stuff) wait_until do - page.find("#status_message_text").value.match(/#{stuff}/) + find('#status_message_text').value.include?(stuff) + end +end + +And /^I want to mention (?:him|her) from the profile$/ do + click_link("Mention") + wait_for_ajax_to_finish + within('#facebox') do + click_publisher end end diff --git a/features/support/publishing_cuke_helpers.rb b/features/support/publishing_cuke_helpers.rb index 6bfa7a290423fff983f8842ec75c288a8a0c55dd..de447cc7a3195b2884929f949f2e439ad9ae092e 100644 --- a/features/support/publishing_cuke_helpers.rb +++ b/features/support/publishing_cuke_helpers.rb @@ -13,7 +13,7 @@ module PublishingCukeHelpers def click_publisher page.execute_script(' $("#publisher").removeClass("closed"); - $("#publisher").find("textarea").focus(); + $("#publisher").find("#status_message_fake_text").focus(); ') end @@ -79,7 +79,7 @@ module PublishingCukeHelpers end def comment_on_show_page(comment_text) - within("#post-interactions") do + within("#post-interactions") do focus_comment_box(".label.comment") make_comment(comment_text, "new-comment-text") end diff --git a/vendor/assets/javascripts/jquery.autoresize.js b/lib/assets/javascripts/jquery.autoresize.js similarity index 100% rename from vendor/assets/javascripts/jquery.autoresize.js rename to lib/assets/javascripts/jquery.autoresize.js diff --git a/spec/javascripts/app/views/publisher_view_spec.js b/spec/javascripts/app/views/publisher_view_spec.js index c18408a9f47c376bd1349198a5e93a02a25928ed..0e301847c3c75ce5623d46e994353258e7b9ed4c 100644 --- a/spec/javascripts/app/views/publisher_view_spec.js +++ b/spec/javascripts/app/views/publisher_view_spec.js @@ -1,76 +1,268 @@ +/* Copyright (c) 2010-2012, Diaspora Inc. This file is + * licensed under the Affero General Public License version 3 or later. See + * the COPYRIGHT file. + */ + describe("app.views.Publisher", function() { - beforeEach(function() { - // should be jasmine helper - loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}}); + describe("standalone", function() { + beforeEach(function() { + // should be jasmine helper + loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}}); - spec.loadFixture("aspects_index"); - this.view = new app.views.Publisher(); - }); + spec.loadFixture("aspects_index"); + this.view = new app.views.Publisher({ + standalone: true + }); + }); - describe("#open", function() { - it("removes the 'closed' class from the publisher element", function() { - expect($(this.view.el)).toHaveClass("closed"); - this.view.open($.Event()); - expect($(this.view.el)).not.toHaveClass("closed"); + it("hides the close button in standalone mode", function() { + expect(this.view.$('#hide_publisher').is(':visible')).toBeFalsy(); }); }); - describe("#close", function() { + context("plain publisher", function() { beforeEach(function() { - this.view.open($.Event()); + // should be jasmine helper + loginAs({name: "alice", avatar : {small : "http://avatar.com/photo.jpg"}}); + + spec.loadFixture("aspects_index"); + this.view = new app.views.Publisher(); + }); + + describe("#open", function() { + it("removes the 'closed' class from the publisher element", function() { + expect($(this.view.el)).toHaveClass("closed"); + this.view.open($.Event()); + expect($(this.view.el)).not.toHaveClass("closed"); + }); + }); + + describe("#close", function() { + beforeEach(function() { + this.view.open($.Event()); + }); + + it("removes the 'active' class from the publisher element", function(){ + this.view.close($.Event()); + expect($(this.view.el)).toHaveClass("closed"); + }) + + it("resets the element's height", function() { + $(this.view.el).find("#status_message_fake_text").height(100); + this.view.close($.Event()); + expect($(this.view.el).find("#status_message_fake_text").attr("style")).not.toContain("height"); + }); + }); + + describe("#clear", function() { + it("calls close", function(){ + spyOn(this.view, "close"); + + this.view.clear($.Event()); + expect(this.view.close).toHaveBeenCalled(); + }) + + it("clears all textareas", function(){ + _.each(this.view.$("textarea"), function(element){ + $(element).val('this is some stuff'); + expect($(element).val()).not.toBe(""); + }); + + this.view.clear($.Event()); + + _.each(this.view.$("textarea"), function(element){ + expect($(element).val()).toBe(""); + }); + }) + + it("removes all photos from the dropzone area", function(){ + var self = this; + _.times(3, function(){ + self.view.el_photozone.append($("<li>")) + }); + + expect(this.view.el_photozone.html()).not.toBe(""); + this.view.clear($.Event()); + expect(this.view.el_photozone.html()).toBe(""); + }) + + it("removes all photo values appended by the photo uploader", function(){ + $(this.view.el).prepend("<input name='photos[]' value='3'/>") + var photoValuesInput = this.view.$("input[name='photos[]']"); + + photoValuesInput.val("3") + this.view.clear($.Event()); + expect(this.view.$("input[name='photos[]']").length).toBe(0); + }) + }); + }); + + context("#toggleService", function(){ + beforeEach( function(){ + spec.loadFixture('aspects_index_services'); + this.view = new app.views.Publisher(); + }); + + it("toggles the 'dim' class on a clicked item", function() { + var first = $(".service_icon").eq(0); + var second = $(".service_icon").eq(1); + + expect(first.hasClass('dim')).toBeTruthy(); + expect(second.hasClass('dim')).toBeTruthy(); + + first.trigger('click'); + + expect(first.hasClass('dim')).toBeFalsy(); + expect(second.hasClass('dim')).toBeTruthy(); + + first.trigger('click'); + + expect(first.hasClass('dim')).toBeTruthy(); + expect(second.hasClass('dim')).toBeTruthy(); + }); + + describe("#_createCounter", function() { + it("gets called in when you toggle service icons", function(){ + spyOn(this.view, '_createCounter'); + $(".service_icon").first().trigger('click'); + expect(this.view._createCounter).toHaveBeenCalled(); + }); + + it("removes the 'old' .counter span", function(){ + spyOn($.fn, "remove"); + $(".service_icon").first().trigger('click'); + expect($.fn.remove).toHaveBeenCalled(); + }); }); - it("removes the 'active' class from the publisher element", function(){ - this.view.close($.Event()); - expect($(this.view.el)).toHaveClass("closed"); - }) + describe("#_toggleServiceField", function() { + it("gets called when you toggle service icons", function(){ + spyOn(this.view, '_toggleServiceField'); + $(".service_icon").first().trigger('click'); + expect(this.view._toggleServiceField).toHaveBeenCalled(); + }); + + it("toggles the hidden input field", function(){ + expect($('input[name="services[]"]').length).toBe(0); + $(".service_icon").first().trigger('click'); + expect($('input[name="services[]"]').length).toBe(1); + $(".service_icon").first().trigger('click'); + expect($('input[name="services[]"]').length).toBe(0); + }); + + it("toggles the correct input", function() { + var first = $(".service_icon").eq(0); + var second = $(".service_icon").eq(1); + + first.trigger('click'); + second.trigger('click'); + + expect($('input[name="services[]"]').length).toBe(2); + + first.trigger('click'); - it("resets the element's height", function() { - $(this.view.el).find("#status_message_fake_text").height(100); - this.view.close($.Event()); - expect($(this.view.el).find("#status_message_fake_text").attr("style")).not.toContain("height"); + var prov1 = first.attr('id'); + var prov2 = second.attr('id'); + + expect($('input[name="services[]"][value="'+prov1+'"]').length).toBe(0); + expect($('input[name="services[]"][value="'+prov2+'"]').length).toBe(1); + }); }); }); - describe("#clear", function() { - it("calls close", function(){ - spyOn(this.view, "close"); + context("aspect selection", function(){ + beforeEach( function(){ + spec.loadFixture('status_message_new'); + + this.radio_els = $('#publisher .dropdown li.radio'); + this.check_els = $('#publisher .dropdown li.aspect_selector'); + + this.view = new app.views.Publisher(); + this.view.open(); + }); - this.view.clear($.Event()); - expect(this.view.close); - }) + it("initializes with 'all_aspects'", function(){ + expect(this.radio_els.first().hasClass('selected')).toBeFalsy(); + expect(this.radio_els.last().hasClass('selected')).toBeTruthy(); - it("clears all textareas", function(){ - _.each(this.view.$("textarea"), function(element){ - $(element).val('this is some stuff'); - expect($(element).val()).not.toBe(""); + _.each(this.check_els, function(el){ + expect($(el).hasClass('selected')).toBeFalsy(); }); + }); - this.view.clear($.Event()); + it("toggles the selected entry visually", function(){ + this.check_els.last().trigger('click'); - _.each(this.view.$("textarea"), function(element){ - expect($(element).val()).toBe(""); + _.each(this.radio_els, function(el){ + expect($(el).hasClass('selected')).toBeFalsy(); }); - }) - it("removes all photos from the dropzone area", function(){ - var self = this; - _.times(3, function(){ - self.view.$("#photodropzone").append($("<li>")) + expect(this.check_els.first().hasClass('selected')).toBeFalsy(); + expect(this.check_els.last().hasClass('selected')).toBeTruthy(); + }); + + describe("#_updateSelectedAspectIds", function(){ + beforeEach(function(){ + this.li = $('<li data-aspect_id="42" />'); + this.view.$('.dropdown_list').append(this.li); }); - expect(this.view.$("#photodropzone").html()).not.toBe(""); - this.view.clear($.Event()); - expect(this.view.$("#photodropzone").html()).toBe(""); - }) + it("gets called when aspects are selected", function(){ + spyOn(this.view, "_updateSelectedAspectIds"); + this.check_els.last().trigger('click'); + expect(this.view._updateSelectedAspectIds).toHaveBeenCalled(); + }); - it("removes all photo values appended by the photo uploader", function(){ - $(this.view.el).prepend("<input name='photos[]' value='3'/>") - var photoValuesInput = this.view.$("input[name='photos[]']"); + it("removes a previous selection and inserts the current one", function() { + var selected = this.view.$('input[name="aspect_ids[]"]'); + expect(selected.length).toBe(1); + expect(selected.first().val()).toBe('all_aspects'); - photoValuesInput.val("3") - this.view.clear($.Event()); - expect(this.view.$("input[name='photos[]']").length).toBe(0); - }) + this.li.trigger('click'); + + selected = this.view.$('input[name="aspect_ids[]"]'); + expect(selected.length).toBe(1); + expect(selected.first().val()).toBe('42'); + }); + + it("toggles the same item", function() { + expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(1); + + this.li.trigger('click'); + expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(1); + + this.li.trigger('click'); + expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(0); + }); + + it("keeps other fields with different values", function() { + var li2 = $("<li data-aspect_id=99></li>"); + this.view.$('.dropdown_list').append(li2); + + this.li.trigger('click'); + expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(1); + + li2.trigger('click'); + expect(this.view.$('input[name="aspect_ids[]"]').length).toBe(2); + }); + }); + + describe("#_addHiddenAspectInput", function(){ + it("gets called when aspects are selected", function(){ + spyOn(this.view, "_addHiddenAspectInput"); + this.check_els.last().trigger('click'); + expect(this.view._addHiddenAspectInput).toHaveBeenCalled(); + }); + + it("adds a hidden input to the form", function(){ + var id = 42; + + this.view._addHiddenAspectInput(id); + var input = this.view.$('input[name="aspect_ids[]"][value="'+id+'"]'); + + expect(input.length).toBe(1); + expect(input.val()).toBe('42'); + }); + }); }); }); diff --git a/spec/javascripts/bookmarklet-spec.js b/spec/javascripts/bookmarklet-spec.js index c776b9e1ab75aa0becec61924b6f21fed6d86b4b..fee57d0e7480426b66d8a6886305020f3e1d35b6 100644 --- a/spec/javascripts/bookmarklet-spec.js +++ b/spec/javascripts/bookmarklet-spec.js @@ -11,11 +11,12 @@ describe("bookmarklet", function() { }); it('verifies the publisher is loaded', function(){ - expect(typeof Publisher === "object").toBeTruthy(); + expect(typeof app.publisher === "object").toBeTruthy(); }); it('verifies we are using the bookmarklet', function(){ - expect(Publisher.bookmarklet).toBeTruthy(); + expect(app.publisher.options.standalone).toBeTruthy(); + expect(app.publisher.$('#hide_publisher').is(':visible')).toBeFalsy(); }); }); diff --git a/spec/javascripts/publisher-spec.js b/spec/javascripts/publisher-spec.js deleted file mode 100644 index af84eeb39692a4427873003d87c8eb2a3200610d..0000000000000000000000000000000000000000 --- a/spec/javascripts/publisher-spec.js +++ /dev/null @@ -1,177 +0,0 @@ -/* Copyright (c) 2010-2011, Diaspora Inc. This file is -* licensed under the Affero General Public License version 3 or later. See -* the COPYRIGHT file. -*/ - -describe("Publisher", function() { - - Publisher.open = function(){ this.form().removeClass("closed"); } - - describe("toggleCounter", function(){ - beforeEach( function(){ - spec.loadFixture('aspects_index_services'); - }); - - it("gets called in when you toggle service icons", function(){ - spyOn(Publisher, 'createCounter'); - Publisher.toggleServiceField($(".service_icon").first()); - expect(Publisher.createCounter).toHaveBeenCalled(); - }); - - it("removes the .counter span", function(){ - spyOn($.fn, "remove"); - Publisher.createCounter($(".service_icon").first()); - expect($.fn.remove).toHaveBeenCalled(); - }); - }); - - describe("bindAspectToggles", function() { - beforeEach( function(){ - spec.loadFixture('status_message_new'); - Publisher.open(); - }); - - it('gets called on initialize', function(){ - spyOn(Publisher, 'bindAspectToggles'); - Publisher.initialize(); - expect(Publisher.bindAspectToggles).toHaveBeenCalled(); - }); - - it('correctly initializes an all_aspects state', function(){ - Publisher.initialize(); - - expect($("#publisher .dropdown .dropdown_list li.radio").first().hasClass("selected")).toBeFalsy(); - expect($("#publisher .dropdown .dropdown_list li.radio").last().hasClass("selected")).toBeTruthy(); - - $.each($("#publihser .dropdown .dropdown_list li.aspect_selector"), function(index, element){ - expect($(element).hasClass("selected")).toBeFalsy(); - }); - }); - - it('toggles selected only on the clicked icon', function(){ - Publisher.initialize(); - - $("#publisher .dropdown .dropdown_list li.aspect_selector").last().click(); - - $.each($("#publisher .dropdown .dropdown_list li.radio"), function(index, element){ - expect($(element).hasClass("selected")).toBeFalsy(); - }); - - expect($("#publisher .dropdown .dropdown_list li.aspect_selector").first().hasClass("selected")).toBeFalsy(); - expect($("#publisher .dropdown .dropdown_list li.aspect_selector").last().hasClass("selected")).toBeTruthy(); - }); - - it('calls toggleAspectIds with the clicked element', function(){ - spyOn(Publisher, 'toggleAspectIds'); - Publisher.bindAspectToggles(); - var aspectBadge = $("#publisher .dropdown .dropdown_list li").last(); - aspectBadge.click(); - expect(Publisher.toggleAspectIds.mostRecentCall.args[0].get(0)).toEqual(aspectBadge.get(0)); - }); - }); - - describe('toggleAspectIds', function(){ - beforeEach( function(){ - spec.loadFixture('status_message_new'); - li = $("<li data-aspect_id=42></li>"); - }); - - it('adds a hidden field to the form if there is not one already', function(){ - expect($('#publisher [name="aspect_ids[]"]').length).toBe(1); - expect($('#publisher [name="aspect_ids[]"]').last().attr('value')).toBe('all_aspects'); - Publisher.toggleAspectIds(li); - expect($('#publisher [name="aspect_ids[]"]').length).toBe(1); - expect($('#publisher [name="aspect_ids[]"]').last().attr('value')).toBe('42'); - }); - - it('removes the hidden field if its already there', function() { - expect($('#publisher [name="aspect_ids[]"]').length).toBe(1); - - Publisher.toggleAspectIds(li); - expect($('#publisher [name="aspect_ids[]"]').length).toBe(1); - - Publisher.toggleAspectIds(li); - expect($('#publisher [name="aspect_ids[]"]').length).toBe(0); - }); - - it('does not remove a hidden field with a different value', function() { - var li2 = $("<li data-aspect_id=99></li>"); - - Publisher.toggleAspectIds(li); - expect($('#publisher [name="aspect_ids[]"]').length).toBe(1); - - Publisher.toggleAspectIds(li2); - expect($('#publisher [name="aspect_ids[]"]').length).toBe(2); - }); - }); - - describe("bindServiceIcons", function() { - beforeEach( function(){ - spec.loadFixture('aspects_index_services'); - }); - - it('gets called on initialize', function(){ - spyOn(Publisher, 'bindServiceIcons'); - Publisher.initialize(); - expect(Publisher.bindServiceIcons).toHaveBeenCalled(); - }); - - it('toggles dim only on the clicked icon', function(){ - expect($(".service_icon#facebook").hasClass("dim")).toBeTruthy(); - expect($(".service_icon#twitter").hasClass("dim")).toBeTruthy(); - - Publisher.bindServiceIcons(); - $(".service_icon#facebook").click(); - - expect($(".service_icon#facebook").hasClass("dim")).toBeFalsy(); - expect($(".service_icon#twitter").hasClass("dim")).toBeTruthy(); - }); - - it('binds to the services icons and toggles the hidden field', function(){ - spyOn(Publisher, 'toggleServiceField'); - Publisher.bindServiceIcons(); - $(".service_icon#facebook").click(); - - expect(Publisher.toggleServiceField).toHaveBeenCalled(); - }); - }); - - describe('toggleServiceField', function(){ - beforeEach( function(){ - spec.loadFixture('aspects_index_services'); - }); - - it('adds a hidden field to the form if there is not one already', function(){ - expect($('#publisher [name="services[]"]').length).toBe(0); - Publisher.toggleServiceField($(".service_icon#facebook").first()); - expect($('#publisher [name="services[]"]').length).toBe(1); - expect($('#publisher [name="services[]"]').attr('value')).toBe("facebook"); - }); - - it('removes the hidden field if its already there', function() { - Publisher.toggleServiceField($(".service_icon#facebook").first()); - expect($('#publisher [name="services[]"]').length).toBe(1); - - Publisher.toggleServiceField($(".service_icon#facebook").first()); - expect($('#publisher [name="services[]"]').length).toBe(0); - }); - - it('does not remove a hidden field with a different value', function() { - Publisher.toggleServiceField($(".service_icon#facebook").first()); - expect($('#publisher [name="services[]"]').length).toBe(1); - - Publisher.toggleServiceField($(".service_icon#twitter").first()); - expect($('#publisher [name="services[]"]').length).toBe(2); - }); - }); - - describe("input", function(){ - beforeEach(function(){ - spec.loadFixture('aspects_index_prefill'); - }); - it("returns the status_message_fake_text textarea", function(){ - expect(Publisher.input()[0].id).toBe('status_message_fake_text'); - expect(Publisher.input().length).toBe(1); - }); - }); -});