bootstrap-tag.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /* ==========================================================
  2. * bootstrap-tag.js v2.2.5
  3. * https://github.com/fdeschenes/bootstrap-tag
  4. * ==========================================================
  5. * Copyright 2012 Francois Deschenes.
  6. *
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * ========================================================== */
  19. !function ( $ ) {
  20. 'use strict' // jshint ;_;
  21. var Tag = function ( element, options ) {
  22. this.element = $(element)
  23. this.options = $.extend(true, {}, $.fn.tag.defaults, options)
  24. this.values = $.grep($.map(this.element.val().split(','), $.trim), function ( value ) { return value.length > 0 })
  25. this.show()
  26. }
  27. Tag.prototype = {
  28. constructor: Tag
  29. , show: function () {
  30. var that = this
  31. that.element.parent().prepend(that.element.detach().hide())
  32. that.element
  33. .wrap($('<div class="tags">'))
  34. .parent()
  35. .on('click', function () {
  36. that.input.focus()
  37. })
  38. if (that.values.length) {
  39. $.each(that.values, function () {
  40. that.createBadge(this)
  41. })
  42. }
  43. that.input = $('<input type="text">')
  44. .attr('placeholder', that.options.placeholder)
  45. .insertAfter(that.element)
  46. .on('focus', function () {
  47. that.element.parent().addClass('tags-hover')
  48. })
  49. .on('blur', function () {
  50. if (!that.skip) {
  51. that.process()
  52. that.element.parent().removeClass('tags-hover')
  53. that.element.siblings('.tag').removeClass('tag-important')
  54. }
  55. that.skip = false
  56. })
  57. .on('keydown', function ( event ) {
  58. if ( event.keyCode == 188 || event.keyCode == 13 || event.keyCode == 9 ) {
  59. if ( $.trim($(this).val()) && ( !that.element.siblings('.typeahead').length || that.element.siblings('.typeahead').is(':hidden') ) ) {
  60. if ( event.keyCode != 9 ) event.preventDefault()
  61. that.process()
  62. } else if ( event.keyCode == 188 ) {
  63. if ( !that.element.siblings('.typeahead').length || that.element.siblings('.typeahead').is(':hidden') ) {
  64. event.preventDefault()
  65. } else {
  66. that.input.data('typeahead').select()
  67. event.stopPropagation()
  68. event.preventDefault()
  69. }
  70. }
  71. } else if ( !$.trim($(this).val()) && event.keyCode == 8 ) {
  72. var count = that.element.siblings('.tag').length
  73. if (count) {
  74. var tag = that.element.siblings('.tag:eq(' + (count - 1) + ')')
  75. if (tag.hasClass('tag-important')) that.remove(count - 1)
  76. else tag.addClass('tag-important')
  77. }
  78. } else {
  79. that.element.siblings('.tag').removeClass('tag-important')
  80. }
  81. })
  82. .bs_typeahead({
  83. source: that.options.source
  84. , matcher: function ( value ) {
  85. return ~value.toLowerCase().indexOf(this.query.toLowerCase()) && (that.inValues(value) == -1 || that.options.allowDuplicates)
  86. }
  87. , updater: $.proxy(that.add, that)
  88. })
  89. $(that.input.data('bs_typeahead').$menu).on('mousedown', function() {
  90. that.skip = true
  91. })
  92. this.element.trigger('shown')
  93. }
  94. , inValues: function ( value ) {
  95. if (this.options.caseInsensitive) {
  96. var index = -1
  97. $.each(this.values, function (indexInArray, valueOfElement) {
  98. if ( valueOfElement.toLowerCase() == value.toLowerCase() ) {
  99. index = indexInArray
  100. return false
  101. }
  102. })
  103. return index
  104. } else {
  105. return $.inArray(value, this.values)
  106. }
  107. }
  108. , createBadge: function ( value ) {
  109. var that = this
  110. $('<span/>', {
  111. 'class' : "tag"
  112. })
  113. .text(value)
  114. .append($('<button type="button" class="close">&times;</button>')
  115. .on('click', function () {
  116. that.remove(that.element.siblings('.tag').index($(this).closest('.tag')))
  117. })
  118. )
  119. .insertBefore(that.element)
  120. }
  121. , add: function ( value ) {
  122. var that = this
  123. if ( !that.options.allowDuplicates ) {
  124. var index = that.inValues(value)
  125. if ( index != -1 ) {
  126. var badge = that.element.siblings('.tag:eq(' + index + ')')
  127. badge.addClass('tag-warning')
  128. setTimeout(function () {
  129. $(badge).removeClass('tag-warning')
  130. }, 500)
  131. return
  132. }
  133. }
  134. this.values.push(value)
  135. this.createBadge(value)
  136. this.element.val(this.values.join(', '))
  137. this.element.trigger('added', [value])
  138. }
  139. , remove: function ( index ) {
  140. if ( index >= 0 ) {
  141. var value = this.values.splice(index, 1)
  142. this.element.siblings('.tag:eq(' + index + ')').remove()
  143. this.element.val(this.values.join(', '))
  144. this.element.trigger('removed', [value])
  145. }
  146. }
  147. , process: function () {
  148. var values = $.grep($.map(this.input.val().split(','), $.trim), function ( value ) { return value.length > 0 }),
  149. that = this
  150. $.each(values, function() {
  151. that.add(this)
  152. })
  153. this.input.val('')
  154. }
  155. , skip: false
  156. }
  157. var old = $.fn.tag
  158. $.fn.tag = function ( option ) {
  159. return this.each(function () {
  160. var that = $(this)
  161. , data = that.data('tag')
  162. , options = typeof option == 'object' && option
  163. if (!data) that.data('tag', (data = new Tag(this, options)))
  164. if (typeof option == 'string') data[option]()
  165. })
  166. }
  167. $.fn.tag.defaults = {
  168. allowDuplicates: false
  169. , caseInsensitive: true
  170. , placeholder: ''
  171. , source: []
  172. }
  173. $.fn.tag.Constructor = Tag
  174. $.fn.tag.noConflict = function () {
  175. $.fn.tag = old
  176. return this
  177. }
  178. $(window).on('load', function () {
  179. $('[data-provide="tag"]').each(function () {
  180. var that = $(this)
  181. if (that.data('tag')) return
  182. that.tag(that.data())
  183. })
  184. })
  185. }(window.jQuery);