releaf 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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);