apipie-dsl 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 );