solidus_reviews 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +18 -0
  5. data/CONTRIBUTING.md +28 -0
  6. data/Gemfile +14 -0
  7. data/LICENSE.md +26 -0
  8. data/README.md +93 -0
  9. data/Rakefile +21 -0
  10. data/app/assets/images/store/reviews/delete.gif +0 -0
  11. data/app/assets/images/store/reviews/star.gif +0 -0
  12. data/app/assets/javascripts/spree/backend/solidus_reviews.js +7 -0
  13. data/app/assets/javascripts/spree/frontend/solidus_reviews.js +7 -0
  14. data/app/assets/stylesheets/spree/backend/solidus_reviews.css +3 -0
  15. data/app/assets/stylesheets/spree/frontend/solidus_reviews.scss +83 -0
  16. data/app/controllers/spree/admin/feedback_reviews_controller.rb +6 -0
  17. data/app/controllers/spree/admin/review_settings_controller.rb +21 -0
  18. data/app/controllers/spree/admin/reviews_controller.rb +34 -0
  19. data/app/controllers/spree/feedback_reviews_controller.rb +41 -0
  20. data/app/controllers/spree/products_controller_decorator.rb +5 -0
  21. data/app/controllers/spree/reviews_controller.rb +48 -0
  22. data/app/helpers/spree/reviews_helper.rb +17 -0
  23. data/app/models/spree/feedback_review.rb +17 -0
  24. data/app/models/spree/product_decorator.rb +21 -0
  25. data/app/models/spree/review.rb +36 -0
  26. data/app/models/spree/reviews_ability.rb +17 -0
  27. data/app/models/spree/reviews_configuration.rb +26 -0
  28. data/app/overrides/add_reviews_after_product_properties.rb +5 -0
  29. data/app/overrides/add_reviews_tab_to_admin.rb +4 -0
  30. data/app/overrides/add_reviews_to_admin_configuration_sidebar.rb +5 -0
  31. data/app/views/spree/admin/feedback_reviews/index.html.erb +44 -0
  32. data/app/views/spree/admin/review_settings/edit.html.erb +40 -0
  33. data/app/views/spree/admin/reviews/_form.html.erb +22 -0
  34. data/app/views/spree/admin/reviews/edit.html.erb +26 -0
  35. data/app/views/spree/admin/reviews/index.html.erb +111 -0
  36. data/app/views/spree/feedback_reviews/_form.html.erb +13 -0
  37. data/app/views/spree/feedback_reviews/_summary.html.erb +6 -0
  38. data/app/views/spree/feedback_reviews/create.js.erb +7 -0
  39. data/app/views/spree/reviews/_form.html.erb +34 -0
  40. data/app/views/spree/reviews/_stars.html.erb +13 -0
  41. data/app/views/spree/reviews/index.html.erb +1 -0
  42. data/app/views/spree/reviews/new.html.erb +3 -0
  43. data/app/views/spree/shared/_rating.html.erb +15 -0
  44. data/app/views/spree/shared/_review.html.erb +28 -0
  45. data/app/views/spree/shared/_review_summary.html.erb +9 -0
  46. data/app/views/spree/shared/_reviews.html.erb +15 -0
  47. data/app/views/spree/shared/_shortrating.html.erb +9 -0
  48. data/bin/rails +5 -0
  49. data/config/initializers/constants.rb +1 -0
  50. data/config/initializers/load_preferences.rb +5 -0
  51. data/config/locales/de-CH.yml +68 -0
  52. data/config/locales/de.yml +68 -0
  53. data/config/locales/en-GB.yml +68 -0
  54. data/config/locales/en.yml +67 -0
  55. data/config/locales/es.yml +68 -0
  56. data/config/locales/fr.yml +68 -0
  57. data/config/locales/pl.yml +72 -0
  58. data/config/locales/pt-BR.yml +68 -0
  59. data/config/locales/pt.yml +69 -0
  60. data/config/locales/ro.yml +76 -0
  61. data/config/locales/ru.yml +73 -0
  62. data/config/locales/sv.yml +66 -0
  63. data/config/locales/tr.yml +68 -0
  64. data/config/locales/uk.yml +75 -0
  65. data/config/locales/zh-CN.yml +65 -0
  66. data/config/locales/zh-TW.yml +65 -0
  67. data/config/routes.rb +17 -0
  68. data/db/migrate/20081020220724_create_reviews.rb +18 -0
  69. data/db/migrate/20101222083309_create_feedback_reviews.rb +17 -0
  70. data/db/migrate/20110406083603_add_rating_to_products.rb +21 -0
  71. data/db/migrate/20110606150524_add_user_to_reviews.rb +9 -0
  72. data/db/migrate/20110806093221_add_ip_address_to_reviews.rb +9 -0
  73. data/db/migrate/20120110172331_namespace_tables.rb +6 -0
  74. data/db/migrate/20120123141326_recalculate_ratings.rb +20 -0
  75. data/db/migrate/20120712182514_add_locale_to_reviews.rb +9 -0
  76. data/db/migrate/20120712182627_add_locale_to_feedback_reviews.rb +9 -0
  77. data/db/migrate/20140703200946_add_show_identifier_to_reviews.rb +6 -0
  78. data/db/sample/ratings.yml +16 -0
  79. data/db/sample/reviews.yml +59 -0
  80. data/lib/generators/solidus_reviews/install/install_generator.rb +30 -0
  81. data/lib/solidus_reviews/factories/feedback_review_factory.rb +8 -0
  82. data/lib/solidus_reviews/factories/review_factory.rb +20 -0
  83. data/lib/solidus_reviews/factories.rb +5 -0
  84. data/lib/solidus_reviews.rb +6 -0
  85. data/lib/spree_reviews/engine.rb +17 -0
  86. data/solidus_reviews.gemspec +35 -0
  87. data/spec/controllers/admin/feedback_reviews_controller_spec.rb +29 -0
  88. data/spec/controllers/admin/review_settings_controller_spec.rb +57 -0
  89. data/spec/controllers/admin/reviews_controller_spec.rb +63 -0
  90. data/spec/controllers/feedback_reviews_controller_spec.rb +71 -0
  91. data/spec/controllers/products_controller_spec.rb +9 -0
  92. data/spec/controllers/reviews_controller_spec.rb +136 -0
  93. data/spec/features/admin_spec.rb +35 -0
  94. data/spec/features/reviews_spec.rb +122 -0
  95. data/spec/helpers/review_helper_spec.rb +28 -0
  96. data/spec/models/feedback_review_spec.rb +78 -0
  97. data/spec/models/product_spec.rb +62 -0
  98. data/spec/models/review_spec.rb +168 -0
  99. data/spec/models/reviews_ability_spec.rb +46 -0
  100. data/spec/models/reviews_configuration_spec.rb +45 -0
  101. data/spec/spec_helper.rb +55 -0
  102. data/vendor/assets/javascripts/jquery.rating.js +376 -0
  103. metadata +357 -0
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::Review do
4
+
5
+ context 'validations' do
6
+ it 'validates by default' do
7
+ build(:review).should be_valid
8
+ end
9
+
10
+ it 'validates with a nil user' do
11
+ build(:review, user: nil).should be_valid
12
+ end
13
+
14
+ it 'does not validate with a nil review' do
15
+ build(:review, review: nil).should_not be_valid
16
+ end
17
+
18
+ context 'rating' do
19
+ it 'does not validate when no rating is specified' do
20
+ build(:review, rating: nil).should_not be_valid
21
+ end
22
+
23
+ it 'does not validate when the rating is not a number' do
24
+ build(:review, rating: 'not_a_number').should_not be_valid
25
+ end
26
+
27
+ it 'does not validate when the rating is a float' do
28
+ build(:review, rating: 2.718).should_not be_valid
29
+ end
30
+
31
+ it 'does not validate when the rating is less than 1' do
32
+ build(:review, rating: 0).should_not be_valid
33
+ build(:review, rating: -5).should_not be_valid
34
+ end
35
+
36
+ it 'does not validate when the rating is greater than 5' do
37
+ build(:review, rating: 6).should_not be_valid
38
+ build(:review, rating: 8).should_not be_valid
39
+ end
40
+
41
+ (1..5).each do |i|
42
+ it "validates when the rating is #{i}" do
43
+ build(:review, rating: i).should be_valid
44
+ end
45
+ end
46
+ end
47
+
48
+ context 'review body' do
49
+ it 'should not be valid without a body' do
50
+ build(:review, review: nil).should_not be_valid
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'scopes' do
56
+ context 'most_recent_first' do
57
+ let!(:review_1) { create(:review, created_at: 10.days.ago) }
58
+ let!(:review_2) { create(:review, created_at: 2.days.ago) }
59
+ let!(:review_3) { create(:review, created_at: 5.days.ago) }
60
+
61
+ it 'properly runs most_recent_first queries' do
62
+ Spree::Review.most_recent_first.to_a.should eq([review_2, review_3, review_1])
63
+ end
64
+
65
+ it 'defaults to most_recent_first queries' do
66
+ Spree::Review.all.to_a.should eq([review_2, review_3, review_1])
67
+ end
68
+ end
69
+
70
+ context 'oldest_first' do
71
+ let!(:review_1) { create(:review, created_at: 10.days.ago) }
72
+ let!(:review_2) { create(:review, created_at: 2.days.ago) }
73
+ let!(:review_3) { create(:review, created_at: 5.days.ago) }
74
+ let!(:review_4) { create(:review, created_at: 1.days.ago) }
75
+
76
+ before do
77
+ reset_spree_preferences
78
+ end
79
+
80
+ it 'properly runs oldest_first queries' do
81
+ Spree::Review.oldest_first.to_a.should eq([review_1, review_3, review_2, review_4])
82
+ end
83
+
84
+ it 'uses oldest_first for preview' do
85
+ Spree::Review.preview.to_a.should eq([review_1, review_3])
86
+ end
87
+ end
88
+
89
+ context 'localized' do
90
+ let!(:en_review_1) { create(:review, locale: 'en', created_at: 10.days.ago) }
91
+ let!(:en_review_2) { create(:review, locale: 'en', created_at: 2.days.ago) }
92
+ let!(:en_review_3) { create(:review, locale: 'en', created_at: 5.days.ago) }
93
+
94
+ let!(:es_review_1) { create(:review, locale: 'es', created_at: 10.days.ago) }
95
+ let!(:fr_review_1) { create(:review, locale: 'fr', created_at: 10.days.ago) }
96
+
97
+ it 'properly runs localized queries' do
98
+ Spree::Review.localized('en').to_a.should eq([en_review_2, en_review_3, en_review_1])
99
+ Spree::Review.localized('es').to_a.should eq([es_review_1])
100
+ Spree::Review.localized('fr').to_a.should eq([fr_review_1])
101
+ end
102
+ end
103
+
104
+ context 'approved / not_approved / default_approval_filter' do
105
+ let!(:approved_review_1) { create(:review, approved: true, created_at: 10.days.ago) }
106
+ let!(:approved_review_2) { create(:review, approved: true, created_at: 2.days.ago) }
107
+ let!(:approved_review_3) { create(:review, approved: true, created_at: 5.days.ago) }
108
+
109
+ let!(:unapproved_review_1) { create(:review, approved: false, created_at: 7.days.ago) }
110
+ let!(:unapproved_review_2) { create(:review, approved: false, created_at: 1.days.ago) }
111
+
112
+ it 'properly runs approved and unapproved queries' do
113
+ Spree::Review.approved.to_a.should eq([approved_review_2, approved_review_3, approved_review_1])
114
+ Spree::Review.not_approved.to_a.should eq([unapproved_review_2, unapproved_review_1])
115
+
116
+ Spree::Reviews::Config[:include_unapproved_reviews] = true
117
+ Spree::Review.default_approval_filter.to_a.should eq([unapproved_review_2,
118
+ approved_review_2,
119
+ approved_review_3,
120
+ unapproved_review_1,
121
+ approved_review_1])
122
+
123
+ Spree::Reviews::Config[:include_unapproved_reviews] = false
124
+ Spree::Review.default_approval_filter.to_a.should eq([approved_review_2, approved_review_3, approved_review_1])
125
+ end
126
+ end
127
+ end
128
+
129
+ context "#recalculate_product_rating" do
130
+ let(:product) { create(:product) }
131
+ let!(:review) { create(:review, product: product) }
132
+
133
+ before { product.reviews << review }
134
+
135
+ it "if approved" do
136
+ review.should_receive(:recalculate_product_rating)
137
+ review.approved = true
138
+ review.save!
139
+ end
140
+
141
+ it "if not approved" do
142
+ review.should_not_receive(:recalculate_product_rating)
143
+ review.save!
144
+ end
145
+
146
+ it "updates the product average rating" do
147
+ review.product.should_receive(:recalculate_rating)
148
+ review.approved = true
149
+ review.save!
150
+ end
151
+ end
152
+
153
+ context "#feedback_stars" do
154
+ let!(:review) { create(:review) }
155
+ before do
156
+ 3.times do |i|
157
+ f = Spree::FeedbackReview.new
158
+ f.review = review
159
+ f.rating = (i+1)
160
+ f.save
161
+ end
162
+ end
163
+
164
+ it "should return the average rating from feedback reviews" do
165
+ review.feedback_stars.should == 2
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ require "cancan/matchers"
4
+
5
+ describe Spree::ReviewsAbility do
6
+ context '.allow_anonymous_reviews?' do
7
+ it 'should depend on Spree::Reviews::Config[:require_login]' do
8
+ Spree::Reviews::Config[:require_login] = false
9
+ Spree::ReviewsAbility.allow_anonymous_reviews?.should be true
10
+ Spree::Reviews::Config[:require_login] = true
11
+ Spree::ReviewsAbility.allow_anonymous_reviews?.should be false
12
+ end
13
+ end
14
+
15
+ context 'permissions' do
16
+ let(:user_without_email) { double(:user, email: nil) }
17
+ let(:user_with_email) { double(:user, email: 'a@b.com') }
18
+
19
+ context 'when anonymous reviews are allowed' do
20
+ before do
21
+ Spree::Reviews::Config[:require_login] = false
22
+ end
23
+
24
+ it 'lets anyone create a review or feedback review' do
25
+ [user_without_email, user_with_email].each do |u|
26
+ Spree::ReviewsAbility.new(u).should be_able_to(:create, Spree::Review.new)
27
+ Spree::ReviewsAbility.new(u).should be_able_to(:create, Spree::FeedbackReview.new)
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'when anonymous reviews are not allowed' do
33
+ before do
34
+ Spree::Reviews::Config[:require_login] = true
35
+ end
36
+
37
+ it 'only allows users with an email to create a review or feedback review' do
38
+ Spree::ReviewsAbility.new(user_without_email).should_not be_able_to(:create, Spree::Review.new)
39
+ Spree::ReviewsAbility.new(user_without_email).should_not be_able_to(:create, Spree::FeedbackReview.new)
40
+
41
+ Spree::ReviewsAbility.new(user_with_email).should be_able_to(:create, Spree::Review.new)
42
+ Spree::ReviewsAbility.new(user_with_email).should be_able_to(:create, Spree::FeedbackReview.new)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Spree::ReviewsConfiguration do
4
+ subject { Spree::ReviewsConfiguration.new }
5
+
6
+ before do
7
+ reset_spree_preferences
8
+ end
9
+
10
+ it 'should have the include_unapproved_reviews preference' do
11
+ subject.should respond_to(:preferred_include_unapproved_reviews)
12
+ subject.should respond_to(:preferred_include_unapproved_reviews=)
13
+ subject.preferred_include_unapproved_reviews.should be false
14
+ end
15
+
16
+ it 'should have the preview_size preference' do
17
+ subject.should respond_to(:preferred_preview_size)
18
+ subject.should respond_to(:preferred_preview_size=)
19
+ subject.preferred_preview_size.should eq(2)
20
+ end
21
+
22
+ it 'should have the show_email preference' do
23
+ subject.should respond_to(:preferred_show_email)
24
+ subject.should respond_to(:preferred_show_email=)
25
+ subject.preferred_show_email.should be false
26
+ end
27
+
28
+ it 'should have the feedback_rating preference' do
29
+ subject.should respond_to(:preferred_feedback_rating)
30
+ subject.should respond_to(:preferred_feedback_rating=)
31
+ subject.preferred_feedback_rating.should be false
32
+ end
33
+
34
+ it 'should have the require_login preference' do
35
+ subject.should respond_to(:preferred_require_login)
36
+ subject.should respond_to(:preferred_require_login=)
37
+ subject.preferred_require_login.should be true
38
+ end
39
+
40
+ it 'should have the track_locale preference', pending: 'this leaks from elsewhere, causes it to be true' do
41
+ subject.should respond_to(:preferred_track_locale)
42
+ subject.should respond_to(:preferred_track_locale=)
43
+ subject.preferred_track_locale.should be false
44
+ end
45
+ end
@@ -0,0 +1,55 @@
1
+ require 'simplecov'
2
+ SimpleCov.start 'rails'
3
+
4
+ ENV["RAILS_ENV"] = "test"
5
+
6
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
7
+ require 'rspec/rails'
8
+ require 'ffaker'
9
+ require 'database_cleaner'
10
+ require 'capybara'
11
+ require 'capybara/rspec'
12
+ require 'capybara/rails'
13
+ require 'capybara/poltergeist'
14
+
15
+ Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each {|f| require f }
16
+
17
+ require 'spree/testing_support/factories'
18
+ require 'spree/testing_support/controller_requests'
19
+ require 'spree/testing_support/authorization_helpers'
20
+ require 'spree/testing_support/preferences'
21
+ require 'spree/testing_support/url_helpers'
22
+
23
+ require 'solidus_reviews/factories'
24
+
25
+ FactoryGirl.find_definitions
26
+
27
+ # Bugfix for Solidus < 1.3 using ffaker 1.x
28
+ if defined?(Faker)
29
+ FFaker = Faker
30
+ end
31
+
32
+ RSpec.configure do |config|
33
+ config.include Spree::TestingSupport::ControllerRequests, type: :controller
34
+ config.include Spree::TestingSupport::Preferences
35
+ config.include FactoryGirl::Syntax::Methods
36
+ config.include Spree::TestingSupport::UrlHelpers
37
+ config.infer_spec_type_from_file_location!
38
+ config.use_transactional_fixtures = false
39
+
40
+ config.before(:suite) do
41
+ DatabaseCleaner.strategy = :transaction
42
+ DatabaseCleaner.clean_with(:truncation)
43
+ end
44
+
45
+ config.before do
46
+ DatabaseCleaner.strategy = RSpec.current_example.metadata[:js] ? :truncation : :transaction
47
+ DatabaseCleaner.start
48
+ end
49
+
50
+ config.after do
51
+ DatabaseCleaner.clean
52
+ end
53
+
54
+ Capybara.javascript_driver = :poltergeist
55
+ end
@@ -0,0 +1,376 @@
1
+ /*
2
+ ### jQuery Star Rating Plugin v4.11 - 2013-03-14 ###
3
+ * Home: http://www.fyneworks.com/jquery/star-rating/
4
+ * Code: http://code.google.com/p/jquery-star-rating-plugin/
5
+ *
6
+ * Licensed under http://en.wikipedia.org/wiki/MIT_License
7
+ ###
8
+ */
9
+
10
+ /*# AVOID COLLISIONS #*/
11
+ ;if(window.jQuery) (function($){
12
+ /*# AVOID COLLISIONS #*/
13
+
14
+ // IE6 Background Image Fix
15
+ if ((!$.support.opacity && !$.support.style)) try { document.execCommand("BackgroundImageCache", false, true)} catch(e) { };
16
+ // Thanks to http://www.visualjquery.com/rating/rating_redux.html
17
+
18
+ // plugin initialization
19
+ $.fn.rating = function(options){
20
+ if(this.length==0) return this; // quick fail
21
+
22
+ // Handle API methods
23
+ if(typeof arguments[0]=='string'){
24
+ // Perform API methods on individual elements
25
+ if(this.length>1){
26
+ var args = arguments;
27
+ return this.each(function(){
28
+ $.fn.rating.apply($(this), args);
29
+ });
30
+ };
31
+ // Invoke API method handler
32
+ $.fn.rating[arguments[0]].apply(this, $.makeArray(arguments).slice(1) || []);
33
+ // Quick exit...
34
+ return this;
35
+ };
36
+
37
+ // Initialize options for this call
38
+ var options = $.extend(
39
+ {}/* new object */,
40
+ $.fn.rating.options/* default options */,
41
+ options || {} /* just-in-time options */
42
+ );
43
+
44
+ // Allow multiple controls with the same name by making each call unique
45
+ $.fn.rating.calls++;
46
+
47
+ // loop through each matched element
48
+ this
49
+ .not('.star-rating-applied')
50
+ .addClass('star-rating-applied')
51
+ .each(function(){
52
+
53
+ // Load control parameters / find context / etc
54
+ var control, input = $(this);
55
+ var eid = (this.name || 'unnamed-rating').replace(/\[|\]/g, '_').replace(/^\_+|\_+$/g,'');
56
+ var context = $(this.form || document.body);
57
+
58
+ // FIX: http://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=23
59
+ var raters = context.data('rating');
60
+ if(!raters || raters.call!=$.fn.rating.calls) raters = { count:0, call:$.fn.rating.calls };
61
+ var rater = raters[eid] || context.data('rating'+eid);
62
+
63
+ // if rater is available, verify that the control still exists
64
+ if(rater) control = rater.data('rating');
65
+
66
+ if(rater && control)//{// save a byte!
67
+ // add star to control if rater is available and the same control still exists
68
+ control.count++;
69
+
70
+ //}// save a byte!
71
+ else{
72
+ // create new control if first star or control element was removed/replaced
73
+
74
+ // Initialize options for this rater
75
+ control = $.extend(
76
+ {}/* new object */,
77
+ options || {} /* current call options */,
78
+ ($.metadata? input.metadata(): ($.meta?input.data():null)) || {}, /* metadata options */
79
+ { count:0, stars: [], inputs: [] }
80
+ );
81
+
82
+ // increment number of rating controls
83
+ control.serial = raters.count++;
84
+
85
+ // create rating element
86
+ rater = $('<span class="star-rating-control"/>');
87
+ input.before(rater);
88
+
89
+ // Mark element for initialization (once all stars are ready)
90
+ rater.addClass('rating-to-be-drawn');
91
+
92
+ // Accept readOnly setting from 'disabled' property
93
+ if(input.attr('disabled') || input.hasClass('disabled')) control.readOnly = true;
94
+
95
+ // Accept required setting from class property (class='required')
96
+ if(input.hasClass('required')) control.required = true;
97
+
98
+ // Create 'cancel' button
99
+ rater.append(
100
+ control.cancel = $('<div class="rating-cancel"><a title="' + control.cancel + '">' + control.cancelValue + '</a></div>')
101
+ .on('mouseover',function(){
102
+ $(this).rating('drain');
103
+ $(this).addClass('star-rating-hover');
104
+ //$(this).rating('focus');
105
+ })
106
+ .on('mouseout',function(){
107
+ $(this).rating('draw');
108
+ $(this).removeClass('star-rating-hover');
109
+ //$(this).rating('blur');
110
+ })
111
+ .on('click',function(){
112
+ $(this).rating('select');
113
+ })
114
+ .data('rating', control)
115
+ );
116
+
117
+ }; // first element of group
118
+
119
+ // insert rating star (thanks Jan Fanslau rev125 for blind support https://code.google.com/p/jquery-star-rating-plugin/issues/detail?id=125)
120
+ var star = $('<div role="text" aria-label="'+ this.title +'" class="star-rating rater-'+ control.serial +'"><a title="' + (this.title || this.value) + '">' + this.value + '</a></div>');
121
+ rater.append(star);
122
+
123
+ // inherit attributes from input element
124
+ if(this.id) star.attr('id', this.id);
125
+ if(this.className) star.addClass(this.className);
126
+
127
+ // Half-stars?
128
+ if(control.half) control.split = 2;
129
+
130
+ // Prepare division control
131
+ if(typeof control.split=='number' && control.split>0){
132
+ var stw = ($.fn.width ? star.width() : 0) || control.starWidth;
133
+ var spi = (control.count % control.split), spw = Math.floor(stw/control.split);
134
+ star
135
+ // restrict star's width and hide overflow (already in CSS)
136
+ .width(spw)
137
+ // move the star left by using a negative margin
138
+ // this is work-around to IE's stupid box model (position:relative doesn't work)
139
+ .find('a').css({ 'margin-left':'-'+ (spi*spw) +'px' })
140
+ };
141
+
142
+ // readOnly?
143
+ if(control.readOnly)//{ //save a byte!
144
+ // Mark star as readOnly so user can customize display
145
+ star.addClass('star-rating-readonly');
146
+ //} //save a byte!
147
+ else//{ //save a byte!
148
+ // Enable hover css effects
149
+ star.addClass('star-rating-live')
150
+ // Attach mouse events
151
+ .on('mouseover',function(){
152
+ $(this).rating('fill');
153
+ $(this).rating('focus');
154
+ })
155
+ .on('mouseout',function(){
156
+ $(this).rating('draw');
157
+ $(this).rating('blur');
158
+ })
159
+ .on('click',function(){
160
+ $(this).rating('select');
161
+ })
162
+ ;
163
+ //}; //save a byte!
164
+
165
+ // set current selection
166
+ if(this.checked) control.current = star;
167
+
168
+ // set current select for links
169
+ if(this.nodeName=="A"){
170
+ if($(this).hasClass('selected'))
171
+ control.current = star;
172
+ };
173
+
174
+ // hide input element
175
+ input.hide();
176
+
177
+ // backward compatibility, form element to plugin
178
+ input.on('change.rating',function(event){
179
+ if(event.selfTriggered) return false;
180
+ $(this).rating('select');
181
+ });
182
+
183
+ // attach reference to star to input element and vice-versa
184
+ star.data('rating.input', input.data('rating.star', star));
185
+
186
+ // store control information in form (or body when form not available)
187
+ control.stars[control.stars.length] = star[0];
188
+ control.inputs[control.inputs.length] = input[0];
189
+ control.rater = raters[eid] = rater;
190
+ control.context = context;
191
+
192
+ input.data('rating', control);
193
+ rater.data('rating', control);
194
+ star.data('rating', control);
195
+ context.data('rating', raters);
196
+ context.data('rating'+eid, rater); // required for ajax forms
197
+ }); // each element
198
+
199
+ // Initialize ratings (first draw)
200
+ $('.rating-to-be-drawn').rating('draw').removeClass('rating-to-be-drawn');
201
+
202
+ return this; // don't break the chain...
203
+ };
204
+
205
+ /*--------------------------------------------------------*/
206
+
207
+ /*
208
+ ### Core functionality and API ###
209
+ */
210
+ $.extend($.fn.rating, {
211
+ // Used to append a unique serial number to internal control ID
212
+ // each time the plugin is invoked so same name controls can co-exist
213
+ calls: 0,
214
+
215
+ focus: function(){
216
+ var control = this.data('rating'); if(!control) return this;
217
+ if(!control.focus) return this; // quick fail if not required
218
+ // find data for event
219
+ var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
220
+ // focus handler, as requested by focusdigital.co.uk
221
+ if(control.focus) control.focus.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
222
+ }, // $.fn.rating.focus
223
+
224
+ blur: function(){
225
+ var control = this.data('rating'); if(!control) return this;
226
+ if(!control.blur) return this; // quick fail if not required
227
+ // find data for event
228
+ var input = $(this).data('rating.input') || $( this.tagName=='INPUT' ? this : null );
229
+ // blur handler, as requested by focusdigital.co.uk
230
+ if(control.blur) control.blur.apply(input[0], [input.val(), $('a', input.data('rating.star'))[0]]);
231
+ }, // $.fn.rating.blur
232
+
233
+ fill: function(){ // fill to the current mouse position.
234
+ var control = this.data('rating'); if(!control) return this;
235
+ // do not execute when control is in read-only mode
236
+ if(control.readOnly) return;
237
+ // Reset all stars and highlight them up to this element
238
+ this.rating('drain');
239
+ this.prevAll().addBack().filter('.rater-'+ control.serial).addClass('star-rating-hover');
240
+ },// $.fn.rating.fill
241
+
242
+ drain: function() { // drain all the stars.
243
+ var control = this.data('rating'); if(!control) return this;
244
+ // do not execute when control is in read-only mode
245
+ if(control.readOnly) return;
246
+ // Reset all stars
247
+ control.rater.children().filter('.rater-'+ control.serial).removeClass('star-rating-on').removeClass('star-rating-hover');
248
+ },// $.fn.rating.drain
249
+
250
+ draw: function(){ // set value and stars to reflect current selection
251
+ var control = this.data('rating'); if(!control) return this;
252
+ // Clear all stars
253
+ this.rating('drain');
254
+ // Set control value
255
+ var current = $( control.current );//? control.current.data('rating.input') : null );
256
+ var starson = current.length ? current.prevAll().addBack().filter('.rater-'+ control.serial) : null;
257
+ if(starson) starson.addClass('star-rating-on');
258
+ // Show/hide 'cancel' button
259
+ control.cancel[control.readOnly || control.required?'hide':'show']();
260
+ // Add/remove read-only classes to remove hand pointer
261
+ this.siblings()[control.readOnly?'addClass':'removeClass']('star-rating-readonly');
262
+ },// $.fn.rating.draw
263
+
264
+
265
+
266
+
267
+
268
+ select: function(value,wantCallBack){ // select a value
269
+ var control = this.data('rating'); if(!control) return this;
270
+ // do not execute when control is in read-only mode
271
+ if(control.readOnly) return;
272
+ // clear selection
273
+ control.current = null;
274
+ // programmatically (based on user input)
275
+ if(typeof value!='undefined' || this.length>1){
276
+ // select by index (0 based)
277
+ if(typeof value=='number')
278
+ return $(control.stars[value]).rating('select',undefined,wantCallBack);
279
+ // select by literal value (must be passed as a string
280
+ if(typeof value=='string'){
281
+ //return
282
+ $.each(control.stars, function(){
283
+ //console.log($(this).data('rating.input'), $(this).data('rating.input').val(), value, $(this).data('rating.input').val()==value?'BINGO!':'');
284
+ if($(this).data('rating.input').val()==value) $(this).rating('select',undefined,wantCallBack);
285
+ });
286
+ // don't break the chain
287
+ return this;
288
+ };
289
+ }
290
+ else{
291
+ control.current = this[0].tagName=='INPUT' ?
292
+ this.data('rating.star') :
293
+ (this.is('.rater-'+ control.serial) ? this : null);
294
+ };
295
+ // Update rating control state
296
+ this.data('rating', control);
297
+ // Update display
298
+ this.rating('draw');
299
+ // find current input and its sibblings
300
+ var current = $( control.current ? control.current.data('rating.input') : null );
301
+ var lastipt = $( control.inputs ).filter(':checked');
302
+ var deadipt = $( control.inputs ).not(current);
303
+ // check and uncheck elements as required
304
+ deadipt.prop('checked',false);//.removeAttr('checked');
305
+ current.prop('checked',true);//.attr('checked','checked');
306
+ // trigger change on current or last selected input
307
+ $(current.length? current : lastipt ).trigger({ type:'change', selfTriggered:true });
308
+ // click callback, as requested here: http://plugins.jquery.com/node/1655
309
+ if((wantCallBack || wantCallBack == undefined) && control.callback) control.callback.apply(current[0], [current.val(), $('a', control.current)[0]]);// callback event
310
+ // don't break the chain
311
+ return this;
312
+ },// $.fn.rating.select
313
+
314
+
315
+
316
+
317
+
318
+ readOnly: function(toggle, disable){ // make the control read-only (still submits value)
319
+ var control = this.data('rating'); if(!control) return this;
320
+ // setread-only status
321
+ control.readOnly = toggle || toggle==undefined ? true : false;
322
+ // enable/disable control value submission
323
+ if(disable) $(control.inputs).attr("disabled", "disabled");
324
+ else $(control.inputs).removeAttr("disabled");
325
+ // Update rating control state
326
+ this.data('rating', control);
327
+ // Update display
328
+ this.rating('draw');
329
+ },// $.fn.rating.readOnly
330
+
331
+ disable: function(){ // make read-only and never submit value
332
+ this.rating('readOnly', true, true);
333
+ },// $.fn.rating.disable
334
+
335
+ enable: function(){ // make read/write and submit value
336
+ this.rating('readOnly', false, false);
337
+ }// $.fn.rating.select
338
+
339
+ });
340
+
341
+ /*--------------------------------------------------------*/
342
+
343
+ /*
344
+ ### Default Settings ###
345
+ eg.: You can override default control like this:
346
+ $.fn.rating.options.cancel = 'Clear';
347
+ */
348
+ $.fn.rating.options = { //$.extend($.fn.rating, { options: {
349
+ cancel: 'Cancel Rating', // advisory title for the 'cancel' link
350
+ cancelValue: '', // value to submit when user click the 'cancel' link
351
+ split: 0, // split the star into how many parts?
352
+
353
+ // Width of star image in case the plugin can't work it out. This can happen if
354
+ // the jQuery.dimensions plugin is not available OR the image is hidden at installation
355
+ starWidth: 16//,
356
+
357
+ //NB.: These don't need to be pre-defined (can be undefined/null) so let's save some code!
358
+ //half: false, // just a shortcut to control.split = 2
359
+ //required: false, // disables the 'cancel' button so user can only select one of the specified values
360
+ //readOnly: false, // disable rating plugin interaction/ values cannot be.one('change', //focus: function(){}, // executed when stars are focused
361
+ //blur: function(){}, // executed when stars are focused
362
+ //callback: function(){}, // executed when a star is clicked
363
+ }; //} });
364
+
365
+ /*--------------------------------------------------------*/
366
+
367
+
368
+ // auto-initialize plugin
369
+ $(function(){
370
+ $('input[type=radio].star').rating();
371
+ });
372
+
373
+
374
+ /*# AVOID COLLISIONS #*/
375
+ })(jQuery);
376
+ /*# AVOID COLLISIONS #*/