qalam_merit 4.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +84 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +358 -0
  7. data/RELEASING.md +16 -0
  8. data/Rakefile +44 -0
  9. data/TESTING.txt +12 -0
  10. data/config/locales/en.yml +5 -0
  11. data/lib/merit.rb +95 -0
  12. data/lib/merit/badge_rules_methods.rb +30 -0
  13. data/lib/merit/base_target_finder.rb +31 -0
  14. data/lib/merit/class_methods.rb +41 -0
  15. data/lib/merit/controller_extensions.rb +71 -0
  16. data/lib/merit/generators/active_record/install_generator.rb +40 -0
  17. data/lib/merit/generators/active_record/merit_generator.rb +25 -0
  18. data/lib/merit/generators/active_record/remove_generator.rb +30 -0
  19. data/lib/merit/generators/active_record/templates/add_merit_fields_to_model.erb +7 -0
  20. data/lib/merit/generators/active_record/templates/create_badges_sashes.erb +14 -0
  21. data/lib/merit/generators/active_record/templates/create_merit_actions.erb +16 -0
  22. data/lib/merit/generators/active_record/templates/create_merit_activity_logs.erb +12 -0
  23. data/lib/merit/generators/active_record/templates/create_merit_badges.erb +17 -0
  24. data/lib/merit/generators/active_record/templates/create_sashes.erb +8 -0
  25. data/lib/merit/generators/active_record/templates/create_scores_and_points.erb +16 -0
  26. data/lib/merit/generators/active_record/templates/remove_merit_fields_from_model.erb +7 -0
  27. data/lib/merit/generators/active_record/templates/remove_merit_tables.erb +12 -0
  28. data/lib/merit/generators/install_generator.rb +30 -0
  29. data/lib/merit/generators/merit_generator.rb +33 -0
  30. data/lib/merit/generators/remove_generator.rb +23 -0
  31. data/lib/merit/generators/templates/badge.erb +69 -0
  32. data/lib/merit/generators/templates/merit.erb +18 -0
  33. data/lib/merit/generators/templates/merit_badge_rules.erb +50 -0
  34. data/lib/merit/generators/templates/merit_point_rules.erb +31 -0
  35. data/lib/merit/generators/templates/merit_rank_rules.erb +32 -0
  36. data/lib/merit/generators/templates/qalam_badge_sash.erb +20 -0
  37. data/lib/merit/generators/templates/qalam_sash.erb +14 -0
  38. data/lib/merit/judge.rb +102 -0
  39. data/lib/merit/models/action_concern.rb +50 -0
  40. data/lib/merit/models/active_record/action.rb +11 -0
  41. data/lib/merit/models/active_record/activity_log.rb +11 -0
  42. data/lib/merit/models/active_record/badges_sash.rb +13 -0
  43. data/lib/merit/models/active_record/sash.rb +32 -0
  44. data/lib/merit/models/active_record/score.rb +25 -0
  45. data/lib/merit/models/badges_sash_concern.rb +13 -0
  46. data/lib/merit/models/base/badges_sash.rb +15 -0
  47. data/lib/merit/models/base/sash.rb +55 -0
  48. data/lib/merit/models/sash_concern.rb +53 -0
  49. data/lib/merit/point_rules_methods.rb +33 -0
  50. data/lib/merit/rank_rules_methods.rb +58 -0
  51. data/lib/merit/reputation_change_observer.rb +19 -0
  52. data/lib/merit/rule.rb +35 -0
  53. data/lib/merit/rules_matcher.rb +24 -0
  54. data/lib/merit/sash_finder.rb +15 -0
  55. data/lib/merit/target_finder.rb +43 -0
  56. data/qalam_merit.gemspec +23 -0
  57. data/test/dummy/Rakefile +7 -0
  58. data/test/dummy/app/controllers/admin/users_controller.rb +9 -0
  59. data/test/dummy/app/controllers/api/comments_controller.rb +5 -0
  60. data/test/dummy/app/controllers/api/users_controller.rb +5 -0
  61. data/test/dummy/app/controllers/application_controller.rb +7 -0
  62. data/test/dummy/app/controllers/comments_controller.rb +56 -0
  63. data/test/dummy/app/controllers/registrations_controller.rb +21 -0
  64. data/test/dummy/app/controllers/users_controller.rb +38 -0
  65. data/test/dummy/app/helpers/application_helper.rb +2 -0
  66. data/test/dummy/app/models/address.rb +3 -0
  67. data/test/dummy/app/models/comment.rb +13 -0
  68. data/test/dummy/app/models/dummy_observer.rb +3 -0
  69. data/test/dummy/app/models/merit/badge_rules.rb +66 -0
  70. data/test/dummy/app/models/merit/point_rules.rb +44 -0
  71. data/test/dummy/app/models/merit/rank_rules.rb +24 -0
  72. data/test/dummy/app/models/user.rb +23 -0
  73. data/test/dummy/app/views/admin/users/index.html.erb +26 -0
  74. data/test/dummy/app/views/comments/_form.html.erb +29 -0
  75. data/test/dummy/app/views/comments/edit.html.erb +6 -0
  76. data/test/dummy/app/views/comments/index.html.erb +35 -0
  77. data/test/dummy/app/views/comments/new.html.erb +5 -0
  78. data/test/dummy/app/views/comments/show.html.erb +23 -0
  79. data/test/dummy/app/views/layouts/application.html.erb +24 -0
  80. data/test/dummy/app/views/users/_form.html.erb +22 -0
  81. data/test/dummy/app/views/users/edit.html.erb +6 -0
  82. data/test/dummy/app/views/users/index.html.erb +27 -0
  83. data/test/dummy/app/views/users/new.html.erb +5 -0
  84. data/test/dummy/app/views/users/show.html.erb +18 -0
  85. data/test/dummy/config.ru +4 -0
  86. data/test/dummy/config/application.rb +26 -0
  87. data/test/dummy/config/application_api_only.rb +28 -0
  88. data/test/dummy/config/boot.rb +10 -0
  89. data/test/dummy/config/database.yml +22 -0
  90. data/test/dummy/config/environment.rb +5 -0
  91. data/test/dummy/config/environment_api_only.rb +7 -0
  92. data/test/dummy/config/environments/development.rb +24 -0
  93. data/test/dummy/config/environments/production.rb +51 -0
  94. data/test/dummy/config/environments/test.rb +36 -0
  95. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  96. data/test/dummy/config/initializers/inflections.rb +10 -0
  97. data/test/dummy/config/initializers/merit.rb +47 -0
  98. data/test/dummy/config/initializers/mime_types.rb +5 -0
  99. data/test/dummy/config/initializers/new_framework_defaults.rb +3 -0
  100. data/test/dummy/config/initializers/secret_token.rb +12 -0
  101. data/test/dummy/config/initializers/session_store.rb +8 -0
  102. data/test/dummy/config/locales/en.yml +5 -0
  103. data/test/dummy/config/mongoid.yml +13 -0
  104. data/test/dummy/config/routes.rb +16 -0
  105. data/test/dummy/db/migrate/20110421191249_create_users.rb +12 -0
  106. data/test/dummy/db/migrate/20110421191250_create_comments.rb +16 -0
  107. data/test/dummy/db/migrate/20120318022220_add_fields_to_users.rb +11 -0
  108. data/test/dummy/db/migrate/20130321082817_add_fields_to_comments.rb +11 -0
  109. data/test/dummy/db/migrate/20130329224406_create_merit_actions.rb +18 -0
  110. data/test/dummy/db/migrate/20130329224407_create_merit_activity_logs.rb +15 -0
  111. data/test/dummy/db/migrate/20130329224408_create_sashes.rb +11 -0
  112. data/test/dummy/db/migrate/20130329224409_create_badges_sashes.rb +16 -0
  113. data/test/dummy/db/migrate/20130329224410_create_scores_and_points.rb +20 -0
  114. data/test/dummy/db/migrate/20140211144001_create_addresses.rb +11 -0
  115. data/test/dummy/db/migrate/20140819133931_add_target_data_to_merit_actions.rb +5 -0
  116. data/test/dummy/db/schema.rb +89 -0
  117. data/test/dummy/db/seeds.rb +19 -0
  118. data/test/dummy/public/404.html +26 -0
  119. data/test/dummy/public/422.html +26 -0
  120. data/test/dummy/public/500.html +26 -0
  121. data/test/dummy/public/favicon.ico +0 -0
  122. data/test/dummy/public/rails.js +404 -0
  123. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  124. data/test/dummy/public/stylesheets/scaffold.css +56 -0
  125. data/test/dummy/script/rails +6 -0
  126. data/test/integration/navigation_test.rb +332 -0
  127. data/test/support/integration_case.rb +5 -0
  128. data/test/test_helper.rb +40 -0
  129. data/test/unit/action_test.rb +12 -0
  130. data/test/unit/base_target_finder_test.rb +64 -0
  131. data/test/unit/merit_unit_test.rb +33 -0
  132. data/test/unit/rule_unit_test.rb +57 -0
  133. data/test/unit/rules_matcher_test.rb +37 -0
  134. data/test/unit/sash_finder_test.rb +27 -0
  135. data/test/unit/sash_test.rb +104 -0
  136. data/test/unit/score_test.rb +13 -0
  137. data/test/unit/target_finder_test.rb +98 -0
  138. metadata +360 -0
@@ -0,0 +1,89 @@
1
+ # This file is auto-generated from the current state of the database. Instead
2
+ # of editing this file, please use the migrations feature of Active Record to
3
+ # incrementally modify your database, and then regenerate this schema definition.
4
+ #
5
+ # This file is the source Rails uses to define your schema when running `rails
6
+ # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
7
+ # be faster and is potentially less error prone than running all of your
8
+ # migrations from scratch. Old migrations may fail to apply correctly if those
9
+ # migrations use external dependencies or application code.
10
+ #
11
+ # It's strongly recommended that you check this file into your version control system.
12
+
13
+ ActiveRecord::Schema.define(version: 2014_08_19_133931) do
14
+
15
+ create_table "addresses", force: :cascade do |t|
16
+ t.integer "user_id"
17
+ t.index ["user_id"], name: "index_addresses_on_user_id"
18
+ end
19
+
20
+ create_table "badges_sashes", force: :cascade do |t|
21
+ t.integer "badge_id"
22
+ t.integer "sash_id"
23
+ t.boolean "notified_user", default: false
24
+ t.datetime "created_at"
25
+ t.index ["badge_id", "sash_id"], name: "index_badges_sashes_on_badge_id_and_sash_id"
26
+ t.index ["badge_id"], name: "index_badges_sashes_on_badge_id"
27
+ t.index ["sash_id"], name: "index_badges_sashes_on_sash_id"
28
+ end
29
+
30
+ create_table "comments", force: :cascade do |t|
31
+ t.string "name"
32
+ t.text "comment"
33
+ t.integer "user_id"
34
+ t.integer "votes", default: 0
35
+ t.datetime "created_at", null: false
36
+ t.datetime "updated_at", null: false
37
+ t.integer "sash_id"
38
+ t.integer "level", default: 0
39
+ end
40
+
41
+ create_table "merit_actions", force: :cascade do |t|
42
+ t.integer "user_id"
43
+ t.string "action_method"
44
+ t.integer "action_value"
45
+ t.boolean "had_errors", default: false
46
+ t.string "target_model"
47
+ t.integer "target_id"
48
+ t.boolean "processed", default: false
49
+ t.datetime "created_at", null: false
50
+ t.datetime "updated_at", null: false
51
+ t.text "target_data"
52
+ end
53
+
54
+ create_table "merit_activity_logs", force: :cascade do |t|
55
+ t.integer "action_id"
56
+ t.string "related_change_type"
57
+ t.integer "related_change_id"
58
+ t.string "description"
59
+ t.datetime "created_at"
60
+ end
61
+
62
+ create_table "merit_score_points", force: :cascade do |t|
63
+ t.integer "score_id"
64
+ t.integer "num_points", default: 0
65
+ t.string "log"
66
+ t.datetime "created_at"
67
+ t.index ["score_id"], name: "index_merit_score_points_on_score_id"
68
+ end
69
+
70
+ create_table "merit_scores", force: :cascade do |t|
71
+ t.integer "sash_id"
72
+ t.string "category", default: "default"
73
+ t.index ["sash_id"], name: "index_merit_scores_on_sash_id"
74
+ end
75
+
76
+ create_table "sashes", force: :cascade do |t|
77
+ t.datetime "created_at", null: false
78
+ t.datetime "updated_at", null: false
79
+ end
80
+
81
+ create_table "users", force: :cascade do |t|
82
+ t.string "name"
83
+ t.datetime "created_at", null: false
84
+ t.datetime "updated_at", null: false
85
+ t.integer "sash_id"
86
+ t.integer "level", default: 0
87
+ end
88
+
89
+ end
@@ -0,0 +1,19 @@
1
+ commenter = User.create(:name => 'the-commenter-guy')
2
+ social = User.create(:name => 'social-skilled-man')
3
+
4
+ User.create(:name => 'bored-or-speechless')
5
+ User.create(:name => 'friend')
6
+
7
+ (1..9).each do |i|
8
+ Comment.create(
9
+ :name => "Title #{i}",
10
+ :comment => "Comment #{i}",
11
+ :user_id => social.id,
12
+ :votes => 3
13
+ )
14
+ Comment.create(
15
+ :name => "Title #{i}",
16
+ :comment => "Comment #{i}",
17
+ :user_id => commenter.id
18
+ )
19
+ end
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ <p>We've been notified about this issue and we'll take a look at it shortly.</p>
24
+ </div>
25
+ </body>
26
+ </html>
File without changes
@@ -0,0 +1,404 @@
1
+ (function($, undefined) {
2
+
3
+ /**
4
+ * Unobtrusive scripting adapter for jQuery
5
+ * https://github.com/rails/jquery-ujs
6
+ *
7
+ * Requires jQuery 1.7.0 or later.
8
+ *
9
+ * Released under the MIT license
10
+ *
11
+ */
12
+
13
+ // Cut down on the number of issues from people inadvertently including jquery_ujs twice
14
+ // by detecting and raising an error when it happens.
15
+ if ( $.rails !== undefined ) {
16
+ $.error('jquery-ujs has already been loaded!');
17
+ }
18
+
19
+ // Shorthand to make it a little easier to call public rails functions from within rails.js
20
+ var rails;
21
+ var $document = $(document);
22
+
23
+ $.rails = rails = {
24
+ // Link elements bound by jquery-ujs
25
+ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
26
+
27
+ // Button elements bound by jquery-ujs
28
+ buttonClickSelector: 'button[data-remote], button[data-confirm]',
29
+
30
+ // Select elements bound by jquery-ujs
31
+ inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
32
+
33
+ // Form elements bound by jquery-ujs
34
+ formSubmitSelector: 'form',
35
+
36
+ // Form input elements bound by jquery-ujs
37
+ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
38
+
39
+ // Form input elements disabled during form submission
40
+ disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
41
+
42
+ // Form input elements re-enabled after form submission
43
+ enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
44
+
45
+ // Form required input elements
46
+ requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
47
+
48
+ // Form file input elements
49
+ fileInputSelector: 'input[type=file]',
50
+
51
+ // Link onClick disable selector with possible reenable after remote submission
52
+ linkDisableSelector: 'a[data-disable-with]',
53
+
54
+ // Make sure that every Ajax request sends the CSRF token
55
+ CSRFProtection: function(xhr) {
56
+ var token = $('meta[name="csrf-token"]').attr('content');
57
+ if (token) xhr.setRequestHeader('X-CSRF-Token', token);
58
+ },
59
+
60
+ // making sure that all forms have actual up-to-date token(cached forms contain old one)
61
+ refreshCSRFTokens: function(){
62
+ var csrfToken = $('meta[name=csrf-token]').attr('content');
63
+ var csrfParam = $('meta[name=csrf-param]').attr('content');
64
+ $('form input[name="' + csrfParam + '"]').val(csrfToken);
65
+ },
66
+
67
+ // Triggers an event on an element and returns false if the event result is false
68
+ fire: function(obj, name, data) {
69
+ var event = $.Event(name);
70
+ obj.trigger(event, data);
71
+ return event.result !== false;
72
+ },
73
+
74
+ // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
75
+ confirm: function(message) {
76
+ return confirm(message);
77
+ },
78
+
79
+ // Default ajax function, may be overridden with custom function in $.rails.ajax
80
+ ajax: function(options) {
81
+ return $.ajax(options);
82
+ },
83
+
84
+ // Default way to get an element's href. May be overridden at $.rails.href.
85
+ href: function(element) {
86
+ return element.attr('href');
87
+ },
88
+
89
+ // Submits "remote" forms and links with ajax
90
+ handleRemote: function(element) {
91
+ var method, url, data, elCrossDomain, crossDomain, withCredentials, dataType, options;
92
+
93
+ if (rails.fire(element, 'ajax:before')) {
94
+ elCrossDomain = element.data('cross-domain');
95
+ crossDomain = elCrossDomain === undefined ? null : elCrossDomain;
96
+ withCredentials = element.data('with-credentials') || null;
97
+ dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType);
98
+
99
+ if (element.is('form')) {
100
+ method = element.attr('method');
101
+ url = element.attr('action');
102
+ data = element.serializeArray();
103
+ // memoized value from clicked submit button
104
+ var button = element.data('ujs:submit-button');
105
+ if (button) {
106
+ data.push(button);
107
+ element.data('ujs:submit-button', null);
108
+ }
109
+ } else if (element.is(rails.inputChangeSelector)) {
110
+ method = element.data('method');
111
+ url = element.data('url');
112
+ data = element.serialize();
113
+ if (element.data('params')) data = data + "&" + element.data('params');
114
+ } else if (element.is(rails.buttonClickSelector)) {
115
+ method = element.data('method') || 'get';
116
+ url = element.data('url');
117
+ data = element.serialize();
118
+ if (element.data('params')) data = data + "&" + element.data('params');
119
+ } else {
120
+ method = element.data('method');
121
+ url = rails.href(element);
122
+ data = element.data('params') || null;
123
+ }
124
+
125
+ options = {
126
+ type: method || 'GET', data: data, dataType: dataType,
127
+ // stopping the "ajax:beforeSend" event will cancel the ajax request
128
+ beforeSend: function(xhr, settings) {
129
+ if (settings.dataType === undefined) {
130
+ xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
131
+ }
132
+ if (rails.fire(element, 'ajax:beforeSend', [xhr, settings])) {
133
+ element.trigger('ajax:send', xhr);
134
+ } else {
135
+ return false;
136
+ }
137
+ },
138
+ success: function(data, status, xhr) {
139
+ element.trigger('ajax:success', [data, status, xhr]);
140
+ },
141
+ complete: function(xhr, status) {
142
+ element.trigger('ajax:complete', [xhr, status]);
143
+ },
144
+ error: function(xhr, status, error) {
145
+ element.trigger('ajax:error', [xhr, status, error]);
146
+ },
147
+ crossDomain: crossDomain
148
+ };
149
+
150
+ // There is no withCredentials for IE6-8 when
151
+ // "Enable native XMLHTTP support" is disabled
152
+ if (withCredentials) {
153
+ options.xhrFields = {
154
+ withCredentials: withCredentials
155
+ };
156
+ }
157
+
158
+ // Only pass url to `ajax` options if not blank
159
+ if (url) { options.url = url; }
160
+
161
+ return rails.ajax(options);
162
+ } else {
163
+ return false;
164
+ }
165
+ },
166
+
167
+ // Handles "data-method" on links such as:
168
+ // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
169
+ handleMethod: function(link) {
170
+ var href = rails.href(link),
171
+ method = link.data('method'),
172
+ target = link.attr('target'),
173
+ csrfToken = $('meta[name=csrf-token]').attr('content'),
174
+ csrfParam = $('meta[name=csrf-param]').attr('content'),
175
+ form = $('<form method="post" action="' + href + '"></form>'),
176
+ metadataInput = '<input name="_method" value="' + method + '" type="hidden" />';
177
+
178
+ if (csrfParam !== undefined && csrfToken !== undefined) {
179
+ metadataInput += '<input name="' + csrfParam + '" value="' + csrfToken + '" type="hidden" />';
180
+ }
181
+
182
+ if (target) { form.attr('target', target); }
183
+
184
+ form.hide().append(metadataInput).appendTo('body');
185
+ form.submit();
186
+ },
187
+
188
+ /* Disables form elements:
189
+ - Caches element value in 'ujs:enable-with' data store
190
+ - Replaces element text with value of 'data-disable-with' attribute
191
+ - Sets disabled property to true
192
+ */
193
+ disableFormElements: function(form) {
194
+ form.find(rails.disableSelector).each(function() {
195
+ var element = $(this), method = element.is('button') ? 'html' : 'val';
196
+ element.data('ujs:enable-with', element[method]());
197
+ element[method](element.data('disable-with'));
198
+ element.prop('disabled', true);
199
+ });
200
+ },
201
+
202
+ /* Re-enables disabled form elements:
203
+ - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
204
+ - Sets disabled property to false
205
+ */
206
+ enableFormElements: function(form) {
207
+ form.find(rails.enableSelector).each(function() {
208
+ var element = $(this), method = element.is('button') ? 'html' : 'val';
209
+ if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
210
+ element.prop('disabled', false);
211
+ });
212
+ },
213
+
214
+ /* For 'data-confirm' attribute:
215
+ - Fires `confirm` event
216
+ - Shows the confirmation dialog
217
+ - Fires the `confirm:complete` event
218
+
219
+ Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
220
+ Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
221
+ Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
222
+ return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
223
+ */
224
+ allowAction: function(element) {
225
+ var message = element.data('confirm'),
226
+ answer = false, callback;
227
+ if (!message) { return true; }
228
+
229
+ if (rails.fire(element, 'confirm')) {
230
+ answer = rails.confirm(message);
231
+ callback = rails.fire(element, 'confirm:complete', [answer]);
232
+ }
233
+ return answer && callback;
234
+ },
235
+
236
+ // Helper function which checks for blank inputs in a form that match the specified CSS selector
237
+ blankInputs: function(form, specifiedSelector, nonBlank) {
238
+ var inputs = $(), input, valueToCheck,
239
+ selector = specifiedSelector || 'input,textarea',
240
+ allInputs = form.find(selector);
241
+
242
+ allInputs.each(function() {
243
+ input = $(this);
244
+ valueToCheck = input.is('input[type=checkbox],input[type=radio]') ? input.is(':checked') : input.val();
245
+ // If nonBlank and valueToCheck are both truthy, or nonBlank and valueToCheck are both falsey
246
+ if (!valueToCheck === !nonBlank) {
247
+
248
+ // Don't count unchecked required radio if other radio with same name is checked
249
+ if (input.is('input[type=radio]') && allInputs.filter('input[type=radio]:checked[name="' + input.attr('name') + '"]').length) {
250
+ return true; // Skip to next input
251
+ }
252
+
253
+ inputs = inputs.add(input);
254
+ }
255
+ });
256
+ return inputs.length ? inputs : false;
257
+ },
258
+
259
+ // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
260
+ nonBlankInputs: function(form, specifiedSelector) {
261
+ return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
262
+ },
263
+
264
+ // Helper function, needed to provide consistent behavior in IE
265
+ stopEverything: function(e) {
266
+ $(e.target).trigger('ujs:everythingStopped');
267
+ e.stopImmediatePropagation();
268
+ return false;
269
+ },
270
+
271
+ // replace element's html with the 'data-disable-with' after storing original html
272
+ // and prevent clicking on it
273
+ disableElement: function(element) {
274
+ element.data('ujs:enable-with', element.html()); // store enabled state
275
+ element.html(element.data('disable-with')); // set to disabled state
276
+ element.bind('click.railsDisable', function(e) { // prevent further clicking
277
+ return rails.stopEverything(e);
278
+ });
279
+ },
280
+
281
+ // restore element to its original state which was disabled by 'disableElement' above
282
+ enableElement: function(element) {
283
+ if (element.data('ujs:enable-with') !== undefined) {
284
+ element.html(element.data('ujs:enable-with')); // set to old enabled state
285
+ element.removeData('ujs:enable-with'); // clean up cache
286
+ }
287
+ element.unbind('click.railsDisable'); // enable element
288
+ }
289
+
290
+ };
291
+
292
+ if (rails.fire($document, 'rails:attachBindings')) {
293
+
294
+ $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
295
+
296
+ $document.delegate(rails.linkDisableSelector, 'ajax:complete', function() {
297
+ rails.enableElement($(this));
298
+ });
299
+
300
+ $document.delegate(rails.linkClickSelector, 'click.rails', function(e) {
301
+ var link = $(this), method = link.data('method'), data = link.data('params'), metaClick = e.metaKey || e.ctrlKey;
302
+ if (!rails.allowAction(link)) return rails.stopEverything(e);
303
+
304
+ if (!metaClick && link.is(rails.linkDisableSelector)) rails.disableElement(link);
305
+
306
+ if (link.data('remote') !== undefined) {
307
+ if (metaClick && (!method || method === 'GET') && !data) { return true; }
308
+
309
+ var handleRemote = rails.handleRemote(link);
310
+ // response from rails.handleRemote() will either be false or a deferred object promise.
311
+ if (handleRemote === false) {
312
+ rails.enableElement(link);
313
+ } else {
314
+ handleRemote.error( function() { rails.enableElement(link); } );
315
+ }
316
+ return false;
317
+
318
+ } else if (link.data('method')) {
319
+ rails.handleMethod(link);
320
+ return false;
321
+ }
322
+ });
323
+
324
+ $document.delegate(rails.buttonClickSelector, 'click.rails', function(e) {
325
+ var button = $(this);
326
+ if (!rails.allowAction(button)) return rails.stopEverything(e);
327
+
328
+ rails.handleRemote(button);
329
+ return false;
330
+ });
331
+
332
+ $document.delegate(rails.inputChangeSelector, 'change.rails', function(e) {
333
+ var link = $(this);
334
+ if (!rails.allowAction(link)) return rails.stopEverything(e);
335
+
336
+ rails.handleRemote(link);
337
+ return false;
338
+ });
339
+
340
+ $document.delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
341
+ var form = $(this),
342
+ remote = form.data('remote') !== undefined,
343
+ blankRequiredInputs,
344
+ nonBlankFileInputs;
345
+
346
+ if (!rails.allowAction(form)) return rails.stopEverything(e);
347
+
348
+ // skip other logic when required values are missing or file upload is present
349
+ if (form.attr('novalidate') == undefined) {
350
+ blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector);
351
+ if (blankRequiredInputs && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
352
+ return rails.stopEverything(e);
353
+ }
354
+ }
355
+
356
+ if (remote) {
357
+ nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
358
+ if (nonBlankFileInputs) {
359
+ // slight timeout so that the submit button gets properly serialized
360
+ // (make it easy for event handler to serialize form without disabled values)
361
+ setTimeout(function(){ rails.disableFormElements(form); }, 13);
362
+ var aborted = rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
363
+
364
+ // re-enable form elements if event bindings return false (canceling normal form submission)
365
+ if (!aborted) { setTimeout(function(){ rails.enableFormElements(form); }, 13); }
366
+
367
+ return aborted;
368
+ }
369
+
370
+ rails.handleRemote(form);
371
+ return false;
372
+
373
+ } else {
374
+ // slight timeout so that the submit button gets properly serialized
375
+ setTimeout(function(){ rails.disableFormElements(form); }, 13);
376
+ }
377
+ });
378
+
379
+ $document.delegate(rails.formInputClickSelector, 'click.rails', function(event) {
380
+ var button = $(this);
381
+
382
+ if (!rails.allowAction(button)) return rails.stopEverything(event);
383
+
384
+ // register the pressed submit button
385
+ var name = button.attr('name'),
386
+ data = name ? {name:name, value:button.val()} : null;
387
+
388
+ button.closest('form').data('ujs:submit-button', data);
389
+ });
390
+
391
+ $document.delegate(rails.formSubmitSelector, 'ajax:send.rails', function(event) {
392
+ if (this == event.target) rails.disableFormElements($(this));
393
+ });
394
+
395
+ $document.delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
396
+ if (this == event.target) rails.enableFormElements($(this));
397
+ });
398
+
399
+ $(function(){
400
+ rails.refreshCSRFTokens();
401
+ });
402
+ }
403
+
404
+ })( jQuery );