solidus_reviews 1.0.0

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