releaf 0.1.1 → 0.1.2

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 (201) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +15 -12
  3. data/Rakefile +8 -0
  4. data/app/assets/javascripts/releaf/controllers/base.js +0 -40
  5. data/app/assets/javascripts/releaf/include/field.type_date_or_datetime_or_time.js +43 -0
  6. data/app/assets/javascripts/releaf/include/nested_fields.js +58 -65
  7. data/app/assets/javascripts/releaf/include/sortable.js +19 -17
  8. data/app/assets/stylesheets/releaf/controllers/releaf/content.css.erb +6 -0
  9. data/app/assets/stylesheets/releaf/include/field.css.erb +37 -13
  10. data/app/controllers/releaf/admins_controller.rb +13 -6
  11. data/app/controllers/releaf/base_application_controller.rb +4 -1
  12. data/app/controllers/releaf/base_controller.rb +415 -154
  13. data/app/controllers/releaf/content_controller.rb +62 -45
  14. data/app/controllers/releaf/roles_controller.rb +11 -9
  15. data/app/controllers/releaf/sessions_controller.rb +23 -1
  16. data/app/controllers/releaf/translations_controller.rb +21 -25
  17. data/app/helpers/releaf/admin_helper.rb +0 -20
  18. data/app/models/releaf/admin.rb +9 -0
  19. data/app/models/releaf/blank_node_base.rb +29 -0
  20. data/app/models/releaf/node.rb +6 -5
  21. data/app/models/releaf/node_base.rb +9 -3
  22. data/app/models/releaf/role.rb +9 -2
  23. data/app/views/layouts/releaf/admin.html.haml +12 -6
  24. data/app/views/layouts/releaf/devise.html.haml +32 -0
  25. data/app/views/releaf/aliases/{_edit.body.haml → _edit.body.html.haml} +0 -0
  26. data/app/views/releaf/aliases/{_edit.header.haml → _edit.header.html.haml} +1 -1
  27. data/app/views/releaf/aliases/_index.row.html.haml +5 -0
  28. data/app/views/releaf/aliases/{_secondary_panel.haml → _secondary_panel.html.haml} +0 -0
  29. data/app/views/releaf/base/_edit.body.html.haml +11 -0
  30. data/app/views/releaf/base/_edit.field.html.haml +23 -0
  31. data/app/views/releaf/base/{_edit.field.type_autocomplete.haml → _edit.field_type_autocomplete.html.haml} +1 -1
  32. data/app/views/releaf/base/{_edit.field.type_checkbox.haml → _edit.field_type_boolean.html.haml} +1 -1
  33. data/app/views/releaf/base/{_edit.field.type_date.haml → _edit.field_type_date.html.haml} +0 -0
  34. data/app/views/releaf/base/{_edit.field.type_datetime.haml → _edit.field_type_datetime.html.haml} +0 -0
  35. data/app/views/releaf/base/{_edit.field.type_delete_nested.haml → _edit.field_type_delete_nested.html.haml} +0 -0
  36. data/app/views/releaf/base/{_edit.field.type_file.haml → _edit.field_type_file.html.haml} +0 -0
  37. data/app/views/releaf/base/_edit.field_type_image.html.haml +12 -0
  38. data/app/views/releaf/base/_edit.field_type_item.html.haml +31 -0
  39. data/app/views/releaf/base/{_edit.field.type_link.haml → _edit.field_type_link.html.haml} +0 -0
  40. data/app/views/releaf/base/_edit.field_type_link_i18n.html.haml +26 -0
  41. data/app/views/releaf/base/{_edit.field.type_password.haml → _edit.field_type_password.html.haml} +0 -0
  42. data/app/views/releaf/base/{_edit.field.type_richtext.haml → _edit.field_type_richtext.html.haml} +0 -0
  43. data/app/views/releaf/base/{_edit.field.type_richtext_i18n.haml → _edit.field_type_richtext_i18n.html.haml} +0 -0
  44. data/app/views/releaf/base/{_edit.field.type_text.haml → _edit.field_type_text.html.haml} +0 -0
  45. data/app/views/releaf/base/{_edit.field.type_text_i18n.haml → _edit.field_type_text_i18n.html.haml} +0 -0
  46. data/app/views/releaf/base/{_edit.field.type_textarea.haml → _edit.field_type_textarea.html.haml} +0 -0
  47. data/app/views/releaf/base/{_edit.field.type_textarea_i18n.haml → _edit.field_type_textarea_i18n.html.haml} +0 -0
  48. data/app/views/releaf/base/{_edit.field.type_time.haml → _edit.field_type_time.html.haml} +0 -0
  49. data/app/views/releaf/base/_edit.fields.association.html.haml +48 -0
  50. data/app/views/releaf/base/_edit.fields.has_many_template.html.haml +15 -0
  51. data/app/views/releaf/base/_edit.fields.html.haml +20 -0
  52. data/app/views/releaf/base/{_edit.footer.haml → _edit.footer.html.haml} +2 -2
  53. data/app/views/releaf/base/_edit.form.html.haml +4 -0
  54. data/app/views/releaf/base/_edit.header.html.haml +3 -0
  55. data/app/views/releaf/base/{_index.body.haml → _index.body.html.haml} +2 -2
  56. data/app/views/releaf/base/_index.cell.html.haml +20 -0
  57. data/app/views/releaf/base/_index.footer.html.haml +28 -0
  58. data/app/views/releaf/base/_index.header.html.haml +4 -0
  59. data/app/views/releaf/base/_index.row.html.haml +6 -0
  60. data/app/views/releaf/base/_index.search.html.haml +5 -0
  61. data/app/views/releaf/base/_index.table.html.haml +24 -0
  62. data/app/views/releaf/base/_secondary_panel.html.haml +10 -0
  63. data/app/views/releaf/base/_show.body.html.haml +3 -0
  64. data/app/views/releaf/base/_show.field.html.haml +20 -0
  65. data/app/views/releaf/base/_show.field_type_date.html.haml +9 -0
  66. data/app/views/releaf/base/_show.field_type_date_time.html.haml +9 -0
  67. data/app/views/releaf/base/_show.field_type_email.html.haml +6 -0
  68. data/app/views/releaf/base/_show.field_type_file.html.haml +7 -0
  69. data/app/views/releaf/base/_show.field_type_image.html.haml +10 -0
  70. data/app/views/releaf/base/_show.field_type_item.html.haml +11 -0
  71. data/app/views/releaf/base/_show.field_type_link.html.haml +14 -0
  72. data/app/views/releaf/base/_show.field_type_password.html.haml +6 -0
  73. data/app/views/releaf/base/_show.field_type_richtext.html.haml +6 -0
  74. data/app/views/releaf/base/_show.field_type_text.html.haml +6 -0
  75. data/app/views/releaf/base/_show.field_type_textarea.html.haml +6 -0
  76. data/app/views/releaf/base/_show.field_type_time.html.haml +9 -0
  77. data/app/views/releaf/base/_show.field_type_url.html.haml +7 -0
  78. data/app/views/releaf/base/_show.fields.association.html.haml +36 -0
  79. data/app/views/releaf/base/_show.fields.has_many_template.html.haml +12 -0
  80. data/app/views/releaf/base/_show.fields.html.haml +17 -0
  81. data/app/views/releaf/base/{_show.footer.haml → _show.footer.html.haml} +6 -4
  82. data/app/views/releaf/base/_show.header.html.haml +3 -0
  83. data/app/views/releaf/base/confirm_destroy.html.haml +17 -0
  84. data/app/views/releaf/base/edit.html.haml +1 -0
  85. data/app/views/releaf/base/index.html.haml +3 -0
  86. data/app/views/releaf/base/new.html.haml +1 -0
  87. data/app/views/releaf/base/show.html.haml +3 -0
  88. data/app/views/releaf/content/{_edit.body.haml → _edit.body.html.haml} +7 -6
  89. data/app/views/releaf/content/_edit.content_fields.html.haml +11 -0
  90. data/app/views/releaf/content/_edit.fields.html.haml +17 -0
  91. data/app/views/releaf/content/{_edit.slug.haml → _edit.slug.html.haml} +0 -0
  92. data/app/views/releaf/content/{_get_content_form.haml → _get_content_form.html.haml} +2 -2
  93. data/app/views/releaf/content/{_secondary_panel.haml → _secondary_panel.html.haml} +0 -0
  94. data/app/views/releaf/content/{_tree_level.haml → _tree_level.html.haml} +0 -0
  95. data/app/views/releaf/content/{ajax.new.haml → ajax.new.html.haml} +0 -0
  96. data/app/views/releaf/content/{index.haml → index.html.haml} +0 -0
  97. data/app/views/releaf/roles/_edit.field.permissions.html.haml +5 -0
  98. data/app/views/releaf/roles/{_show.field.default.haml → _show.field.default.html.haml} +1 -1
  99. data/app/views/releaf/roles/{_show.field.permissions.haml → _show.field.permissions.html.haml} +2 -2
  100. data/app/views/releaf/translations/{_edit.body.haml → _edit.body.html.haml} +0 -0
  101. data/app/views/releaf/translations/{_edit.header.haml → _edit.header.html.haml} +1 -1
  102. data/app/views/releaf/translations/_index.row.html.haml +5 -0
  103. data/app/views/releaf/translations/{_secondary_panel.haml → _secondary_panel.html.haml} +0 -0
  104. data/lib/generators/releaf/templates/initializers/releaf.rb +0 -39
  105. data/lib/generators/releaf/templates/initializers/releaf_i18n.rb +17 -0
  106. data/lib/generators/releaf/templates/javascripts/3rd_party/jquery.jcarousel.js +1058 -0
  107. data/lib/generators/releaf/templates/javascripts/lib/init_jcarousel.js +40 -0
  108. data/lib/generators/releaf/templates/migrations/create_releaf_roles.rb +1 -0
  109. data/lib/generators/releaf/templates/migrations/create_releaf_tinymce_assets.rb +1 -0
  110. data/lib/generators/releaf/templates/models/admin_ability.rb +1 -1
  111. data/lib/i18n/backend/releaf.rb +5 -4
  112. data/lib/i18n/backend/releaf/translation.rb +5 -0
  113. data/lib/i18n/backend/releaf/translation_data.rb +7 -0
  114. data/lib/i18n/backend/releaf/translation_group.rb +4 -0
  115. data/lib/releaf.rb +4 -0
  116. data/lib/releaf/boolean_at.rb +77 -0
  117. data/lib/releaf/globalize3/fallbacks.rb +19 -0
  118. data/lib/releaf/resources.rb +101 -0
  119. data/lib/releaf/slug.rb +99 -60
  120. data/lib/releaf/version.rb +1 -1
  121. data/spec/spec_helper.rb +6 -0
  122. data/templates/releaf/installer.rb +15 -4
  123. metadata +275 -329
  124. data/app/views/releaf/aliases/_index.row.haml +0 -5
  125. data/app/views/releaf/base/_edit.body.haml +0 -19
  126. data/app/views/releaf/base/_edit.field.haml +0 -96
  127. data/app/views/releaf/base/_edit.field.type_image.haml +0 -14
  128. data/app/views/releaf/base/_edit.field.type_select.haml +0 -11
  129. data/app/views/releaf/base/_edit.form.haml +0 -4
  130. data/app/views/releaf/base/_edit.has_many_associations.haml +0 -45
  131. data/app/views/releaf/base/_edit.header.haml +0 -3
  132. data/app/views/releaf/base/_index.cell.haml +0 -19
  133. data/app/views/releaf/base/_index.footer.haml +0 -22
  134. data/app/views/releaf/base/_index.header.haml +0 -4
  135. data/app/views/releaf/base/_index.row.haml +0 -6
  136. data/app/views/releaf/base/_index.search.haml +0 -5
  137. data/app/views/releaf/base/_index.table.haml +0 -8
  138. data/app/views/releaf/base/_secondary_panel.haml +0 -9
  139. data/app/views/releaf/base/_show.body.haml +0 -7
  140. data/app/views/releaf/base/_show.field.haml +0 -78
  141. data/app/views/releaf/base/_show.field.type_html.haml +0 -1
  142. data/app/views/releaf/base/_show.header.haml +0 -3
  143. data/app/views/releaf/base/confirm_destroy.haml +0 -10
  144. data/app/views/releaf/base/edit.haml +0 -1
  145. data/app/views/releaf/base/index.haml +0 -3
  146. data/app/views/releaf/base/new.haml +0 -1
  147. data/app/views/releaf/base/show.haml +0 -3
  148. data/app/views/releaf/content/_edit.content_fields.haml +0 -19
  149. data/app/views/releaf/content/_show.field.content.haml +0 -11
  150. data/app/views/releaf/roles/_edit.field.admins.haml +0 -0
  151. data/app/views/releaf/roles/_edit.field.permissions.haml +0 -5
  152. data/app/views/releaf/translations/_index.row.haml +0 -5
  153. data/spec/dummy/README.rdoc +0 -261
  154. data/spec/dummy/Rakefile +0 -7
  155. data/spec/dummy/app/assets/images/rails.png +0 -0
  156. data/spec/dummy/app/assets/javascripts/application.js +0 -15
  157. data/spec/dummy/app/assets/stylesheets/application.css +0 -13
  158. data/spec/dummy/app/controllers/application_controller.rb +0 -3
  159. data/spec/dummy/app/helpers/application_helper.rb +0 -2
  160. data/spec/dummy/app/models/admin_ability.rb +0 -51
  161. data/spec/dummy/app/models/settings.rb +0 -2
  162. data/spec/dummy/app/views/layouts/application.html.haml +0 -10
  163. data/spec/dummy/config.ru +0 -4
  164. data/spec/dummy/config/application.rb +0 -68
  165. data/spec/dummy/config/boot.rb +0 -6
  166. data/spec/dummy/config/common_fields.yml.example +0 -17
  167. data/spec/dummy/config/database.yml +0 -42
  168. data/spec/dummy/config/environment.rb +0 -5
  169. data/spec/dummy/config/environments/development.rb +0 -37
  170. data/spec/dummy/config/environments/production.rb +0 -67
  171. data/spec/dummy/config/environments/test.rb +0 -37
  172. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  173. data/spec/dummy/config/initializers/devise.rb +0 -232
  174. data/spec/dummy/config/initializers/dragonfly.rb +0 -1
  175. data/spec/dummy/config/initializers/inflections.rb +0 -15
  176. data/spec/dummy/config/initializers/mime_types.rb +0 -5
  177. data/spec/dummy/config/initializers/releaf.rb +0 -59
  178. data/spec/dummy/config/initializers/releaf_i18n.rb +0 -8
  179. data/spec/dummy/config/initializers/releaf_store_current_template.rb +0 -25
  180. data/spec/dummy/config/initializers/secret_token.rb +0 -7
  181. data/spec/dummy/config/initializers/session_store.rb +0 -8
  182. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  183. data/spec/dummy/config/locales/devise.en.yml +0 -58
  184. data/spec/dummy/config/locales/en.yml +0 -5
  185. data/spec/dummy/config/routes.rb +0 -11
  186. data/spec/dummy/db/migrate/20130204164516_create_settings.rb +0 -17
  187. data/spec/dummy/db/migrate/20130204164523_create_releaf_nodes.rb +0 -25
  188. data/spec/dummy/db/migrate/20130204164524_create_releaf_roles.rb +0 -11
  189. data/spec/dummy/db/migrate/20130204164525_create_releaf_translations.rb +0 -31
  190. data/spec/dummy/db/migrate/20130204164526_create_releaf_admins.rb +0 -53
  191. data/spec/dummy/db/schema.rb +0 -112
  192. data/spec/dummy/db/seeds.rb +0 -64
  193. data/spec/dummy/doc/README_FOR_APP +0 -2
  194. data/spec/dummy/log/development.log +0 -74
  195. data/spec/dummy/log/test.log +0 -129
  196. data/spec/dummy/public/404.html +0 -26
  197. data/spec/dummy/public/422.html +0 -26
  198. data/spec/dummy/public/500.html +0 -25
  199. data/spec/dummy/public/favicon.ico +0 -0
  200. data/spec/dummy/script/rails +0 -6
  201. data/spec/dummy/tmp/cache/9C5/660/settings%3Ai18n_updated_at +0 -1
@@ -0,0 +1,11 @@
1
+ .nested_object
2
+ - f.object.common_field_names.each do |common_field_name|
3
+ - if has_template? "_edit.field.#{common_field_name}"
4
+ = render "edit.field.#{common_field_name}", :name => common_field_name, :f => f
5
+ - else
6
+ = render 'edit.field', :name => common_field_name, :f => f, :view_prefix => 'edit.field.content'
7
+
8
+
9
+ - if f.object.content
10
+ = f.fields_for :content_object, f.object.content_object do |ff|
11
+ = render 'edit.fields', :f => ff, :fields => content_fields_to_display(ff.object.class), :view_prefix => 'edit.field.content'
@@ -0,0 +1,17 @@
1
+ - fields.each do |field|
2
+ - if field.is_a?(String) or field.is_a?(Symbol)
3
+ - reflection = f.object.class.reflections[field.to_sym]
4
+
5
+ - if (field.is_a?(String) or field.is_a?(Symbol)) && !reflection
6
+ - if has_template? "_#{view_prefix}.#{field}"
7
+ = render "#{view_prefix}.#{field}", :f => f, :name => field
8
+ - else
9
+ = render 'edit.field', :f => f, :name => field, :view_prefix => view_prefix
10
+
11
+ - elsif field.is_a? Hash
12
+ - field.each_pair do |field, subfields|
13
+ - reflection = f.object.class.reflections[field.to_sym]
14
+ = render 'edit.fields.association', :f => f, :field => field, :subfields => subfields, :reflection => reflection, :view_prefix => view_prefix
15
+ - elsif reflection
16
+ - subfields = content_fields_to_display(reflection.klass)
17
+ = render 'edit.fields.association', :f => f, :field => field, :subfields => subfields, :reflection => reflection, :view_prefix => view_prefix
@@ -1,7 +1,7 @@
1
- = fields_for "#{@controller.current_object_class_name}[content_attributes]", @item do |ff|
1
+ = fields_for "resource[content_attributes]", @resource do |ff|
2
2
 
3
3
  - (ff.object.class.column_names - %w[id created_at updated_at]).each do |name|
4
- - if @controller.has_template( "_edit.field.#{name}" )
4
+ - if has_template? "_edit.field.#{name}"
5
5
  = render "edit.field.#{name}", :name => name, :f => ff
6
6
  - else
7
7
  = render 'edit.field', :name => name, :f => ff
@@ -0,0 +1,5 @@
1
+ - AdminAbility::PERMISSIONS.each do |permissions|
2
+ - if permissions.is_a? String
3
+ = render 'edit.field_type_boolean', :name => "#{permissions}_permission", :f => f
4
+ - elsif permissions.is_a? Array
5
+ = render 'edit.field_type_item', :name => "#{permissions[0]}_permissions", :select_options => permissions[1], :f => f
@@ -2,5 +2,5 @@
2
2
  .label_wrap
3
3
  .label= name.sub(/_(uid|html|id)$/, '').humanize + ':'
4
4
  .value
5
- = @item.send("#{name}?") ? I18n.t("common.true") : I18n.t("common.false")
5
+ = @resource.send("#{name}?") ? I18n.t("common.true") : I18n.t("common.false")
6
6
  .clear
@@ -3,9 +3,9 @@
3
3
  .field.no_label_wrap_padding
4
4
  .label_wrap
5
5
  .label Manage #{permissions}:
6
- .value_wrap= @item.send(:"#{permissions}_permission")
6
+ .value_wrap= @resource.send(:"#{permissions}_permission")
7
7
  - elsif permissions.is_a? Array
8
8
  .field.no_label_wrap_padding
9
9
  .label_wrap
10
10
  .label #{permissions[0]} permissions:
11
- .value_wrap= @item.send(:"#{permissions[0]}_permissions")
11
+ .value_wrap= @resource.send(:"#{permissions[0]}_permissions")
@@ -11,7 +11,7 @@
11
11
  .groupInfo
12
12
  .field
13
13
  %label{:for => "context"} Konteksts:
14
- %input#context{:name => "translation_group[scope]", :size => "50", :type => "text", :value => @item.scope}/
14
+ %input#context{:name => "translation_group[scope]", :size => "50", :type => "text", :value => @resource.scope}/
15
15
  .translations_table.translations_table_head
16
16
  .th.code_header{:style => "width: 208px; "}>< Nosaukums
17
17
  - Settings.i18n_locales.each do |locale|
@@ -0,0 +1,5 @@
1
+ %a.row{ 'data-id' => resource.id, :href => "#{url_for( :action => list_action, :id => resource.group_id )}#translation_#{resource.id}" }
2
+ %span= resource.translation_group.scope
3
+ %span= resource.key
4
+ - Settings.i18n_locales.each do |locale|
5
+ %span= resource.locales[locale]
@@ -18,42 +18,3 @@ Releaf.setup do |conf|
18
18
  # conf.devise_for 'releaf/admin'
19
19
 
20
20
  end
21
-
22
-
23
- module ActionDispatch::Routing
24
- class Mapper
25
- def mount_releaf_at(mount_location)
26
-
27
- post '/tinymce_assets' => 'releaf/tinymce_assets#create'
28
-
29
- devise_for Releaf.devise_for, :path => mount_location, :controllers => { :sessions => "releaf/sessions" }
30
-
31
- scope mount_location do
32
- namespace :releaf, :path => nil do
33
-
34
- resources :admins, :roles do
35
- get :confirm_destroy, :on => :member
36
- match :urls, :on => :collection
37
- end
38
-
39
- resources :nodes, :controller => "content", :path => "content" do
40
- member do
41
- get :confirm_destroy
42
- get :get_content_form
43
- end
44
-
45
- get :generate_url, :on => :collection
46
- end
47
-
48
- resources :translation_groups, :controller => "translations", :path => "translations" do
49
- get :confirm_destroy, :on => :member
50
- match :urls, :on => :collection
51
- end
52
- root :to => "content#index"
53
- end
54
- end
55
-
56
- end
57
- end
58
- end
59
-
@@ -1,3 +1,18 @@
1
+ module UseKeyForMissing
2
+ def call(exception, locale, key, options)
3
+ if exception.is_a?(I18n::MissingTranslation)
4
+ key.to_s.split('.').last.humanize
5
+ else
6
+ super
7
+ end
8
+ end
9
+ end
10
+ I18n.exception_handler.extend UseKeyForMissing
11
+
12
+
13
+
14
+
15
+
1
16
  I18N_CACHE = ActiveSupport::Cache::MemoryStore.new
2
17
  require 'i18n/backend/releaf'
3
18
  I18n.backend = I18n::Backend::Releaf.new
@@ -6,3 +21,5 @@ I18n.backend = I18n::Backend::Releaf.new
6
21
  if !($0.end_with?('rake') || $0.end_with?('cucumber'))
7
22
  I18n.backend.reload_cache
8
23
  end
24
+
25
+
@@ -0,0 +1,1058 @@
1
+ /*!
2
+ * jCarousel - Riding carousels with jQuery
3
+ * http://sorgalla.com/jcarousel/
4
+ *
5
+ * Copyright (c) 2006 Jan Sorgalla (http://sorgalla.com)
6
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
7
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
8
+ *
9
+ * Built on top of the jQuery library
10
+ * http://jquery.com
11
+ *
12
+ * Inspired by the "Carousel Component" by Bill Scott
13
+ * http://billwscott.com/carousel/
14
+ */
15
+
16
+ /*global window, jQuery */
17
+
18
+ (function($) {
19
+ // Default configuration properties.
20
+ var defaults = {
21
+ vertical: false,
22
+ rtl: false,
23
+ start: 1,
24
+ offset: 1,
25
+ size: null,
26
+ scroll: 3,
27
+ visible: null,
28
+ animation: 'normal',
29
+ easing: 'swing',
30
+ auto: 0,
31
+ wrap: null,
32
+ initCallback: null,
33
+ setupCallback: null,
34
+ reloadCallback: null,
35
+ itemLoadCallback: null,
36
+ itemFirstInCallback: null,
37
+ itemFirstOutCallback: null,
38
+ itemLastInCallback: null,
39
+ itemLastOutCallback: null,
40
+ itemVisibleInCallback: null,
41
+ itemVisibleOutCallback: null,
42
+ animationStepCallback: null,
43
+ buttonNextHTML: '<div></div>',
44
+ buttonPrevHTML: '<div></div>',
45
+ buttonNextEvent: 'click',
46
+ buttonPrevEvent: 'click',
47
+ buttonNextCallback: null,
48
+ buttonPrevCallback: null,
49
+ itemFallbackDimension: null
50
+ }, windowLoaded = false;
51
+
52
+ $(window).bind('load.jcarousel', function() { windowLoaded = true; });
53
+
54
+ /**
55
+ * The jCarousel object.
56
+ *
57
+ * @constructor
58
+ * @class jcarousel
59
+ * @param e {HTMLElement} The element to create the carousel for.
60
+ * @param o {Object} A set of key/value pairs to set as configuration properties.
61
+ * @cat Plugins/jCarousel
62
+ */
63
+ $.jcarousel = function(e, o) {
64
+ this.options = $.extend({}, defaults, o || {});
65
+
66
+ this.locked = false;
67
+ this.autoStopped = false;
68
+
69
+ this.container = null;
70
+ this.clip = null;
71
+ this.list = null;
72
+ this.buttonNext = null;
73
+ this.buttonPrev = null;
74
+ this.buttonNextState = null;
75
+ this.buttonPrevState = null;
76
+
77
+ // Only set if not explicitly passed as option
78
+ if (!o || o.rtl === undefined) {
79
+ this.options.rtl = ($(e).attr('dir') || $('html').attr('dir') || '').toLowerCase() == 'rtl';
80
+ }
81
+
82
+ this.wh = !this.options.vertical ? 'width' : 'height';
83
+ this.lt = !this.options.vertical ? (this.options.rtl ? 'right' : 'left') : 'top';
84
+
85
+ // Extract skin class
86
+ var skin = '', split = e.className.split(' ');
87
+
88
+ for (var i = 0; i < split.length; i++) {
89
+ if (split[i].indexOf('jcarousel-skin') != -1) {
90
+ $(e).removeClass(split[i]);
91
+ skin = split[i];
92
+ break;
93
+ }
94
+ }
95
+
96
+ if (e.nodeName.toUpperCase() == 'UL' || e.nodeName.toUpperCase() == 'OL') {
97
+ this.list = $(e);
98
+ this.clip = this.list.parents('.jcarousel-clip');
99
+ this.container = this.list.parents('.jcarousel-container');
100
+ } else {
101
+ this.container = $(e);
102
+ this.list = this.container.find('ul,ol').eq(0);
103
+ this.clip = this.container.find('.jcarousel-clip');
104
+ }
105
+
106
+ if (this.clip.size() === 0) {
107
+ this.clip = this.list.wrap('<div></div>').parent();
108
+ }
109
+
110
+ if (this.container.size() === 0) {
111
+ this.container = this.clip.wrap('<div></div>').parent();
112
+ }
113
+
114
+ if (skin !== '' && this.container.parent()[0].className.indexOf('jcarousel-skin') == -1) {
115
+ this.container.wrap('<div class=" '+ skin + '"></div>');
116
+ }
117
+
118
+ this.buttonPrev = $('.jcarousel-prev', this.container);
119
+
120
+ if (this.buttonPrev.size() === 0 && this.options.buttonPrevHTML !== null) {
121
+ this.buttonPrev = $(this.options.buttonPrevHTML).appendTo(this.container);
122
+ }
123
+
124
+ this.buttonPrev.addClass(this.className('jcarousel-prev'));
125
+
126
+ this.buttonNext = $('.jcarousel-next', this.container);
127
+
128
+ if (this.buttonNext.size() === 0 && this.options.buttonNextHTML !== null) {
129
+ this.buttonNext = $(this.options.buttonNextHTML).appendTo(this.container);
130
+ }
131
+
132
+ this.buttonNext.addClass(this.className('jcarousel-next'));
133
+
134
+ this.clip.addClass(this.className('jcarousel-clip')).css({
135
+ position: 'relative'
136
+ });
137
+
138
+ this.list.addClass(this.className('jcarousel-list')).css({
139
+ overflow: 'hidden',
140
+ position: 'relative',
141
+ top: 0,
142
+ margin: 0,
143
+ padding: 0
144
+ }).css((this.options.rtl ? 'right' : 'left'), 0);
145
+
146
+ this.container.addClass(this.className('jcarousel-container')).css({
147
+ position: 'relative'
148
+ });
149
+
150
+ if (!this.options.vertical && this.options.rtl) {
151
+ this.container.addClass('jcarousel-direction-rtl').attr('dir', 'rtl');
152
+ }
153
+
154
+ var di = this.options.visible !== null ? Math.ceil(this.clipping() / this.options.visible) : null;
155
+ var li = this.list.children('li');
156
+
157
+ var self = this;
158
+
159
+ if (li.size() > 0) {
160
+ var wh = 0, j = this.options.offset;
161
+ li.each(function() {
162
+ self.format(this, j++);
163
+ wh += self.dimension(this, di);
164
+ });
165
+
166
+ this.list.css(this.wh, (wh + 100) + 'px');
167
+
168
+ // Only set if not explicitly passed as option
169
+ if (!o || o.size === undefined) {
170
+ this.options.size = li.size();
171
+ }
172
+ }
173
+
174
+ // For whatever reason, .show() does not work in Safari...
175
+ this.container.css('display', 'block');
176
+ this.buttonNext.css('display', 'block');
177
+ this.buttonPrev.css('display', 'block');
178
+
179
+ this.funcNext = function() { self.next(); };
180
+ this.funcPrev = function() { self.prev(); };
181
+ this.funcResize = function() {
182
+ if (self.resizeTimer) {
183
+ clearTimeout(self.resizeTimer);
184
+ }
185
+
186
+ self.resizeTimer = setTimeout(function() {
187
+ self.reload();
188
+ }, 100);
189
+ };
190
+
191
+ if (this.options.initCallback !== null) {
192
+ this.options.initCallback(this, 'init');
193
+ }
194
+
195
+ if (!windowLoaded && $.browser.safari) {
196
+ this.buttons(false, false);
197
+ $(window).bind('load.jcarousel', function() { self.setup(); });
198
+ } else {
199
+ this.setup();
200
+ }
201
+ };
202
+
203
+ // Create shortcut for internal use
204
+ var $jc = $.jcarousel;
205
+
206
+ $jc.fn = $jc.prototype = {
207
+ jcarousel: '0.2.8'
208
+ };
209
+
210
+ $jc.fn.extend = $jc.extend = $.extend;
211
+
212
+ $jc.fn.extend({
213
+ /**
214
+ * Setups the carousel.
215
+ *
216
+ * @method setup
217
+ * @return undefined
218
+ */
219
+ setup: function() {
220
+ this.first = null;
221
+ this.last = null;
222
+ this.prevFirst = null;
223
+ this.prevLast = null;
224
+ this.animating = false;
225
+ this.timer = null;
226
+ this.resizeTimer = null;
227
+ this.tail = null;
228
+ this.inTail = false;
229
+
230
+ if (this.locked) {
231
+ return;
232
+ }
233
+
234
+ this.list.css(this.lt, this.pos(this.options.offset) + 'px');
235
+ var p = this.pos(this.options.start, true);
236
+ this.prevFirst = this.prevLast = null;
237
+ this.animate(p, false);
238
+
239
+ $(window).unbind('resize.jcarousel', this.funcResize).bind('resize.jcarousel', this.funcResize);
240
+
241
+ if (this.options.setupCallback !== null) {
242
+ this.options.setupCallback(this);
243
+ }
244
+ },
245
+
246
+ /**
247
+ * Clears the list and resets the carousel.
248
+ *
249
+ * @method reset
250
+ * @return undefined
251
+ */
252
+ reset: function() {
253
+ this.list.empty();
254
+
255
+ this.list.css(this.lt, '0px');
256
+ this.list.css(this.wh, '10px');
257
+
258
+ if (this.options.initCallback !== null) {
259
+ this.options.initCallback(this, 'reset');
260
+ }
261
+
262
+ this.setup();
263
+ },
264
+
265
+ /**
266
+ * Reloads the carousel and adjusts positions.
267
+ *
268
+ * @method reload
269
+ * @return undefined
270
+ */
271
+ reload: function() {
272
+ if (this.tail !== null && this.inTail) {
273
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + this.tail);
274
+ }
275
+
276
+ this.tail = null;
277
+ this.inTail = false;
278
+
279
+ if (this.options.reloadCallback !== null) {
280
+ this.options.reloadCallback(this);
281
+ }
282
+
283
+ if (this.options.visible !== null) {
284
+ var self = this;
285
+ var di = Math.ceil(this.clipping() / this.options.visible), wh = 0, lt = 0;
286
+ this.list.children('li').each(function(i) {
287
+ wh += self.dimension(this, di);
288
+ if (i + 1 < self.first) {
289
+ lt = wh;
290
+ }
291
+ });
292
+
293
+ this.list.css(this.wh, wh + 'px');
294
+ this.list.css(this.lt, -lt + 'px');
295
+ }
296
+
297
+ this.scroll(this.first, false);
298
+ },
299
+
300
+ /**
301
+ * Locks the carousel.
302
+ *
303
+ * @method lock
304
+ * @return undefined
305
+ */
306
+ lock: function() {
307
+ this.locked = true;
308
+ this.buttons();
309
+ },
310
+
311
+ /**
312
+ * Unlocks the carousel.
313
+ *
314
+ * @method unlock
315
+ * @return undefined
316
+ */
317
+ unlock: function() {
318
+ this.locked = false;
319
+ this.buttons();
320
+ },
321
+
322
+ /**
323
+ * Sets the size of the carousel.
324
+ *
325
+ * @method size
326
+ * @return undefined
327
+ * @param s {Number} The size of the carousel.
328
+ */
329
+ size: function(s) {
330
+ if (s !== undefined) {
331
+ this.options.size = s;
332
+ if (!this.locked) {
333
+ this.buttons();
334
+ }
335
+ }
336
+
337
+ return this.options.size;
338
+ },
339
+
340
+ /**
341
+ * Checks whether a list element exists for the given index (or index range).
342
+ *
343
+ * @method get
344
+ * @return bool
345
+ * @param i {Number} The index of the (first) element.
346
+ * @param i2 {Number} The index of the last element.
347
+ */
348
+ has: function(i, i2) {
349
+ if (i2 === undefined || !i2) {
350
+ i2 = i;
351
+ }
352
+
353
+ if (this.options.size !== null && i2 > this.options.size) {
354
+ i2 = this.options.size;
355
+ }
356
+
357
+ for (var j = i; j <= i2; j++) {
358
+ var e = this.get(j);
359
+ if (!e.length || e.hasClass('jcarousel-item-placeholder')) {
360
+ return false;
361
+ }
362
+ }
363
+
364
+ return true;
365
+ },
366
+
367
+ /**
368
+ * Returns a jQuery object with list element for the given index.
369
+ *
370
+ * @method get
371
+ * @return jQuery
372
+ * @param i {Number} The index of the element.
373
+ */
374
+ get: function(i) {
375
+ return $('>.jcarousel-item-' + i, this.list);
376
+ },
377
+
378
+ /**
379
+ * Adds an element for the given index to the list.
380
+ * If the element already exists, it updates the inner html.
381
+ * Returns the created element as jQuery object.
382
+ *
383
+ * @method add
384
+ * @return jQuery
385
+ * @param i {Number} The index of the element.
386
+ * @param s {String} The innerHTML of the element.
387
+ */
388
+ add: function(i, s) {
389
+ var e = this.get(i), old = 0, n = $(s);
390
+
391
+ if (e.length === 0) {
392
+ var c, j = $jc.intval(i);
393
+ e = this.create(i);
394
+ while (true) {
395
+ c = this.get(--j);
396
+ if (j <= 0 || c.length) {
397
+ if (j <= 0) {
398
+ this.list.prepend(e);
399
+ } else {
400
+ c.after(e);
401
+ }
402
+ break;
403
+ }
404
+ }
405
+ } else {
406
+ old = this.dimension(e);
407
+ }
408
+
409
+ if (n.get(0).nodeName.toUpperCase() == 'LI') {
410
+ e.replaceWith(n);
411
+ e = n;
412
+ } else {
413
+ e.empty().append(s);
414
+ }
415
+
416
+ this.format(e.removeClass(this.className('jcarousel-item-placeholder')), i);
417
+
418
+ var di = this.options.visible !== null ? Math.ceil(this.clipping() / this.options.visible) : null;
419
+ var wh = this.dimension(e, di) - old;
420
+
421
+ if (i > 0 && i < this.first) {
422
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - wh + 'px');
423
+ }
424
+
425
+ this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) + wh + 'px');
426
+
427
+ return e;
428
+ },
429
+
430
+ /**
431
+ * Removes an element for the given index from the list.
432
+ *
433
+ * @method remove
434
+ * @return undefined
435
+ * @param i {Number} The index of the element.
436
+ */
437
+ remove: function(i) {
438
+ var e = this.get(i);
439
+
440
+ // Check if item exists and is not currently visible
441
+ if (!e.length || (i >= this.first && i <= this.last)) {
442
+ return;
443
+ }
444
+
445
+ var d = this.dimension(e);
446
+
447
+ if (i < this.first) {
448
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + d + 'px');
449
+ }
450
+
451
+ e.remove();
452
+
453
+ this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) - d + 'px');
454
+ },
455
+
456
+ /**
457
+ * Moves the carousel forwards.
458
+ *
459
+ * @method next
460
+ * @return undefined
461
+ */
462
+ next: function() {
463
+ if (this.tail !== null && !this.inTail) {
464
+ this.scrollTail(false);
465
+ } else {
466
+ this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'last') && this.options.size !== null && this.last == this.options.size) ? 1 : this.first + this.options.scroll);
467
+ }
468
+ },
469
+
470
+ /**
471
+ * Moves the carousel backwards.
472
+ *
473
+ * @method prev
474
+ * @return undefined
475
+ */
476
+ prev: function() {
477
+ if (this.tail !== null && this.inTail) {
478
+ this.scrollTail(true);
479
+ } else {
480
+ this.scroll(((this.options.wrap == 'both' || this.options.wrap == 'first') && this.options.size !== null && this.first == 1) ? this.options.size : this.first - this.options.scroll);
481
+ }
482
+ },
483
+
484
+ /**
485
+ * Scrolls the tail of the carousel.
486
+ *
487
+ * @method scrollTail
488
+ * @return undefined
489
+ * @param b {Boolean} Whether scroll the tail back or forward.
490
+ */
491
+ scrollTail: function(b) {
492
+ if (this.locked || this.animating || !this.tail) {
493
+ return;
494
+ }
495
+
496
+ this.pauseAuto();
497
+
498
+ var pos = $jc.intval(this.list.css(this.lt));
499
+
500
+ pos = !b ? pos - this.tail : pos + this.tail;
501
+ this.inTail = !b;
502
+
503
+ // Save for callbacks
504
+ this.prevFirst = this.first;
505
+ this.prevLast = this.last;
506
+
507
+ this.animate(pos);
508
+ },
509
+
510
+ /**
511
+ * Scrolls the carousel to a certain position.
512
+ *
513
+ * @method scroll
514
+ * @return undefined
515
+ * @param i {Number} The index of the element to scoll to.
516
+ * @param a {Boolean} Flag indicating whether to perform animation.
517
+ */
518
+ scroll: function(i, a) {
519
+ if (this.locked || this.animating) {
520
+ return;
521
+ }
522
+
523
+ this.pauseAuto();
524
+ this.animate(this.pos(i), a);
525
+ },
526
+
527
+ /**
528
+ * Prepares the carousel and return the position for a certian index.
529
+ *
530
+ * @method pos
531
+ * @return {Number}
532
+ * @param i {Number} The index of the element to scoll to.
533
+ * @param fv {Boolean} Whether to force last item to be visible.
534
+ */
535
+ pos: function(i, fv) {
536
+ var pos = $jc.intval(this.list.css(this.lt));
537
+
538
+ if (this.locked || this.animating) {
539
+ return pos;
540
+ }
541
+
542
+ if (this.options.wrap != 'circular') {
543
+ i = i < 1 ? 1 : (this.options.size && i > this.options.size ? this.options.size : i);
544
+ }
545
+
546
+ var back = this.first > i;
547
+
548
+ // Create placeholders, new list width/height
549
+ // and new list position
550
+ var f = this.options.wrap != 'circular' && this.first <= 1 ? 1 : this.first;
551
+ var c = back ? this.get(f) : this.get(this.last);
552
+ var j = back ? f : f - 1;
553
+ var e = null, l = 0, p = false, d = 0, g;
554
+
555
+ while (back ? --j >= i : ++j < i) {
556
+ e = this.get(j);
557
+ p = !e.length;
558
+ if (e.length === 0) {
559
+ e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
560
+ c[back ? 'before' : 'after' ](e);
561
+
562
+ if (this.first !== null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) {
563
+ g = this.get(this.index(j));
564
+ if (g.length) {
565
+ e = this.add(j, g.clone(true));
566
+ }
567
+ }
568
+ }
569
+
570
+ c = e;
571
+ d = this.dimension(e);
572
+
573
+ if (p) {
574
+ l += d;
575
+ }
576
+
577
+ if (this.first !== null && (this.options.wrap == 'circular' || (j >= 1 && (this.options.size === null || j <= this.options.size)))) {
578
+ pos = back ? pos + d : pos - d;
579
+ }
580
+ }
581
+
582
+ // Calculate visible items
583
+ var clipping = this.clipping(), cache = [], visible = 0, v = 0;
584
+ c = this.get(i - 1);
585
+ j = i;
586
+
587
+ while (++visible) {
588
+ e = this.get(j);
589
+ p = !e.length;
590
+ if (e.length === 0) {
591
+ e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
592
+ // This should only happen on a next scroll
593
+ if (c.length === 0) {
594
+ this.list.prepend(e);
595
+ } else {
596
+ c[back ? 'before' : 'after' ](e);
597
+ }
598
+
599
+ if (this.first !== null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) {
600
+ g = this.get(this.index(j));
601
+ if (g.length) {
602
+ e = this.add(j, g.clone(true));
603
+ }
604
+ }
605
+ }
606
+
607
+ c = e;
608
+ d = this.dimension(e);
609
+ if (d === 0) {
610
+ throw new Error('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
611
+ }
612
+
613
+ if (this.options.wrap != 'circular' && this.options.size !== null && j > this.options.size) {
614
+ cache.push(e);
615
+ } else if (p) {
616
+ l += d;
617
+ }
618
+
619
+ v += d;
620
+
621
+ if (v >= clipping) {
622
+ break;
623
+ }
624
+
625
+ j++;
626
+ }
627
+
628
+ // Remove out-of-range placeholders
629
+ for (var x = 0; x < cache.length; x++) {
630
+ cache[x].remove();
631
+ }
632
+
633
+ // Resize list
634
+ if (l > 0) {
635
+ this.list.css(this.wh, this.dimension(this.list) + l + 'px');
636
+
637
+ if (back) {
638
+ pos -= l;
639
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - l + 'px');
640
+ }
641
+ }
642
+
643
+ // Calculate first and last item
644
+ var last = i + visible - 1;
645
+ if (this.options.wrap != 'circular' && this.options.size && last > this.options.size) {
646
+ last = this.options.size;
647
+ }
648
+
649
+ if (j > last) {
650
+ visible = 0;
651
+ j = last;
652
+ v = 0;
653
+ while (++visible) {
654
+ e = this.get(j--);
655
+ if (!e.length) {
656
+ break;
657
+ }
658
+ v += this.dimension(e);
659
+ if (v >= clipping) {
660
+ break;
661
+ }
662
+ }
663
+ }
664
+
665
+ var first = last - visible + 1;
666
+ if (this.options.wrap != 'circular' && first < 1) {
667
+ first = 1;
668
+ }
669
+
670
+ if (this.inTail && back) {
671
+ pos += this.tail;
672
+ this.inTail = false;
673
+ }
674
+
675
+ this.tail = null;
676
+ if (this.options.wrap != 'circular' && last == this.options.size && (last - visible + 1) >= 1) {
677
+ var m = $jc.intval(this.get(last).css(!this.options.vertical ? 'marginRight' : 'marginBottom'));
678
+ if ((v - m) > clipping) {
679
+ this.tail = v - clipping - m;
680
+ }
681
+ }
682
+
683
+ if (fv && i === this.options.size && this.tail) {
684
+ pos -= this.tail;
685
+ this.inTail = true;
686
+ }
687
+
688
+ // Adjust position
689
+ while (i-- > first) {
690
+ pos += this.dimension(this.get(i));
691
+ }
692
+
693
+ // Save visible item range
694
+ this.prevFirst = this.first;
695
+ this.prevLast = this.last;
696
+ this.first = first;
697
+ this.last = last;
698
+
699
+ return pos;
700
+ },
701
+
702
+ /**
703
+ * Animates the carousel to a certain position.
704
+ *
705
+ * @method animate
706
+ * @return undefined
707
+ * @param p {Number} Position to scroll to.
708
+ * @param a {Boolean} Flag indicating whether to perform animation.
709
+ */
710
+ animate: function(p, a) {
711
+ if (this.locked || this.animating) {
712
+ return;
713
+ }
714
+
715
+ this.animating = true;
716
+
717
+ var self = this;
718
+ var scrolled = function() {
719
+ self.animating = false;
720
+
721
+ if (p === 0) {
722
+ self.list.css(self.lt, 0);
723
+ }
724
+
725
+ if (!self.autoStopped && (self.options.wrap == 'circular' || self.options.wrap == 'both' || self.options.wrap == 'last' || self.options.size === null || self.last < self.options.size || (self.last == self.options.size && self.tail !== null && !self.inTail))) {
726
+ self.startAuto();
727
+ }
728
+
729
+ self.buttons();
730
+ self.notify('onAfterAnimation');
731
+
732
+ // This function removes items which are appended automatically for circulation.
733
+ // This prevents the list from growing infinitely.
734
+ if (self.options.wrap == 'circular' && self.options.size !== null) {
735
+ for (var i = self.prevFirst; i <= self.prevLast; i++) {
736
+ if (i !== null && !(i >= self.first && i <= self.last) && (i < 1 || i > self.options.size)) {
737
+ self.remove(i);
738
+ }
739
+ }
740
+ }
741
+ };
742
+
743
+ this.notify('onBeforeAnimation');
744
+
745
+ // Animate
746
+ if (!this.options.animation || a === false) {
747
+ this.list.css(this.lt, p + 'px');
748
+ scrolled();
749
+ } else {
750
+ var o = !this.options.vertical ? (this.options.rtl ? {'right': p} : {'left': p}) : {'top': p};
751
+ // Define animation settings.
752
+ var settings = {
753
+ duration: this.options.animation,
754
+ easing: this.options.easing,
755
+ complete: scrolled
756
+ };
757
+ // If we have a step callback, specify it as well.
758
+ if ($.isFunction(this.options.animationStepCallback)) {
759
+ settings.step = this.options.animationStepCallback;
760
+ }
761
+ // Start the animation.
762
+ this.list.animate(o, settings);
763
+ }
764
+ },
765
+
766
+ /**
767
+ * Starts autoscrolling.
768
+ *
769
+ * @method auto
770
+ * @return undefined
771
+ * @param s {Number} Seconds to periodically autoscroll the content.
772
+ */
773
+ startAuto: function(s) {
774
+ if (s !== undefined) {
775
+ this.options.auto = s;
776
+ }
777
+
778
+ if (this.options.auto === 0) {
779
+ return this.stopAuto();
780
+ }
781
+
782
+ if (this.timer !== null) {
783
+ return;
784
+ }
785
+
786
+ this.autoStopped = false;
787
+
788
+ var self = this;
789
+ this.timer = window.setTimeout(function() { self.next(); }, this.options.auto * 1000);
790
+ },
791
+
792
+ /**
793
+ * Stops autoscrolling.
794
+ *
795
+ * @method stopAuto
796
+ * @return undefined
797
+ */
798
+ stopAuto: function() {
799
+ this.pauseAuto();
800
+ this.autoStopped = true;
801
+ },
802
+
803
+ /**
804
+ * Pauses autoscrolling.
805
+ *
806
+ * @method pauseAuto
807
+ * @return undefined
808
+ */
809
+ pauseAuto: function() {
810
+ if (this.timer === null) {
811
+ return;
812
+ }
813
+
814
+ window.clearTimeout(this.timer);
815
+ this.timer = null;
816
+ },
817
+
818
+ /**
819
+ * Sets the states of the prev/next buttons.
820
+ *
821
+ * @method buttons
822
+ * @return undefined
823
+ */
824
+ buttons: function(n, p) {
825
+ if (n == null) {
826
+ n = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'first') || this.options.size === null || this.last < this.options.size);
827
+ if (!this.locked && (!this.options.wrap || this.options.wrap == 'first') && this.options.size !== null && this.last >= this.options.size) {
828
+ n = this.tail !== null && !this.inTail;
829
+ }
830
+ }
831
+
832
+ if (p == null) {
833
+ p = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'last') || this.first > 1);
834
+ if (!this.locked && (!this.options.wrap || this.options.wrap == 'last') && this.options.size !== null && this.first == 1) {
835
+ p = this.tail !== null && this.inTail;
836
+ }
837
+ }
838
+
839
+ var self = this;
840
+
841
+ if (this.buttonNext.size() > 0) {
842
+ this.buttonNext.unbind(this.options.buttonNextEvent + '.jcarousel', this.funcNext);
843
+
844
+ if (n) {
845
+ this.buttonNext.bind(this.options.buttonNextEvent + '.jcarousel', this.funcNext);
846
+ }
847
+
848
+ this.buttonNext[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
849
+
850
+ if (this.options.buttonNextCallback !== null && this.buttonNext.data('jcarouselstate') != n) {
851
+ this.buttonNext.each(function() { self.options.buttonNextCallback(self, this, n); }).data('jcarouselstate', n);
852
+ }
853
+ } else {
854
+ if (this.options.buttonNextCallback !== null && this.buttonNextState != n) {
855
+ this.options.buttonNextCallback(self, null, n);
856
+ }
857
+ }
858
+
859
+ if (this.buttonPrev.size() > 0) {
860
+ this.buttonPrev.unbind(this.options.buttonPrevEvent + '.jcarousel', this.funcPrev);
861
+
862
+ if (p) {
863
+ this.buttonPrev.bind(this.options.buttonPrevEvent + '.jcarousel', this.funcPrev);
864
+ }
865
+
866
+ this.buttonPrev[p ? 'removeClass' : 'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled', p ? false : true);
867
+
868
+ if (this.options.buttonPrevCallback !== null && this.buttonPrev.data('jcarouselstate') != p) {
869
+ this.buttonPrev.each(function() { self.options.buttonPrevCallback(self, this, p); }).data('jcarouselstate', p);
870
+ }
871
+ } else {
872
+ if (this.options.buttonPrevCallback !== null && this.buttonPrevState != p) {
873
+ this.options.buttonPrevCallback(self, null, p);
874
+ }
875
+ }
876
+
877
+ this.buttonNextState = n;
878
+ this.buttonPrevState = p;
879
+ },
880
+
881
+ /**
882
+ * Notify callback of a specified event.
883
+ *
884
+ * @method notify
885
+ * @return undefined
886
+ * @param evt {String} The event name
887
+ */
888
+ notify: function(evt) {
889
+ var state = this.prevFirst === null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');
890
+
891
+ // Load items
892
+ this.callback('itemLoadCallback', evt, state);
893
+
894
+ if (this.prevFirst !== this.first) {
895
+ this.callback('itemFirstInCallback', evt, state, this.first);
896
+ this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
897
+ }
898
+
899
+ if (this.prevLast !== this.last) {
900
+ this.callback('itemLastInCallback', evt, state, this.last);
901
+ this.callback('itemLastOutCallback', evt, state, this.prevLast);
902
+ }
903
+
904
+ this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
905
+ this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
906
+ },
907
+
908
+ callback: function(cb, evt, state, i1, i2, i3, i4) {
909
+ if (this.options[cb] == null || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation')) {
910
+ return;
911
+ }
912
+
913
+ var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb];
914
+
915
+ if (!$.isFunction(callback)) {
916
+ return;
917
+ }
918
+
919
+ var self = this;
920
+
921
+ if (i1 === undefined) {
922
+ callback(self, state, evt);
923
+ } else if (i2 === undefined) {
924
+ this.get(i1).each(function() { callback(self, this, i1, state, evt); });
925
+ } else {
926
+ var call = function(i) {
927
+ self.get(i).each(function() { callback(self, this, i, state, evt); });
928
+ };
929
+ for (var i = i1; i <= i2; i++) {
930
+ if (i !== null && !(i >= i3 && i <= i4)) {
931
+ call(i);
932
+ }
933
+ }
934
+ }
935
+ },
936
+
937
+ create: function(i) {
938
+ return this.format('<li></li>', i);
939
+ },
940
+
941
+ format: function(e, i) {
942
+ e = $(e);
943
+ var split = e.get(0).className.split(' ');
944
+ for (var j = 0; j < split.length; j++) {
945
+ if (split[j].indexOf('jcarousel-') != -1) {
946
+ e.removeClass(split[j]);
947
+ }
948
+ }
949
+ e.addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-' + i)).css({
950
+ 'float': (this.options.rtl ? 'right' : 'left'),
951
+ 'list-style': 'none'
952
+ }).attr('jcarouselindex', i);
953
+ return e;
954
+ },
955
+
956
+ className: function(c) {
957
+ return c + ' ' + c + (!this.options.vertical ? '-horizontal' : '-vertical');
958
+ },
959
+
960
+ dimension: function(e, d) {
961
+ var el = $(e);
962
+
963
+ if (d == null) {
964
+ return !this.options.vertical ?
965
+ (el.outerWidth(true) || $jc.intval(this.options.itemFallbackDimension)) :
966
+ (el.outerHeight(true) || $jc.intval(this.options.itemFallbackDimension));
967
+ } else {
968
+ var w = !this.options.vertical ?
969
+ d - $jc.intval(el.css('marginLeft')) - $jc.intval(el.css('marginRight')) :
970
+ d - $jc.intval(el.css('marginTop')) - $jc.intval(el.css('marginBottom'));
971
+
972
+ $(el).css(this.wh, w + 'px');
973
+
974
+ return this.dimension(el);
975
+ }
976
+ },
977
+
978
+ clipping: function() {
979
+ return !this.options.vertical ?
980
+ this.clip[0].offsetWidth - $jc.intval(this.clip.css('borderLeftWidth')) - $jc.intval(this.clip.css('borderRightWidth')) :
981
+ this.clip[0].offsetHeight - $jc.intval(this.clip.css('borderTopWidth')) - $jc.intval(this.clip.css('borderBottomWidth'));
982
+ },
983
+
984
+ index: function(i, s) {
985
+ if (s == null) {
986
+ s = this.options.size;
987
+ }
988
+
989
+ return Math.round((((i-1) / s) - Math.floor((i-1) / s)) * s) + 1;
990
+ }
991
+ });
992
+
993
+ $jc.extend({
994
+ /**
995
+ * Gets/Sets the global default configuration properties.
996
+ *
997
+ * @method defaults
998
+ * @return {Object}
999
+ * @param d {Object} A set of key/value pairs to set as configuration properties.
1000
+ */
1001
+ defaults: function(d) {
1002
+ return $.extend(defaults, d || {});
1003
+ },
1004
+
1005
+ intval: function(v) {
1006
+ v = parseInt(v, 10);
1007
+ return isNaN(v) ? 0 : v;
1008
+ },
1009
+
1010
+ windowLoaded: function() {
1011
+ windowLoaded = true;
1012
+ }
1013
+ });
1014
+
1015
+ /**
1016
+ * Creates a carousel for all matched elements.
1017
+ *
1018
+ * @example $("#mycarousel").jcarousel();
1019
+ * @before <ul id="mycarousel" class="jcarousel-skin-name"><li>First item</li><li>Second item</li></ul>
1020
+ * @result
1021
+ *
1022
+ * <div class="jcarousel-skin-name">
1023
+ * <div class="jcarousel-container">
1024
+ * <div class="jcarousel-clip">
1025
+ * <ul class="jcarousel-list">
1026
+ * <li class="jcarousel-item-1">First item</li>
1027
+ * <li class="jcarousel-item-2">Second item</li>
1028
+ * </ul>
1029
+ * </div>
1030
+ * <div disabled="disabled" class="jcarousel-prev jcarousel-prev-disabled"></div>
1031
+ * <div class="jcarousel-next"></div>
1032
+ * </div>
1033
+ * </div>
1034
+ *
1035
+ * @method jcarousel
1036
+ * @return jQuery
1037
+ * @param o {Hash|String} A set of key/value pairs to set as configuration properties or a method name to call on a formerly created instance.
1038
+ */
1039
+ $.fn.jcarousel = function(o) {
1040
+ if (typeof o == 'string') {
1041
+ var instance = $(this).data('jcarousel'), args = Array.prototype.slice.call(arguments, 1);
1042
+ return instance[o].apply(instance, args);
1043
+ } else {
1044
+ return this.each(function() {
1045
+ var instance = $(this).data('jcarousel');
1046
+ if (instance) {
1047
+ if (o) {
1048
+ $.extend(instance.options, o);
1049
+ }
1050
+ instance.reload();
1051
+ } else {
1052
+ $(this).data('jcarousel', new $jc(this, o));
1053
+ }
1054
+ });
1055
+ }
1056
+ };
1057
+
1058
+ })(jQuery);