tag_it 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/tag_it.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "tag_it/version"
2
+
3
+ module TagIt
4
+ end
@@ -0,0 +1,3 @@
1
+ module TagIt
2
+ VERSION = "0.0.1"
3
+ end
data/screenshot.png ADDED
Binary file
data/tag_it.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "tag_it/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "tag_it"
7
+ s.version = TagIt::VERSION
8
+ s.authors = ["Arun Kumar Arjunan"]
9
+ s.email = ["arun.immanuel1608@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = %q{Rails 3.1 version of tag-it}
12
+ s.description = %q{Rails 3.1 version of tag-it}
13
+
14
+ s.rubyforge_project = "tag_it"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,383 @@
1
+ /*
2
+ * jQuery UI Tag-it!
3
+ *
4
+ * @version v2.0 (06/2011)
5
+ *
6
+ * Copyright 2011, Levy Carneiro Jr.
7
+ * Released under the MIT license.
8
+ * http://aehlke.github.com/tag-it/LICENSE
9
+ *
10
+ * Homepage:
11
+ * http://aehlke.github.com/tag-it/
12
+ *
13
+ * Authors:
14
+ * Levy Carneiro Jr.
15
+ * Martin Rehfeld
16
+ * Tobias Schmidt
17
+ * Skylar Challand
18
+ * Alex Ehlke
19
+ *
20
+ * Maintainer:
21
+ * Alex Ehlke - Twitter: @aehlke
22
+ *
23
+ * Dependencies:
24
+ * jQuery v1.4+
25
+ * jQuery UI v1.8+
26
+ */
27
+ (function($) {
28
+
29
+ $.widget('ui.tagit', {
30
+ options: {
31
+ itemName : 'item',
32
+ fieldName : 'tags',
33
+ availableTags : [],
34
+ tagSource : null,
35
+ removeConfirmation: false,
36
+ caseSensitive : true,
37
+
38
+ // When enabled, quotes are not neccesary
39
+ // for inputting multi-word tags.
40
+ allowSpaces: false,
41
+
42
+ // Whether to animate tag removals or not.
43
+ animate: true,
44
+
45
+ // The below options are for using a single field instead of several
46
+ // for our form values.
47
+ //
48
+ // When enabled, will use a single hidden field for the form,
49
+ // rather than one per tag. It will delimit tags in the field
50
+ // with singleFieldDelimiter.
51
+ //
52
+ // The easiest way to use singleField is to just instantiate tag-it
53
+ // on an INPUT element, in which case singleField is automatically
54
+ // set to true, and singleFieldNode is set to that element. This
55
+ // way, you don't need to fiddle with these options.
56
+ singleField: false,
57
+
58
+ singleFieldDelimiter: ',',
59
+
60
+ // Set this to an input DOM node to use an existing form field.
61
+ // Any text in it will be erased on init. But it will be
62
+ // populated with the text of tags as they are created,
63
+ // delimited by singleFieldDelimiter.
64
+ //
65
+ // If this is not set, we create an input node for it,
66
+ // with the name given in settings.fieldName,
67
+ // ignoring settings.itemName.
68
+ singleFieldNode: null,
69
+
70
+ // Optionally set a tabindex attribute on the input that gets
71
+ // created for tag-it.
72
+ tabIndex: null,
73
+
74
+
75
+ // Event callbacks.
76
+ onTagAdded : null,
77
+ onTagRemoved: null,
78
+ onTagClicked: null
79
+ },
80
+
81
+
82
+ _create: function() {
83
+ // for handling static scoping inside callbacks
84
+ var that = this;
85
+
86
+ // There are 2 kinds of DOM nodes this widget can be instantiated on:
87
+ // 1. UL, OL, or some element containing either of these.
88
+ // 2. INPUT, in which case 'singleField' is overridden to true,
89
+ // a UL is created and the INPUT is hidden.
90
+ if (this.element.is('input')) {
91
+ this.tagList = $('<ul></ul>').insertAfter(this.element);
92
+ this.options.singleField = true;
93
+ this.options.singleFieldNode = this.element;
94
+ this.element.css('display', 'none');
95
+ } else {
96
+ this.tagList = this.element.find('ul, ol').andSelf().last();
97
+ }
98
+
99
+ this._tagInput = $('<input type="text">').addClass('ui-widget-content');
100
+ if (this.options.tabIndex) {
101
+ this._tagInput.attr('tabindex', this.options.tabIndex);
102
+ }
103
+
104
+ this.options.tagSource = this.options.tagSource || function(search, showChoices) {
105
+ var filter = search.term.toLowerCase();
106
+ var choices = $.grep(that.options.availableTags, function(element) {
107
+ // Only match autocomplete options that begin with the search term.
108
+ // (Case insensitive.)
109
+ return (element.toLowerCase().indexOf(filter) === 0);
110
+ });
111
+ showChoices(that._subtractArray(choices, that.assignedTags()));
112
+ };
113
+
114
+ this.tagList
115
+ .addClass('tagit')
116
+ .addClass('ui-widget ui-widget-content ui-corner-all')
117
+ // Create the input field.
118
+ .append($('<li class="tagit-new"></li>').append(this._tagInput))
119
+ .click(function(e) {
120
+ var target = $(e.target);
121
+ if (target.hasClass('tagit-label')) {
122
+ that._trigger('onTagClicked', e, target.closest('.tagit-choice'));
123
+ } else {
124
+ // Sets the focus() to the input field, if the user
125
+ // clicks anywhere inside the UL. This is needed
126
+ // because the input field needs to be of a small size.
127
+ that._tagInput.focus();
128
+ }
129
+ });
130
+
131
+ // Add existing tags from the list, if any.
132
+ this.tagList.children('li').each(function() {
133
+ if (!$(this).hasClass('tagit-new')) {
134
+ that.createTag($(this).html(), $(this).attr('class'));
135
+ $(this).remove();
136
+ }
137
+ });
138
+
139
+ // Single field support.
140
+ if (this.options.singleField) {
141
+ if (this.options.singleFieldNode) {
142
+ // Add existing tags from the input field.
143
+ var node = $(this.options.singleFieldNode);
144
+ var tags = node.val().split(this.options.singleFieldDelimiter);
145
+ node.val('');
146
+ $.each(tags, function(index, tag) {
147
+ that.createTag(tag);
148
+ });
149
+ } else {
150
+ // Create our single field input after our list.
151
+ this.options.singleFieldNode = this.tagList.after('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '">');
152
+ }
153
+ }
154
+
155
+ // Events.
156
+ this._tagInput
157
+ .keydown(function(event) {
158
+ // Backspace is not detected within a keypress, so it must use keydown.
159
+ if (event.which == $.ui.keyCode.BACKSPACE && that._tagInput.val() === '') {
160
+ var tag = that._lastTag();
161
+ if (!that.options.removeConfirmation || tag.hasClass('remove')) {
162
+ // When backspace is pressed, the last tag is deleted.
163
+ that.removeTag(tag);
164
+ } else if (that.options.removeConfirmation) {
165
+ tag.addClass('remove ui-state-highlight');
166
+ }
167
+ } else if (that.options.removeConfirmation) {
168
+ that._lastTag().removeClass('remove ui-state-highlight');
169
+ }
170
+
171
+ // Comma/Space/Enter are all valid delimiters for new tags,
172
+ // except when there is an open quote or if setting allowSpaces = true.
173
+ // Tab will also create a tag, unless the tag input is empty, in which case it isn't caught.
174
+ if (
175
+ event.which == $.ui.keyCode.COMMA ||
176
+ event.which == $.ui.keyCode.ENTER ||
177
+ (
178
+ event.which == $.ui.keyCode.TAB &&
179
+ that._tagInput.val() !== ''
180
+ ) ||
181
+ (
182
+ event.which == $.ui.keyCode.SPACE &&
183
+ that.options.allowSpaces !== true &&
184
+ (
185
+ $.trim(that._tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
186
+ (
187
+ $.trim(that._tagInput.val()).charAt(0) == '"' &&
188
+ $.trim(that._tagInput.val()).charAt($.trim(that._tagInput.val()).length - 1) == '"' &&
189
+ $.trim(that._tagInput.val()).length - 1 !== 0
190
+ )
191
+ )
192
+ )
193
+ ) {
194
+ event.preventDefault();
195
+ that.createTag(that._cleanedInput());
196
+
197
+ // The autocomplete doesn't close automatically when TAB is pressed.
198
+ // So let's ensure that it closes.
199
+ that._tagInput.autocomplete('close');
200
+ }
201
+ }).blur(function(e){
202
+ // Create a tag when the element loses focus (unless it's empty).
203
+ that.createTag(that._cleanedInput());
204
+ });
205
+
206
+
207
+ // Autocomplete.
208
+ if (this.options.availableTags || this.options.tagSource) {
209
+ this._tagInput.autocomplete({
210
+ source: this.options.tagSource,
211
+ select: function(event, ui) {
212
+ // Delete the last tag if we autocomplete something despite the input being empty
213
+ // This happens because the input's blur event causes the tag to be created when
214
+ // the user clicks an autocomplete item.
215
+ // The only artifact of this is that while the user holds down the mouse button
216
+ // on the selected autocomplete item, a tag is shown with the pre-autocompleted text,
217
+ // and is changed to the autocompleted text upon mouseup.
218
+ if (that._tagInput.val() === '') {
219
+ that.removeTag(that._lastTag(), false);
220
+ }
221
+ that.createTag(ui.item.value);
222
+ // Preventing the tag input to be updated with the chosen value.
223
+ return false;
224
+ }
225
+ });
226
+ }
227
+ },
228
+
229
+ _cleanedInput: function() {
230
+ // Returns the contents of the tag input, cleaned and ready to be passed to createTag
231
+ return $.trim(this._tagInput.val().replace(/^"(.*)"$/, '$1'));
232
+ },
233
+
234
+ _lastTag: function() {
235
+ return this.tagList.children('.tagit-choice:last');
236
+ },
237
+
238
+ assignedTags: function() {
239
+ // Returns an array of tag string values
240
+ var that = this;
241
+ var tags = [];
242
+ if (this.options.singleField) {
243
+ tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
244
+ if (tags[0] === '') {
245
+ tags = [];
246
+ }
247
+ } else {
248
+ this.tagList.children('.tagit-choice').each(function() {
249
+ tags.push(that.tagLabel(this));
250
+ });
251
+ }
252
+ return tags;
253
+ },
254
+
255
+ _updateSingleTagsField: function(tags) {
256
+ // Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
257
+ $(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter));
258
+ },
259
+
260
+ _subtractArray: function(a1, a2) {
261
+ var result = [];
262
+ for (var i = 0; i < a1.length; i++) {
263
+ if ($.inArray(a1[i], a2) == -1) {
264
+ result.push(a1[i]);
265
+ }
266
+ }
267
+ return result;
268
+ },
269
+
270
+ tagLabel: function(tag) {
271
+ // Returns the tag's string label.
272
+ if (this.options.singleField) {
273
+ return $(tag).children('.tagit-label').text();
274
+ } else {
275
+ return $(tag).children('input').val();
276
+ }
277
+ },
278
+
279
+ _isNew: function(value) {
280
+ var that = this;
281
+ var isNew = true;
282
+ this.tagList.children('.tagit-choice').each(function(i) {
283
+ if (that._formatStr(value) == that._formatStr(that.tagLabel(this))) {
284
+ isNew = false;
285
+ return false;
286
+ }
287
+ });
288
+ return isNew;
289
+ },
290
+
291
+ _formatStr: function(str) {
292
+ if (this.options.caseSensitive) {
293
+ return str;
294
+ }
295
+ return $.trim(str.toLowerCase());
296
+ },
297
+
298
+ createTag: function(value, additionalClass) {
299
+ var that = this;
300
+ // Automatically trims the value of leading and trailing whitespace.
301
+ value = $.trim(value);
302
+
303
+ if (!this._isNew(value) || value === '') {
304
+ return false;
305
+ }
306
+
307
+ var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
308
+
309
+ // Create tag.
310
+ var tag = $('<li></li>')
311
+ .addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
312
+ .addClass(additionalClass)
313
+ .append(label);
314
+
315
+ // Button for removing the tag.
316
+ var removeTagIcon = $('<span></span>')
317
+ .addClass('ui-icon ui-icon-close');
318
+ var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
319
+ .addClass('close')
320
+ .append(removeTagIcon)
321
+ .click(function(e) {
322
+ // Removes a tag when the little 'x' is clicked.
323
+ that.removeTag(tag);
324
+ });
325
+ tag.append(removeTag);
326
+
327
+ // Unless options.singleField is set, each tag has a hidden input field inline.
328
+ if (this.options.singleField) {
329
+ var tags = this.assignedTags();
330
+ tags.push(value);
331
+ this._updateSingleTagsField(tags);
332
+ } else {
333
+ var escapedValue = label.html();
334
+ tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.itemName + '[' + this.options.fieldName + '][]">');
335
+ }
336
+
337
+ this._trigger('onTagAdded', null, tag);
338
+
339
+ // Cleaning the input.
340
+ this._tagInput.val('');
341
+
342
+ // insert tag
343
+ this._tagInput.parent().before(tag);
344
+ },
345
+
346
+ removeTag: function(tag, animate) {
347
+ animate = animate || this.options.animate;
348
+
349
+ tag = $(tag);
350
+
351
+ this._trigger('onTagRemoved', null, tag);
352
+
353
+ if (this.options.singleField) {
354
+ var tags = this.assignedTags();
355
+ var removedTagLabel = this.tagLabel(tag);
356
+ tags = $.grep(tags, function(el){
357
+ return el != removedTagLabel;
358
+ });
359
+ this._updateSingleTagsField(tags);
360
+ }
361
+ // Animate the removal.
362
+ if (animate) {
363
+ tag.fadeOut('fast').hide('blind', {direction: 'horizontal'}, 'fast', function(){
364
+ tag.remove();
365
+ }).dequeue();
366
+ } else {
367
+ tag.remove();
368
+ }
369
+ },
370
+
371
+ removeAll: function() {
372
+ // Removes all tags.
373
+ var that = this;
374
+ this.tagList.children('.tagit-choice').each(function(index, tag) {
375
+ that.removeTag(tag, false);
376
+ });
377
+ }
378
+
379
+ });
380
+
381
+ })(jQuery);
382
+
383
+
@@ -0,0 +1,39 @@
1
+ @charset "UTF-8";
2
+
3
+
4
+ #nav {
5
+ left:0;
6
+ }
7
+ #header {
8
+ padding-top:3em;
9
+ }
10
+
11
+ #header h1, #header h2 {
12
+ margin: 0 0 .2em 0;
13
+ padding-top: 1.8em;
14
+ }
15
+ hr + h3, hr + h4 {
16
+ margin-top: 0;
17
+ }
18
+ hr {
19
+ margin-bottom: .4em;
20
+ }
21
+
22
+
23
+ ul#nav li {
24
+ padding:0;
25
+ }
26
+
27
+ .myform {
28
+ padding:20px 0px;
29
+ }
30
+ .myform div.line {
31
+ clear:both;
32
+ min-height:50px;
33
+ margin-bottom:15px;
34
+ }
35
+ .myform label {
36
+ display:block;
37
+ font-weight:bold;
38
+ margin-bottom:5px;
39
+ }