refinerycms 0.9.7.10 → 0.9.7.11

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 (57) hide show
  1. data/Gemfile +9 -9
  2. data/bin/refinery-upgrade-096-to-097 +1 -1
  3. data/changelog.md +180 -158
  4. data/features/refinery/create_inquiries.feature +5 -7
  5. data/features/refinery/dashboard.feature +28 -16
  6. data/features/refinery/manage_files.feature +14 -19
  7. data/features/refinery/manage_images.feature +13 -26
  8. data/features/refinery/manage_inquiries.feature +6 -8
  9. data/features/refinery/manage_pages.feature +6 -0
  10. data/features/refinery/manage_users.feature +26 -0
  11. data/features/refinery/search.feature +37 -0
  12. data/features/refinery/site_bar.feature +1 -1
  13. data/features/step_definitions/refinery/file_steps.rb +4 -0
  14. data/features/step_definitions/refinery/image_steps.rb +4 -0
  15. data/features/step_definitions/refinery/inquiry_steps.rb +12 -0
  16. data/features/step_definitions/refinery/page_steps.rb +6 -2
  17. data/features/support/paths.rb +9 -0
  18. data/public/images/refinery/carousel-left.png +0 -0
  19. data/public/images/refinery/carousel-right.png +0 -0
  20. data/public/javascripts/jquery/jquery.jcarousel.js +896 -0
  21. data/public/javascripts/refinery/admin.js +50 -17
  22. data/public/javascripts/wymeditor/lang/da.js +47 -0
  23. data/public/stylesheets/refinery/refinery.css +20 -1
  24. data/vendor/plugins/images/app/controllers/admin/images_controller.rb +8 -8
  25. data/vendor/plugins/images/app/views/admin/images/_existing_image.html.erb +1 -1
  26. data/vendor/plugins/images/app/views/admin/images/_grid_view.html.erb +2 -1
  27. data/vendor/plugins/images/app/views/admin/images/_list_view_image.html.erb +2 -1
  28. data/vendor/plugins/images/app/views/admin/images/insert.html.erb +1 -1
  29. data/vendor/plugins/inquiries/app/controllers/admin/inquiries_controller.rb +2 -6
  30. data/vendor/plugins/inquiries/app/views/admin/inquiries/_inquiry.html.erb +2 -1
  31. data/vendor/plugins/inquiries/app/views/admin/inquiries/show.html.erb +2 -1
  32. data/vendor/plugins/inquiries/config/locales/pt-BR.yml +2 -0
  33. data/vendor/plugins/pages/app/views/admin/pages/_form_new_page_parts.html.erb +2 -2
  34. data/vendor/plugins/pages/app/views/admin/pages/_form_page_parts.html.erb +3 -3
  35. data/vendor/plugins/pages/app/views/admin/pages/_page.html.erb +1 -0
  36. data/vendor/plugins/pages/app/views/admin/pages/_sortable_list.html.erb +3 -1
  37. data/vendor/plugins/pages/app/views/admin/pages/index.html.erb +2 -6
  38. data/vendor/plugins/pages/config/locales/en.yml +2 -0
  39. data/vendor/plugins/pages/config/locales/pt-BR.yml +2 -0
  40. data/vendor/plugins/refinery/app/views/admin/_head.html.erb +6 -4
  41. data/vendor/plugins/refinery/app/views/shared/_head.html.erb +4 -3
  42. data/vendor/plugins/refinery/app/views/shared/_html5_for_ie.js.erb +5 -0
  43. data/vendor/plugins/refinery/app/views/shared/admin/_form_actions.html.erb +4 -1
  44. data/vendor/plugins/refinery/app/views/shared/admin/_search.html.erb +1 -1
  45. data/vendor/plugins/refinery/app/views/shared/admin/_sortable_list.html.erb +4 -4
  46. data/vendor/plugins/refinery/lib/crud.rb +62 -29
  47. data/vendor/plugins/refinery/lib/generators/refinery_plugin/templates/rails/init.rb +1 -1
  48. data/vendor/plugins/refinery/lib/generators/refinery_plugin/templates/views/admin/_sortable_list.html.erb +3 -1
  49. data/vendor/plugins/refinery/lib/generators/refinery_plugin/templates/views/admin/index.html.erb +1 -3
  50. data/vendor/plugins/refinery/lib/refinery.rb +1 -1
  51. data/vendor/plugins/refinery/lib/tasks/refinery.rake +5 -0
  52. data/vendor/plugins/refinery/plugins.md +1 -1
  53. data/vendor/plugins/refinery_dialogs/app/views/layouts/admin_dialog.html.erb +1 -0
  54. data/vendor/plugins/resources/app/controllers/admin/resources_controller.rb +2 -5
  55. data/vendor/plugins/resources/app/views/admin/resources/_resource.html.erb +2 -1
  56. metadata +15 -5
  57. data/app/controllers/application.rb +0 -3
@@ -2,20 +2,20 @@
2
2
  Feature: Create Inquiries
3
3
  In order to contact the website owner
4
4
  I want to create an inquiry
5
-
5
+
6
6
  Background:
7
7
  Given A Refinery user exists
8
8
  And I have no inquiries
9
9
  And I have a page titled "Contact Us" with a custom url "/contact"
10
10
  And I have a page titled "Thank You" with a custom url "/contact/thank_you"
11
-
11
+
12
12
  Scenario: Contact page
13
13
  When I go to the contact page
14
14
  Then I should see "Name *"
15
15
  And I should see "Email *"
16
16
  And I should see "Phone"
17
- And I should see "Message *"
18
-
17
+ And I should see "Message *"
18
+
19
19
  Scenario: Create a valid inquiry
20
20
  When I go to the contact page
21
21
  And I fill in "Name *" with "Philip"
@@ -24,10 +24,8 @@ Feature: Create Inquiries
24
24
  And I press "Send"
25
25
  Then I should be on the contact thank you page
26
26
  And I should see "Thank You"
27
- And I should see "received your inquiry"
28
- And I should see "Return to the home page"
29
27
  And I should have 1 inquiries
30
-
28
+
31
29
  Scenario: Create an invalid inquiry
32
30
  When I go to the contact page
33
31
  And I press "Send"
@@ -6,14 +6,38 @@ Feature: Dashboard
6
6
 
7
7
  Background:
8
8
  Given I am a logged in refinery user
9
-
10
- Scenario: Translation options available
11
9
  When I go to the Dashboard
10
+
11
+ Scenario: Add New Page Button
12
+ When I follow "Add a new page"
13
+ Then I should be on the new page form
14
+ When I fill in "Title" with "Page test from Dashboard"
15
+ And I press "Save"
16
+ Then I should be on the dashboard
17
+ And I should see "'Page test from Dashboard' was successfully created."
18
+ And I should see "Page test from dashboard page was created"
19
+
20
+ Scenario: Update a Page Button
21
+ When I follow "Update a page"
22
+ Then I should be on the list of pages
23
+
24
+ Scenario: Upload a File Button
25
+ When I follow "Upload a file"
26
+ Then I should be on the new file form
27
+
28
+ Scenario: Upload an Image Button
29
+ When I follow "Upload an image"
30
+ Then I should be on the new image form
31
+
32
+ Scenario: See Home Page Button
33
+ When I follow "See home page"
34
+ Then I should be on the home page
35
+
36
+ Scenario: Translation options available
12
37
  Then I should see "English Change Language"
13
38
 
14
39
  Scenario: Change Language to Slovenian and back to English
15
- When I go to the dashboard
16
- And I follow "English Change Language"
40
+ When I follow "English Change Language"
17
41
  And I follow "Slovenian"
18
42
  Then I should be on the dashboard
19
43
  And I should see "Slovenian Spremeni Jezik"
@@ -24,15 +48,3 @@ Feature: Dashboard
24
48
  Then I should be on the dashboard
25
49
  And I should see "Switch to your website"
26
50
  And I should not see "Spremeni Jezik"
27
-
28
- Scenario: Upload an Image Button
29
-
30
- Scenario: Add New Page Button
31
- When I go to the dashboard
32
- And I follow "Add a new page"
33
- Then I should be on the new page form
34
- When I fill in "Title" with "Page test from Dashboard"
35
- And I press "Save"
36
- Then I should be on the dashboard
37
- And I should see "'Page test from Dashboard' was successfully created."
38
- And I should see "Page test from dashboard page was created"
@@ -3,43 +3,38 @@ Feature: Manage Files
3
3
  In order to control the content on my website
4
4
  As an administrator
5
5
  I want to create and manage files
6
-
6
+
7
7
  Background:
8
8
  Given I am a logged in refinery user
9
-
9
+
10
10
  Scenario: Create Valid File
11
11
  Given I have no files
12
12
  When I go to the list of files
13
13
  And I follow "Upload New File"
14
- When I attach the file at "features/uploads/refinery_is_awesome.txt"
14
+ And I attach the file at "features/uploads/refinery_is_awesome.txt"
15
15
  And I press "Save"
16
16
  Then the file "refinery_is_awesome.txt" should have uploaded successfully
17
17
  And I should have 1 file
18
-
18
+
19
19
  Scenario: Edit Existing File
20
- Given I have no files
21
- When I upload the file at "features/uploads/refinery_is_awesome.txt"
22
- Then I go to the list of files
20
+ Given I have test file titled "refinery_is_awesome.txt"
21
+ When I go to the list of files
23
22
  And I follow "Edit this file"
24
23
  And I attach the file at "features/uploads/beach.jpeg"
25
24
  And I press "Save"
26
25
  Then the file "beach.jpeg" should have uploaded successfully
27
26
  And I should have 1 file
28
-
27
+
29
28
  Scenario: Download Existing File
30
29
  Given I have no files
31
30
  When I upload the file at "features/uploads/refinery_is_awesome.txt"
32
- Then I go to the list of files
31
+ And I go to the list of files
33
32
  And I follow "Download this file"
34
33
  Then I should see "http://www.refineryhq.com/"
35
-
34
+
36
35
  Scenario: Files Delete
37
- #The 'follow "Remove this file forever" line is failing -- needs fixed.
38
- #Given I have no files
39
- #When I upload the file at "features/uploads/refinery_is_awesome.txt"
40
- #Then I go to the list of files
41
- #And I follow "Remove this file forever"
42
- #And I press "OK"
43
- #Then I should see "Flash message here.. not in place?"
44
- #And I should have 0 files
45
-
36
+ Given I have test file titled "refinery_is_awesome.txt"
37
+ When I go to the list of files
38
+ And I follow "Remove this file forever"
39
+ Then I should see "'Refinery Is Awesome' was successfully destroyed."
40
+ And I should have 0 files
@@ -3,15 +3,15 @@ Feature: Manage Images
3
3
  In order to control the content on my website
4
4
  As an administrator
5
5
  I want to create and manage images
6
-
6
+
7
7
  Background:
8
8
  Given I am a logged in refinery user
9
-
9
+
10
10
  Scenario: Create Valid Image
11
11
  Given I have no images
12
12
  When I go to the list of images
13
13
  And I follow "Create New Image"
14
- When I attach the image at "features/uploads/beach.jpeg"
14
+ And I attach the image at "features/uploads/beach.jpeg"
15
15
  And I press "Save"
16
16
  Then the image "beach.jpeg" should have uploaded successfully
17
17
  And I should have the correct default number of images
@@ -21,41 +21,28 @@ Feature: Manage Images
21
21
  And the image should have content_type "image/jpeg"
22
22
  #This line handles properly if you have any plugins installed. Ie: Portfolio will generate two additional thumbs.
23
23
  And the image should have all default thumbnail generations
24
-
24
+
25
25
  Scenario: Create Invalid Image (format)
26
26
  Given I have no images
27
27
  When I go to the list of images
28
28
  And I follow "Create New Image"
29
- When I attach the image at "features/uploads/beach.INVALID"
29
+ And I attach the image at "features/uploads/beach.INVALID"
30
30
  And I press "Save"
31
31
  Then I should see "Your image must be either a JPG, PNG or GIF"
32
32
  And I should have 0 images
33
-
34
-
33
+
35
34
  Scenario: Edit Existing Image
36
- Given I have no images
35
+ Given I have test image titled "beach.jpg"
37
36
  When I go to the list of images
38
- And I follow "Create New Image"
39
- When I attach the image at "features/uploads/beach.jpeg"
40
- And I press "Save"
41
- Then the image "beach.jpeg" should have uploaded successfully
42
- Then I go to the list of images
43
37
  And I follow "Edit this image"
44
38
  And I attach the image at "features/uploads/id-rather-be-here.jpg"
45
39
  And I press "Save"
46
- #Note: The following line is a workaround for the flash. It isn't working, so this catches the created image's name='' attribute.
47
- Then I should see "Id Rather Be Here"
40
+ Then I should see "'Id Rather Be Here' was successfully updated."
48
41
  And I should have the correct default number of images
49
-
42
+
50
43
  Scenario: Delete Image
51
- Given I have no images
44
+ Given I have test image titled "beach.jpg"
52
45
  When I go to the list of images
53
- And I follow "Create New Image"
54
- When I attach the image at "features/uploads/beach.jpeg"
55
- And I press "Save"
56
- Then the image "beach.jpeg" should have uploaded successfully
57
- Then I go to the list of images
58
- #Delete functionality doesn't test properly, yet.
59
- #And I follow "Remove this image forever"
60
- #And I press "Ok"
61
- #Then I should have 0 images
46
+ And I follow "Remove this image forever"
47
+ Then I should see "'Beach' was successfully destroyed. "
48
+ And I should have 0 images
@@ -46,11 +46,9 @@ Feature: Manage Inquiries
46
46
  And I should see "Back to all Inquiries"
47
47
  And I should see "Remove this inquiry forever"
48
48
 
49
- # not worth it unless someone can confirm javascript confirmations??
50
- #Scenario: Inquiries Delete
51
- # When I go to the list of inquiries
52
- # And I follow "Read the inquiry"
53
- # And I follow "Remove this inquiry forever"
54
- # And I press "OK"
55
- # Then I should see "'David Jones' was successfully destroyed."
56
- # And I should have 0 inquiries
49
+ Scenario: Inquiries Delete
50
+ When I go to the list of inquiries
51
+ And I follow "Read the inquiry"
52
+ And I follow "Remove this inquiry forever"
53
+ Then I should see "'David Jones' was successfully destroyed."
54
+ And I should have 0 inquiries
@@ -37,3 +37,9 @@ Feature: Manage Pages
37
37
  And I press "Save"
38
38
  Then I should have 3 pages
39
39
  And I should have a page at /about--2
40
+
41
+ Scenario: Delete Page
42
+ Given I have test page titled "test"
43
+ When I go to the list of pages
44
+ And I follow "Delete this page"
45
+ Then I should see "'test' was successfully destroyed. "
@@ -27,3 +27,29 @@ Feature: Manage Users
27
27
  And I am a logged in refinery user
28
28
  When I go to the list of users
29
29
  Then I should see "resolve"
30
+
31
+ Scenario: Create User
32
+ Given I have a user named "resolve"
33
+ And I am a logged in refinery user
34
+ When I go to the list of users
35
+ And I follow "Create New User"
36
+ And I fill in "Login" with "cucumber"
37
+ And I fill in "Email" with "green@cucumber.com"
38
+ And I fill in "Password" with "greenandjuicy"
39
+ And I fill in "Password confirmation" with "greenandjuicy"
40
+ And I press "Save"
41
+ Then I should be on the list of users
42
+ And I should see "cucumber was successfully created."
43
+ And I should see "cucumber (green@cucumber.com)"
44
+
45
+ Scenario: Edit User
46
+ Given I have a user named "resolve"
47
+ And I am a logged in refinery user
48
+ When I go to the list of users
49
+ And I follow "Edit this user"
50
+ And I fill in "Login" with "cucumber"
51
+ And I fill in "Email" with "green@cucumber.com"
52
+ And I press "Save"
53
+ Then I should be on the list of users
54
+ And I should see "cucumber was successfully updated."
55
+ And I should see "cucumber (green@cucumber.com)"
@@ -0,0 +1,37 @@
1
+ Feature: Search
2
+ In order find content more quickly
3
+ As an administrator
4
+ I want to use search
5
+
6
+ Background:
7
+ Given I am a logged in refinery user
8
+
9
+ Scenario Outline: Search Existing Item
10
+ Given I have test <item> titled "<title>"
11
+ When I go to the list of <location>
12
+ And I fill in "search" with "<title>"
13
+ And I press "Search"
14
+ Then I should see "<title>"
15
+
16
+ Examples:
17
+ | item | title | location |
18
+ |page |testitem|pages |
19
+ |image |testitem|images |
20
+ |file |testitem|files |
21
+ |inquiry |testitem|inquiries |
22
+ |inquiry |testitem|spam inquiries|
23
+
24
+ Scenario Outline: Search Nonexisting Item
25
+ Given I have no <item>
26
+ When I go to the list of <location>
27
+ And I fill in "search" with "nonexisting"
28
+ And I press "Search"
29
+ Then I should see "Sorry, no results found"
30
+
31
+ Examples:
32
+ | item | location |
33
+ |pages |pages |
34
+ |images |images |
35
+ |files |files |
36
+ |inquiries|inquiries |
37
+ |inquiries|spam inquiries|
@@ -21,4 +21,4 @@ Feature: Site Bar
21
21
  Given A Refinery user exists
22
22
  And I am a logged in customer
23
23
  When I go to the home page
24
- Then I should not see "Switch to your website editor"
24
+ Then I should not see "Switch to your website editor"
@@ -2,6 +2,10 @@ Given /^I have no files$/ do
2
2
  Resource.delete_all
3
3
  end
4
4
 
5
+ Given /^I have test file titled "([^"]*)"$/ do |file_name|
6
+ Resource.create(:content_type => 'application/x-debian-package', :filename => file_name, :size => 5000)
7
+ end
8
+
5
9
  When /^I attach the file at "([^"]*)"$/ do |file_path|
6
10
  attach_file('resource[uploaded_data]', File.join(Rails.root, file_path))
7
11
  end
@@ -2,6 +2,10 @@ Given /^I have no images$/ do
2
2
  Image.delete_all
3
3
  end
4
4
 
5
+ Given /^I have test image titled "([^"]*)"$/ do |file_name|
6
+ Image.create(:content_type => 'image/jpeg', :filename => file_name, :size => 5000)
7
+ end
8
+
5
9
  When /^I attach the image at "([^"]*)"$/ do |file_path|
6
10
  attach_file('image[uploaded_data]', File.join(Rails.root, file_path))
7
11
  end
@@ -11,3 +11,15 @@ Given /^I have an inquiry from "([^"]*)" with email "([^\"]*)" and message "([^\
11
11
  :email => email,
12
12
  :message => message)
13
13
  end
14
+
15
+ Given /^I have test inquiry titled "([^"]*)"$/ do |title|
16
+ Inquiry.create(:name => title,
17
+ :email => 'test@cukes.com',
18
+ :message => 'cuking ...',
19
+ :spam => false)
20
+
21
+ Inquiry.create(:name => title,
22
+ :email => 'test@cukes.com',
23
+ :message => 'cuking ...',
24
+ :spam => true)
25
+ end
@@ -1,11 +1,11 @@
1
- Given /^I (only )?have a page titled (.+) with a custom url (.+)?$/ do |only, title, link_url|
1
+ Given /^I (only )?have a page titled "([^"]*)" with a custom url "([^"]*)"?$/ do |only, title, link_url|
2
2
  Page.delete_all if only
3
3
 
4
4
  Page.create(:title => title,
5
5
  :link_url => link_url)
6
6
  end
7
7
 
8
- Given /^I (only )?have pages titled (.+)$/ do |only, titles|
8
+ Given /^I (only )?have pages titled "?([^"]*)"?$/ do |only, titles|
9
9
  Page.delete_all if only
10
10
  titles.split(', ').each do |title|
11
11
  Page.create(:title => title)
@@ -16,6 +16,10 @@ Given /^I have no pages$/ do
16
16
  Page.delete_all
17
17
  end
18
18
 
19
+ Given /^I have test page titled "([^"]*)"$/ do |title|
20
+ Page.create(:title => title)
21
+ end
22
+
19
23
  Then /^I should have ([0-9]+) pages?$/ do |count|
20
24
  Page.count.should == count.to_i
21
25
  end
@@ -23,9 +23,15 @@ module NavigationHelpers
23
23
  when /the list of files/
24
24
  admin_resources_path
25
25
 
26
+ when /the new file form/
27
+ new_admin_resource_path
28
+
26
29
  when /the list of images/
27
30
  admin_images_path
28
31
 
32
+ when /the new image form/
33
+ new_admin_image_path
34
+
29
35
  when /the contact page/
30
36
  new_inquiry_path
31
37
 
@@ -38,6 +44,9 @@ module NavigationHelpers
38
44
  when /the list of inquiries/
39
45
  admin_inquiries_path
40
46
 
47
+ when /the list of spam inquiries/
48
+ spam_admin_inquiries_path
49
+
41
50
  when /the (d|D)ashboard/
42
51
  admin_dashboard_index_path
43
52
 
@@ -0,0 +1,896 @@
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
+ (function($) {
17
+ /**
18
+ * Creates a carousel for all matched elements.
19
+ *
20
+ * @example $("#mycarousel").jcarousel();
21
+ * @before <ul id="mycarousel" class="jcarousel-skin-name"><li>First item</li><li>Second item</li></ul>
22
+ * @result
23
+ *
24
+ * <div class="jcarousel-skin-name">
25
+ * <div class="jcarousel-container">
26
+ * <div disabled="disabled" class="jcarousel-prev jcarousel-prev-disabled"></div>
27
+ * <div class="jcarousel-clip">
28
+ * <ul class="jcarousel-list">
29
+ * <li class="jcarousel-item-1">First item</li>
30
+ * <li class="jcarousel-item-2">Second item</li>
31
+ * </ul>
32
+ * </div>
33
+ * <div class="jcarousel-next"></div>
34
+ * </div>
35
+ * </div>
36
+ *
37
+ * @method jcarousel
38
+ * @return jQuery
39
+ * @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.
40
+ */
41
+ $.fn.jcarousel = function(o) {
42
+ if (typeof o == 'string') {
43
+ var instance = $(this).data('jcarousel'), args = Array.prototype.slice.call(arguments, 1);
44
+ return instance[o].apply(instance, args);
45
+ } else
46
+ return this.each(function() {
47
+ $(this).data('jcarousel', new $jc(this, o));
48
+ });
49
+ };
50
+
51
+ // Default configuration properties.
52
+ var defaults = {
53
+ vertical: false,
54
+ listTag: 'ul',
55
+ itemTag: 'li',
56
+ start: 1,
57
+ offset: 1,
58
+ size: null,
59
+ scroll: 3,
60
+ visible: null,
61
+ animation: 'normal',
62
+ easing: 'swing',
63
+ auto: 0,
64
+ wrap: null,
65
+ initCallback: null,
66
+ reloadCallback: null,
67
+ itemLoadCallback: null,
68
+ itemFirstInCallback: null,
69
+ itemFirstOutCallback: null,
70
+ itemLastInCallback: null,
71
+ itemLastOutCallback: null,
72
+ itemVisibleInCallback: null,
73
+ itemVisibleOutCallback: null,
74
+ buttonNextHTML: '<div></div>',
75
+ buttonPrevHTML: '<div></div>',
76
+ buttonNextEvent: 'click',
77
+ buttonPrevEvent: 'click',
78
+ buttonNextCallback: null,
79
+ buttonPrevCallback: null
80
+ };
81
+
82
+ /**
83
+ * The jCarousel object.
84
+ *
85
+ * @constructor
86
+ * @class jcarousel
87
+ * @param e {HTMLElement} The element to create the carousel for.
88
+ * @param o {Object} A set of key/value pairs to set as configuration properties.
89
+ * @cat Plugins/jCarousel
90
+ */
91
+ $.jcarousel = function(e, o) {
92
+ this.options = $.extend({}, defaults, o || {});
93
+
94
+ this.locked = false;
95
+
96
+ this.container = null;
97
+ this.clip = null;
98
+ this.list = null;
99
+ this.buttonNext = null;
100
+ this.buttonPrev = null;
101
+
102
+ this.wh = !this.options.vertical ? 'width' : 'height';
103
+ this.lt = !this.options.vertical ? 'left' : 'top';
104
+
105
+ // Extract skin class
106
+ var skin = '', split = e.className.split(' ');
107
+
108
+ for (var i = 0; i < split.length; i++) {
109
+ if (split[i].indexOf('jcarousel-skin') != -1) {
110
+ $(e).removeClass(split[i]);
111
+ skin = split[i];
112
+ break;
113
+ }
114
+ }
115
+
116
+ if (e.nodeName == this.options.listTag.toUpperCase()) {
117
+ this.list = $(e);
118
+ this.container = this.list.parent();
119
+
120
+ if (this.container.hasClass('jcarousel-clip')) {
121
+ if (!this.container.parent().hasClass('jcarousel-container'))
122
+ this.container = this.container.wrap('<div></div>');
123
+
124
+ this.container = this.container.parent();
125
+ } else if (!this.container.hasClass('jcarousel-container'))
126
+ this.container = this.list.wrap('<div></div>').parent();
127
+ } else {
128
+ this.container = $(e);
129
+ this.list = this.container.find(this.options.listTag).eq(0);
130
+ }
131
+
132
+ if (skin != '' && this.container.parent()[0].className.indexOf('jcarousel-skin') == -1)
133
+ this.container.wrap('<div class=" '+ skin + '"></div>');
134
+
135
+ this.clip = this.list.parent();
136
+
137
+ if (!this.clip.length || !this.clip.hasClass('jcarousel-clip'))
138
+ this.clip = this.list.wrap('<div></div>').parent();
139
+
140
+ this.buttonNext = $('.jcarousel-next', this.container);
141
+
142
+ if (this.buttonNext.size() == 0 && this.options.buttonNextHTML != null)
143
+ this.buttonNext = this.clip.after(this.options.buttonNextHTML).next();
144
+
145
+ this.buttonNext.addClass(this.className('jcarousel-next'));
146
+
147
+ this.buttonPrev = $('.jcarousel-prev', this.container);
148
+
149
+ if (this.buttonPrev.size() == 0 && this.options.buttonPrevHTML != null)
150
+ this.buttonPrev = this.clip.before(this.options.buttonPrevHTML).prev();
151
+
152
+ this.buttonPrev.addClass(this.className('jcarousel-prev'));
153
+
154
+ this.clip.addClass(this.className('jcarousel-clip')).css({
155
+ overflow: 'hidden',
156
+ position: 'relative'
157
+ });
158
+ this.list.addClass(this.className('jcarousel-list')).css({
159
+ overflow: 'hidden',
160
+ position: 'relative',
161
+ top: 0,
162
+ left: 0,
163
+ margin: 0,
164
+ padding: 0
165
+ });
166
+ this.container.addClass(this.className('jcarousel-container')).css({
167
+ position: 'relative'
168
+ });
169
+
170
+ var di = this.options.visible != null ? Math.ceil(this.clipping() / this.options.visible) : null;
171
+ var li = this.list.children(this.options.itemTag);
172
+
173
+ var self = this;
174
+
175
+ if (li.size() > 0) {
176
+ var wh = 0, i = this.options.offset;
177
+ li.each(function() {
178
+ self.format(this, i++);
179
+ wh += self.dimension(this, di);
180
+ });
181
+
182
+ this.list.css(this.wh, wh + 'px');
183
+
184
+ // Only set if not explicitly passed as option
185
+ if (!o || o.size === undefined)
186
+ this.options.size = li.size();
187
+ }
188
+
189
+ // For whatever reason, .show() does not work in Safari...
190
+ this.container.css('display', 'block');
191
+ this.buttonNext.css('display', 'block');
192
+ this.buttonPrev.css('display', 'block');
193
+
194
+ this.funcNext = function() { self.next(); };
195
+ this.funcPrev = function() { self.prev(); };
196
+ this.funcResize = function() { self.reload(); };
197
+
198
+ if (this.options.initCallback != null)
199
+ this.options.initCallback(this, 'init');
200
+
201
+ if ($.browser.safari) {
202
+ this.buttons(false, false);
203
+ $(window).bind('load.jcarousel', function() { self.setup(); });
204
+ } else
205
+ this.setup();
206
+ };
207
+
208
+ // Create shortcut for internal use
209
+ var $jc = $.jcarousel;
210
+
211
+ $jc.fn = $jc.prototype = {
212
+ jcarousel: '0.2.4'
213
+ };
214
+
215
+ $jc.fn.extend = $jc.extend = $.extend;
216
+
217
+ $jc.fn.extend({
218
+ /**
219
+ * Setups the carousel.
220
+ *
221
+ * @method setup
222
+ * @return undefined
223
+ */
224
+ setup: function() {
225
+ this.first = null;
226
+ this.last = null;
227
+ this.prevFirst = null;
228
+ this.prevLast = null;
229
+ this.animating = false;
230
+ this.timer = null;
231
+ this.tail = null;
232
+ this.inTail = false;
233
+
234
+ if (this.locked)
235
+ return;
236
+
237
+ this.list.css(this.lt, this.pos(this.options.offset) + 'px');
238
+ var p = this.pos(this.options.start);
239
+ this.prevFirst = this.prevLast = null;
240
+ this.animate(p, false);
241
+
242
+ $(window).unbind('resize.jcarousel', this.funcResize).bind('resize.jcarousel', this.funcResize);
243
+ },
244
+
245
+ /**
246
+ * Clears the list and resets the carousel.
247
+ *
248
+ * @method reset
249
+ * @return undefined
250
+ */
251
+ reset: function() {
252
+ this.list.empty();
253
+
254
+ this.list.css(this.lt, '0px');
255
+ this.list.css(this.wh, '10px');
256
+
257
+ if (this.options.initCallback != null)
258
+ this.options.initCallback(this, 'reset');
259
+
260
+ this.setup();
261
+ },
262
+
263
+ /**
264
+ * Reloads the carousel and adjusts positions.
265
+ *
266
+ * @method reload
267
+ * @return undefined
268
+ */
269
+ reload: function() {
270
+ if (this.tail != null && this.inTail)
271
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + this.tail);
272
+
273
+ this.tail = null;
274
+ this.inTail = false;
275
+
276
+ if (this.options.reloadCallback != null)
277
+ this.options.reloadCallback(this);
278
+
279
+ if (this.options.visible != null) {
280
+ var self = this;
281
+ var di = Math.ceil(this.clipping() / this.options.visible), wh = 0, lt = 0;
282
+ $(this.options.itemTag, this.list).each(function(i) {
283
+ wh += self.dimension(this, di);
284
+ if (i + 1 < self.first)
285
+ lt = wh;
286
+ });
287
+
288
+ this.list.css(this.wh, wh + 'px');
289
+ this.list.css(this.lt, -lt + 'px');
290
+ }
291
+
292
+ this.scroll(this.first, false);
293
+ },
294
+
295
+ /**
296
+ * Locks the carousel.
297
+ *
298
+ * @method lock
299
+ * @return undefined
300
+ */
301
+ lock: function() {
302
+ this.locked = true;
303
+ this.buttons();
304
+ },
305
+
306
+ /**
307
+ * Unlocks the carousel.
308
+ *
309
+ * @method unlock
310
+ * @return undefined
311
+ */
312
+ unlock: function() {
313
+ this.locked = false;
314
+ this.buttons();
315
+ },
316
+
317
+ /**
318
+ * Sets the size of the carousel.
319
+ *
320
+ * @method size
321
+ * @return undefined
322
+ * @param s {Number} The size of the carousel.
323
+ */
324
+ size: function(s) {
325
+ if (s != undefined) {
326
+ this.options.size = s;
327
+ if (!this.locked)
328
+ this.buttons();
329
+ }
330
+
331
+ return this.options.size;
332
+ },
333
+
334
+ /**
335
+ * Checks whether a list element exists for the given index (or index range).
336
+ *
337
+ * @method get
338
+ * @return bool
339
+ * @param i {Number} The index of the (first) element.
340
+ * @param i2 {Number} The index of the last element.
341
+ */
342
+ has: function(i, i2) {
343
+ if (i2 == undefined || !i2)
344
+ i2 = i;
345
+
346
+ if (this.options.size !== null && i2 > this.options.size)
347
+ i2 = this.options.size;
348
+
349
+ for (var j = i; j <= i2; j++) {
350
+ var e = this.get(j);
351
+ if (!e.length || e.hasClass('jcarousel-item-placeholder'))
352
+ return false;
353
+ }
354
+
355
+ return true;
356
+ },
357
+
358
+ /**
359
+ * Returns a jQuery object with list element for the given index.
360
+ *
361
+ * @method get
362
+ * @return jQuery
363
+ * @param i {Number} The index of the element.
364
+ */
365
+ get: function(i) {
366
+ return $('.jcarousel-item-' + i, this.list);
367
+ },
368
+
369
+ /**
370
+ * Adds an element for the given index to the list.
371
+ * If the element already exists, it updates the inner html.
372
+ * Returns the created element as jQuery object.
373
+ *
374
+ * @method add
375
+ * @return jQuery
376
+ * @param i {Number} The index of the element.
377
+ * @param s {String} The innerHTML of the element.
378
+ */
379
+ add: function(i, s) {
380
+ var e = this.get(i), old = 0, add = 0;
381
+
382
+ if (e.length == 0) {
383
+ var c, e = this.create(i), j = $jc.intval(i);
384
+ while (c = this.get(--j)) {
385
+ if (j <= 0 || c.length) {
386
+ j <= 0 ? this.list.prepend(e) : c.after(e);
387
+ break;
388
+ }
389
+ }
390
+ } else
391
+ old = this.dimension(e);
392
+
393
+ e.removeClass(this.className('jcarousel-item-placeholder'));
394
+ typeof s == 'string' ? e.html(s) : e.empty().append(s);
395
+
396
+ var di = this.options.visible != null ? Math.ceil(this.clipping() / this.options.visible) : null;
397
+ var wh = this.dimension(e, di) - old;
398
+
399
+ if (i > 0 && i < this.first)
400
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - wh + 'px');
401
+
402
+ this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) + wh + 'px');
403
+
404
+ return e;
405
+ },
406
+
407
+ /**
408
+ * Removes an element for the given index from the list.
409
+ *
410
+ * @method remove
411
+ * @return undefined
412
+ * @param i {Number} The index of the element.
413
+ */
414
+ remove: function(i) {
415
+ var e = this.get(i);
416
+
417
+ // Check if item exists and is not currently visible
418
+ if (!e.length || (i >= this.first && i <= this.last))
419
+ return;
420
+
421
+ var d = this.dimension(e);
422
+
423
+ if (i < this.first)
424
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) + d + 'px');
425
+
426
+ e.remove();
427
+
428
+ this.list.css(this.wh, $jc.intval(this.list.css(this.wh)) - d + 'px');
429
+ },
430
+
431
+ /**
432
+ * Moves the carousel forwards.
433
+ *
434
+ * @method next
435
+ * @return undefined
436
+ */
437
+ next: function() {
438
+ this.stopAuto();
439
+
440
+ if (this.tail != null && !this.inTail)
441
+ this.scrollTail(false);
442
+ else
443
+ 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);
444
+ },
445
+
446
+ /**
447
+ * Moves the carousel backwards.
448
+ *
449
+ * @method prev
450
+ * @return undefined
451
+ */
452
+ prev: function() {
453
+ this.stopAuto();
454
+
455
+ if (this.tail != null && this.inTail)
456
+ this.scrollTail(true);
457
+ else
458
+ 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);
459
+ },
460
+
461
+ /**
462
+ * Scrolls the tail of the carousel.
463
+ *
464
+ * @method scrollTail
465
+ * @return undefined
466
+ * @param b {Boolean} Whether scroll the tail back or forward.
467
+ */
468
+ scrollTail: function(b) {
469
+ if (this.locked || this.animating || !this.tail)
470
+ return;
471
+
472
+ var pos = $jc.intval(this.list.css(this.lt));
473
+
474
+ !b ? pos -= this.tail : pos += this.tail;
475
+ this.inTail = !b;
476
+
477
+ // Save for callbacks
478
+ this.prevFirst = this.first;
479
+ this.prevLast = this.last;
480
+
481
+ this.animate(pos);
482
+ },
483
+
484
+ /**
485
+ * Scrolls the carousel to a certain position.
486
+ *
487
+ * @method scroll
488
+ * @return undefined
489
+ * @param i {Number} The index of the element to scoll to.
490
+ * @param a {Boolean} Flag indicating whether to perform animation.
491
+ */
492
+ scroll: function(i, a) {
493
+ if (this.locked || this.animating)
494
+ return;
495
+
496
+ this.animate(this.pos(i), a);
497
+ },
498
+
499
+ /**
500
+ * Prepares the carousel and return the position for a certian index.
501
+ *
502
+ * @method pos
503
+ * @return {Number}
504
+ * @param i {Number} The index of the element to scoll to.
505
+ */
506
+ pos: function(i) {
507
+ var pos = $jc.intval(this.list.css(this.lt));
508
+
509
+ if (this.locked || this.animating)
510
+ return pos;
511
+
512
+ if (this.options.wrap != 'circular')
513
+ i = i < 1 ? 1 : (this.options.size && i > this.options.size ? this.options.size : i);
514
+
515
+ var back = this.first > i;
516
+
517
+ // Create placeholders, new list width/height
518
+ // and new list position
519
+ var f = this.options.wrap != 'circular' && this.first <= 1 ? 1 : this.first;
520
+ var c = back ? this.get(f) : this.get(this.last);
521
+ var j = back ? f : f - 1;
522
+ var e = null, l = 0, p = false, d = 0, g;
523
+
524
+ while (back ? --j >= i : ++j < i) {
525
+ e = this.get(j);
526
+ p = !e.length;
527
+ if (e.length == 0) {
528
+ e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
529
+ c[back ? 'before' : 'after' ](e);
530
+
531
+ if (this.first != null && this.options.wrap == 'circular' && this.options.size !== null && (j <= 0 || j > this.options.size)) {
532
+ g = this.get(this.index(j));
533
+ if (g.length)
534
+ this.add(j, g.children().clone(true));
535
+ }
536
+ }
537
+
538
+ c = e;
539
+ d = this.dimension(e);
540
+
541
+ if (p)
542
+ l += d;
543
+
544
+ if (this.first != null && (this.options.wrap == 'circular' || (j >= 1 && (this.options.size == null || j <= this.options.size))))
545
+ pos = back ? pos + d : pos - d;
546
+ }
547
+
548
+ // Calculate visible items
549
+ var clipping = this.clipping();
550
+ var cache = [];
551
+ var visible = 0, j = i, v = 0;
552
+ var c = this.get(i - 1);
553
+
554
+ while (++visible) {
555
+ e = this.get(j);
556
+ p = !e.length;
557
+ if (e.length == 0) {
558
+ e = this.create(j).addClass(this.className('jcarousel-item-placeholder'));
559
+ // This should only happen on a next scroll
560
+ c.length == 0 ? this.list.prepend(e) : 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
+ this.add(j, g.find('>*').clone(true));
566
+ }
567
+ }
568
+
569
+ c = e;
570
+ var d = this.dimension(e);
571
+ if (d == 0) {
572
+ alert('jCarousel: No width/height set for items. This will cause an infinite loop. Aborting...');
573
+ return 0;
574
+ }
575
+
576
+ if (this.options.wrap != 'circular' && this.options.size !== null && j > this.options.size)
577
+ cache.push(e);
578
+ else if (p)
579
+ l += d;
580
+
581
+ v += d;
582
+
583
+ if (v >= clipping)
584
+ break;
585
+
586
+ j++;
587
+ }
588
+
589
+ // Remove out-of-range placeholders
590
+ for (var x = 0; x < cache.length; x++)
591
+ cache[x].remove();
592
+
593
+ // Resize list
594
+ if (l > 0) {
595
+ this.list.css(this.wh, this.dimension(this.list) + l + 'px');
596
+
597
+ if (back) {
598
+ pos -= l;
599
+ this.list.css(this.lt, $jc.intval(this.list.css(this.lt)) - l + 'px');
600
+ }
601
+ }
602
+
603
+ // Calculate first and last item
604
+ var last = i + visible - 1;
605
+ if (this.options.wrap != 'circular' && this.options.size && last > this.options.size)
606
+ last = this.options.size;
607
+
608
+ if (j > last) {
609
+ visible = 0, j = last, v = 0;
610
+ while (++visible) {
611
+ var e = this.get(j--);
612
+ if (!e.length)
613
+ break;
614
+ v += this.dimension(e);
615
+ if (v >= clipping)
616
+ break;
617
+ }
618
+ }
619
+
620
+ var first = last - visible + 1;
621
+ if (this.options.wrap != 'circular' && first < 1)
622
+ first = 1;
623
+
624
+ if (this.inTail && back) {
625
+ pos += this.tail;
626
+ this.inTail = false;
627
+ }
628
+
629
+ this.tail = null;
630
+ if (this.options.wrap != 'circular' && last == this.options.size && (last - visible + 1) >= 1) {
631
+ var m = $jc.margin(this.get(last), !this.options.vertical ? 'marginRight' : 'marginBottom');
632
+ if ((v - m) > clipping)
633
+ this.tail = v - clipping - m;
634
+ }
635
+
636
+ // Adjust position
637
+ while (i-- > first)
638
+ pos += this.dimension(this.get(i));
639
+
640
+ // Save visible item range
641
+ this.prevFirst = this.first;
642
+ this.prevLast = this.last;
643
+ this.first = first;
644
+ this.last = last;
645
+
646
+ return pos;
647
+ },
648
+
649
+ /**
650
+ * Animates the carousel to a certain position.
651
+ *
652
+ * @method animate
653
+ * @return undefined
654
+ * @param p {Number} Position to scroll to.
655
+ * @param a {Boolean} Flag indicating whether to perform animation.
656
+ */
657
+ animate: function(p, a) {
658
+ if (this.locked || this.animating)
659
+ return;
660
+
661
+ this.animating = true;
662
+
663
+ var self = this;
664
+ var scrolled = function() {
665
+ self.animating = false;
666
+
667
+ if (p == 0)
668
+ self.list.css(self.lt, 0);
669
+
670
+ if (self.options.wrap == 'circular' || self.options.wrap == 'both' || self.options.wrap == 'last' || self.options.size == null || self.last < self.options.size)
671
+ self.startAuto();
672
+
673
+ self.buttons();
674
+ self.notify('onAfterAnimation');
675
+ };
676
+
677
+ this.notify('onBeforeAnimation');
678
+
679
+ // Animate
680
+ if (!this.options.animation || a == false) {
681
+ this.list.css(this.lt, p + 'px');
682
+ scrolled();
683
+ } else {
684
+ var o = !this.options.vertical ? {'left': p} : {'top': p};
685
+ this.list.animate(o, this.options.animation, this.options.easing, scrolled);
686
+ }
687
+ },
688
+
689
+ /**
690
+ * Starts autoscrolling.
691
+ *
692
+ * @method auto
693
+ * @return undefined
694
+ * @param s {Number} Seconds to periodically autoscroll the content.
695
+ */
696
+ startAuto: function(s) {
697
+ if (s != undefined)
698
+ this.options.auto = s;
699
+
700
+ if (this.options.auto == 0)
701
+ return this.stopAuto();
702
+
703
+ if (this.timer != null)
704
+ return;
705
+
706
+ var self = this;
707
+ this.timer = setTimeout(function() { self.next(); }, this.options.auto * 1000);
708
+ },
709
+
710
+ /**
711
+ * Stops autoscrolling.
712
+ *
713
+ * @method stopAuto
714
+ * @return undefined
715
+ */
716
+ stopAuto: function() {
717
+ if (this.timer == null)
718
+ return;
719
+
720
+ clearTimeout(this.timer);
721
+ this.timer = null;
722
+ },
723
+
724
+ /**
725
+ * Sets the states of the prev/next buttons.
726
+ *
727
+ * @method buttons
728
+ * @return undefined
729
+ */
730
+ buttons: function(n, p) {
731
+ if (n == undefined || n == null) {
732
+ var n = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'first') || this.options.size == null || this.last < this.options.size);
733
+ if (!this.locked && (!this.options.wrap || this.options.wrap == 'first') && this.options.size != null && this.last >= this.options.size)
734
+ n = this.tail != null && !this.inTail;
735
+ }
736
+
737
+ if (p == undefined || p == null) {
738
+ var p = !this.locked && this.options.size !== 0 && ((this.options.wrap && this.options.wrap != 'last') || this.first > 1);
739
+ if (!this.locked && (!this.options.wrap || this.options.wrap == 'last') && this.options.size != null && this.first == 1)
740
+ p = this.tail != null && this.inTail;
741
+ }
742
+
743
+ var self = this;
744
+
745
+ this.buttonNext[n ? 'bind' : 'unbind'](this.options.buttonNextEvent + '.jcarousel', this.funcNext)[n ? 'removeClass' : 'addClass'](this.className('jcarousel-next-disabled')).attr('disabled', n ? false : true);
746
+ this.buttonPrev[p ? 'bind' : 'unbind'](this.options.buttonPrevEvent + '.jcarousel', this.funcPrev)[p ? 'removeClass' : 'addClass'](this.className('jcarousel-prev-disabled')).attr('disabled', p ? false : true);
747
+
748
+ if (this.buttonNext.length > 0 && (this.buttonNext[0].jcarouselstate == undefined || this.buttonNext[0].jcarouselstate != n) && this.options.buttonNextCallback != null) {
749
+ this.buttonNext.each(function() { self.options.buttonNextCallback(self, this, n); });
750
+ this.buttonNext[0].jcarouselstate = n;
751
+ }
752
+
753
+ if (this.buttonPrev.length > 0 && (this.buttonPrev[0].jcarouselstate == undefined || this.buttonPrev[0].jcarouselstate != p) && this.options.buttonPrevCallback != null) {
754
+ this.buttonPrev.each(function() { self.options.buttonPrevCallback(self, this, p); });
755
+ this.buttonPrev[0].jcarouselstate = p;
756
+ }
757
+ },
758
+
759
+ /**
760
+ * Notify callback of a specified event.
761
+ *
762
+ * @method notify
763
+ * @return undefined
764
+ * @param evt {String} The event name
765
+ */
766
+ notify: function(evt) {
767
+ var state = this.prevFirst == null ? 'init' : (this.prevFirst < this.first ? 'next' : 'prev');
768
+
769
+ // Load items
770
+ this.callback('itemLoadCallback', evt, state);
771
+
772
+ if (this.prevFirst !== this.first) {
773
+ this.callback('itemFirstInCallback', evt, state, this.first);
774
+ this.callback('itemFirstOutCallback', evt, state, this.prevFirst);
775
+ }
776
+
777
+ if (this.prevLast !== this.last) {
778
+ this.callback('itemLastInCallback', evt, state, this.last);
779
+ this.callback('itemLastOutCallback', evt, state, this.prevLast);
780
+ }
781
+
782
+ this.callback('itemVisibleInCallback', evt, state, this.first, this.last, this.prevFirst, this.prevLast);
783
+ this.callback('itemVisibleOutCallback', evt, state, this.prevFirst, this.prevLast, this.first, this.last);
784
+ },
785
+
786
+ callback: function(cb, evt, state, i1, i2, i3, i4) {
787
+ if (this.options[cb] == undefined || (typeof this.options[cb] != 'object' && evt != 'onAfterAnimation'))
788
+ return;
789
+
790
+ var callback = typeof this.options[cb] == 'object' ? this.options[cb][evt] : this.options[cb];
791
+
792
+ if (!$.isFunction(callback))
793
+ return;
794
+
795
+ var self = this;
796
+
797
+ if (i1 === undefined)
798
+ callback(self, state, evt);
799
+ else if (i2 === undefined)
800
+ this.get(i1).each(function() { callback(self, this, i1, state, evt); });
801
+ else {
802
+ for (var i = i1; i <= i2; i++)
803
+ if (i !== null && !(i >= i3 && i <= i4))
804
+ this.get(i).each(function() { callback(self, this, i, state, evt); });
805
+ }
806
+ },
807
+
808
+ create: function(i) {
809
+ return this.format('<' + this.options.itemTag + '></' + this.options.itemTag + '>', i);
810
+ },
811
+
812
+ format: function(e, i) {
813
+ var $e = $(e).addClass(this.className('jcarousel-item')).addClass(this.className('jcarousel-item-' + i)).css({
814
+ 'float': 'left',
815
+ 'list-style': 'none'
816
+ });
817
+ $e.attr('jcarouselindex', i);
818
+ return $e;
819
+ },
820
+
821
+ className: function(c) {
822
+ return c + ' ' + c + (!this.options.vertical ? '-horizontal' : '-vertical');
823
+ },
824
+
825
+ dimension: function(e, d) {
826
+ var el = e.jquery != undefined ? e[0] : e;
827
+
828
+ var old = !this.options.vertical ?
829
+ el.offsetWidth + $jc.margin(el, 'marginLeft') + $jc.margin(el, 'marginRight') :
830
+ el.offsetHeight + $jc.margin(el, 'marginTop') + $jc.margin(el, 'marginBottom');
831
+
832
+ if (d == undefined || old == d)
833
+ return old;
834
+
835
+ var w = !this.options.vertical ?
836
+ d - $jc.margin(el, 'marginLeft') - $jc.margin(el, 'marginRight') :
837
+ d - $jc.margin(el, 'marginTop') - $jc.margin(el, 'marginBottom');
838
+
839
+ $(el).css(this.wh, w + 'px');
840
+
841
+ return this.dimension(el);
842
+ },
843
+
844
+ clipping: function() {
845
+ return !this.options.vertical ?
846
+ this.clip[0].offsetWidth - $jc.intval(this.clip.css('borderLeftWidth')) - $jc.intval(this.clip.css('borderRightWidth')) :
847
+ this.clip[0].offsetHeight - $jc.intval(this.clip.css('borderTopWidth')) - $jc.intval(this.clip.css('borderBottomWidth'));
848
+ },
849
+
850
+ index: function(i, s) {
851
+ if (s == undefined)
852
+ s = this.options.size;
853
+
854
+ return Math.round((((i-1) / s) - Math.floor((i-1) / s)) * s) + 1;
855
+ }
856
+ });
857
+
858
+ $jc.extend({
859
+ /**
860
+ * Gets/Sets the global default configuration properties.
861
+ *
862
+ * @method defaults
863
+ * @return {Object}
864
+ * @param d {Object} A set of key/value pairs to set as configuration properties.
865
+ */
866
+ defaults: function(d) {
867
+ return $.extend(defaults, d || {});
868
+ },
869
+
870
+ margin: function(e, p) {
871
+ if (!e)
872
+ return 0;
873
+
874
+ var el = e.jquery != undefined ? e[0] : e;
875
+
876
+ if (p == 'marginRight' && $.browser.safari) {
877
+ var old = {'display': 'block', 'float': 'none', 'width': 'auto'}, oWidth, oWidth2;
878
+
879
+ $.swap(el, old, function() { oWidth = el.offsetWidth; });
880
+
881
+ old['marginRight'] = 0;
882
+ $.swap(el, old, function() { oWidth2 = el.offsetWidth; });
883
+
884
+ return oWidth2 - oWidth;
885
+ }
886
+
887
+ return $jc.intval($.css(el, p));
888
+ },
889
+
890
+ intval: function(v) {
891
+ v = parseInt(v);
892
+ return isNaN(v) ? 0 : v;
893
+ }
894
+ });
895
+
896
+ })(jQuery);