apipie-dsl 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +485 -0
  4. data/app/controllers/apipie_dsl/apipie_dsls_controller.rb +190 -0
  5. data/app/helpers/apipie_dsl_helper.rb +110 -0
  6. data/app/public/apipie_dsl/javascripts/apipie_dsl.js +6 -0
  7. data/app/public/apipie_dsl/javascripts/bundled/bootstrap-collapse.js +138 -0
  8. data/app/public/apipie_dsl/javascripts/bundled/bootstrap.js +1726 -0
  9. data/app/public/apipie_dsl/javascripts/bundled/jquery.js +5 -0
  10. data/app/public/apipie_dsl/javascripts/bundled/prettify.js +28 -0
  11. data/app/public/apipie_dsl/stylesheets/application.css +7 -0
  12. data/app/public/apipie_dsl/stylesheets/bundled/bootstrap-responsive.min.css +12 -0
  13. data/app/public/apipie_dsl/stylesheets/bundled/bootstrap.min.css +689 -0
  14. data/app/public/apipie_dsl/stylesheets/bundled/prettify.css +30 -0
  15. data/app/views/apipie_dsl/apipie_dsls/_index_class_meth.erb +11 -0
  16. data/app/views/apipie_dsl/apipie_dsls/_index_class_prop.erb +13 -0
  17. data/app/views/apipie_dsl/apipie_dsls/_languages.erb +6 -0
  18. data/app/views/apipie_dsl/apipie_dsls/_metadata.erb +1 -0
  19. data/app/views/apipie_dsl/apipie_dsls/_method.erb +34 -0
  20. data/app/views/apipie_dsl/apipie_dsls/_method_detail.erb +58 -0
  21. data/app/views/apipie_dsl/apipie_dsls/_params.html.erb +47 -0
  22. data/app/views/apipie_dsl/apipie_dsls/_params_plain.html.erb +19 -0
  23. data/app/views/apipie_dsl/apipie_dsls/_property.erb +24 -0
  24. data/app/views/apipie_dsl/apipie_dsls/_property_detail.erb +35 -0
  25. data/app/views/apipie_dsl/apipie_dsls/_raises.html.erb +23 -0
  26. data/app/views/apipie_dsl/apipie_dsls/_returns.html.erb +53 -0
  27. data/app/views/apipie_dsl/apipie_dsls/apipie_dsl_404.html.erb +17 -0
  28. data/app/views/apipie_dsl/apipie_dsls/class.html.erb +75 -0
  29. data/app/views/apipie_dsl/apipie_dsls/custom_help.html.erb +10 -0
  30. data/app/views/apipie_dsl/apipie_dsls/getting_started.html.erb +4 -0
  31. data/app/views/apipie_dsl/apipie_dsls/index.html.erb +72 -0
  32. data/app/views/apipie_dsl/apipie_dsls/method.html.erb +52 -0
  33. data/app/views/apipie_dsl/apipie_dsls/plain.html.erb +116 -0
  34. data/app/views/apipie_dsl/apipie_dsls/static.html.erb +158 -0
  35. data/app/views/layouts/apipie_dsl/apipie_dsl.html.erb +26 -0
  36. data/lib/apipie-dsl.rb +3 -0
  37. data/lib/apipie_dsl.rb +28 -0
  38. data/lib/apipie_dsl/Rakefile +6 -0
  39. data/lib/apipie_dsl/apipie_dsl_module.rb +51 -0
  40. data/lib/apipie_dsl/application.rb +321 -0
  41. data/lib/apipie_dsl/class_description.rb +107 -0
  42. data/lib/apipie_dsl/configuration.rb +87 -0
  43. data/lib/apipie_dsl/dsl.rb +596 -0
  44. data/lib/apipie_dsl/errors.rb +68 -0
  45. data/lib/apipie_dsl/exception_description.rb +39 -0
  46. data/lib/apipie_dsl/markup.rb +41 -0
  47. data/lib/apipie_dsl/method_description.rb +112 -0
  48. data/lib/apipie_dsl/parameter_description.rb +152 -0
  49. data/lib/apipie_dsl/railtie.rb +15 -0
  50. data/lib/apipie_dsl/return_description.rb +71 -0
  51. data/lib/apipie_dsl/routing.rb +17 -0
  52. data/lib/apipie_dsl/see_description.rb +35 -0
  53. data/lib/apipie_dsl/static_dispatcher.rb +70 -0
  54. data/lib/apipie_dsl/tag_list_description.rb +11 -0
  55. data/lib/apipie_dsl/tasks/apipie_dsl/cache.rake +43 -0
  56. data/lib/apipie_dsl/tasks/apipie_dsl/static.rake +43 -0
  57. data/lib/apipie_dsl/tasks/apipie_dsl/static_json.rake +29 -0
  58. data/lib/apipie_dsl/tasks_utils.rb +137 -0
  59. data/lib/apipie_dsl/utils.rb +83 -0
  60. data/lib/apipie_dsl/validator.rb +479 -0
  61. data/lib/apipie_dsl/version.rb +5 -0
  62. data/lib/generators/apipie_dsl/install/install_generator.rb +21 -0
  63. data/lib/generators/apipie_dsl/install/templates/initializer.rb.erb +9 -0
  64. data/lib/generators/apipie_dsl/views_generator.rb +14 -0
  65. data/test/test_helper.rb +6 -0
  66. metadata +220 -0
@@ -0,0 +1,190 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApipieDsl
4
+ class ApipieDslsController < ActionController::Base
5
+ include ActionView::Context
6
+ include ApipieDslHelper
7
+
8
+ layout ApipieDSL.configuration.layout
9
+
10
+ around_action :set_script_name
11
+ before_action :authenticate
12
+
13
+ def authenticate
14
+ if ApipieDSL.configuration.authenticate
15
+ instance_eval(&ApipieDSL.configuration.authenticate)
16
+ end
17
+ end
18
+
19
+ def index
20
+ params[:version] ||= ApipieDSL.configuration.default_version
21
+ params[:section] ||= ApipieDSL.configuration.default_section
22
+
23
+ get_format
24
+
25
+ respond_to do |format|
26
+ if ApipieDSL.configuration.use_cache?
27
+ render_from_cache
28
+ return
29
+ end
30
+
31
+ @language = get_language
32
+ @section = params[:section]
33
+
34
+ ApipieDSL.load_documentation if ApipieDSL.configuration.reload_dsl? || (Rails.version.to_i >= 4.0 && !Rails.application.config.eager_load)
35
+
36
+ I18n.locale = @language
37
+
38
+ @doc = ApipieDSL.docs(params[:version], params[:class], params[:method], @language, @section)
39
+ @doc = authorized_doc
40
+
41
+ format.json do
42
+ if @doc
43
+ render json: @doc
44
+ else
45
+ head :not_found
46
+ end
47
+ end
48
+
49
+ format.html do
50
+ unless @doc
51
+ render 'apipie_dsl_404', status: 404
52
+ return
53
+ end
54
+
55
+ @versions = ApipieDSL.available_versions
56
+ @doc = @doc[:docs]
57
+ @doc[:link_extension] = (@language ? ".#{@language}" : '') + ApipieDSL.configuration.link_extension
58
+ render 'getting_started' and return if @doc[:classes].blank?
59
+
60
+ @klass = @doc[:classes].first if params[:class].present?
61
+ @method = @klass[:methods].first if params[:method].present?
62
+ @languages = ApipieDSL.configuration.languages
63
+
64
+ if @klass && @method
65
+ render 'method'
66
+ elsif @klass
67
+ render 'class'
68
+ elsif params[:class].present? || params[:method].present?
69
+ render 'apipie_dsl_404', status: 404
70
+ elsif @section == 'help'
71
+ render 'custom_help'
72
+ else
73
+ render 'index'
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def apipie_dsl_checksum
80
+ end
81
+
82
+ private
83
+
84
+ helper_method :heading
85
+
86
+ def get_language
87
+ return nil unless ApipieDSL.configuration.translate
88
+
89
+ lang = Apipie.configuration.default_locale
90
+ %i[class method version section].each do |par|
91
+ next unless params[par]
92
+
93
+ splitted = params[par].split('.')
94
+ if splitted.length > 1 && ApipieDSL.configuration.languages.include?(splitted.last)
95
+ lang = splitted.last
96
+ params[par].sub!(".#{lang}", '')
97
+ end
98
+ end
99
+ lang
100
+ end
101
+
102
+ def authorized_doc
103
+ return if @doc.nil?
104
+ return @doc unless ApipieDSL.configuration.authorize
105
+
106
+ new_doc = { docs: @doc[:docs].clone }
107
+
108
+ new_doc[:docs][:classes] = if @doc[:docs][:classes].is_a?(Array)
109
+ @doc[:docs][:classes].select do |klass|
110
+ authorize_class(klass)
111
+ end
112
+ else
113
+ @doc[:docs][:classes].select do |_class_name, klass|
114
+ authorize_class(klass)
115
+ end
116
+ end
117
+
118
+ new_doc
119
+ end
120
+
121
+ def authorize_class(klass)
122
+ if instance_exec(klass[:id], nil, klass, &ApipieDSL.configuration.authorize)
123
+ klass[:methods] = klass[:methods].select do |m|
124
+ instance_exec(klass[:id], m[:name], m, &ApipieDSL.configuration.authorize)
125
+ end
126
+ true
127
+ else
128
+ false
129
+ end
130
+ end
131
+
132
+ def get_format
133
+ %i[class method version section].each do |par|
134
+ next unless params[par]
135
+
136
+ %i[html json].each do |format|
137
+ extension = ".#{format}"
138
+ if params[par].include?(extension)
139
+ params[par] = params[par].sub(extension, '')
140
+ params[:format] = format
141
+ end
142
+ end
143
+ end
144
+ request.format = params[:format] if params[:format]
145
+ end
146
+
147
+ def render_from_cache
148
+ path = ApipieDSL.configuration.doc_base_url.dup
149
+ # some params can contain dot, but only one in row
150
+ if %i[class method format version].any? { |p| params[p].to_s =~ /\.\./ }
151
+ head :bad_request and return
152
+ end
153
+
154
+ path << '/' << params[:version] if params[:version].present?
155
+ path << '/' << params[:section] if params[:section].present?
156
+ path << '/' << params[:class] if params[:class].present?
157
+ path << '/' << params[:method] if params[:method].present?
158
+ path << if params[:format].present?
159
+ ".#{params[:format]}"
160
+ else
161
+ '.html'
162
+ end
163
+
164
+ # we sanitize the params before so in ideal case, this condition
165
+ # will be never satisfied. It's here for cases somebody adds new
166
+ # param into the path later and forgets about sanitation.
167
+
168
+ head :bad_request and return if path =~ /\.\./
169
+
170
+ cache_file = File.join(ApipieDSL.configuration.cache_dir, path)
171
+ if File.exist?(cache_file)
172
+ content_type = case params[:format]
173
+ when 'json' then 'application/json'
174
+ else 'text/html'
175
+ end
176
+ send_file cache_file, type: content_type, disposition: 'inline'
177
+ else
178
+ Rails.logger.error("DSL doc cache not found for '#{path}'. Perhaps you have forgot to run `rake apipie_dsl:cache`")
179
+ head :not_found
180
+ end
181
+ end
182
+
183
+ def set_script_name
184
+ ApipieDSL.request_script_name = request.env['SCRIPT_NAME']
185
+ yield
186
+ ensure
187
+ ApipieDSL.request_script_name = nil
188
+ end
189
+ end
190
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'apipie_dsl/tasks_utils'
4
+
5
+ module ApipieDslHelper
6
+ include ActionView::Helpers::TagHelper
7
+
8
+ def heading(title, level = 1)
9
+ content_tag("h#{level}") do
10
+ title
11
+ end
12
+ end
13
+
14
+ def escaped_method_name(method, escaping = '')
15
+ return method.gsub('?', escaping) if method.is_a?(String)
16
+ end
17
+
18
+ def resolve_default(default)
19
+ case default
20
+ when nil
21
+ 'nil'
22
+ when ''
23
+ "\"\""
24
+ else
25
+ default
26
+ end
27
+ end
28
+
29
+ def method_signature(method_desc)
30
+ return "#{method_desc[:name]}" if method_desc[:params].empty?
31
+
32
+ params = method_desc[:params].map do |param|
33
+ default = resolve_default(param[:default])
34
+ case param[:type]
35
+ when 'required'
36
+ param[:name].to_s
37
+ when 'optional'
38
+ if param[:expected_type] == 'list'
39
+ "*#{param[:name]}"
40
+ else
41
+ "#{param[:name]} = #{default}"
42
+ end
43
+ when 'keyword'
44
+ "#{param[:name]}: #{default}"
45
+ end
46
+ end.join(', ')
47
+
48
+ block_param = method_desc[:params].find { |p| p[:type] == 'block' }
49
+
50
+ signature_parts = [method_desc[:name]]
51
+ signature_parts << "(#{params})" unless params.empty?
52
+ signature_parts << " #{block_param[:schema]}" if block_param
53
+ signature_parts.join
54
+ end
55
+
56
+ def class_references(obj, version, link_extension)
57
+ # Try to convert to a constant in case of LazyValidator usage
58
+ # Will raise const missing exception in case of wrong usage of the method
59
+ if obj.is_a?(String)
60
+ obj = defined?(Rails) ? obj.constantize : obj.split('::').reduce(::Module, :const_get)
61
+ end
62
+ return obj.to_s unless [::Module, ::Class, ::Array].include?(obj.class)
63
+
64
+ refs = [obj].flatten.map do |o|
65
+ next o unless [::Module, ::Class].include?(o.class)
66
+
67
+ referenced = ApipieDSL.refs[version][ApipieDSL.get_class_name(o)]
68
+ next o if referenced.nil?
69
+
70
+ "<a href='#{referenced.doc_url(referenced.sections.first)}#{link_extension}'>#{o}</a>"
71
+ end
72
+ return refs.first if refs.size < 2
73
+
74
+ refs
75
+ end
76
+
77
+ def dsl_sections
78
+ ApipieDSL.configuration.sections
79
+ end
80
+
81
+ def in_section?(section, klass)
82
+ ApipieDslHelper.in_section?(section, klass)
83
+ end
84
+
85
+ def self.in_section?(section, klass)
86
+ class_desc = ApipieDSL.get_class_description(klass)
87
+ raise ApipieDSL::Error, "Cannot find #{klass} description" if class_desc.nil?
88
+ return true if section.empty?
89
+
90
+ class_desc.sections.include?(section)
91
+ end
92
+
93
+ def section_ext(section)
94
+ "/#{section}"
95
+ end
96
+
97
+ def current_version(classes)
98
+ if classes.is_a?(Array)
99
+ classes.first[:version]
100
+ elsif classes.is_a?(Hash)
101
+ classes.values.first[:version]
102
+ else
103
+ raise ApipieDSL::Error, "Cannot find current version for #{classes}"
104
+ end
105
+ end
106
+
107
+ def render_help
108
+ render template: ApipieDSL.configuration.help_layout
109
+ end
110
+ end
@@ -0,0 +1,6 @@
1
+ $(document).ready(function() {
2
+ if (typeof prettyPrint == 'function') {
3
+ $('pre.ruby').addClass('prettyprint lang-rb');
4
+ prettyPrint();
5
+ }
6
+ });
@@ -0,0 +1,138 @@
1
+ /* =============================================================
2
+ * bootstrap-collapse.js v2.0.2
3
+ * http://twitter.github.com/bootstrap/javascript.html#collapse
4
+ * =============================================================
5
+ * Copyright 2012 Twitter, Inc.
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
+
20
+ !function( $ ){
21
+
22
+ "use strict"
23
+
24
+ var Collapse = function ( element, options ) {
25
+ this.$element = $(element)
26
+ this.options = $.extend({}, $.fn.collapse.defaults, options)
27
+
28
+ if (this.options["parent"]) {
29
+ this.$parent = $(this.options["parent"])
30
+ }
31
+
32
+ this.options.toggle && this.toggle()
33
+ }
34
+
35
+ Collapse.prototype = {
36
+
37
+ constructor: Collapse
38
+
39
+ , dimension: function () {
40
+ var hasWidth = this.$element.hasClass('width')
41
+ return hasWidth ? 'width' : 'height'
42
+ }
43
+
44
+ , show: function () {
45
+ var dimension = this.dimension()
46
+ , scroll = $.camelCase(['scroll', dimension].join('-'))
47
+ , actives = this.$parent && this.$parent.find('.in')
48
+ , hasData
49
+
50
+ if (actives && actives.length) {
51
+ hasData = actives.data('collapse')
52
+ actives.collapse('hide')
53
+ hasData || actives.data('collapse', null)
54
+ }
55
+
56
+ this.$element[dimension](0)
57
+ this.transition('addClass', 'show', 'shown')
58
+ this.$element[dimension](this.$element[0][scroll])
59
+
60
+ }
61
+
62
+ , hide: function () {
63
+ var dimension = this.dimension()
64
+ this.reset(this.$element[dimension]())
65
+ this.transition('removeClass', 'hide', 'hidden')
66
+ this.$element[dimension](0)
67
+ }
68
+
69
+ , reset: function ( size ) {
70
+ var dimension = this.dimension()
71
+
72
+ this.$element
73
+ .removeClass('collapse')
74
+ [dimension](size || 'auto')
75
+ [0].offsetWidth
76
+
77
+ this.$element[size ? 'addClass' : 'removeClass']('collapse')
78
+
79
+ return this
80
+ }
81
+
82
+ , transition: function ( method, startEvent, completeEvent ) {
83
+ var that = this
84
+ , complete = function () {
85
+ if (startEvent == 'show') that.reset()
86
+ that.$element.trigger(completeEvent)
87
+ }
88
+
89
+ this.$element
90
+ .trigger(startEvent)
91
+ [method]('in')
92
+
93
+ $.support.transition && this.$element.hasClass('collapse') ?
94
+ this.$element.one($.support.transition.end, complete) :
95
+ complete()
96
+ }
97
+
98
+ , toggle: function () {
99
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
100
+ }
101
+
102
+ }
103
+
104
+ /* COLLAPSIBLE PLUGIN DEFINITION
105
+ * ============================== */
106
+
107
+ $.fn.collapse = function ( option ) {
108
+ return this.each(function () {
109
+ var $this = $(this)
110
+ , data = $this.data('collapse')
111
+ , options = typeof option == 'object' && option
112
+ if (!data) $this.data('collapse', (data = new Collapse(this, options)))
113
+ if (typeof option == 'string') data[option]()
114
+ })
115
+ }
116
+
117
+ $.fn.collapse.defaults = {
118
+ toggle: true
119
+ }
120
+
121
+ $.fn.collapse.Constructor = Collapse
122
+
123
+
124
+ /* COLLAPSIBLE DATA-API
125
+ * ==================== */
126
+
127
+ $(function () {
128
+ $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
129
+ var $this = $(this), href
130
+ , target = $this.attr('data-target')
131
+ || e.preventDefault()
132
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
133
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
134
+ $(target).collapse(option)
135
+ })
136
+ })
137
+
138
+ }( window.jQuery );
@@ -0,0 +1,1726 @@
1
+ /* ===================================================
2
+ * bootstrap-transition.js v2.0.2
3
+ * http://twitter.github.com/bootstrap/javascript.html#transitions
4
+ * ===================================================
5
+ * Copyright 2012 Twitter, Inc.
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
+
20
+ !function( $ ) {
21
+
22
+ $(function () {
23
+
24
+ "use strict"
25
+
26
+ /* CSS TRANSITION SUPPORT (https://gist.github.com/373874)
27
+ * ======================================================= */
28
+
29
+ $.support.transition = (function () {
30
+ var thisBody = document.body || document.documentElement
31
+ , thisStyle = thisBody.style
32
+ , support = thisStyle.transition !== undefined || thisStyle.WebkitTransition !== undefined || thisStyle.MozTransition !== undefined || thisStyle.MsTransition !== undefined || thisStyle.OTransition !== undefined
33
+
34
+ return support && {
35
+ end: (function () {
36
+ var transitionEnd = "TransitionEnd"
37
+ if ( $.browser.webkit ) {
38
+ transitionEnd = "webkitTransitionEnd"
39
+ } else if ( $.browser.mozilla ) {
40
+ transitionEnd = "transitionend"
41
+ } else if ( $.browser.opera ) {
42
+ transitionEnd = "oTransitionEnd"
43
+ }
44
+ return transitionEnd
45
+ }())
46
+ }
47
+ })()
48
+
49
+ })
50
+
51
+ }( window.jQuery );/* ==========================================================
52
+ * bootstrap-alert.js v2.0.2
53
+ * http://twitter.github.com/bootstrap/javascript.html#alerts
54
+ * ==========================================================
55
+ * Copyright 2012 Twitter, Inc.
56
+ *
57
+ * Licensed under the Apache License, Version 2.0 (the "License");
58
+ * you may not use this file except in compliance with the License.
59
+ * You may obtain a copy of the License at
60
+ *
61
+ * http://www.apache.org/licenses/LICENSE-2.0
62
+ *
63
+ * Unless required by applicable law or agreed to in writing, software
64
+ * distributed under the License is distributed on an "AS IS" BASIS,
65
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
66
+ * See the License for the specific language governing permissions and
67
+ * limitations under the License.
68
+ * ========================================================== */
69
+
70
+
71
+ !function( $ ){
72
+
73
+ "use strict"
74
+
75
+ /* ALERT CLASS DEFINITION
76
+ * ====================== */
77
+
78
+ var dismiss = '[data-dismiss="alert"]'
79
+ , Alert = function ( el ) {
80
+ $(el).on('click', dismiss, this.close)
81
+ }
82
+
83
+ Alert.prototype = {
84
+
85
+ constructor: Alert
86
+
87
+ , close: function ( e ) {
88
+ var $this = $(this)
89
+ , selector = $this.attr('data-target')
90
+ , $parent
91
+
92
+ if (!selector) {
93
+ selector = $this.attr('href')
94
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
95
+ }
96
+
97
+ $parent = $(selector)
98
+ $parent.trigger('close')
99
+
100
+ e && e.preventDefault()
101
+
102
+ $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent())
103
+
104
+ $parent
105
+ .trigger('close')
106
+ .removeClass('in')
107
+
108
+ function removeElement() {
109
+ $parent
110
+ .trigger('closed')
111
+ .remove()
112
+ }
113
+
114
+ $.support.transition && $parent.hasClass('fade') ?
115
+ $parent.on($.support.transition.end, removeElement) :
116
+ removeElement()
117
+ }
118
+
119
+ }
120
+
121
+
122
+ /* ALERT PLUGIN DEFINITION
123
+ * ======================= */
124
+
125
+ $.fn.alert = function ( option ) {
126
+ return this.each(function () {
127
+ var $this = $(this)
128
+ , data = $this.data('alert')
129
+ if (!data) $this.data('alert', (data = new Alert(this)))
130
+ if (typeof option == 'string') data[option].call($this)
131
+ })
132
+ }
133
+
134
+ $.fn.alert.Constructor = Alert
135
+
136
+
137
+ /* ALERT DATA-API
138
+ * ============== */
139
+
140
+ $(function () {
141
+ $('body').on('click.alert.data-api', dismiss, Alert.prototype.close)
142
+ })
143
+
144
+ }( window.jQuery );/* ============================================================
145
+ * bootstrap-button.js v2.0.2
146
+ * http://twitter.github.com/bootstrap/javascript.html#buttons
147
+ * ============================================================
148
+ * Copyright 2012 Twitter, Inc.
149
+ *
150
+ * Licensed under the Apache License, Version 2.0 (the "License");
151
+ * you may not use this file except in compliance with the License.
152
+ * You may obtain a copy of the License at
153
+ *
154
+ * http://www.apache.org/licenses/LICENSE-2.0
155
+ *
156
+ * Unless required by applicable law or agreed to in writing, software
157
+ * distributed under the License is distributed on an "AS IS" BASIS,
158
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
159
+ * See the License for the specific language governing permissions and
160
+ * limitations under the License.
161
+ * ============================================================ */
162
+
163
+ !function( $ ){
164
+
165
+ "use strict"
166
+
167
+ /* BUTTON PUBLIC CLASS DEFINITION
168
+ * ============================== */
169
+
170
+ var Button = function ( element, options ) {
171
+ this.$element = $(element)
172
+ this.options = $.extend({}, $.fn.button.defaults, options)
173
+ }
174
+
175
+ Button.prototype = {
176
+
177
+ constructor: Button
178
+
179
+ , setState: function ( state ) {
180
+ var d = 'disabled'
181
+ , $el = this.$element
182
+ , data = $el.data()
183
+ , val = $el.is('input') ? 'val' : 'html'
184
+
185
+ state = state + 'Text'
186
+ data.resetText || $el.data('resetText', $el[val]())
187
+
188
+ $el[val](data[state] || this.options[state])
189
+
190
+ // push to event loop to allow forms to submit
191
+ setTimeout(function () {
192
+ state == 'loadingText' ?
193
+ $el.addClass(d).attr(d, d) :
194
+ $el.removeClass(d).removeAttr(d)
195
+ }, 0)
196
+ }
197
+
198
+ , toggle: function () {
199
+ var $parent = this.$element.parent('[data-toggle="buttons-radio"]')
200
+
201
+ $parent && $parent
202
+ .find('.active')
203
+ .removeClass('active')
204
+
205
+ this.$element.toggleClass('active')
206
+ }
207
+
208
+ }
209
+
210
+
211
+ /* BUTTON PLUGIN DEFINITION
212
+ * ======================== */
213
+
214
+ $.fn.button = function ( option ) {
215
+ return this.each(function () {
216
+ var $this = $(this)
217
+ , data = $this.data('button')
218
+ , options = typeof option == 'object' && option
219
+ if (!data) $this.data('button', (data = new Button(this, options)))
220
+ if (option == 'toggle') data.toggle()
221
+ else if (option) data.setState(option)
222
+ })
223
+ }
224
+
225
+ $.fn.button.defaults = {
226
+ loadingText: 'loading...'
227
+ }
228
+
229
+ $.fn.button.Constructor = Button
230
+
231
+
232
+ /* BUTTON DATA-API
233
+ * =============== */
234
+
235
+ $(function () {
236
+ $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) {
237
+ var $btn = $(e.target)
238
+ if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn')
239
+ $btn.button('toggle')
240
+ })
241
+ })
242
+
243
+ }( window.jQuery );/* ==========================================================
244
+ * bootstrap-carousel.js v2.0.2
245
+ * http://twitter.github.com/bootstrap/javascript.html#carousel
246
+ * ==========================================================
247
+ * Copyright 2012 Twitter, Inc.
248
+ *
249
+ * Licensed under the Apache License, Version 2.0 (the "License");
250
+ * you may not use this file except in compliance with the License.
251
+ * You may obtain a copy of the License at
252
+ *
253
+ * http://www.apache.org/licenses/LICENSE-2.0
254
+ *
255
+ * Unless required by applicable law or agreed to in writing, software
256
+ * distributed under the License is distributed on an "AS IS" BASIS,
257
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
258
+ * See the License for the specific language governing permissions and
259
+ * limitations under the License.
260
+ * ========================================================== */
261
+
262
+
263
+ !function( $ ){
264
+
265
+ "use strict"
266
+
267
+ /* CAROUSEL CLASS DEFINITION
268
+ * ========================= */
269
+
270
+ var Carousel = function (element, options) {
271
+ this.$element = $(element)
272
+ this.options = $.extend({}, $.fn.carousel.defaults, options)
273
+ this.options.slide && this.slide(this.options.slide)
274
+ this.options.pause == 'hover' && this.$element
275
+ .on('mouseenter', $.proxy(this.pause, this))
276
+ .on('mouseleave', $.proxy(this.cycle, this))
277
+ }
278
+
279
+ Carousel.prototype = {
280
+
281
+ cycle: function () {
282
+ this.interval = setInterval($.proxy(this.next, this), this.options.interval)
283
+ return this
284
+ }
285
+
286
+ , to: function (pos) {
287
+ var $active = this.$element.find('.active')
288
+ , children = $active.parent().children()
289
+ , activePos = children.index($active)
290
+ , that = this
291
+
292
+ if (pos > (children.length - 1) || pos < 0) return
293
+
294
+ if (this.sliding) {
295
+ return this.$element.one('slid', function () {
296
+ that.to(pos)
297
+ })
298
+ }
299
+
300
+ if (activePos == pos) {
301
+ return this.pause().cycle()
302
+ }
303
+
304
+ return this.slide(pos > activePos ? 'next' : 'prev', $(children[pos]))
305
+ }
306
+
307
+ , pause: function () {
308
+ clearInterval(this.interval)
309
+ this.interval = null
310
+ return this
311
+ }
312
+
313
+ , next: function () {
314
+ if (this.sliding) return
315
+ return this.slide('next')
316
+ }
317
+
318
+ , prev: function () {
319
+ if (this.sliding) return
320
+ return this.slide('prev')
321
+ }
322
+
323
+ , slide: function (type, next) {
324
+ var $active = this.$element.find('.active')
325
+ , $next = next || $active[type]()
326
+ , isCycling = this.interval
327
+ , direction = type == 'next' ? 'left' : 'right'
328
+ , fallback = type == 'next' ? 'first' : 'last'
329
+ , that = this
330
+
331
+ this.sliding = true
332
+
333
+ isCycling && this.pause()
334
+
335
+ $next = $next.length ? $next : this.$element.find('.item')[fallback]()
336
+
337
+ if ($next.hasClass('active')) return
338
+
339
+ if (!$.support.transition && this.$element.hasClass('slide')) {
340
+ this.$element.trigger('slide')
341
+ $active.removeClass('active')
342
+ $next.addClass('active')
343
+ this.sliding = false
344
+ this.$element.trigger('slid')
345
+ } else {
346
+ $next.addClass(type)
347
+ $next[0].offsetWidth // force reflow
348
+ $active.addClass(direction)
349
+ $next.addClass(direction)
350
+ this.$element.trigger('slide')
351
+ this.$element.one($.support.transition.end, function () {
352
+ $next.removeClass([type, direction].join(' ')).addClass('active')
353
+ $active.removeClass(['active', direction].join(' '))
354
+ that.sliding = false
355
+ setTimeout(function () { that.$element.trigger('slid') }, 0)
356
+ })
357
+ }
358
+
359
+ isCycling && this.cycle()
360
+
361
+ return this
362
+ }
363
+
364
+ }
365
+
366
+
367
+ /* CAROUSEL PLUGIN DEFINITION
368
+ * ========================== */
369
+
370
+ $.fn.carousel = function ( option ) {
371
+ return this.each(function () {
372
+ var $this = $(this)
373
+ , data = $this.data('carousel')
374
+ , options = typeof option == 'object' && option
375
+ if (!data) $this.data('carousel', (data = new Carousel(this, options)))
376
+ if (typeof option == 'number') data.to(option)
377
+ else if (typeof option == 'string' || (option = options.slide)) data[option]()
378
+ else data.cycle()
379
+ })
380
+ }
381
+
382
+ $.fn.carousel.defaults = {
383
+ interval: 5000
384
+ , pause: 'hover'
385
+ }
386
+
387
+ $.fn.carousel.Constructor = Carousel
388
+
389
+
390
+ /* CAROUSEL DATA-API
391
+ * ================= */
392
+
393
+ $(function () {
394
+ $('body').on('click.carousel.data-api', '[data-slide]', function ( e ) {
395
+ var $this = $(this), href
396
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
397
+ , options = !$target.data('modal') && $.extend({}, $target.data(), $this.data())
398
+ $target.carousel(options)
399
+ e.preventDefault()
400
+ })
401
+ })
402
+
403
+ }( window.jQuery );/* =============================================================
404
+ * bootstrap-collapse.js v2.0.2
405
+ * http://twitter.github.com/bootstrap/javascript.html#collapse
406
+ * =============================================================
407
+ * Copyright 2012 Twitter, Inc.
408
+ *
409
+ * Licensed under the Apache License, Version 2.0 (the "License");
410
+ * you may not use this file except in compliance with the License.
411
+ * You may obtain a copy of the License at
412
+ *
413
+ * http://www.apache.org/licenses/LICENSE-2.0
414
+ *
415
+ * Unless required by applicable law or agreed to in writing, software
416
+ * distributed under the License is distributed on an "AS IS" BASIS,
417
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
418
+ * See the License for the specific language governing permissions and
419
+ * limitations under the License.
420
+ * ============================================================ */
421
+
422
+ !function( $ ){
423
+
424
+ "use strict"
425
+
426
+ var Collapse = function ( element, options ) {
427
+ this.$element = $(element)
428
+ this.options = $.extend({}, $.fn.collapse.defaults, options)
429
+
430
+ if (this.options["parent"]) {
431
+ this.$parent = $(this.options["parent"])
432
+ }
433
+
434
+ this.options.toggle && this.toggle()
435
+ }
436
+
437
+ Collapse.prototype = {
438
+
439
+ constructor: Collapse
440
+
441
+ , dimension: function () {
442
+ var hasWidth = this.$element.hasClass('width')
443
+ return hasWidth ? 'width' : 'height'
444
+ }
445
+
446
+ , show: function () {
447
+ var dimension = this.dimension()
448
+ , scroll = $.camelCase(['scroll', dimension].join('-'))
449
+ , actives = this.$parent && this.$parent.find('.in')
450
+ , hasData
451
+
452
+ if (actives && actives.length) {
453
+ hasData = actives.data('collapse')
454
+ actives.collapse('hide')
455
+ hasData || actives.data('collapse', null)
456
+ }
457
+
458
+ this.$element[dimension](0)
459
+ this.transition('addClass', 'show', 'shown')
460
+ this.$element[dimension](this.$element[0][scroll])
461
+
462
+ }
463
+
464
+ , hide: function () {
465
+ var dimension = this.dimension()
466
+ this.reset(this.$element[dimension]())
467
+ this.transition('removeClass', 'hide', 'hidden')
468
+ this.$element[dimension](0)
469
+ }
470
+
471
+ , reset: function ( size ) {
472
+ var dimension = this.dimension()
473
+
474
+ this.$element
475
+ .removeClass('collapse')
476
+ [dimension](size || 'auto')
477
+ [0].offsetWidth
478
+
479
+ this.$element[size ? 'addClass' : 'removeClass']('collapse')
480
+
481
+ return this
482
+ }
483
+
484
+ , transition: function ( method, startEvent, completeEvent ) {
485
+ var that = this
486
+ , complete = function () {
487
+ if (startEvent == 'show') that.reset()
488
+ that.$element.trigger(completeEvent)
489
+ }
490
+
491
+ this.$element
492
+ .trigger(startEvent)
493
+ [method]('in')
494
+
495
+ $.support.transition && this.$element.hasClass('collapse') ?
496
+ this.$element.one($.support.transition.end, complete) :
497
+ complete()
498
+ }
499
+
500
+ , toggle: function () {
501
+ this[this.$element.hasClass('in') ? 'hide' : 'show']()
502
+ }
503
+
504
+ }
505
+
506
+ /* COLLAPSIBLE PLUGIN DEFINITION
507
+ * ============================== */
508
+
509
+ $.fn.collapse = function ( option ) {
510
+ return this.each(function () {
511
+ var $this = $(this)
512
+ , data = $this.data('collapse')
513
+ , options = typeof option == 'object' && option
514
+ if (!data) $this.data('collapse', (data = new Collapse(this, options)))
515
+ if (typeof option == 'string') data[option]()
516
+ })
517
+ }
518
+
519
+ $.fn.collapse.defaults = {
520
+ toggle: true
521
+ }
522
+
523
+ $.fn.collapse.Constructor = Collapse
524
+
525
+
526
+ /* COLLAPSIBLE DATA-API
527
+ * ==================== */
528
+
529
+ $(function () {
530
+ $('body').on('click.collapse.data-api', '[data-toggle=collapse]', function ( e ) {
531
+ var $this = $(this), href
532
+ , target = $this.attr('data-target')
533
+ || e.preventDefault()
534
+ || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
535
+ , option = $(target).data('collapse') ? 'toggle' : $this.data()
536
+ $(target).collapse(option)
537
+ })
538
+ })
539
+
540
+ }( window.jQuery );/* ============================================================
541
+ * bootstrap-dropdown.js v2.0.2
542
+ * http://twitter.github.com/bootstrap/javascript.html#dropdowns
543
+ * ============================================================
544
+ * Copyright 2012 Twitter, Inc.
545
+ *
546
+ * Licensed under the Apache License, Version 2.0 (the "License");
547
+ * you may not use this file except in compliance with the License.
548
+ * You may obtain a copy of the License at
549
+ *
550
+ * http://www.apache.org/licenses/LICENSE-2.0
551
+ *
552
+ * Unless required by applicable law or agreed to in writing, software
553
+ * distributed under the License is distributed on an "AS IS" BASIS,
554
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
555
+ * See the License for the specific language governing permissions and
556
+ * limitations under the License.
557
+ * ============================================================ */
558
+
559
+
560
+ !function( $ ){
561
+
562
+ "use strict"
563
+
564
+ /* DROPDOWN CLASS DEFINITION
565
+ * ========================= */
566
+
567
+ var toggle = '[data-toggle="dropdown"]'
568
+ , Dropdown = function ( element ) {
569
+ var $el = $(element).on('click.dropdown.data-api', this.toggle)
570
+ $('html').on('click.dropdown.data-api', function () {
571
+ $el.parent().removeClass('open')
572
+ })
573
+ }
574
+
575
+ Dropdown.prototype = {
576
+
577
+ constructor: Dropdown
578
+
579
+ , toggle: function ( e ) {
580
+ var $this = $(this)
581
+ , selector = $this.attr('data-target')
582
+ , $parent
583
+ , isActive
584
+
585
+ if (!selector) {
586
+ selector = $this.attr('href')
587
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
588
+ }
589
+
590
+ $parent = $(selector)
591
+ $parent.length || ($parent = $this.parent())
592
+
593
+ isActive = $parent.hasClass('open')
594
+
595
+ clearMenus()
596
+ !isActive && $parent.toggleClass('open')
597
+
598
+ return false
599
+ }
600
+
601
+ }
602
+
603
+ function clearMenus() {
604
+ $(toggle).parent().removeClass('open')
605
+ }
606
+
607
+
608
+ /* DROPDOWN PLUGIN DEFINITION
609
+ * ========================== */
610
+
611
+ $.fn.dropdown = function ( option ) {
612
+ return this.each(function () {
613
+ var $this = $(this)
614
+ , data = $this.data('dropdown')
615
+ if (!data) $this.data('dropdown', (data = new Dropdown(this)))
616
+ if (typeof option == 'string') data[option].call($this)
617
+ })
618
+ }
619
+
620
+ $.fn.dropdown.Constructor = Dropdown
621
+
622
+
623
+ /* APPLY TO STANDARD DROPDOWN ELEMENTS
624
+ * =================================== */
625
+
626
+ $(function () {
627
+ $('html').on('click.dropdown.data-api', clearMenus)
628
+ $('body').on('click.dropdown.data-api', toggle, Dropdown.prototype.toggle)
629
+ })
630
+
631
+ }( window.jQuery );/* =========================================================
632
+ * bootstrap-modal.js v2.0.2
633
+ * http://twitter.github.com/bootstrap/javascript.html#modals
634
+ * =========================================================
635
+ * Copyright 2012 Twitter, Inc.
636
+ *
637
+ * Licensed under the Apache License, Version 2.0 (the "License");
638
+ * you may not use this file except in compliance with the License.
639
+ * You may obtain a copy of the License at
640
+ *
641
+ * http://www.apache.org/licenses/LICENSE-2.0
642
+ *
643
+ * Unless required by applicable law or agreed to in writing, software
644
+ * distributed under the License is distributed on an "AS IS" BASIS,
645
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
646
+ * See the License for the specific language governing permissions and
647
+ * limitations under the License.
648
+ * ========================================================= */
649
+
650
+
651
+ !function( $ ){
652
+
653
+ "use strict"
654
+
655
+ /* MODAL CLASS DEFINITION
656
+ * ====================== */
657
+
658
+ var Modal = function ( content, options ) {
659
+ this.options = options
660
+ this.$element = $(content)
661
+ .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
662
+ }
663
+
664
+ Modal.prototype = {
665
+
666
+ constructor: Modal
667
+
668
+ , toggle: function () {
669
+ return this[!this.isShown ? 'show' : 'hide']()
670
+ }
671
+
672
+ , show: function () {
673
+ var that = this
674
+
675
+ if (this.isShown) return
676
+
677
+ $('body').addClass('modal-open')
678
+
679
+ this.isShown = true
680
+ this.$element.trigger('show')
681
+
682
+ escape.call(this)
683
+ backdrop.call(this, function () {
684
+ var transition = $.support.transition && that.$element.hasClass('fade')
685
+
686
+ !that.$element.parent().length && that.$element.appendTo(document.body) //don't move modals dom position
687
+
688
+ that.$element
689
+ .show()
690
+
691
+ if (transition) {
692
+ that.$element[0].offsetWidth // force reflow
693
+ }
694
+
695
+ that.$element.addClass('in')
696
+
697
+ transition ?
698
+ that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
699
+ that.$element.trigger('shown')
700
+
701
+ })
702
+ }
703
+
704
+ , hide: function ( e ) {
705
+ e && e.preventDefault()
706
+
707
+ if (!this.isShown) return
708
+
709
+ var that = this
710
+ this.isShown = false
711
+
712
+ $('body').removeClass('modal-open')
713
+
714
+ escape.call(this)
715
+
716
+ this.$element
717
+ .trigger('hide')
718
+ .removeClass('in')
719
+
720
+ $.support.transition && this.$element.hasClass('fade') ?
721
+ hideWithTransition.call(this) :
722
+ hideModal.call(this)
723
+ }
724
+
725
+ }
726
+
727
+
728
+ /* MODAL PRIVATE METHODS
729
+ * ===================== */
730
+
731
+ function hideWithTransition() {
732
+ var that = this
733
+ , timeout = setTimeout(function () {
734
+ that.$element.off($.support.transition.end)
735
+ hideModal.call(that)
736
+ }, 500)
737
+
738
+ this.$element.one($.support.transition.end, function () {
739
+ clearTimeout(timeout)
740
+ hideModal.call(that)
741
+ })
742
+ }
743
+
744
+ function hideModal( that ) {
745
+ this.$element
746
+ .hide()
747
+ .trigger('hidden')
748
+
749
+ backdrop.call(this)
750
+ }
751
+
752
+ function backdrop( callback ) {
753
+ var that = this
754
+ , animate = this.$element.hasClass('fade') ? 'fade' : ''
755
+
756
+ if (this.isShown && this.options.backdrop) {
757
+ var doAnimate = $.support.transition && animate
758
+
759
+ this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
760
+ .appendTo(document.body)
761
+
762
+ if (this.options.backdrop != 'static') {
763
+ this.$backdrop.click($.proxy(this.hide, this))
764
+ }
765
+
766
+ if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
767
+
768
+ this.$backdrop.addClass('in')
769
+
770
+ doAnimate ?
771
+ this.$backdrop.one($.support.transition.end, callback) :
772
+ callback()
773
+
774
+ } else if (!this.isShown && this.$backdrop) {
775
+ this.$backdrop.removeClass('in')
776
+
777
+ $.support.transition && this.$element.hasClass('fade')?
778
+ this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
779
+ removeBackdrop.call(this)
780
+
781
+ } else if (callback) {
782
+ callback()
783
+ }
784
+ }
785
+
786
+ function removeBackdrop() {
787
+ this.$backdrop.remove()
788
+ this.$backdrop = null
789
+ }
790
+
791
+ function escape() {
792
+ var that = this
793
+ if (this.isShown && this.options.keyboard) {
794
+ $(document).on('keyup.dismiss.modal', function ( e ) {
795
+ e.which == 27 && that.hide()
796
+ })
797
+ } else if (!this.isShown) {
798
+ $(document).off('keyup.dismiss.modal')
799
+ }
800
+ }
801
+
802
+
803
+ /* MODAL PLUGIN DEFINITION
804
+ * ======================= */
805
+
806
+ $.fn.modal = function ( option ) {
807
+ return this.each(function () {
808
+ var $this = $(this)
809
+ , data = $this.data('modal')
810
+ , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
811
+ if (!data) $this.data('modal', (data = new Modal(this, options)))
812
+ if (typeof option == 'string') data[option]()
813
+ else if (options.show) data.show()
814
+ })
815
+ }
816
+
817
+ $.fn.modal.defaults = {
818
+ backdrop: true
819
+ , keyboard: true
820
+ , show: true
821
+ }
822
+
823
+ $.fn.modal.Constructor = Modal
824
+
825
+
826
+ /* MODAL DATA-API
827
+ * ============== */
828
+
829
+ $(function () {
830
+ $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
831
+ var $this = $(this), href
832
+ , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
833
+ , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
834
+
835
+ e.preventDefault()
836
+ $target.modal(option)
837
+ })
838
+ })
839
+
840
+ }( window.jQuery );/* ===========================================================
841
+ * bootstrap-tooltip.js v2.0.2
842
+ * http://twitter.github.com/bootstrap/javascript.html#tooltips
843
+ * Inspired by the original jQuery.tipsy by Jason Frame
844
+ * ===========================================================
845
+ * Copyright 2012 Twitter, Inc.
846
+ *
847
+ * Licensed under the Apache License, Version 2.0 (the "License");
848
+ * you may not use this file except in compliance with the License.
849
+ * You may obtain a copy of the License at
850
+ *
851
+ * http://www.apache.org/licenses/LICENSE-2.0
852
+ *
853
+ * Unless required by applicable law or agreed to in writing, software
854
+ * distributed under the License is distributed on an "AS IS" BASIS,
855
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
856
+ * See the License for the specific language governing permissions and
857
+ * limitations under the License.
858
+ * ========================================================== */
859
+
860
+ !function( $ ) {
861
+
862
+ "use strict"
863
+
864
+ /* TOOLTIP PUBLIC CLASS DEFINITION
865
+ * =============================== */
866
+
867
+ var Tooltip = function ( element, options ) {
868
+ this.init('tooltip', element, options)
869
+ }
870
+
871
+ Tooltip.prototype = {
872
+
873
+ constructor: Tooltip
874
+
875
+ , init: function ( type, element, options ) {
876
+ var eventIn
877
+ , eventOut
878
+
879
+ this.type = type
880
+ this.$element = $(element)
881
+ this.options = this.getOptions(options)
882
+ this.enabled = true
883
+
884
+ if (this.options.trigger != 'manual') {
885
+ eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus'
886
+ eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur'
887
+ this.$element.on(eventIn, this.options.selector, $.proxy(this.enter, this))
888
+ this.$element.on(eventOut, this.options.selector, $.proxy(this.leave, this))
889
+ }
890
+
891
+ this.options.selector ?
892
+ (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
893
+ this.fixTitle()
894
+ }
895
+
896
+ , getOptions: function ( options ) {
897
+ options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data())
898
+
899
+ if (options.delay && typeof options.delay == 'number') {
900
+ options.delay = {
901
+ show: options.delay
902
+ , hide: options.delay
903
+ }
904
+ }
905
+
906
+ return options
907
+ }
908
+
909
+ , enter: function ( e ) {
910
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
911
+
912
+ if (!self.options.delay || !self.options.delay.show) {
913
+ self.show()
914
+ } else {
915
+ self.hoverState = 'in'
916
+ setTimeout(function() {
917
+ if (self.hoverState == 'in') {
918
+ self.show()
919
+ }
920
+ }, self.options.delay.show)
921
+ }
922
+ }
923
+
924
+ , leave: function ( e ) {
925
+ var self = $(e.currentTarget)[this.type](this._options).data(this.type)
926
+
927
+ if (!self.options.delay || !self.options.delay.hide) {
928
+ self.hide()
929
+ } else {
930
+ self.hoverState = 'out'
931
+ setTimeout(function() {
932
+ if (self.hoverState == 'out') {
933
+ self.hide()
934
+ }
935
+ }, self.options.delay.hide)
936
+ }
937
+ }
938
+
939
+ , show: function () {
940
+ var $tip
941
+ , inside
942
+ , pos
943
+ , actualWidth
944
+ , actualHeight
945
+ , placement
946
+ , tp
947
+
948
+ if (this.hasContent() && this.enabled) {
949
+ $tip = this.tip()
950
+ this.setContent()
951
+
952
+ if (this.options.animation) {
953
+ $tip.addClass('fade')
954
+ }
955
+
956
+ placement = typeof this.options.placement == 'function' ?
957
+ this.options.placement.call(this, $tip[0], this.$element[0]) :
958
+ this.options.placement
959
+
960
+ inside = /in/.test(placement)
961
+
962
+ $tip
963
+ .remove()
964
+ .css({ top: 0, left: 0, display: 'block' })
965
+ .appendTo(inside ? this.$element : document.body)
966
+
967
+ pos = this.getPosition(inside)
968
+
969
+ actualWidth = $tip[0].offsetWidth
970
+ actualHeight = $tip[0].offsetHeight
971
+
972
+ switch (inside ? placement.split(' ')[1] : placement) {
973
+ case 'bottom':
974
+ tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}
975
+ break
976
+ case 'top':
977
+ tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}
978
+ break
979
+ case 'left':
980
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}
981
+ break
982
+ case 'right':
983
+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}
984
+ break
985
+ }
986
+
987
+ $tip
988
+ .css(tp)
989
+ .addClass(placement)
990
+ .addClass('in')
991
+ }
992
+ }
993
+
994
+ , setContent: function () {
995
+ var $tip = this.tip()
996
+ $tip.find('.tooltip-inner').html(this.getTitle())
997
+ $tip.removeClass('fade in top bottom left right')
998
+ }
999
+
1000
+ , hide: function () {
1001
+ var that = this
1002
+ , $tip = this.tip()
1003
+
1004
+ $tip.removeClass('in')
1005
+
1006
+ function removeWithAnimation() {
1007
+ var timeout = setTimeout(function () {
1008
+ $tip.off($.support.transition.end).remove()
1009
+ }, 500)
1010
+
1011
+ $tip.one($.support.transition.end, function () {
1012
+ clearTimeout(timeout)
1013
+ $tip.remove()
1014
+ })
1015
+ }
1016
+
1017
+ $.support.transition && this.$tip.hasClass('fade') ?
1018
+ removeWithAnimation() :
1019
+ $tip.remove()
1020
+ }
1021
+
1022
+ , fixTitle: function () {
1023
+ var $e = this.$element
1024
+ if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
1025
+ $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title')
1026
+ }
1027
+ }
1028
+
1029
+ , hasContent: function () {
1030
+ return this.getTitle()
1031
+ }
1032
+
1033
+ , getPosition: function (inside) {
1034
+ return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), {
1035
+ width: this.$element[0].offsetWidth
1036
+ , height: this.$element[0].offsetHeight
1037
+ })
1038
+ }
1039
+
1040
+ , getTitle: function () {
1041
+ var title
1042
+ , $e = this.$element
1043
+ , o = this.options
1044
+
1045
+ title = $e.attr('data-original-title')
1046
+ || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title)
1047
+
1048
+ title = (title || '').toString().replace(/(^\s*|\s*$)/, "")
1049
+
1050
+ return title
1051
+ }
1052
+
1053
+ , tip: function () {
1054
+ return this.$tip = this.$tip || $(this.options.template)
1055
+ }
1056
+
1057
+ , validate: function () {
1058
+ if (!this.$element[0].parentNode) {
1059
+ this.hide()
1060
+ this.$element = null
1061
+ this.options = null
1062
+ }
1063
+ }
1064
+
1065
+ , enable: function () {
1066
+ this.enabled = true
1067
+ }
1068
+
1069
+ , disable: function () {
1070
+ this.enabled = false
1071
+ }
1072
+
1073
+ , toggleEnabled: function () {
1074
+ this.enabled = !this.enabled
1075
+ }
1076
+
1077
+ , toggle: function () {
1078
+ this[this.tip().hasClass('in') ? 'hide' : 'show']()
1079
+ }
1080
+
1081
+ }
1082
+
1083
+
1084
+ /* TOOLTIP PLUGIN DEFINITION
1085
+ * ========================= */
1086
+
1087
+ $.fn.tooltip = function ( option ) {
1088
+ return this.each(function () {
1089
+ var $this = $(this)
1090
+ , data = $this.data('tooltip')
1091
+ , options = typeof option == 'object' && option
1092
+ if (!data) $this.data('tooltip', (data = new Tooltip(this, options)))
1093
+ if (typeof option == 'string') data[option]()
1094
+ })
1095
+ }
1096
+
1097
+ $.fn.tooltip.Constructor = Tooltip
1098
+
1099
+ $.fn.tooltip.defaults = {
1100
+ animation: true
1101
+ , delay: 0
1102
+ , selector: false
1103
+ , placement: 'top'
1104
+ , trigger: 'hover'
1105
+ , title: ''
1106
+ , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
1107
+ }
1108
+
1109
+ }( window.jQuery );/* ===========================================================
1110
+ * bootstrap-popover.js v2.0.2
1111
+ * http://twitter.github.com/bootstrap/javascript.html#popovers
1112
+ * ===========================================================
1113
+ * Copyright 2012 Twitter, Inc.
1114
+ *
1115
+ * Licensed under the Apache License, Version 2.0 (the "License");
1116
+ * you may not use this file except in compliance with the License.
1117
+ * You may obtain a copy of the License at
1118
+ *
1119
+ * http://www.apache.org/licenses/LICENSE-2.0
1120
+ *
1121
+ * Unless required by applicable law or agreed to in writing, software
1122
+ * distributed under the License is distributed on an "AS IS" BASIS,
1123
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1124
+ * See the License for the specific language governing permissions and
1125
+ * limitations under the License.
1126
+ * =========================================================== */
1127
+
1128
+
1129
+ !function( $ ) {
1130
+
1131
+ "use strict"
1132
+
1133
+ var Popover = function ( element, options ) {
1134
+ this.init('popover', element, options)
1135
+ }
1136
+
1137
+ /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js
1138
+ ========================================== */
1139
+
1140
+ Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, {
1141
+
1142
+ constructor: Popover
1143
+
1144
+ , setContent: function () {
1145
+ var $tip = this.tip()
1146
+ , title = this.getTitle()
1147
+ , content = this.getContent()
1148
+
1149
+ $tip.find('.popover-title')[ $.type(title) == 'object' ? 'append' : 'html' ](title)
1150
+ $tip.find('.popover-content > *')[ $.type(content) == 'object' ? 'append' : 'html' ](content)
1151
+
1152
+ $tip.removeClass('fade top bottom left right in')
1153
+ }
1154
+
1155
+ , hasContent: function () {
1156
+ return this.getTitle() || this.getContent()
1157
+ }
1158
+
1159
+ , getContent: function () {
1160
+ var content
1161
+ , $e = this.$element
1162
+ , o = this.options
1163
+
1164
+ content = $e.attr('data-content')
1165
+ || (typeof o.content == 'function' ? o.content.call($e[0]) : o.content)
1166
+
1167
+ content = content.toString().replace(/(^\s*|\s*$)/, "")
1168
+
1169
+ return content
1170
+ }
1171
+
1172
+ , tip: function() {
1173
+ if (!this.$tip) {
1174
+ this.$tip = $(this.options.template)
1175
+ }
1176
+ return this.$tip
1177
+ }
1178
+
1179
+ })
1180
+
1181
+
1182
+ /* POPOVER PLUGIN DEFINITION
1183
+ * ======================= */
1184
+
1185
+ $.fn.popover = function ( option ) {
1186
+ return this.each(function () {
1187
+ var $this = $(this)
1188
+ , data = $this.data('popover')
1189
+ , options = typeof option == 'object' && option
1190
+ if (!data) $this.data('popover', (data = new Popover(this, options)))
1191
+ if (typeof option == 'string') data[option]()
1192
+ })
1193
+ }
1194
+
1195
+ $.fn.popover.Constructor = Popover
1196
+
1197
+ $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, {
1198
+ placement: 'right'
1199
+ , content: ''
1200
+ , template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><h3 class="popover-title"></h3><div class="popover-content"><p></p></div></div></div>'
1201
+ })
1202
+
1203
+ }( window.jQuery );/* =============================================================
1204
+ * bootstrap-scrollspy.js v2.0.2
1205
+ * http://twitter.github.com/bootstrap/javascript.html#scrollspy
1206
+ * =============================================================
1207
+ * Copyright 2012 Twitter, Inc.
1208
+ *
1209
+ * Licensed under the Apache License, Version 2.0 (the "License");
1210
+ * you may not use this file except in compliance with the License.
1211
+ * You may obtain a copy of the License at
1212
+ *
1213
+ * http://www.apache.org/licenses/LICENSE-2.0
1214
+ *
1215
+ * Unless required by applicable law or agreed to in writing, software
1216
+ * distributed under the License is distributed on an "AS IS" BASIS,
1217
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1218
+ * See the License for the specific language governing permissions and
1219
+ * limitations under the License.
1220
+ * ============================================================== */
1221
+
1222
+ !function ( $ ) {
1223
+
1224
+ "use strict"
1225
+
1226
+ /* SCROLLSPY CLASS DEFINITION
1227
+ * ========================== */
1228
+
1229
+ function ScrollSpy( element, options) {
1230
+ var process = $.proxy(this.process, this)
1231
+ , $element = $(element).is('body') ? $(window) : $(element)
1232
+ , href
1233
+ this.options = $.extend({}, $.fn.scrollspy.defaults, options)
1234
+ this.$scrollElement = $element.on('scroll.scroll.data-api', process)
1235
+ this.selector = (this.options.target
1236
+ || ((href = $(element).attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
1237
+ || '') + ' .nav li > a'
1238
+ this.$body = $('body').on('click.scroll.data-api', this.selector, process)
1239
+ this.refresh()
1240
+ this.process()
1241
+ }
1242
+
1243
+ ScrollSpy.prototype = {
1244
+
1245
+ constructor: ScrollSpy
1246
+
1247
+ , refresh: function () {
1248
+ this.targets = this.$body
1249
+ .find(this.selector)
1250
+ .map(function () {
1251
+ var href = $(this).attr('href')
1252
+ return /^#\w/.test(href) && $(href).length ? href : null
1253
+ })
1254
+
1255
+ this.offsets = $.map(this.targets, function (id) {
1256
+ return $(id).position().top
1257
+ })
1258
+ }
1259
+
1260
+ , process: function () {
1261
+ var scrollTop = this.$scrollElement.scrollTop() + this.options.offset
1262
+ , offsets = this.offsets
1263
+ , targets = this.targets
1264
+ , activeTarget = this.activeTarget
1265
+ , i
1266
+
1267
+ for (i = offsets.length; i--;) {
1268
+ activeTarget != targets[i]
1269
+ && scrollTop >= offsets[i]
1270
+ && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
1271
+ && this.activate( targets[i] )
1272
+ }
1273
+ }
1274
+
1275
+ , activate: function (target) {
1276
+ var active
1277
+
1278
+ this.activeTarget = target
1279
+
1280
+ this.$body
1281
+ .find(this.selector).parent('.active')
1282
+ .removeClass('active')
1283
+
1284
+ active = this.$body
1285
+ .find(this.selector + '[href="' + target + '"]')
1286
+ .parent('li')
1287
+ .addClass('active')
1288
+
1289
+ if ( active.parent('.dropdown-menu') ) {
1290
+ active.closest('li.dropdown').addClass('active')
1291
+ }
1292
+ }
1293
+
1294
+ }
1295
+
1296
+
1297
+ /* SCROLLSPY PLUGIN DEFINITION
1298
+ * =========================== */
1299
+
1300
+ $.fn.scrollspy = function ( option ) {
1301
+ return this.each(function () {
1302
+ var $this = $(this)
1303
+ , data = $this.data('scrollspy')
1304
+ , options = typeof option == 'object' && option
1305
+ if (!data) $this.data('scrollspy', (data = new ScrollSpy(this, options)))
1306
+ if (typeof option == 'string') data[option]()
1307
+ })
1308
+ }
1309
+
1310
+ $.fn.scrollspy.Constructor = ScrollSpy
1311
+
1312
+ $.fn.scrollspy.defaults = {
1313
+ offset: 10
1314
+ }
1315
+
1316
+
1317
+ /* SCROLLSPY DATA-API
1318
+ * ================== */
1319
+
1320
+ $(function () {
1321
+ $('[data-spy="scroll"]').each(function () {
1322
+ var $spy = $(this)
1323
+ $spy.scrollspy($spy.data())
1324
+ })
1325
+ })
1326
+
1327
+ }( window.jQuery );/* ========================================================
1328
+ * bootstrap-tab.js v2.0.2
1329
+ * http://twitter.github.com/bootstrap/javascript.html#tabs
1330
+ * ========================================================
1331
+ * Copyright 2012 Twitter, Inc.
1332
+ *
1333
+ * Licensed under the Apache License, Version 2.0 (the "License");
1334
+ * you may not use this file except in compliance with the License.
1335
+ * You may obtain a copy of the License at
1336
+ *
1337
+ * http://www.apache.org/licenses/LICENSE-2.0
1338
+ *
1339
+ * Unless required by applicable law or agreed to in writing, software
1340
+ * distributed under the License is distributed on an "AS IS" BASIS,
1341
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1342
+ * See the License for the specific language governing permissions and
1343
+ * limitations under the License.
1344
+ * ======================================================== */
1345
+
1346
+
1347
+ !function( $ ){
1348
+
1349
+ "use strict"
1350
+
1351
+ /* TAB CLASS DEFINITION
1352
+ * ==================== */
1353
+
1354
+ var Tab = function ( element ) {
1355
+ this.element = $(element)
1356
+ }
1357
+
1358
+ Tab.prototype = {
1359
+
1360
+ constructor: Tab
1361
+
1362
+ , show: function () {
1363
+ var $this = this.element
1364
+ , $ul = $this.closest('ul:not(.dropdown-menu)')
1365
+ , selector = $this.attr('data-target')
1366
+ , previous
1367
+ , $target
1368
+
1369
+ if (!selector) {
1370
+ selector = $this.attr('href')
1371
+ selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
1372
+ }
1373
+
1374
+ if ( $this.parent('li').hasClass('active') ) return
1375
+
1376
+ previous = $ul.find('.active a').last()[0]
1377
+
1378
+ $this.trigger({
1379
+ type: 'show'
1380
+ , relatedTarget: previous
1381
+ })
1382
+
1383
+ $target = $(selector)
1384
+
1385
+ this.activate($this.parent('li'), $ul)
1386
+ this.activate($target, $target.parent(), function () {
1387
+ $this.trigger({
1388
+ type: 'shown'
1389
+ , relatedTarget: previous
1390
+ })
1391
+ })
1392
+ }
1393
+
1394
+ , activate: function ( element, container, callback) {
1395
+ var $active = container.find('> .active')
1396
+ , transition = callback
1397
+ && $.support.transition
1398
+ && $active.hasClass('fade')
1399
+
1400
+ function next() {
1401
+ $active
1402
+ .removeClass('active')
1403
+ .find('> .dropdown-menu > .active')
1404
+ .removeClass('active')
1405
+
1406
+ element.addClass('active')
1407
+
1408
+ if (transition) {
1409
+ element[0].offsetWidth // reflow for transition
1410
+ element.addClass('in')
1411
+ } else {
1412
+ element.removeClass('fade')
1413
+ }
1414
+
1415
+ if ( element.parent('.dropdown-menu') ) {
1416
+ element.closest('li.dropdown').addClass('active')
1417
+ }
1418
+
1419
+ callback && callback()
1420
+ }
1421
+
1422
+ transition ?
1423
+ $active.one($.support.transition.end, next) :
1424
+ next()
1425
+
1426
+ $active.removeClass('in')
1427
+ }
1428
+ }
1429
+
1430
+
1431
+ /* TAB PLUGIN DEFINITION
1432
+ * ===================== */
1433
+
1434
+ $.fn.tab = function ( option ) {
1435
+ return this.each(function () {
1436
+ var $this = $(this)
1437
+ , data = $this.data('tab')
1438
+ if (!data) $this.data('tab', (data = new Tab(this)))
1439
+ if (typeof option == 'string') data[option]()
1440
+ })
1441
+ }
1442
+
1443
+ $.fn.tab.Constructor = Tab
1444
+
1445
+
1446
+ /* TAB DATA-API
1447
+ * ============ */
1448
+
1449
+ $(function () {
1450
+ $('body').on('click.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
1451
+ e.preventDefault()
1452
+ $(this).tab('show')
1453
+ })
1454
+ })
1455
+
1456
+ }( window.jQuery );/* =============================================================
1457
+ * bootstrap-typeahead.js v2.0.2
1458
+ * http://twitter.github.com/bootstrap/javascript.html#typeahead
1459
+ * =============================================================
1460
+ * Copyright 2012 Twitter, Inc.
1461
+ *
1462
+ * Licensed under the Apache License, Version 2.0 (the "License");
1463
+ * you may not use this file except in compliance with the License.
1464
+ * You may obtain a copy of the License at
1465
+ *
1466
+ * http://www.apache.org/licenses/LICENSE-2.0
1467
+ *
1468
+ * Unless required by applicable law or agreed to in writing, software
1469
+ * distributed under the License is distributed on an "AS IS" BASIS,
1470
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1471
+ * See the License for the specific language governing permissions and
1472
+ * limitations under the License.
1473
+ * ============================================================ */
1474
+
1475
+ !function( $ ){
1476
+
1477
+ "use strict"
1478
+
1479
+ var Typeahead = function ( element, options ) {
1480
+ this.$element = $(element)
1481
+ this.options = $.extend({}, $.fn.typeahead.defaults, options)
1482
+ this.matcher = this.options.matcher || this.matcher
1483
+ this.sorter = this.options.sorter || this.sorter
1484
+ this.highlighter = this.options.highlighter || this.highlighter
1485
+ this.$menu = $(this.options.menu).appendTo('body')
1486
+ this.source = this.options.source
1487
+ this.shown = false
1488
+ this.listen()
1489
+ }
1490
+
1491
+ Typeahead.prototype = {
1492
+
1493
+ constructor: Typeahead
1494
+
1495
+ , select: function () {
1496
+ var val = this.$menu.find('.active').attr('data-value')
1497
+ this.$element.val(val)
1498
+ this.$element.change();
1499
+ return this.hide()
1500
+ }
1501
+
1502
+ , show: function () {
1503
+ var pos = $.extend({}, this.$element.offset(), {
1504
+ height: this.$element[0].offsetHeight
1505
+ })
1506
+
1507
+ this.$menu.css({
1508
+ top: pos.top + pos.height
1509
+ , left: pos.left
1510
+ })
1511
+
1512
+ this.$menu.show()
1513
+ this.shown = true
1514
+ return this
1515
+ }
1516
+
1517
+ , hide: function () {
1518
+ this.$menu.hide()
1519
+ this.shown = false
1520
+ return this
1521
+ }
1522
+
1523
+ , lookup: function (event) {
1524
+ var that = this
1525
+ , items
1526
+ , q
1527
+
1528
+ this.query = this.$element.val()
1529
+
1530
+ if (!this.query) {
1531
+ return this.shown ? this.hide() : this
1532
+ }
1533
+
1534
+ items = $.grep(this.source, function (item) {
1535
+ if (that.matcher(item)) return item
1536
+ })
1537
+
1538
+ items = this.sorter(items)
1539
+
1540
+ if (!items.length) {
1541
+ return this.shown ? this.hide() : this
1542
+ }
1543
+
1544
+ return this.render(items.slice(0, this.options.items)).show()
1545
+ }
1546
+
1547
+ , matcher: function (item) {
1548
+ return ~item.toLowerCase().indexOf(this.query.toLowerCase())
1549
+ }
1550
+
1551
+ , sorter: function (items) {
1552
+ var beginswith = []
1553
+ , caseSensitive = []
1554
+ , caseInsensitive = []
1555
+ , item
1556
+
1557
+ while (item = items.shift()) {
1558
+ if (!item.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item)
1559
+ else if (~item.indexOf(this.query)) caseSensitive.push(item)
1560
+ else caseInsensitive.push(item)
1561
+ }
1562
+
1563
+ return beginswith.concat(caseSensitive, caseInsensitive)
1564
+ }
1565
+
1566
+ , highlighter: function (item) {
1567
+ return item.replace(new RegExp('(' + this.query + ')', 'ig'), function ($1, match) {
1568
+ return '<strong>' + match + '</strong>'
1569
+ })
1570
+ }
1571
+
1572
+ , render: function (items) {
1573
+ var that = this
1574
+
1575
+ items = $(items).map(function (i, item) {
1576
+ i = $(that.options.item).attr('data-value', item)
1577
+ i.find('a').html(that.highlighter(item))
1578
+ return i[0]
1579
+ })
1580
+
1581
+ items.first().addClass('active')
1582
+ this.$menu.html(items)
1583
+ return this
1584
+ }
1585
+
1586
+ , next: function (event) {
1587
+ var active = this.$menu.find('.active').removeClass('active')
1588
+ , next = active.next()
1589
+
1590
+ if (!next.length) {
1591
+ next = $(this.$menu.find('li')[0])
1592
+ }
1593
+
1594
+ next.addClass('active')
1595
+ }
1596
+
1597
+ , prev: function (event) {
1598
+ var active = this.$menu.find('.active').removeClass('active')
1599
+ , prev = active.prev()
1600
+
1601
+ if (!prev.length) {
1602
+ prev = this.$menu.find('li').last()
1603
+ }
1604
+
1605
+ prev.addClass('active')
1606
+ }
1607
+
1608
+ , listen: function () {
1609
+ this.$element
1610
+ .on('blur', $.proxy(this.blur, this))
1611
+ .on('keypress', $.proxy(this.keypress, this))
1612
+ .on('keyup', $.proxy(this.keyup, this))
1613
+
1614
+ if ($.browser.webkit || $.browser.msie) {
1615
+ this.$element.on('keydown', $.proxy(this.keypress, this))
1616
+ }
1617
+
1618
+ this.$menu
1619
+ .on('click', $.proxy(this.click, this))
1620
+ .on('mouseenter', 'li', $.proxy(this.mouseenter, this))
1621
+ }
1622
+
1623
+ , keyup: function (e) {
1624
+ switch(e.keyCode) {
1625
+ case 40: // down arrow
1626
+ case 38: // up arrow
1627
+ break
1628
+
1629
+ case 9: // tab
1630
+ case 13: // enter
1631
+ if (!this.shown) return
1632
+ this.select()
1633
+ break
1634
+
1635
+ case 27: // escape
1636
+ if (!this.shown) return
1637
+ this.hide()
1638
+ break
1639
+
1640
+ default:
1641
+ this.lookup()
1642
+ }
1643
+
1644
+ e.stopPropagation()
1645
+ e.preventDefault()
1646
+ }
1647
+
1648
+ , keypress: function (e) {
1649
+ if (!this.shown) return
1650
+
1651
+ switch(e.keyCode) {
1652
+ case 9: // tab
1653
+ case 13: // enter
1654
+ case 27: // escape
1655
+ e.preventDefault()
1656
+ break
1657
+
1658
+ case 38: // up arrow
1659
+ e.preventDefault()
1660
+ this.prev()
1661
+ break
1662
+
1663
+ case 40: // down arrow
1664
+ e.preventDefault()
1665
+ this.next()
1666
+ break
1667
+ }
1668
+
1669
+ e.stopPropagation()
1670
+ }
1671
+
1672
+ , blur: function (e) {
1673
+ var that = this
1674
+ setTimeout(function () { that.hide() }, 150)
1675
+ }
1676
+
1677
+ , click: function (e) {
1678
+ e.stopPropagation()
1679
+ e.preventDefault()
1680
+ this.select()
1681
+ }
1682
+
1683
+ , mouseenter: function (e) {
1684
+ this.$menu.find('.active').removeClass('active')
1685
+ $(e.currentTarget).addClass('active')
1686
+ }
1687
+
1688
+ }
1689
+
1690
+
1691
+ /* TYPEAHEAD PLUGIN DEFINITION
1692
+ * =========================== */
1693
+
1694
+ $.fn.typeahead = function ( option ) {
1695
+ return this.each(function () {
1696
+ var $this = $(this)
1697
+ , data = $this.data('typeahead')
1698
+ , options = typeof option == 'object' && option
1699
+ if (!data) $this.data('typeahead', (data = new Typeahead(this, options)))
1700
+ if (typeof option == 'string') data[option]()
1701
+ })
1702
+ }
1703
+
1704
+ $.fn.typeahead.defaults = {
1705
+ source: []
1706
+ , items: 8
1707
+ , menu: '<ul class="typeahead dropdown-menu"></ul>'
1708
+ , item: '<li><a href="#"></a></li>'
1709
+ }
1710
+
1711
+ $.fn.typeahead.Constructor = Typeahead
1712
+
1713
+
1714
+ /* TYPEAHEAD DATA-API
1715
+ * ================== */
1716
+
1717
+ $(function () {
1718
+ $('body').on('focus.typeahead.data-api', '[data-provide="typeahead"]', function (e) {
1719
+ var $this = $(this)
1720
+ if ($this.data('typeahead')) return
1721
+ e.preventDefault()
1722
+ $this.typeahead($this.data())
1723
+ })
1724
+ })
1725
+
1726
+ }( window.jQuery );