refinerycms 0.9.7.10 → 0.9.7.11

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