tagmanager-rails 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,600 @@
1
+ /* ===================================================
2
+ * bootstrap-tagmanager.js v2.3
3
+ * http://welldonethings.com/tags/manager
4
+ * ===================================================
5
+ * Copyright 2012 Max Favilli
6
+ *
7
+ * Licensed under the Mozilla Public License, Version 2.0 You may not use this work except in compliance with the License.
8
+ *
9
+ * http://www.mozilla.org/MPL/2.0/
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ * ========================================================== */
17
+
18
+ "use strict";
19
+
20
+ (function (jQuery) {
21
+ if (typeof console === "undefined" || typeof console.log === "undefined") {
22
+ console = {};
23
+ console.log = function () { };
24
+ }
25
+
26
+ jQuery.fn.tagsManager = function (options,tagToManipulate) {
27
+ var tagManagerOptions = {
28
+ prefilled: null,
29
+ CapitalizeFirstLetter: false,
30
+ preventSubmitOnEnter: true,
31
+ isClearInputOnEsc: true,
32
+ typeahead: false,
33
+ typeaheadAjaxSource: null,
34
+ typeaheadAjaxPolling: false,
35
+ typeaheadOverrides: null,
36
+ typeaheadSource: null,
37
+ AjaxPush: null,
38
+ AjaxPushAllTags: null,
39
+ delimeters: [44, 188, 13, 9],
40
+ backspace: [8],
41
+ maxTags: 0,
42
+ hiddenTagListName: null,
43
+ hiddenTagListId: null,
44
+ deleteTagsOnBackspace: true,
45
+ tagsContainer: null,
46
+ tagCloseIcon: 'x',
47
+ tagClass: '',
48
+ validator: null,
49
+ onlyTagList: false
50
+ };
51
+
52
+ var TypeaheadOverrides = (function () {
53
+ function TypeaheadOverrides() {
54
+ this.instanceSelectHandler = null;
55
+ this.selectedClass = "selected";
56
+ this.select = null;
57
+ if ("typeahead" in jQuery.fn) {
58
+ this.instanceSelectHandler = jQuery.fn.typeahead.Constructor.prototype.select;
59
+ this.select = function (overrides) {
60
+ this.$menu.find(".active").addClass(overrides.selectedClass);
61
+ overrides.instanceSelectHandler.apply(this, arguments);
62
+ };
63
+ }
64
+ }
65
+ return TypeaheadOverrides;
66
+ })();
67
+
68
+ // exit when no matched elements
69
+ if (!(0 in this)) {
70
+ return this;
71
+ }
72
+
73
+ tagManagerOptions.typeaheadOverrides = new TypeaheadOverrides();
74
+
75
+ jQuery.extend(tagManagerOptions, options);
76
+
77
+ if (tagManagerOptions.hiddenTagListName === null) {
78
+ tagManagerOptions.hiddenTagListName = "hidden-" + this.attr('name');
79
+ }
80
+
81
+ var obj = this;
82
+ var objName = obj.attr('name').replace(/[^\w]/g, '_');
83
+ var queuedTag = "";
84
+ var delimeters = tagManagerOptions.delimeters;
85
+ var backspace = tagManagerOptions.backspace;
86
+ var isInitialized = false;
87
+
88
+ var setupTypeahead = function () {
89
+ if (!obj.typeahead) return;
90
+
91
+ if (tagManagerOptions.typeaheadSource != null && jQuery.isFunction(tagManagerOptions.typeaheadSource)) {
92
+ obj.typeahead({ source: tagManagerOptions.typeaheadSource });
93
+ } else if (tagManagerOptions.typeaheadSource != null) {
94
+ obj.typeahead();
95
+ setTypeaheadSource(tagManagerOptions.typeaheadSource);
96
+ } else if (tagManagerOptions.typeaheadAjaxSource != null) {
97
+ if (!tagManagerOptions.typeaheadAjaxPolling) {
98
+ obj.typeahead();
99
+
100
+ if (typeof (tagManagerOptions.typeaheadAjaxSource) == "string") {
101
+ jQuery.ajax({
102
+ cache: false,
103
+ type: "POST",
104
+ contentType: "application/json",
105
+ dataType: "json",
106
+ url: tagManagerOptions.typeaheadAjaxSource,
107
+ data: JSON.stringify({ typeahead: "" }),
108
+ success: function (data) { onTypeaheadAjaxSuccess(data, true); }
109
+ });
110
+ }
111
+ } else if (tagManagerOptions.typeaheadAjaxPolling) {
112
+ obj.typeahead({ source: ajaxPolling });
113
+ }
114
+ } else if (tagManagerOptions.typeaheadDelegate) {
115
+ obj.typeahead(tagManagerOptions.typeaheadDelegate);
116
+ }
117
+
118
+ var data = obj.data('typeahead');
119
+ if (data) {
120
+ // set the overrided handler
121
+ data.select = jQuery.proxy(tagManagerOptions.typeaheadOverrides.select,
122
+ obj.data('typeahead'),
123
+ tagManagerOptions.typeaheadOverrides);
124
+ }
125
+ };
126
+
127
+ var onTypeaheadAjaxSuccess = function(data, isSetTypeaheadSource, process) {
128
+ // format data if it is an asp.net 3.5 response
129
+ if ("d" in data) {
130
+ data = data.d;
131
+ }
132
+
133
+ if (data && data.tags) {
134
+ var sourceAjaxArray = [];
135
+ sourceAjaxArray.length = 0;
136
+ jQuery.each(data.tags, function (key, val) {
137
+ sourceAjaxArray.push(val.tag);
138
+ if (isSetTypeaheadSource) {
139
+ setTypeaheadSource(sourceAjaxArray);
140
+ }
141
+ });
142
+
143
+ if (jQuery.isFunction(process)) {
144
+ process(sourceAjaxArray);
145
+ }
146
+ }
147
+ };
148
+
149
+ var setTypeaheadSource = function (source) {
150
+ obj.data('active', true);
151
+ obj.data('typeahead').source = source;
152
+ obj.data('active', false);
153
+ };
154
+
155
+ var ajaxPolling = function (query, process) {
156
+ if (typeof (tagManagerOptions.typeaheadAjaxSource) == "string") {
157
+ jQuery.ajax({
158
+ cache: false,
159
+ type: "POST",
160
+ contentType: "application/json",
161
+ dataType: "json",
162
+ url: tagManagerOptions.typeaheadAjaxSource,
163
+ data: JSON.stringify({ typeahead: query }),
164
+ success: function (data) { onTypeaheadAjaxSuccess(data, false, process); }
165
+ });
166
+ }
167
+ };
168
+
169
+ var trimTag = function (tag) {
170
+ var txt = jQuery.trim(tag);
171
+
172
+ var l = txt.length;
173
+ var t = 0;
174
+
175
+ for (var i = l - 1; i >= 0; i--) {
176
+ if (-1 == jQuery.inArray(txt.charCodeAt(i), delimeters)) break;
177
+ t++;
178
+ }
179
+
180
+ txt = txt.substring(0, l - t);
181
+ l = txt.length;
182
+ t = 0;
183
+
184
+ //remove from head
185
+ for (var i = 0; i < l; i++) {
186
+ if (-1 == jQuery.inArray(txt.charCodeAt(i), delimeters)) break;
187
+ t++;
188
+ }
189
+
190
+ txt = txt.substring(t, l);
191
+ return txt;
192
+ };
193
+
194
+ var popTag = function () {
195
+ var tlis = obj.data("tlis");
196
+ var tlid = obj.data("tlid");
197
+
198
+ if (tlid.length > 0) {
199
+ var tagId = tlid.pop();
200
+ tlis.pop();
201
+ // console.log("TagIdToRemove: " + tagId);
202
+ jQuery("#" + objName + "_" + tagId).remove();
203
+ refreshHiddenTagList();
204
+ // console.log(tlis);
205
+ }
206
+ };
207
+
208
+ var empty = function () {
209
+ var tlis = obj.data("tlis");
210
+ var tlid = obj.data("tlid");
211
+
212
+ while (tlid.length > 0) {
213
+ var tagId = tlid.pop();
214
+ tlis.pop();
215
+ // console.log("TagIdToRemove: " + tagId);
216
+ jQuery("#" + objName + "_" + tagId).remove();
217
+ refreshHiddenTagList();
218
+ // console.log(tlis);
219
+ }
220
+ };
221
+
222
+ var refreshHiddenTagList = function () {
223
+ var tlis = obj.data("tlis");
224
+ var lhiddenTagList = obj.data("lhiddenTagList");
225
+
226
+ obj.trigger('tags:refresh', tlis.join(","));
227
+
228
+ if (lhiddenTagList) {
229
+ jQuery(lhiddenTagList).val(tlis.join(",")).change();
230
+ }
231
+ };
232
+
233
+ var spliceTag = function (tagId) {
234
+ var tlis = obj.data("tlis");
235
+ var tlid = obj.data("tlid");
236
+
237
+ var p = jQuery.inArray(tagId, tlid);
238
+
239
+ // console.log("TagIdToRemove: " + tagId);
240
+ // console.log("position: " + p);
241
+
242
+ if (-1 != p) {
243
+ jQuery("#" + objName + "_" + tagId).remove();
244
+ tlis.splice(p, 1);
245
+ tlid.splice(p, 1);
246
+ refreshHiddenTagList();
247
+ // console.log(tlis);
248
+ }
249
+
250
+ if (tagManagerOptions.maxTags > 0 && tlis.length < tagManagerOptions.maxTags) {
251
+ obj.show();
252
+ }
253
+ };
254
+
255
+ var pushAllTags = function (e, tagstring) {
256
+ if (tagManagerOptions.AjaxPushAllTags) {
257
+ jQuery.post(tagManagerOptions.AjaxPushAllTags, { tags: tagstring });
258
+ }
259
+ };
260
+
261
+ var pushTag = function (tag, objToPush, isValid) {
262
+ if (!tag || (!isValid) || tag.length <= 0) return;
263
+
264
+ if(tagManagerOptions.onlyTagList){
265
+ if (tagManagerOptions.typeaheadSource != null) {
266
+ if((jQuery.inArray(tag, tagManagerOptions.typeaheadSource)) == -1){
267
+ return;
268
+ }
269
+ }
270
+ }
271
+
272
+ if (tagManagerOptions.CapitalizeFirstLetter && tag.length > 1) {
273
+ tag = tag.charAt(0).toUpperCase() + tag.slice(1).toLowerCase();
274
+ }
275
+
276
+ // call the validator (if any) and do not let the tag pass if invalid
277
+ if (jQuery.isFunction(tagManagerOptions.validator)) {
278
+ if (!tagManagerOptions.validator(tag)) return;
279
+ }
280
+
281
+ var tlis = obj.data("tlis");
282
+ var tlid = obj.data("tlid");
283
+
284
+ // dont accept new tags beyond the defined maximum
285
+ if (tagManagerOptions.maxTags > 0 && tlis.length >= tagManagerOptions.maxTags) return;
286
+
287
+ var alreadyInList = false;
288
+ var tlisLowerCase = tlis.map(function(elem) { return elem.toLowerCase(); });
289
+ var p = jQuery.inArray(tag.toLowerCase(), tlisLowerCase);
290
+ if (-1 != p) {
291
+ // console.log("tag:" + tag + " !!already in list!!");
292
+ alreadyInList = true;
293
+ }
294
+
295
+ if (alreadyInList) {
296
+ var pTagId = tlid[p];
297
+ jQuery("#" + objName + "_" + pTagId).stop()
298
+ .animate({ backgroundColor: tagManagerOptions.blinkBGColor_1 }, 100)
299
+ .animate({ backgroundColor: tagManagerOptions.blinkBGColor_2 }, 100)
300
+ .animate({ backgroundColor: tagManagerOptions.blinkBGColor_1 }, 100)
301
+ .animate({ backgroundColor: tagManagerOptions.blinkBGColor_2 }, 100)
302
+ .animate({ backgroundColor: tagManagerOptions.blinkBGColor_1 }, 100)
303
+ .animate({ backgroundColor: tagManagerOptions.blinkBGColor_2 }, 100);
304
+ } else {
305
+ var max = Math.max.apply(null, tlid);
306
+ max = max == -Infinity ? 0 : max;
307
+
308
+ var tagId = ++max;
309
+ tlis.push(tag);
310
+ tlid.push(tagId);
311
+
312
+ if (tagManagerOptions.AjaxPush != null) {
313
+ jQuery.post(tagManagerOptions.AjaxPush, { tag: tag });
314
+ }
315
+
316
+ // console.log("tagList: " + tlis);
317
+
318
+ var newTagId = objName + '_' + tagId;
319
+ var newTagRemoveId = objName + '_Remover_' + tagId;
320
+ var html = '';
321
+ var cl = tagManagerOptions.tagClass ? ' '+tagManagerOptions.tagClass : '';
322
+ html += '<span class="myTag'+cl+'" id="' + newTagId + '"><span>' + tag + '&nbsp;&nbsp;</span><a href="#" class="myTagRemover" id="' + newTagRemoveId + '" TagIdToRemove="' + tagId + '" title="Remove">' + tagManagerOptions.tagCloseIcon + '</a></span> ';
323
+
324
+ if (tagManagerOptions.tagsContainer != null) {
325
+ jQuery(tagManagerOptions.tagsContainer).append(html);
326
+ } else {
327
+ obj.before(html);
328
+ }
329
+
330
+ jQuery("#" + newTagRemoveId).on("click", obj, function (e) {
331
+ e.preventDefault();
332
+ var TagIdToRemove = parseInt(jQuery(this).attr("TagIdToRemove"));
333
+ spliceTag(TagIdToRemove, e.data);
334
+ });
335
+
336
+ refreshHiddenTagList();
337
+
338
+ if (tagManagerOptions.maxTags > 0 && tlis.length >= tagManagerOptions.maxTags) {
339
+ obj.hide();
340
+ }
341
+ }
342
+ obj.val("");
343
+ };
344
+
345
+ var initialize = function () {
346
+ if (tagManagerOptions.AjaxPushAllTags) {
347
+ obj.on('tags:refresh', pushAllTags);
348
+ }
349
+ };
350
+
351
+ if (!isInitialized) {
352
+ initialize();
353
+ }
354
+
355
+ return this.each(function () {
356
+
357
+ var tagIsValid = false;
358
+ var isSelectedFromList = false;
359
+
360
+ if (typeof options == 'string') {
361
+ switch (options) {
362
+ case "empty":
363
+ empty();
364
+ break;
365
+ case "popTag":
366
+ popTag();
367
+ break;
368
+ case "pushTag":
369
+ pushTag(tagToManipulate, null, true);
370
+ break;
371
+ }
372
+ return;
373
+ }
374
+
375
+ //let's store some instance specific data directly into the DOM object
376
+ var tlis = new Array();
377
+ var tlid = new Array();
378
+ obj.data("tlis", tlis); //list of string tags
379
+ obj.data("tlid", tlid); //list of ID of the string tags
380
+
381
+ if (tagManagerOptions.hiddenTagListId == null) { /* if hidden input not given default activity */
382
+ var hiddenTag = $("input[name='" + tagManagerOptions.hiddenTagListName + "']");
383
+ if (hiddenTag.length > 0) {
384
+ hiddenTag.remove();
385
+ }
386
+
387
+ var html = "";
388
+ html += "<input name='" + tagManagerOptions.hiddenTagListName + "' type='hidden' value=''/>";
389
+ obj.after(html);
390
+ obj.data("lhiddenTagList",
391
+ obj.siblings("input[name='" + tagManagerOptions.hiddenTagListName + "']")[0]
392
+ );
393
+ } else {
394
+ obj.data("lhiddenTagList", jQuery('#' + tagManagerOptions.hiddenTagListId))
395
+ }
396
+
397
+ if (tagManagerOptions.typeahead) {
398
+ setupTypeahead();
399
+ //obj.typeahead({ source: SourceArray })
400
+ }
401
+
402
+ obj.on("focus", function (e) {
403
+ if (jQuery(this).popover) {
404
+ jQuery(this).popover("hide");
405
+ //jQuery(this).popover = null;
406
+ }
407
+ });
408
+
409
+ // clear input field on Esc
410
+ if (tagManagerOptions.isClearInputOnEsc) {
411
+ obj.on("keyup", function (e) {
412
+ if (e.which == 27) {
413
+ jQuery(this).val("");
414
+ e.cancelBubble = true;
415
+ e.returnValue = false;
416
+ e.stopPropagation();
417
+ e.preventDefault();
418
+ return false;
419
+ }
420
+ });
421
+ }
422
+
423
+ // disable submit on enter for this input field
424
+ obj.on("keypress", function (e) {
425
+ if (jQuery(this).popover) {
426
+ jQuery(this).popover("hide");
427
+ //jQuery(this).popover = null;
428
+ }
429
+
430
+ if (tagManagerOptions.preventSubmitOnEnter) {
431
+ if (e.which == 13) {
432
+ e.cancelBubble = true;
433
+ e.returnValue = false;
434
+ e.stopPropagation();
435
+ e.preventDefault();
436
+ //e.keyCode = 9;
437
+ return false;
438
+ }
439
+ }
440
+
441
+ var p = jQuery.inArray(e.which, delimeters);
442
+ var isKeyInList = '0' in jQuery(".typeahead:visible");
443
+ if (!isKeyInList && (- 1 != p)) {
444
+ //user just entered a valid delimeter
445
+ tagIsValid = true;
446
+ var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
447
+ user_input = trimTag(user_input);
448
+ pushTag(user_input, e.data, tagIsValid);
449
+ e.preventDefault();
450
+ // console.log("pushTag: keypress");
451
+ }
452
+ else {
453
+ tagIsValid = false;
454
+ }
455
+
456
+ // console.log("keypress: " + e.which);
457
+ });
458
+
459
+ if (tagManagerOptions.deleteTagsOnBackspace) {
460
+ obj.on("keydown", obj, function (e) {
461
+ var p = jQuery.inArray(e.which, backspace);
462
+ if (-1 != p) {
463
+ //user just entered backspace or equivalent
464
+ var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
465
+ var i = user_input.length;
466
+ if (i <= 0) {
467
+ // console.log("backspace detected");
468
+ e.preventDefault();
469
+ popTag();
470
+ }
471
+ }
472
+ });
473
+ }
474
+
475
+ obj.change(function (e) {
476
+ e.cancelBubble = true;
477
+ e.returnValue = false;
478
+ e.stopPropagation();
479
+ e.preventDefault();
480
+
481
+ var selectedItemClass = tagManagerOptions.typeaheadOverrides.selectedClass;
482
+ var listItemSelector = '.' + selectedItemClass;
483
+
484
+ // check the typeahead list selection
485
+ var data = $(this).data('typeahead');
486
+ if (data) {
487
+ isSelectedFromList = $(this).data('typeahead').$menu.find("*")
488
+ .filter(listItemSelector)
489
+ .hasClass(selectedItemClass);
490
+
491
+ if (isSelectedFromList) {
492
+ tagIsValid = true;
493
+ }
494
+ }
495
+
496
+ if (!tagIsValid) {
497
+ return false;
498
+ }
499
+
500
+ var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
501
+ var is_explorer = navigator.userAgent.indexOf('MSIE') > -1;
502
+ var is_firefox = navigator.userAgent.indexOf('Firefox') > -1;
503
+ var is_safari = navigator.userAgent.indexOf("Safari") > -1;
504
+
505
+ if (!is_chrome && !is_safari)
506
+ jQuery(this).focus();
507
+
508
+ // console.log('Handler for .change() called, value selected:' + obj.val());
509
+ var ao = jQuery(".typeahead:visible");
510
+ if (ao[0] != undefined) {
511
+ // console.log('change: typeaheadIsVisible is visible');
512
+ //when the user click with the mouse on the typeahead li element we get the change event fired twice, once when the input field loose focus and later with the input field value is replaced with li value
513
+
514
+ var isClear = !isSelectedFromList;
515
+
516
+ if (isSelectedFromList) {
517
+ // if user selected from list
518
+ var user_input = $(this).data('typeahead').$menu.find(listItemSelector).attr('data-value');
519
+ user_input = trimTag(user_input);
520
+ if (queuedTag == jQuery(this).val() && queuedTag == user_input) {
521
+ isClear = true;
522
+ } else {
523
+ pushTag(user_input, null, true);
524
+ queuedTag = user_input;
525
+ // console.log('Handler for .change() called, typeahead value pushed:' + queuedTag);
526
+ }
527
+ isSelectedFromList = false;
528
+ $(this).data('typeahead').$menu.find(listItemSelector).removeClass(selectedItemClass);
529
+ }
530
+
531
+ if (isClear) {
532
+ queuedTag = "";
533
+ jQuery(this).val(queuedTag);
534
+ }
535
+ } else {
536
+ // console.log('change: typeaheadIsVisible is NOT visible');
537
+ var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
538
+ user_input = trimTag(user_input);
539
+ pushTag(user_input, null, true);
540
+ // console.log("pushTag: change ");
541
+ }
542
+
543
+ tagIsValid = false;
544
+
545
+ return false; //cancel bubble
546
+ });
547
+
548
+ if (1 == 1 || !tagManagerOptions.typeahead) {
549
+ obj.on("blur", function (e) {
550
+ //lost focus
551
+ e.cancelBubble = true;
552
+ e.returnValue = false;
553
+ e.stopPropagation();
554
+ e.preventDefault();
555
+
556
+ var push = true;
557
+ if (tagManagerOptions.typeahead) {
558
+ var ao = jQuery(".typeahead:visible");
559
+ if (ao[0] != undefined) {
560
+ // console.log('blur: typeaheadIsVisible is visible');
561
+ push = false;
562
+ } else {
563
+ // console.log('blur: typeaheadIsVisible is NOT visible');
564
+ push = true;
565
+ }
566
+ }
567
+
568
+ if (push) {
569
+ // console.log('lost focus');
570
+ var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
571
+ user_input = trimTag(user_input);
572
+ pushTag(user_input, null, tagIsValid);
573
+ // console.log("pushTag: blur");
574
+ }
575
+
576
+ return false;
577
+ });
578
+ }
579
+
580
+ if (tagManagerOptions.prefilled != null) {
581
+ if (typeof (tagManagerOptions.prefilled) == "object") {
582
+ var pta = tagManagerOptions.prefilled;
583
+ jQuery.each(pta, function (key, val) {
584
+ var a = 1;
585
+ pushTag(val, obj, true);
586
+ });
587
+ } else if (typeof (tagManagerOptions.prefilled) == "string") {
588
+ var pta = tagManagerOptions.prefilled.split(',');
589
+
590
+ jQuery.each(pta, function (key, val) {
591
+ var a = 1;
592
+ pushTag(val, obj, true);
593
+ });
594
+
595
+ }
596
+ }
597
+ });
598
+
599
+ }
600
+ })(jQuery);