/Resources/public/js/bootstrap-tagmanager.js

https://bitbucket.org/cmedia/cmediatagbundle · JavaScript · 460 lines · 331 code · 65 blank · 64 comment · 101 complexity · 982b87b55f274652b69be6fe1b7fd165 MD5 · raw file

  1. /* ===================================================
  2. * bootstrap-tagmanager.js v2.2
  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. "use strict";
  18. (function (jQuery) {
  19. if (typeof console === "undefined" || typeof console.log === "undefined") {
  20. console = {};
  21. console.log = function () { };
  22. }
  23. jQuery.fn.tagsManager = function (options,tagToManipulate) {
  24. var tagManagerOptions = {
  25. prefilled: null,
  26. CapitalizeFirstLetter: false,
  27. preventSubmitOnEnter: true,
  28. typeahead: false,
  29. typeaheadAjaxSource: null,
  30. typeaheadAjaxPolling: false,
  31. typeaheadSource: null,
  32. AjaxPush: null,
  33. delimeters: [44, 188, 13],
  34. backspace: [8],
  35. maxTags: 0,
  36. hiddenTagListName: null,
  37. hiddenTagListId: null,
  38. deleteTagsOnBackspace: true,
  39. tagsContainer: null,
  40. tagCloseIcon: 'x'
  41. };
  42. jQuery.extend(tagManagerOptions, options);
  43. if (tagManagerOptions.hiddenTagListName === null) {
  44. tagManagerOptions.hiddenTagListName = "hidden-" + this.attr('name');
  45. }
  46. var obj = this;
  47. var objName = obj.attr('name').replace(/[^\w]/g, '_');
  48. var queuedTag = "";
  49. var delimeters = tagManagerOptions.delimeters;
  50. var backspace = tagManagerOptions.backspace;
  51. var setupTypeahead = function () {
  52. if (!obj.typeahead) return;
  53. if (tagManagerOptions.typeaheadSource != null && jQuery.isFunction(tagManagerOptions.typeaheadSource)) {
  54. obj.typeahead({ source: tagManagerOptions.typeaheadSource });
  55. } else if (tagManagerOptions.typeaheadSource != null) {
  56. obj.typeahead();
  57. obj.data('active', true);
  58. obj.data('typeahead').source = tagManagerOptions.typeaheadSource;
  59. obj.data('active', false);
  60. } else if (tagManagerOptions.typeaheadAjaxSource != null) {
  61. if (!tagManagerOptions.typeaheadAjaxPolling) {
  62. obj.typeahead();
  63. jQuery.getJSON(tagManagerOptions.typeaheadAjaxSource+obj.attr('value'), function (data) {
  64. var sourceAjaxArray = [];
  65. if (data != undefined) {
  66. sourceAjaxArray.length = 0;
  67. jQuery.each(data, function (key, val) {
  68. // var a = 1;
  69. sourceAjaxArray.push(val);
  70. obj.data('active', true);
  71. obj.data('typeahead').source = sourceAjaxArray;
  72. obj.data('active', false);
  73. });
  74. }
  75. });
  76. } else if (tagManagerOptions.typeaheadAjaxPolling) {
  77. obj.typeahead({ source: ajaxPolling });
  78. }
  79. } else if (tagManagerOptions.typeaheadDelegate) {
  80. obj.typeahead(tagManagerOptions.typeaheadDelegate)
  81. }
  82. };
  83. var ajaxPolling = function (query, process) {
  84. jQuery.getJSON(tagManagerOptions.typeaheadAjaxSource, function (data) {
  85. var sourceAjaxArray = [];
  86. if (data != undefined && data.tags != undefined) {
  87. sourceAjaxArray.length = 0;
  88. jQuery.each(data.tags, function (key, val) {
  89. var a = 1;
  90. sourceAjaxArray.push(val.tag);
  91. });
  92. process(sourceAjaxArray);
  93. }
  94. });
  95. };
  96. var trimTag = function (tag) {
  97. var txt = jQuery.trim(tag);
  98. var l = txt.length;
  99. var t = 0;
  100. for (var i = l - 1; i >= 0; i--) {
  101. if (-1 == jQuery.inArray(txt.charCodeAt(i), delimeters)) break;
  102. t++;
  103. }
  104. txt = txt.substring(0, l - t);
  105. l = txt.length;
  106. t = 0;
  107. //remove from head
  108. for (var i = 0; i < l; i++) {
  109. if (-1 == jQuery.inArray(txt.charCodeAt(i), delimeters)) break;
  110. t++;
  111. }
  112. txt = txt.substring(t, l);
  113. return txt;
  114. };
  115. //$(this).on('popTag', function (e){
  116. // var tlis = obj.data("tlis");
  117. // var tlid = obj.data("tlid");
  118. // if (tlid.length > 0) {
  119. // var tagId = tlid.pop();
  120. // tlis.pop();
  121. // // console.log("TagIdToRemove: " + tagId);
  122. // jQuery("#" + objName + "_" + tagId).remove();
  123. // refreshHiddenTagList();
  124. // // console.log(tlis);
  125. // }
  126. //});
  127. var popTag = function () {
  128. var tlis = obj.data("tlis");
  129. var tlid = obj.data("tlid");
  130. if (tlid.length > 0) {
  131. var tagId = tlid.pop();
  132. tlis.pop();
  133. // console.log("TagIdToRemove: " + tagId);
  134. jQuery("#" + objName + "_" + tagId).remove();
  135. refreshHiddenTagList();
  136. // console.log(tlis);
  137. }
  138. };
  139. var empty = function () {
  140. var tlis = obj.data("tlis");
  141. var tlid = obj.data("tlid");
  142. while (tlid.length > 0) {
  143. var tagId = tlid.pop();
  144. tlis.pop();
  145. // console.log("TagIdToRemove: " + tagId);
  146. jQuery("#" + objName + "_" + tagId).remove();
  147. refreshHiddenTagList();
  148. // console.log(tlis);
  149. }
  150. };
  151. var refreshHiddenTagList = function () {
  152. var tlis = obj.data("tlis");
  153. var lhiddenTagList = obj.data("lhiddenTagList");
  154. if (lhiddenTagList == undefined)
  155. return;
  156. jQuery(lhiddenTagList).val(tlis.join(",")).change();
  157. };
  158. var spliceTag = function (tagId) {
  159. var tlis = obj.data("tlis");
  160. var tlid = obj.data("tlid");
  161. var p = jQuery.inArray(tagId, tlid)
  162. // console.log("TagIdToRemove: " + tagId);
  163. // console.log("position: " + p);
  164. if (-1 != p) {
  165. jQuery("#" + objName + "_" + tagId).remove();
  166. tlis.splice(p, 1);
  167. tlid.splice(p, 1);
  168. refreshHiddenTagList();
  169. // console.log(tlis);
  170. }
  171. if (tagManagerOptions.maxTags > 0 && tlis.length < tagManagerOptions.maxTags) {
  172. obj.show();
  173. }
  174. };
  175. var pushTag = function (tag) {
  176. if (!tag || tag.length <= 0) return;
  177. if (tagManagerOptions.CapitalizeFirstLetter && tag.length > 1) {
  178. tag = tag.charAt(0).toUpperCase() + tag.slice(1).toLowerCase();
  179. }
  180. // call the validator (if any) and do not let the tag pass if invalid
  181. if (tagManagerOptions.validator !== undefined) {
  182. if (tagManagerOptions.validator(tag) !== true) return;
  183. }
  184. var tlis = obj.data("tlis");
  185. var tlid = obj.data("tlid");
  186. // dont accept new tags beyond the defined maximum
  187. if (tagManagerOptions.maxTags > 0 && tlis.length >= tagManagerOptions.maxTags) return;
  188. var alreadyInList = false;
  189. var p = jQuery.inArray(tag, tlis);
  190. if (-1 != p) {
  191. // console.log("tag:" + tag + " !!already in list!!");
  192. alreadyInList = true;
  193. }
  194. if (alreadyInList) {
  195. var pTagId = tlid[p];
  196. jQuery("#" + objName + "_" + pTagId).stop()
  197. .animate({ backgroundColor: tagManagerOptions.blinkBGColor_1 }, 100)
  198. .animate({ backgroundColor: tagManagerOptions.blinkBGColor_2 }, 100)
  199. .animate({ backgroundColor: tagManagerOptions.blinkBGColor_1 }, 100)
  200. .animate({ backgroundColor: tagManagerOptions.blinkBGColor_2 }, 100)
  201. .animate({ backgroundColor: tagManagerOptions.blinkBGColor_1 }, 100)
  202. .animate({ backgroundColor: tagManagerOptions.blinkBGColor_2 }, 100);
  203. } else {
  204. var max = Math.max.apply(null, tlid);
  205. max = max == -Infinity ? 0 : max;
  206. var tagId = ++max;
  207. tlis.push(tag);
  208. tlid.push(tagId);
  209. if (tagManagerOptions.AjaxPush != null) {
  210. jQuery.post(tagManagerOptions.AjaxPush, { tag: tag });
  211. }
  212. // console.log("tagList: " + tlis);
  213. var newTagId = objName + '_' + tagId;
  214. var newTagRemoveId = objName + '_Remover_' + tagId;
  215. var html = '';
  216. html += '<span class="myTag" id="' + newTagId + '"><span>' + tag + '&nbsp;&nbsp;</span><a href="#" class="myTagRemover" id="' + newTagRemoveId + '" TagIdToRemove="' + tagId + '" title="Remove">' + tagManagerOptions.tagCloseIcon + '</a></span>';
  217. if (tagManagerOptions.tagsContainer != null) {
  218. jQuery(tagManagerOptions.tagsContainer).append(html)
  219. } else {
  220. obj.before(html);
  221. }
  222. jQuery("#" + newTagRemoveId).on("click", obj, function (e) {
  223. e.preventDefault();
  224. var TagIdToRemove = parseInt(jQuery(this).attr("TagIdToRemove"));
  225. spliceTag(TagIdToRemove, e.data);
  226. });
  227. refreshHiddenTagList();
  228. if (tagManagerOptions.maxTags > 0 && tlis.length >= tagManagerOptions.maxTags) {
  229. obj.hide();
  230. }
  231. }
  232. obj.val("");
  233. };
  234. return this.each(function () {
  235. if (typeof options == 'string') {
  236. switch (options) {
  237. case "empty":
  238. empty();
  239. break;
  240. case "popTag":
  241. popTag();
  242. break;
  243. case "pushTag":
  244. pushTag(tagToManipulate);
  245. break;
  246. }
  247. return;
  248. }
  249. //let's store some instance specific data directly into the DOM object
  250. var tlis = new Array();
  251. var tlid = new Array();
  252. obj.data("tlis", tlis); //list of string tags
  253. obj.data("tlid", tlid); //list of ID of the string tags
  254. if (tagManagerOptions.hiddenTagListId == null) { /* if hidden input not given default activity */
  255. var html = "";
  256. html += "<input name='" + tagManagerOptions.hiddenTagListName + "' type='hidden' value=''/>";
  257. obj.after(html);
  258. obj.data("lhiddenTagList",
  259. obj.siblings("input[name='" + tagManagerOptions.hiddenTagListName + "']")[0]
  260. );
  261. } else {
  262. obj.data("lhiddenTagList", jQuery('#' + tagManagerOptions.hiddenTagListId))
  263. }
  264. if (tagManagerOptions.typeahead) {
  265. setupTypeahead();
  266. //obj.typeahead({ source: SourceArray })
  267. }
  268. obj.on("focus", function (e) {
  269. if (jQuery(this).popover) {
  270. jQuery(this).popover("hide");
  271. //jQuery(this).popover = null;
  272. }
  273. });
  274. // disable submit on enter for this input field
  275. obj.on("keypress", function (e) {
  276. if (jQuery(this).popover) {
  277. jQuery(this).popover("hide");
  278. //jQuery(this).popover = null;
  279. }
  280. if (tagManagerOptions.preventSubmitOnEnter) {
  281. if (e.which == 13) {
  282. e.cancelBubble = true;
  283. e.returnValue = false;
  284. e.stopPropagation();
  285. e.preventDefault();
  286. //e.keyCode = 9;
  287. }
  288. }
  289. // console.log("keyup: " + e.keyCode);
  290. });
  291. obj.on("keyup", obj, function (e) {
  292. var p = jQuery.inArray(e.which, delimeters);
  293. if (-1 != p) {
  294. //user just entered a valid delimeter
  295. var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
  296. user_input = trimTag(user_input);
  297. pushTag(user_input, e.data);
  298. // console.log("pushTag: keyup");
  299. }
  300. // console.log("keyup: " + e.which);
  301. });
  302. if (tagManagerOptions.deleteTagsOnBackspace) {
  303. obj.on("keydown", obj, function (e) {
  304. var p = jQuery.inArray(e.which, backspace);
  305. if (-1 != p) {
  306. //user just entered backspace or equivalent
  307. var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
  308. var i = user_input.length;
  309. if (i <= 0) {
  310. // console.log("backspace detected");
  311. e.preventDefault();
  312. popTag();
  313. }
  314. }
  315. });
  316. }
  317. obj.change(function (e) {
  318. e.cancelBubble = true;
  319. e.returnValue = false;
  320. e.stopPropagation();
  321. e.preventDefault();
  322. var is_chrome = navigator.userAgent.indexOf('Chrome') > -1;
  323. var is_explorer = navigator.userAgent.indexOf('MSIE') > -1;
  324. var is_firefox = navigator.userAgent.indexOf('Firefox') > -1;
  325. var is_safari = navigator.userAgent.indexOf("Safari") > -1;
  326. if (!is_chrome && !is_safari)
  327. jQuery(this).focus();
  328. // console.log('Handler for .change() called, value selected:' + obj.val());
  329. var ao = jQuery(".typeahead:visible");
  330. if (ao[0] != undefined) {
  331. // console.log('change: typeaheadIsVisible is visible');
  332. //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
  333. var user_input = $(this).data('typeahead').$menu.find('.active').attr('data-value');
  334. user_input = trimTag(user_input);
  335. if (queuedTag == obj.val() && queuedTag == user_input) {
  336. queuedTag = "";
  337. obj.val(queuedTag);
  338. } else {
  339. pushTag(user_input);
  340. queuedTag = user_input;
  341. // console.log('Handler for .change() called, typeahead value pushed:' + queuedTag);
  342. }
  343. } else {
  344. // console.log('change: typeaheadIsVisible is NOT visible');
  345. var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
  346. user_input = trimTag(user_input);
  347. pushTag(user_input);
  348. // console.log("pushTag: change ");
  349. }
  350. });
  351. if (1 == 1 || !tagManagerOptions.typeahead) {
  352. obj.on("blur", function (e) {
  353. //lost focus
  354. e.cancelBubble = true;
  355. e.returnValue = false;
  356. e.stopPropagation();
  357. e.preventDefault();
  358. var push = true;
  359. if (tagManagerOptions.typeahead) {
  360. var ao = jQuery(".typeahead:visible");
  361. if (ao[0] != undefined) {
  362. // console.log('blur: typeaheadIsVisible is visible');
  363. push = false;
  364. } else {
  365. // console.log('blur: typeaheadIsVisible is NOT visible');
  366. push = true;
  367. }
  368. }
  369. if (push) {
  370. // console.log('lost focus');
  371. var user_input = jQuery(this).val(); //user_input = jQuery().inArray(delimeters[p]);
  372. user_input = trimTag(user_input);
  373. pushTag(user_input);
  374. // console.log("pushTag: blur");
  375. }
  376. });
  377. }
  378. if (tagManagerOptions.prefilled != null) {
  379. if (typeof (tagManagerOptions.prefilled) == "object") {
  380. var pta = tagManagerOptions.prefilled;
  381. jQuery.each(pta, function (key, val) {
  382. var a = 1;
  383. pushTag(val, obj);
  384. });
  385. } else if (typeof (tagManagerOptions.prefilled) == "string") {
  386. var pta = tagManagerOptions.prefilled.split(',');
  387. jQuery.each(pta, function (key, val) {
  388. var a = 1;
  389. pushTag(val, obj);
  390. });
  391. }
  392. }
  393. });
  394. }
  395. })(jQuery);