integrate tags plugins
This commit is contained in:
parent
5d526717d6
commit
6977150f04
6 changed files with 212 additions and 0 deletions
2
Gemfile
2
Gemfile
|
@ -21,6 +21,8 @@ gem "git"
|
||||||
gem "acts_as_list"
|
gem "acts_as_list"
|
||||||
gem 'rdiscount'
|
gem 'rdiscount'
|
||||||
|
|
||||||
|
gem 'acts-as-taggable-on', '~>2.1.0'
|
||||||
|
|
||||||
group :assets do
|
group :assets do
|
||||||
gem 'sass-rails', " ~> 3.1.0"
|
gem 'sass-rails', " ~> 3.1.0"
|
||||||
gem 'coffee-rails', "~> 3.1.0"
|
gem 'coffee-rails', "~> 3.1.0"
|
||||||
|
|
|
@ -54,6 +54,8 @@ GEM
|
||||||
activesupport (= 3.1.0)
|
activesupport (= 3.1.0)
|
||||||
activesupport (3.1.0)
|
activesupport (3.1.0)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
|
acts-as-taggable-on (2.1.1)
|
||||||
|
rails
|
||||||
acts_as_list (0.1.4)
|
acts_as_list (0.1.4)
|
||||||
addressable (2.2.6)
|
addressable (2.2.6)
|
||||||
ansi (1.3.0)
|
ansi (1.3.0)
|
||||||
|
@ -246,6 +248,7 @@ PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
|
acts-as-taggable-on (~> 2.1.0)
|
||||||
acts_as_list
|
acts_as_list
|
||||||
annotate!
|
annotate!
|
||||||
autotest
|
autotest
|
||||||
|
|
|
@ -9,6 +9,8 @@ class Project < ActiveRecord::Base
|
||||||
has_many :notes, :dependent => :destroy
|
has_many :notes, :dependent => :destroy
|
||||||
has_many :snippets, :dependent => :destroy
|
has_many :snippets, :dependent => :destroy
|
||||||
|
|
||||||
|
acts_as_taggable
|
||||||
|
|
||||||
validates :name,
|
validates :name,
|
||||||
:uniqueness => true,
|
:uniqueness => true,
|
||||||
:presence => true,
|
:presence => true,
|
||||||
|
|
28
db/migrate/20111101222453_acts_as_taggable_on_migration.rb
Normal file
28
db/migrate/20111101222453_acts_as_taggable_on_migration.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class ActsAsTaggableOnMigration < ActiveRecord::Migration
|
||||||
|
def self.up
|
||||||
|
create_table :tags do |t|
|
||||||
|
t.string :name
|
||||||
|
end
|
||||||
|
|
||||||
|
create_table :taggings do |t|
|
||||||
|
t.references :tag
|
||||||
|
|
||||||
|
# You should make sure that the column created is
|
||||||
|
# long enough to store the required class names.
|
||||||
|
t.references :taggable, :polymorphic => true
|
||||||
|
t.references :tagger, :polymorphic => true
|
||||||
|
|
||||||
|
t.string :context
|
||||||
|
|
||||||
|
t.datetime :created_at
|
||||||
|
end
|
||||||
|
|
||||||
|
add_index :taggings, :tag_id
|
||||||
|
add_index :taggings, [:taggable_id, :taggable_type, :context]
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.down
|
||||||
|
drop_table :taggings
|
||||||
|
drop_table :tags
|
||||||
|
end
|
||||||
|
end
|
143
vendor/assets/javascripts/jquery.tagify.js
vendored
Normal file
143
vendor/assets/javascripts/jquery.tagify.js
vendored
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/* Author: Alicia Liu */
|
||||||
|
|
||||||
|
(function ($) {
|
||||||
|
|
||||||
|
$.widget("ui.tagify", {
|
||||||
|
options: {
|
||||||
|
delimiters: [13, 188], // what user can type to complete a tag in char codes: [enter], [comma]
|
||||||
|
outputDelimiter: ',', // delimiter for tags in original input field
|
||||||
|
cssClass: 'tagify-container', // CSS class to style the tagify div and tags, see stylesheet
|
||||||
|
addTagPrompt: 'add tags' // placeholder text
|
||||||
|
},
|
||||||
|
|
||||||
|
_create: function() {
|
||||||
|
var self = this,
|
||||||
|
el = self.element,
|
||||||
|
opts = self.options;
|
||||||
|
|
||||||
|
this.tags = [];
|
||||||
|
|
||||||
|
// hide text field and replace with a div that contains it's own input field for entering tags
|
||||||
|
this.tagInput = $("<input type='text'>")
|
||||||
|
.attr( 'placeholder', opts.addTagPrompt )
|
||||||
|
.keypress( function(e) {
|
||||||
|
var $this = $(this),
|
||||||
|
pressed = e.which;
|
||||||
|
|
||||||
|
for ( i in opts.delimiters ) {
|
||||||
|
|
||||||
|
if (pressed == opts.delimiters[i]) {
|
||||||
|
self.add( $this.val() );
|
||||||
|
e.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// for some reason, in Safari, backspace is only recognized on keyup
|
||||||
|
.keyup( function(e) {
|
||||||
|
var $this = $(this),
|
||||||
|
pressed = e.which;
|
||||||
|
|
||||||
|
// if backspace is hit with no input, remove the last tag
|
||||||
|
if (pressed == 8) { // backspace
|
||||||
|
if ( $this.val() == "" ) {
|
||||||
|
self.remove();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.tagDiv = $("<div></div>")
|
||||||
|
.addClass( opts.cssClass )
|
||||||
|
.click( function() {
|
||||||
|
$(this).children('input').focus();
|
||||||
|
})
|
||||||
|
.append( this.tagInput )
|
||||||
|
.insertAfter( el.hide() );
|
||||||
|
|
||||||
|
// if the field isn't empty, parse the field for tags, and prepopulate existing tags
|
||||||
|
var initVal = $.trim( el.val() );
|
||||||
|
|
||||||
|
if ( initVal ) {
|
||||||
|
var initTags = initVal.split( opts.outputDelimiter );
|
||||||
|
$.each( initTags, function(i, tag) {
|
||||||
|
self.add( tag );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_setOption: function( key, value ) {
|
||||||
|
options.key = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
// add a tag, public function
|
||||||
|
add: function(text) {
|
||||||
|
var self = this;
|
||||||
|
text = text || self.tagInput.val();
|
||||||
|
if (text) {
|
||||||
|
var tagIndex = self.tags.length;
|
||||||
|
|
||||||
|
var removeButton = $("<a href='#'>x</a>")
|
||||||
|
.click( function() {
|
||||||
|
self.remove( tagIndex );
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
var newTag = $("<span></span>")
|
||||||
|
.text( text )
|
||||||
|
.append( removeButton );
|
||||||
|
|
||||||
|
self.tagInput.before( newTag );
|
||||||
|
self.tags.push( text );
|
||||||
|
self.tagInput.val('');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// remove a tag by index, public function
|
||||||
|
// if index is blank, remove the last tag
|
||||||
|
remove: function( tagIndex ) {
|
||||||
|
var self = this;
|
||||||
|
if ( tagIndex == null || tagIndex === (self.tags.length - 1) ) {
|
||||||
|
this.tagDiv.children("span").last().remove();
|
||||||
|
self.tags.pop();
|
||||||
|
}
|
||||||
|
if ( typeof(tagIndex) == 'number' ) {
|
||||||
|
// otherwise just hide this tag, and we don't mess up the index
|
||||||
|
this.tagDiv.children( "span:eq(" + tagIndex + ")" ).hide();
|
||||||
|
// we rely on the serialize function to remove null values
|
||||||
|
delete( self.tags[tagIndex] );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// serialize the tags with the given delimiter, and write it back into the tagified field
|
||||||
|
serialize: function() {
|
||||||
|
var self = this;
|
||||||
|
var delim = self.options.outputDelimiter;
|
||||||
|
var tagsStr = self.tags.join( delim );
|
||||||
|
|
||||||
|
// our tags might have deleted entries, remove them here
|
||||||
|
var dupes = new RegExp(delim + delim + '+', 'g'); // regex: /,,+/g
|
||||||
|
var ends = new RegExp('^' + delim + '|' + delim + '$', 'g'); // regex: /^,|,$/g
|
||||||
|
var outputStr = tagsStr.replace( dupes, delim ).replace(ends, '');
|
||||||
|
|
||||||
|
self.element.val(outputStr);
|
||||||
|
return outputStr;
|
||||||
|
},
|
||||||
|
|
||||||
|
inputField: function() {
|
||||||
|
return this.tagInput;
|
||||||
|
},
|
||||||
|
|
||||||
|
containerDiv: function() {
|
||||||
|
return this.tagDiv;
|
||||||
|
},
|
||||||
|
|
||||||
|
// remove the div, and show original input
|
||||||
|
destroy: function() {
|
||||||
|
$.Widget.prototype.destroy.apply(this);
|
||||||
|
this.tagDiv.remove();
|
||||||
|
this.element.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
})(jQuery);
|
34
vendor/assets/stylesheets/jquery-ui/jquery.tagify.css
vendored
Normal file
34
vendor/assets/stylesheets/jquery-ui/jquery.tagify.css
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/* Tagify styles
|
||||||
|
Author: Alicia Liu test
|
||||||
|
*/
|
||||||
|
|
||||||
|
.tagify-container {
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagify-container > span {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 11px 8px 11px;
|
||||||
|
margin: 1px 5px 0px 0px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #d0e1ff;
|
||||||
|
background-color: #d0e1ff;
|
||||||
|
color: #0f326d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagify-container > span > a {
|
||||||
|
padding-left: 5px !important;
|
||||||
|
color: #83a5e1;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagify-container > input {
|
||||||
|
border: 0 none;
|
||||||
|
width: 100px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tagify-container > input:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
Loading…
Reference in a new issue