Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

enable delete link

  • Loading branch information...
commit a6bb07b1f39c0feaabb5ae513f40a15be7ed953c 1 parent 405353a
D. R. Henner authored
2  Gemfile
@@ -14,7 +14,7 @@ gem "jquery-rails"
14 14 # your gem to rubygems.org.
15 15
16 16 # To use debugger
17   -gem 'debugger'
  17 +# gem 'debugger'
18 18 group :development do
19 19 #gem 'mysql2'#, '~> 0.3.7'
20 20 gem "nifty-generators", :git => 'git://github.com/drhenner/nifty-generators.git'
4 app/assets/javascripts/validation_issues.js
... ... @@ -1,2 +1,6 @@
1 1 // Place all the behaviors and hooks related to the matching controller here.
2 2 // All this logic will automatically be available in application.js.
  3 +
  4 +//= require jquery
  5 +//= require jquery_ujs
  6 +//= require_tree validation_issues
373 app/assets/javascripts/validation_issues/rails.js
... ... @@ -0,0 +1,373 @@
  1 +(function($, undefined) {
  2 +
  3 +/**
  4 + * Unobtrusive scripting adapter for jQuery
  5 + *
  6 + * Requires jQuery 1.6.0 or later.
  7 + * https://github.com/rails/jquery-ujs
  8 +
  9 + * Uploading file using rails.js
  10 + * =============================
  11 + *
  12 + * By default, browsers do not allow files to be uploaded via AJAX. As a result, if there are any non-blank file fields
  13 + * in the remote form, this adapter aborts the AJAX submission and allows the form to submit through standard means.
  14 + *
  15 + * The `ajax:aborted:file` event allows you to bind your own handler to process the form submission however you wish.
  16 + *
  17 + * Ex:
  18 + * $('form').live('ajax:aborted:file', function(event, elements){
  19 + * // Implement own remote file-transfer handler here for non-blank file inputs passed in `elements`.
  20 + * // Returning false in this handler tells rails.js to disallow standard form submission
  21 + * return false;
  22 + * });
  23 + *
  24 + * The `ajax:aborted:file` event is fired when a file-type input is detected with a non-blank value.
  25 + *
  26 + * Third-party tools can use this hook to detect when an AJAX file upload is attempted, and then use
  27 + * techniques like the iframe method to upload the file instead.
  28 + *
  29 + * Required fields in rails.js
  30 + * ===========================
  31 + *
  32 + * If any blank required inputs (required="required") are detected in the remote form, the whole form submission
  33 + * is canceled. Note that this is unlike file inputs, which still allow standard (non-AJAX) form submission.
  34 + *
  35 + * The `ajax:aborted:required` event allows you to bind your own handler to inform the user of blank required inputs.
  36 + *
  37 + * !! Note that Opera does not fire the form's submit event if there are blank required inputs, so this event may never
  38 + * get fired in Opera. This event is what causes other browsers to exhibit the same submit-aborting behavior.
  39 + *
  40 + * Ex:
  41 + * $('form').live('ajax:aborted:required', function(event, elements){
  42 + * // Returning false in this handler tells rails.js to submit the form anyway.
  43 + * // The blank required inputs are passed to this function in `elements`.
  44 + * return ! confirm("Would you like to submit the form with missing info?");
  45 + * });
  46 + */
  47 +
  48 + // Shorthand to make it a little easier to call public rails functions from within rails.js
  49 + var rails;
  50 +
  51 + $.rails = rails = {
  52 + // Link elements bound by jquery-ujs
  53 + linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
  54 +
  55 + // Select elements bound by jquery-ujs
  56 + inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
  57 +
  58 + // Form elements bound by jquery-ujs
  59 + formSubmitSelector: 'form',
  60 +
  61 + // Form input elements bound by jquery-ujs
  62 + formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not(button[type])',
  63 +
  64 + // Form input elements disabled during form submission
  65 + disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
  66 +
  67 + // Form input elements re-enabled after form submission
  68 + enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
  69 +
  70 + // Form required input elements
  71 + requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
  72 +
  73 + // Form file input elements
  74 + fileInputSelector: 'input:file',
  75 +
  76 + // Link onClick disable selector with possible reenable after remote submission
  77 + linkDisableSelector: 'a[data-disable-with]',
  78 +
  79 + // Make sure that every Ajax request sends the CSRF token
  80 + CSRFProtection: function(xhr) {
  81 + var token = $('meta[name="csrf-token"]').attr('content');
  82 + if (token) xhr.setRequestHeader('X-CSRF-Token', token);
  83 + },
  84 +
  85 + // Triggers an event on an element and returns false if the event result is false
  86 + fire: function(obj, name, data) {
  87 + var event = $.Event(name);
  88 + obj.trigger(event, data);
  89 + return event.result !== false;
  90 + },
  91 +
  92 + // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
  93 + confirm: function(message) {
  94 + return confirm(message);
  95 + },
  96 +
  97 + // Default ajax function, may be overridden with custom function in $.rails.ajax
  98 + ajax: function(options) {
  99 + return $.ajax(options);
  100 + },
  101 +
  102 + // Submits "remote" forms and links with ajax
  103 + handleRemote: function(element) {
  104 + var method, url, data,
  105 + crossDomain = element.data('cross-domain') || null,
  106 + dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType),
  107 + options;
  108 +
  109 + if (rails.fire(element, 'ajax:before')) {
  110 +
  111 + if (element.is('form')) {
  112 + method = element.attr('method');
  113 + url = element.attr('action');
  114 + data = element.serializeArray();
  115 + // memoized value from clicked submit button
  116 + var button = element.data('ujs:submit-button');
  117 + if (button) {
  118 + data.push(button);
  119 + element.data('ujs:submit-button', null);
  120 + }
  121 + } else if (element.is(rails.inputChangeSelector)) {
  122 + method = element.data('method');
  123 + url = element.data('url');
  124 + data = element.serialize();
  125 + if (element.data('params')) data = data + "&" + element.data('params');
  126 + } else {
  127 + method = element.data('method');
  128 + url = element.attr('href');
  129 + data = element.data('params') || null;
  130 + }
  131 +
  132 + options = {
  133 + type: method || 'GET', data: data, dataType: dataType, crossDomain: crossDomain,
  134 + // stopping the "ajax:beforeSend" event will cancel the ajax request
  135 + beforeSend: function(xhr, settings) {
  136 + if (settings.dataType === undefined) {
  137 + xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
  138 + }
  139 + return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
  140 + },
  141 + success: function(data, status, xhr) {
  142 + element.trigger('ajax:success', [data, status, xhr]);
  143 + },
  144 + complete: function(xhr, status) {
  145 + element.trigger('ajax:complete', [xhr, status]);
  146 + },
  147 + error: function(xhr, status, error) {
  148 + element.trigger('ajax:error', [xhr, status, error]);
  149 + }
  150 + };
  151 + // Only pass url to `ajax` options if not blank
  152 + if (url) { options.url = url; }
  153 +
  154 + return rails.ajax(options);
  155 + } else {
  156 + return false;
  157 + }
  158 + },
  159 +
  160 + // Handles "data-method" on links such as:
  161 + // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
  162 + handleMethod: function(link) {
  163 + var href = link.attr('href'),
  164 + method = link.data('method'),
  165 + target = link.attr('target'),
  166 + csrf_token = $('meta[name=csrf-token]').attr('content'),
  167 + csrf_param = $('meta[name=csrf-param]').attr('content'),
  168 + form = $('<form method="post" action="' + href + '"></form>'),
  169 + metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
  170 +
  171 + if (csrf_param !== undefined && csrf_token !== undefined) {
  172 + metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
  173 + }
  174 +
  175 + if (target) { form.attr('target', target); }
  176 +
  177 + form.hide().append(metadata_input).appendTo('body');
  178 + form.submit();
  179 + },
  180 +
  181 + /* Disables form elements:
  182 + - Caches element value in 'ujs:enable-with' data store
  183 + - Replaces element text with value of 'data-disable-with' attribute
  184 + - Sets disabled property to true
  185 + */
  186 + disableFormElements: function(form) {
  187 + form.find(rails.disableSelector).each(function() {
  188 + var element = $(this), method = element.is('button') ? 'html' : 'val';
  189 + element.data('ujs:enable-with', element[method]());
  190 + element[method](element.data('disable-with'));
  191 + element.prop('disabled', true);
  192 + });
  193 + },
  194 +
  195 + /* Re-enables disabled form elements:
  196 + - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
  197 + - Sets disabled property to false
  198 + */
  199 + enableFormElements: function(form) {
  200 + form.find(rails.enableSelector).each(function() {
  201 + var element = $(this), method = element.is('button') ? 'html' : 'val';
  202 + if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
  203 + element.prop('disabled', false);
  204 + });
  205 + },
  206 +
  207 + /* For 'data-confirm' attribute:
  208 + - Fires `confirm` event
  209 + - Shows the confirmation dialog
  210 + - Fires the `confirm:complete` event
  211 +
  212 + Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
  213 + Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
  214 + Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
  215 + return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
  216 + */
  217 + allowAction: function(element) {
  218 + var message = element.data('confirm'),
  219 + answer = false, callback;
  220 + if (!message) { return true; }
  221 +
  222 + if (rails.fire(element, 'confirm')) {
  223 + answer = rails.confirm(message);
  224 + callback = rails.fire(element, 'confirm:complete', [answer]);
  225 + }
  226 + return answer && callback;
  227 + },
  228 +
  229 + // Helper function which checks for blank inputs in a form that match the specified CSS selector
  230 + blankInputs: function(form, specifiedSelector, nonBlank) {
  231 + var inputs = $(), input,
  232 + selector = specifiedSelector || 'input,textarea';
  233 + form.find(selector).each(function() {
  234 + input = $(this);
  235 + // Collect non-blank inputs if nonBlank option is true, otherwise, collect blank inputs
  236 + if (nonBlank ? input.val() : !input.val()) {
  237 + inputs = inputs.add(input);
  238 + }
  239 + });
  240 + return inputs.length ? inputs : false;
  241 + },
  242 +
  243 + // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
  244 + nonBlankInputs: function(form, specifiedSelector) {
  245 + return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
  246 + },
  247 +
  248 + // Helper function, needed to provide consistent behavior in IE
  249 + stopEverything: function(e) {
  250 + $(e.target).trigger('ujs:everythingStopped');
  251 + e.stopImmediatePropagation();
  252 + return false;
  253 + },
  254 +
  255 + // find all the submit events directly bound to the form and
  256 + // manually invoke them. If anyone returns false then stop the loop
  257 + callFormSubmitBindings: function(form, event) {
  258 + var events = form.data('events'), continuePropagation = true;
  259 + if (events !== undefined && events['submit'] !== undefined) {
  260 + $.each(events['submit'], function(i, obj){
  261 + if (typeof obj.handler === 'function') return continuePropagation = obj.handler(event);
  262 + });
  263 + }
  264 + return continuePropagation;
  265 + },
  266 +
  267 + // replace element's html with the 'data-disable-with' after storing original html
  268 + // and prevent clicking on it
  269 + disableElement: function(element) {
  270 + element.data('ujs:enable-with', element.html()); // store enabled state
  271 + element.html(element.data('disable-with')); // set to disabled state
  272 + element.bind('click.railsDisable', function(e) { // prevent further clicking
  273 + return rails.stopEverything(e)
  274 + });
  275 + },
  276 +
  277 + // restore element to its original state which was disabled by 'disableElement' above
  278 + enableElement: function(element) {
  279 + if (element.data('ujs:enable-with') !== undefined) {
  280 + element.html(element.data('ujs:enable-with')); // set to old enabled state
  281 + // this should be element.removeData('ujs:enable-with')
  282 + // but, there is currently a bug in jquery which makes hyphenated data attributes not get removed
  283 + element.data('ujs:enable-with', false); // clean up cache
  284 + }
  285 + element.unbind('click.railsDisable'); // enable element
  286 + }
  287 +
  288 + };
  289 +
  290 + $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
  291 +
  292 + $(document).delegate(rails.linkDisableSelector, 'ajax:complete', function() {
  293 + rails.enableElement($(this));
  294 + });
  295 +
  296 + $(document).delegate(rails.linkClickSelector, 'click.rails', function(e) {
  297 + var link = $(this), method = link.data('method'), data = link.data('params');
  298 + if (!rails.allowAction(link)) return rails.stopEverything(e);
  299 +
  300 + if (link.is(rails.linkDisableSelector)) rails.disableElement(link);
  301 +
  302 + if (link.data('remote') !== undefined) {
  303 + if ( (e.metaKey || e.ctrlKey) && (!method || method === 'GET') && !data ) { return true; }
  304 +
  305 + if (rails.handleRemote(link) === false) { rails.enableElement(link); }
  306 + return false;
  307 +
  308 + } else if (link.data('method')) {
  309 + rails.handleMethod(link);
  310 + return false;
  311 + }
  312 + });
  313 +
  314 + $(document).delegate(rails.inputChangeSelector, 'change.rails', function(e) {
  315 + var link = $(this);
  316 + if (!rails.allowAction(link)) return rails.stopEverything(e);
  317 +
  318 + rails.handleRemote(link);
  319 + return false;
  320 + });
  321 +
  322 + $(document).delegate(rails.formSubmitSelector, 'submit.rails', function(e) {
  323 + var form = $(this),
  324 + remote = form.data('remote') !== undefined,
  325 + blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
  326 + nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
  327 +
  328 + if (!rails.allowAction(form)) return rails.stopEverything(e);
  329 +
  330 + // skip other logic when required values are missing or file upload is present
  331 + if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
  332 + return rails.stopEverything(e);
  333 + }
  334 +
  335 + if (remote) {
  336 + if (nonBlankFileInputs) {
  337 + return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
  338 + }
  339 +
  340 + // If browser does not support submit bubbling, then this live-binding will be called before direct
  341 + // bindings. Therefore, we should directly call any direct bindings before remotely submitting form.
  342 + if (!$.support.submitBubbles && $().jquery < '1.7' && rails.callFormSubmitBindings(form, e) === false) return rails.stopEverything(e);
  343 +
  344 + rails.handleRemote(form);
  345 + return false;
  346 +
  347 + } else {
  348 + // slight timeout so that the submit button gets properly serialized
  349 + setTimeout(function(){ rails.disableFormElements(form); }, 13);
  350 + }
  351 + });
  352 +
  353 + $(document).delegate(rails.formInputClickSelector, 'click.rails', function(event) {
  354 + var button = $(this);
  355 +
  356 + if (!rails.allowAction(button)) return rails.stopEverything(event);
  357 +
  358 + // register the pressed submit button
  359 + var name = button.attr('name'),
  360 + data = name ? {name:name, value:button.val()} : null;
  361 +
  362 + button.closest('form').data('ujs:submit-button', data);
  363 + });
  364 +
  365 + $(document).delegate(rails.formSubmitSelector, 'ajax:beforeSend.rails', function(event) {
  366 + if (this == event.target) rails.disableFormElements($(this));
  367 + });
  368 +
  369 + $(document).delegate(rails.formSubmitSelector, 'ajax:complete.rails', function(event) {
  370 + if (this == event.target) rails.enableFormElements($(this));
  371 + });
  372 +
  373 +})( jQuery );
6 app/views/layouts/validation_admin_layout.html.erb
@@ -7,7 +7,7 @@
7 7 <%= stylesheet_link_tag 'validation_issues' %>
8 8
9 9 <%= csrf_meta_tag %>
10   - <%#= javascript_include_tag :defaults %>
  10 +
11 11
12 12 <%= yield :head %>
13 13
@@ -38,8 +38,8 @@
38 38 <%= yield :bottom %>
39 39
40 40 </body>
41   - <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script>
42   - <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js'></script>
  41 + <%= javascript_include_tag 'validation_issues.js' %>
  42 +
43 43 <script>
44 44 jQuery(window).ready(
45 45 function() {

0 comments on commit a6bb07b

Please sign in to comment.
Something went wrong with that request. Please try again.