From bb4c2ee85d9aff9ba6eb68934fcc4b8e9efc09ec Mon Sep 17 00:00:00 2001
From: zhitomirskiyi <ilya@joindiaspora.com>
Date: Wed, 30 Mar 2011 18:59:30 -0700
Subject: [PATCH] added a character count to the publisher

---
 app/views/shared/_publisher.html.haml    |  2 +-
 config/assets.yml                        |  1 +
 public/javascripts/publisher.js          | 23 ++++++++--
 public/javascripts/vendor/charCount.js   | 58 ++++++++++++++++++++++++
 public/stylesheets/sass/application.sass |  9 +++-
 spec/javascripts/publisher-spec.js       | 31 ++++++++++---
 spec/javascripts/support/jasmine.yml     |  1 +
 7 files changed, 113 insertions(+), 12 deletions(-)
 create mode 100644 public/javascripts/vendor/charCount.js

diff --git a/app/views/shared/_publisher.html.haml b/app/views/shared/_publisher.html.haml
index 3bab72762d..b16e7f3e17 100644
--- a/app/views/shared/_publisher.html.haml
+++ b/app/views/shared/_publisher.html.haml
@@ -53,7 +53,7 @@
               = image_tag "icons/globe.png", :title => t('.public'), :class => 'public_icon dim', :width => 16, :height => 16
             - if current_user.services
               - for service in current_user.services
-                = image_tag "social_media_logos/#{service.provider}-16x16.png", :title => service.provider.titleize, :class => "service_icon dim", :id =>"#{service.provider}"
+                = image_tag "social_media_logos/#{service.provider}-16x16.png", :title => service.provider.titleize, :class => "service_icon dim", :id =>"#{service.provider}", :maxchar => "#{service.class::MAX_CHARACTERS}"
             = link_to (image_tag "icons/monotone_wrench_settings.png"), "#question_mark_pane", :class => 'question_mark', :rel => 'facebox', :title => t('shared.public_explain.manage')
           = status.submit t('.share'), :disable_with => t('.posting'), :class => 'button', :tabindex => 2
 
diff --git a/config/assets.yml b/config/assets.yml
index c8d4ee51d8..46d9893edb 100644
--- a/config/assets.yml
+++ b/config/assets.yml
@@ -20,6 +20,7 @@ javascripts:
     - public/javascripts/vendor/facebox.js
     - public/javascripts/vendor/timeago.js
     - public/javascripts/vendor/Mustache.js
+    - public/javascripts/vendor/charCount.js
     - public/javascripts/jquery.autocomplete-custom.js
     - public/javascripts/fileuploader-custom.js
     - public/javascripts/keycodes.js
diff --git a/public/javascripts/publisher.js b/public/javascripts/publisher.js
index e28cabde25..b13270d899 100644
--- a/public/javascripts/publisher.js
+++ b/public/javascripts/publisher.js
@@ -287,7 +287,7 @@ var Publisher = {
   bindServiceIcons: function(){
     $(".service_icon").bind("click", function(evt){
       $(this).toggleClass("dim");
-      Publisher.toggleServiceField($(this).attr('id'));
+      Publisher.toggleServiceField($(this));
     });
   },
   bindPublicIcon: function(){
@@ -299,12 +299,14 @@ var Publisher = {
     });
   },
   toggleServiceField: function(service){
-    var hidden_field = $('#publisher [name="services[]"][value="'+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="'+service+'">');
+      '<input id="services_" name="services[]" type="hidden" value="'+provider+'">');
     };
   },
   toggleAspectIds: function(aspectId) {
@@ -316,6 +318,21 @@ var Publisher = {
       '<input id="aspect_ids_" name="aspect_ids[]" type="hidden" value="'+aspectId+'">');
     };
   },
+  createCounter: function(service){
+    var counter = $("#publisher .counter");
+    if (counter.length > 0) { 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 .aspect_badge').bind("click", function(){
       var unremovedAspects = $(this).parent().children('.aspect_badge').length - $(this).parent().children(".aspect_badge.removed").length;
diff --git a/public/javascripts/vendor/charCount.js b/public/javascripts/vendor/charCount.js
new file mode 100644
index 0000000000..67d1442d4e
--- /dev/null
+++ b/public/javascripts/vendor/charCount.js
@@ -0,0 +1,58 @@
+/*
+ * 	Character Count Plugin - jQuery plugin
+ * 	Dynamic character count for text areas and input fields
+ *	written by Alen Grakalic	
+ *	http://cssglobe.com/post/7161/jquery-plugin-simplest-twitterlike-dynamic-character-count-for-textareas
+ *
+ *	Copyright (c) 2009 Alen Grakalic (http://cssglobe.com)
+ *	Dual licensed under the MIT (MIT-LICENSE.txt)
+ *	and GPL (GPL-LICENSE.txt) licenses.
+ *
+ *	Built for jQuery library
+ *	http://jquery.com
+ *
+ */
+ 
+(function($) {
+
+	$.fn.charCount = function(options){
+	  
+		// default configuration properties
+		var defaults = {	
+			allowed: 140,		
+			warning: 25,
+			css: 'counter',
+			counterElement: 'span',
+			cssWarning: 'warning',
+			cssExceeded: 'exceeded',
+			counterText: ''
+		}; 
+			
+		var options = $.extend(defaults, options); 
+		
+		function calculate(obj){
+			var count = $(obj).val().length;
+			var available = options.allowed - count;
+			if(available <= options.warning && available >= 0){
+				$(obj).next().addClass(options.cssWarning);
+			} else {
+				$(obj).next().removeClass(options.cssWarning);
+			}
+			if(available < 0){
+				$(obj).next().addClass(options.cssExceeded);
+			} else {
+				$(obj).next().removeClass(options.cssExceeded);
+			}
+			$(obj).next().html(options.counterText + available);
+		};
+				
+		this.each(function() {  			
+			$(this).after('<'+ options.counterElement +' class="' + options.css + '">'+ options.counterText +'</'+ options.counterElement +'>');
+			calculate(this);
+			$(this).keyup(function(){calculate(this)});
+			$(this).change(function(){calculate(this)});
+		});
+	  
+	};
+
+})(jQuery);
diff --git a/public/stylesheets/sass/application.sass b/public/stylesheets/sass/application.sass
index 8acc42da53..ad1677acf7 100644
--- a/public/stylesheets/sass/application.sass
+++ b/public/stylesheets/sass/application.sass
@@ -839,7 +839,8 @@ label
     form
       textarea
         :height 18px !important
-
+    .counter
+      :display none
   form
     :position relative
     :top 0
@@ -1021,6 +1022,12 @@ label
         .x
           :display block
 
+  .counter
+    :position absolute
+  .warning
+    :color orange
+  .exceeded
+    :color red
 .field_with_submit
   input[type='text']
     :width 82%
diff --git a/spec/javascripts/publisher-spec.js b/spec/javascripts/publisher-spec.js
index 7197d4aa60..7eab22ce7c 100644
--- a/spec/javascripts/publisher-spec.js
+++ b/spec/javascripts/publisher-spec.js
@@ -21,6 +21,24 @@ describe("Publisher", function() {
     });
   });
 
+  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();
+      expect(Publisher.createCounter).toHaveBeenCalled();
+    });
+
+    it("removes the .counter span", function(){
+      spyOn($.fn, "remove");
+      Publisher.createCounter();
+      expect($.fn.remove).toHaveBeenCalled();
+    });
+  });
+
   describe("bindAspectToggles", function() {
     beforeEach( function(){
       spec.loadFixture('status_message_new');
@@ -151,7 +169,7 @@ describe("Publisher", function() {
       Publisher.bindServiceIcons();
       $(".service_icon#facebook").click();
 
-      expect(Publisher.toggleServiceField).toHaveBeenCalledWith("facebook");
+      expect(Publisher.toggleServiceField).toHaveBeenCalledWith($(".service_icon#facebook").first());
     });
   });
 
@@ -162,25 +180,24 @@ describe("Publisher", function() {
 
     it('adds a hidden field to the form if there is not one already', function(){
       expect($('#publisher [name="services[]"]').length).toBe(0);
-      Publisher.toggleServiceField("facebook");
+      Publisher.toggleServiceField($(".service_icon#facebook").first());
       expect($('#publisher [name="services[]"]').length).toBe(1);
       expect($('#publisher [name="services[]"]').attr('value')).toBe("facebook");
-      //<input id="aspect_ids_" name="aspect_ids[]" type="hidden" value="1">
     });
 
     it('removes the hidden field if its already there', function() {
-      Publisher.toggleServiceField("facebook");
+      Publisher.toggleServiceField($(".service_icon#facebook").first());
       expect($('#publisher [name="services[]"]').length).toBe(1);
 
-      Publisher.toggleServiceField("facebook");
+      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("facebook");
+      Publisher.toggleServiceField($(".service_icon#facebook").first());
       expect($('#publisher [name="services[]"]').length).toBe(1);
 
-      Publisher.toggleServiceField("twitter");
+      Publisher.toggleServiceField(($(".service_icon#twitter").first());
       expect($('#publisher [name="services[]"]').length).toBe(2);
     });
   });
diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml
index 4f946da6bc..c7ddaeecb0 100644
--- a/spec/javascripts/support/jasmine.yml
+++ b/spec/javascripts/support/jasmine.yml
@@ -17,6 +17,7 @@ src_files:
   - public/javascripts/vendor/jquery.infieldlabel.js
   - public/javascripts/vendor/jquery.autoresize.min.js
   - public/javascripts/vendor/Mustache.js
+  - public/javascripts/vendor/charCount.js
   - public/javascripts/vendor/timeago.js
   - public/javascripts/vendor/facebox.js
   - public/javascripts/jquery.autocomplete-custom.js
-- 
GitLab