qalam_merit 4.0.4

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 (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 );