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.
- data/Gemfile +9 -9
- data/bin/refinery-upgrade-096-to-097 +1 -1
- data/changelog.md +180 -158
- data/features/refinery/create_inquiries.feature +5 -7
- data/features/refinery/dashboard.feature +28 -16
- data/features/refinery/manage_files.feature +14 -19
- data/features/refinery/manage_images.feature +13 -26
- data/features/refinery/manage_inquiries.feature +6 -8
- data/features/refinery/manage_pages.feature +6 -0
- data/features/refinery/manage_users.feature +26 -0
- data/features/refinery/search.feature +37 -0
- data/features/refinery/site_bar.feature +1 -1
- data/features/step_definitions/refinery/file_steps.rb +4 -0
- data/features/step_definitions/refinery/image_steps.rb +4 -0
- data/features/step_definitions/refinery/inquiry_steps.rb +12 -0
- data/features/step_definitions/refinery/page_steps.rb +6 -2
- data/features/support/paths.rb +9 -0
- data/public/images/refinery/carousel-left.png +0 -0
- data/public/images/refinery/carousel-right.png +0 -0
- data/public/javascripts/jquery/jquery.jcarousel.js +896 -0
- data/public/javascripts/refinery/admin.js +50 -17
- data/public/javascripts/wymeditor/lang/da.js +47 -0
- data/public/stylesheets/refinery/refinery.css +20 -1
- data/vendor/plugins/images/app/controllers/admin/images_controller.rb +8 -8
- data/vendor/plugins/images/app/views/admin/images/_existing_image.html.erb +1 -1
- data/vendor/plugins/images/app/views/admin/images/_grid_view.html.erb +2 -1
- data/vendor/plugins/images/app/views/admin/images/_list_view_image.html.erb +2 -1
- data/vendor/plugins/images/app/views/admin/images/insert.html.erb +1 -1
- data/vendor/plugins/inquiries/app/controllers/admin/inquiries_controller.rb +2 -6
- data/vendor/plugins/inquiries/app/views/admin/inquiries/_inquiry.html.erb +2 -1
- data/vendor/plugins/inquiries/app/views/admin/inquiries/show.html.erb +2 -1
- data/vendor/plugins/inquiries/config/locales/pt-BR.yml +2 -0
- data/vendor/plugins/pages/app/views/admin/pages/_form_new_page_parts.html.erb +2 -2
- data/vendor/plugins/pages/app/views/admin/pages/_form_page_parts.html.erb +3 -3
- data/vendor/plugins/pages/app/views/admin/pages/_page.html.erb +1 -0
- data/vendor/plugins/pages/app/views/admin/pages/_sortable_list.html.erb +3 -1
- data/vendor/plugins/pages/app/views/admin/pages/index.html.erb +2 -6
- data/vendor/plugins/pages/config/locales/en.yml +2 -0
- data/vendor/plugins/pages/config/locales/pt-BR.yml +2 -0
- data/vendor/plugins/refinery/app/views/admin/_head.html.erb +6 -4
- data/vendor/plugins/refinery/app/views/shared/_head.html.erb +4 -3
- data/vendor/plugins/refinery/app/views/shared/_html5_for_ie.js.erb +5 -0
- data/vendor/plugins/refinery/app/views/shared/admin/_form_actions.html.erb +4 -1
- data/vendor/plugins/refinery/app/views/shared/admin/_search.html.erb +1 -1
- data/vendor/plugins/refinery/app/views/shared/admin/_sortable_list.html.erb +4 -4
- data/vendor/plugins/refinery/lib/crud.rb +62 -29
- data/vendor/plugins/refinery/lib/generators/refinery_plugin/templates/rails/init.rb +1 -1
- data/vendor/plugins/refinery/lib/generators/refinery_plugin/templates/views/admin/_sortable_list.html.erb +3 -1
- data/vendor/plugins/refinery/lib/generators/refinery_plugin/templates/views/admin/index.html.erb +1 -3
- data/vendor/plugins/refinery/lib/refinery.rb +1 -1
- data/vendor/plugins/refinery/lib/tasks/refinery.rake +5 -0
- data/vendor/plugins/refinery/plugins.md +1 -1
- data/vendor/plugins/refinery_dialogs/app/views/layouts/admin_dialog.html.erb +1 -0
- data/vendor/plugins/resources/app/controllers/admin/resources_controller.rb +2 -5
- data/vendor/plugins/resources/app/views/admin/resources/_resource.html.erb +2 -1
- metadata +15 -5
- 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
|
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
|
-
|
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
|
21
|
-
When I
|
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
|
-
|
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
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
44
|
+
Given I have test image titled "beach.jpg"
|
52
45
|
When I go to the list of images
|
53
|
-
And I follow "
|
54
|
-
|
55
|
-
And I
|
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
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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|
|
@@ -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 (
|
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 (
|
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
|
data/features/support/paths.rb
CHANGED
@@ -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
|
|
Binary file
|
Binary file
|
@@ -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);
|