Commit 6977150f04be1bfe365e1cd86e3f8143c34a51bf

Authored by Aleksei Kvitinskii
1 parent 5d526717

integrate tags plugins

Gemfile
... ... @@ -21,6 +21,8 @@ gem "git"
21 21 gem "acts_as_list"
22 22 gem 'rdiscount'
23 23  
  24 +gem 'acts-as-taggable-on', '~>2.1.0'
  25 +
24 26 group :assets do
25 27 gem 'sass-rails', " ~> 3.1.0"
26 28 gem 'coffee-rails', "~> 3.1.0"
... ...
Gemfile.lock
... ... @@ -54,6 +54,8 @@ GEM
54 54 activesupport (= 3.1.0)
55 55 activesupport (3.1.0)
56 56 multi_json (~> 1.0)
  57 + acts-as-taggable-on (2.1.1)
  58 + rails
57 59 acts_as_list (0.1.4)
58 60 addressable (2.2.6)
59 61 ansi (1.3.0)
... ... @@ -246,6 +248,7 @@ PLATFORMS
246 248 ruby
247 249  
248 250 DEPENDENCIES
  251 + acts-as-taggable-on (~> 2.1.0)
249 252 acts_as_list
250 253 annotate!
251 254 autotest
... ...
app/models/project.rb
... ... @@ -9,6 +9,8 @@ class Project < ActiveRecord::Base
9 9 has_many :notes, :dependent => :destroy
10 10 has_many :snippets, :dependent => :destroy
11 11  
  12 + acts_as_taggable
  13 +
12 14 validates :name,
13 15 :uniqueness => true,
14 16 :presence => true,
... ...
db/migrate/20111101222453_acts_as_taggable_on_migration.rb 0 → 100644
... ... @@ -0,0 +1,28 @@
  1 +class ActsAsTaggableOnMigration < ActiveRecord::Migration
  2 + def self.up
  3 + create_table :tags do |t|
  4 + t.string :name
  5 + end
  6 +
  7 + create_table :taggings do |t|
  8 + t.references :tag
  9 +
  10 + # You should make sure that the column created is
  11 + # long enough to store the required class names.
  12 + t.references :taggable, :polymorphic => true
  13 + t.references :tagger, :polymorphic => true
  14 +
  15 + t.string :context
  16 +
  17 + t.datetime :created_at
  18 + end
  19 +
  20 + add_index :taggings, :tag_id
  21 + add_index :taggings, [:taggable_id, :taggable_type, :context]
  22 + end
  23 +
  24 + def self.down
  25 + drop_table :taggings
  26 + drop_table :tags
  27 + end
  28 +end
... ...
vendor/assets/javascripts/jquery.tagify.js 0 → 100644
... ... @@ -0,0 +1,143 @@
  1 +/* Author: Alicia Liu */
  2 +
  3 +(function ($) {
  4 +
  5 + $.widget("ui.tagify", {
  6 + options: {
  7 + delimiters: [13, 188], // what user can type to complete a tag in char codes: [enter], [comma]
  8 + outputDelimiter: ',', // delimiter for tags in original input field
  9 + cssClass: 'tagify-container', // CSS class to style the tagify div and tags, see stylesheet
  10 + addTagPrompt: 'add tags' // placeholder text
  11 + },
  12 +
  13 + _create: function() {
  14 + var self = this,
  15 + el = self.element,
  16 + opts = self.options;
  17 +
  18 + this.tags = [];
  19 +
  20 + // hide text field and replace with a div that contains it's own input field for entering tags
  21 + this.tagInput = $("<input type='text'>")
  22 + .attr( 'placeholder', opts.addTagPrompt )
  23 + .keypress( function(e) {
  24 + var $this = $(this),
  25 + pressed = e.which;
  26 +
  27 + for ( i in opts.delimiters ) {
  28 +
  29 + if (pressed == opts.delimiters[i]) {
  30 + self.add( $this.val() );
  31 + e.preventDefault();
  32 + return false;
  33 + }
  34 + }
  35 + })
  36 + // for some reason, in Safari, backspace is only recognized on keyup
  37 + .keyup( function(e) {
  38 + var $this = $(this),
  39 + pressed = e.which;
  40 +
  41 + // if backspace is hit with no input, remove the last tag
  42 + if (pressed == 8) { // backspace
  43 + if ( $this.val() == "" ) {
  44 + self.remove();
  45 + return false;
  46 + }
  47 + return;
  48 + }
  49 + });
  50 +
  51 + this.tagDiv = $("<div></div>")
  52 + .addClass( opts.cssClass )
  53 + .click( function() {
  54 + $(this).children('input').focus();
  55 + })
  56 + .append( this.tagInput )
  57 + .insertAfter( el.hide() );
  58 +
  59 + // if the field isn't empty, parse the field for tags, and prepopulate existing tags
  60 + var initVal = $.trim( el.val() );
  61 +
  62 + if ( initVal ) {
  63 + var initTags = initVal.split( opts.outputDelimiter );
  64 + $.each( initTags, function(i, tag) {
  65 + self.add( tag );
  66 + });
  67 + }
  68 + },
  69 +
  70 + _setOption: function( key, value ) {
  71 + options.key = value;
  72 + },
  73 +
  74 + // add a tag, public function
  75 + add: function(text) {
  76 + var self = this;
  77 + text = text || self.tagInput.val();
  78 + if (text) {
  79 + var tagIndex = self.tags.length;
  80 +
  81 + var removeButton = $("<a href='#'>x</a>")
  82 + .click( function() {
  83 + self.remove( tagIndex );
  84 + return false;
  85 + });
  86 + var newTag = $("<span></span>")
  87 + .text( text )
  88 + .append( removeButton );
  89 +
  90 + self.tagInput.before( newTag );
  91 + self.tags.push( text );
  92 + self.tagInput.val('');
  93 + }
  94 + },
  95 +
  96 + // remove a tag by index, public function
  97 + // if index is blank, remove the last tag
  98 + remove: function( tagIndex ) {
  99 + var self = this;
  100 + if ( tagIndex == null || tagIndex === (self.tags.length - 1) ) {
  101 + this.tagDiv.children("span").last().remove();
  102 + self.tags.pop();
  103 + }
  104 + if ( typeof(tagIndex) == 'number' ) {
  105 + // otherwise just hide this tag, and we don't mess up the index
  106 + this.tagDiv.children( "span:eq(" + tagIndex + ")" ).hide();
  107 + // we rely on the serialize function to remove null values
  108 + delete( self.tags[tagIndex] );
  109 + }
  110 + },
  111 +
  112 + // serialize the tags with the given delimiter, and write it back into the tagified field
  113 + serialize: function() {
  114 + var self = this;
  115 + var delim = self.options.outputDelimiter;
  116 + var tagsStr = self.tags.join( delim );
  117 +
  118 + // our tags might have deleted entries, remove them here
  119 + var dupes = new RegExp(delim + delim + '+', 'g'); // regex: /,,+/g
  120 + var ends = new RegExp('^' + delim + '|' + delim + '$', 'g'); // regex: /^,|,$/g
  121 + var outputStr = tagsStr.replace( dupes, delim ).replace(ends, '');
  122 +
  123 + self.element.val(outputStr);
  124 + return outputStr;
  125 + },
  126 +
  127 + inputField: function() {
  128 + return this.tagInput;
  129 + },
  130 +
  131 + containerDiv: function() {
  132 + return this.tagDiv;
  133 + },
  134 +
  135 + // remove the div, and show original input
  136 + destroy: function() {
  137 + $.Widget.prototype.destroy.apply(this);
  138 + this.tagDiv.remove();
  139 + this.element.show();
  140 + }
  141 + });
  142 +
  143 +})(jQuery);
0 144 \ No newline at end of file
... ...
vendor/assets/stylesheets/jquery-ui/jquery.tagify.css 0 → 100644
... ... @@ -0,0 +1,34 @@
  1 +/* Tagify styles
  2 +Author: Alicia Liu test
  3 +*/
  4 +
  5 +.tagify-container {
  6 +}
  7 +
  8 +.tagify-container > span {
  9 + display: inline-block;
  10 + padding: 8px 11px 8px 11px;
  11 + margin: 1px 5px 0px 0px;
  12 + border-radius: 4px;
  13 + border: 1px solid #d0e1ff;
  14 + background-color: #d0e1ff;
  15 + color: #0f326d;
  16 + font-weight: bold;
  17 + font-size: 14px;
  18 +}
  19 +
  20 +.tagify-container > span > a {
  21 + padding-left: 5px !important;
  22 + color: #83a5e1;
  23 + text-decoration: none;
  24 + font-weight: bold;
  25 +}
  26 +
  27 +.tagify-container > input {
  28 + border: 0 none;
  29 + width: 100px !important;
  30 +}
  31 +
  32 +.tagify-container > input:focus {
  33 + outline: none;
  34 +}
0 35 \ No newline at end of file
... ...