rails.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. /***
  2. * Excerpted from "Agile Web Development with Rails",
  3. * published by The Pragmatic Bookshelf.
  4. * Copyrights apply to this code. It may not be used to create training material,
  5. * courses, books, articles, and the like. Contact us if you are in doubt.
  6. * We make no guarantees that this code is fit for any purpose.
  7. * Visit http://www.pragmaticprogrammer.com/titles/rails4 for more book information.
  8. ***/
  9. /***
  10. * Excerpted from "Agile Web Development with Rails, 4rd Ed.",
  11. * published by The Pragmatic Bookshelf.
  12. * Copyrights apply to this code. It may not be used to create training material,
  13. * courses, books, articles, and the like. Contact us if you are in doubt.
  14. * We make no guarantees that this code is fit for any purpose.
  15. * Visit http://www.pragmaticprogrammer.com/titles/rails4 for more book information.
  16. ***/
  17. (function() {
  18. // Technique from Juriy Zaytsev
  19. // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
  20. function isEventSupported(eventName) {
  21. var el = document.createElement('div');
  22. eventName = 'on' + eventName;
  23. var isSupported = (eventName in el);
  24. if (!isSupported) {
  25. el.setAttribute(eventName, 'return;');
  26. isSupported = typeof el[eventName] == 'function';
  27. }
  28. el = null;
  29. return isSupported;
  30. }
  31. function isForm(element) {
  32. return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
  33. }
  34. function isInput(element) {
  35. if (Object.isElement(element)) {
  36. var name = element.nodeName.toUpperCase()
  37. return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
  38. }
  39. else return false
  40. }
  41. var submitBubbles = isEventSupported('submit'),
  42. changeBubbles = isEventSupported('change')
  43. if (!submitBubbles || !changeBubbles) {
  44. // augment the Event.Handler class to observe custom events when needed
  45. Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
  46. function(init, element, eventName, selector, callback) {
  47. init(element, eventName, selector, callback)
  48. // is the handler being attached to an element that doesn't support this event?
  49. if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
  50. (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
  51. // "submit" => "emulated:submit"
  52. this.eventName = 'emulated:' + this.eventName
  53. }
  54. }
  55. )
  56. }
  57. if (!submitBubbles) {
  58. // discover forms on the page by observing focus events which always bubble
  59. document.on('focusin', 'form', function(focusEvent, form) {
  60. // special handler for the real "submit" event (one-time operation)
  61. if (!form.retrieve('emulated:submit')) {
  62. form.on('submit', function(submitEvent) {
  63. var emulated = form.fire('emulated:submit', submitEvent, true)
  64. // if custom event received preventDefault, cancel the real one too
  65. if (emulated.returnValue === false) submitEvent.preventDefault()
  66. })
  67. form.store('emulated:submit', true)
  68. }
  69. })
  70. }
  71. if (!changeBubbles) {
  72. // discover form inputs on the page
  73. document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
  74. // special handler for real "change" events
  75. if (!input.retrieve('emulated:change')) {
  76. input.on('change', function(changeEvent) {
  77. input.fire('emulated:change', changeEvent, true)
  78. })
  79. input.store('emulated:change', true)
  80. }
  81. })
  82. }
  83. function handleRemote(element) {
  84. var method, url, params;
  85. var event = element.fire("ajax:before");
  86. if (event.stopped) return false;
  87. if (element.tagName.toLowerCase() === 'form') {
  88. method = element.readAttribute('method') || 'post';
  89. url = element.readAttribute('action');
  90. params = element.serialize();
  91. } else {
  92. method = element.readAttribute('data-method') || 'get';
  93. url = element.readAttribute('href');
  94. params = {};
  95. }
  96. new Ajax.Request(url, {
  97. method: method,
  98. parameters: params,
  99. evalScripts: true,
  100. onComplete: function(request) { element.fire("ajax:complete", request); },
  101. onSuccess: function(request) { element.fire("ajax:success", request); },
  102. onFailure: function(request) { element.fire("ajax:failure", request); }
  103. });
  104. element.fire("ajax:after");
  105. }
  106. function handleMethod(element) {
  107. var method = element.readAttribute('data-method'),
  108. url = element.readAttribute('href'),
  109. csrf_param = $$('meta[name=csrf-param]')[0],
  110. csrf_token = $$('meta[name=csrf-token]')[0];
  111. var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
  112. element.parentNode.insert(form);
  113. if (method !== 'post') {
  114. var field = new Element('input', { type: 'hidden', name: '_method', value: method });
  115. form.insert(field);
  116. }
  117. if (csrf_param) {
  118. var param = csrf_param.readAttribute('content'),
  119. token = csrf_token.readAttribute('content'),
  120. field = new Element('input', { type: 'hidden', name: param, value: token });
  121. form.insert(field);
  122. }
  123. form.submit();
  124. }
  125. document.on("click", "*[data-confirm]", function(event, element) {
  126. var message = element.readAttribute('data-confirm');
  127. if (!confirm(message)) event.stop();
  128. });
  129. document.on("click", "a[data-remote]", function(event, element) {
  130. if (event.stopped) return;
  131. handleRemote(element);
  132. event.stop();
  133. });
  134. document.on("click", "a[data-method]", function(event, element) {
  135. if (event.stopped) return;
  136. handleMethod(element);
  137. event.stop();
  138. });
  139. document.on("submit", function(event) {
  140. var element = event.findElement(),
  141. message = element.readAttribute('data-confirm');
  142. if (message && !confirm(message)) {
  143. event.stop();
  144. return false;
  145. }
  146. var inputs = element.select("input[type=submit][data-disable-with]");
  147. inputs.each(function(input) {
  148. input.disabled = true;
  149. input.writeAttribute('data-original-value', input.value);
  150. input.value = input.readAttribute('data-disable-with');
  151. });
  152. var element = event.findElement("form[data-remote]");
  153. if (element) {
  154. handleRemote(element);
  155. event.stop();
  156. }
  157. });
  158. document.on("ajax:after", "form", function(event, element) {
  159. var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
  160. inputs.each(function(input) {
  161. input.value = input.readAttribute('data-original-value');
  162. input.removeAttribute('data-original-value');
  163. input.disabled = false;
  164. });
  165. });
  166. Ajax.Responders.register({
  167. onCreate: function(request) {
  168. var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
  169. if (csrf_meta_tag) {
  170. var header = 'X-CSRF-Token',
  171. token = csrf_meta_tag.readAttribute('content');
  172. if (!request.options.requestHeaders) {
  173. request.options.requestHeaders = {};
  174. }
  175. request.options.requestHeaders[header] = token;
  176. }
  177. }
  178. });
  179. })();