webform-admin.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /**
  2. * @file
  3. * Webform node form interface enhancements.
  4. */
  5. (function ($) {
  6. "use strict";
  7. Drupal.behaviors.webformAdmin = {};
  8. Drupal.behaviors.webformAdmin.attach = function (context) {
  9. // On click or change, make a parent radio button selected.
  10. Drupal.webform.setActive(context);
  11. Drupal.webform.updateTemplate(context);
  12. // Update the template select list upon changing a template.
  13. // Select all link for file extensions.
  14. Drupal.webform.selectCheckboxesLink(context);
  15. // Enhance the normal tableselect.js file to support indentations.
  16. Drupal.webform.tableSelectIndentation(context);
  17. // Automatically download exports if available.
  18. Drupal.webform.downloadExport(context);
  19. // Enhancements for the conditionals administrative page.
  20. Drupal.webform.conditionalAdmin(context);
  21. // Trigger radio/checkbox change when label click automatically selected by
  22. // browser.
  23. Drupal.webform.radioLabelAutoClick(context);
  24. };
  25. Drupal.webform = Drupal.webform || {};
  26. Drupal.webform.setActive = function (context) {
  27. $('.webform-inline-radio', context).click(function (e) {
  28. $(this).closest('.form-type-radio').find('input[type=radio]').webformProp('checked', true);
  29. });
  30. $('.webform-set-active', context).change(function (e) {
  31. if ($(this).val()) {
  32. $(this).closest('.form-type-radio').find('input[type=radio]').webformProp('checked', true);
  33. }
  34. e.preventDefault();
  35. });
  36. // Firefox improperly selects the parent radio button when clicking inside
  37. // a label that contains an input field. The only way of preventing this
  38. // currently is to remove the "for" attribute on the label.
  39. // See https://bugzilla.mozilla.org/show_bug.cgi?id=213519.
  40. if (navigator.userAgent.match(/Firefox/)) {
  41. $('.webform-inline-radio', context).removeAttr('for');
  42. }
  43. };
  44. // Update e-mail templates between default and custom.
  45. Drupal.webform.updateTemplate = function (context) {
  46. var defaultTemplate = $('#edit-templates-default').val();
  47. var $templateSelect = $('#webform-template-fieldset select#edit-template-option', context);
  48. var $templateTextarea = $('#webform-template-fieldset textarea:visible', context);
  49. var updateTemplateSelect = function () {
  50. if ($(this).val() == defaultTemplate) {
  51. $templateSelect.val('default');
  52. }
  53. else {
  54. $templateSelect.val('custom');
  55. }
  56. };
  57. var updateTemplateText = function () {
  58. if ($(this).val() == 'default' && $templateTextarea.val() != defaultTemplate) {
  59. if (confirm(Drupal.settings.webform.revertConfirm)) {
  60. $templateTextarea.val(defaultTemplate);
  61. }
  62. else {
  63. $(this).val('custom');
  64. }
  65. }
  66. };
  67. $templateTextarea.keyup(updateTemplateSelect);
  68. $templateSelect.change(updateTemplateText);
  69. };
  70. Drupal.webform.selectCheckboxesLink = function (context) {
  71. function selectCheckboxes() {
  72. var group = this.className.replace(/.*?webform-select-link-([^ ]*).*/, '$1');
  73. var $checkboxes = $('.webform-select-group-' + group + ' input[type=checkbox]');
  74. var reverseCheck = !$checkboxes[0].checked;
  75. $checkboxes.each(function () {
  76. this.checked = reverseCheck;
  77. });
  78. $checkboxes.trigger('change');
  79. return false;
  80. }
  81. $('a.webform-select-link', context).click(selectCheckboxes);
  82. };
  83. Drupal.webform.tableSelectIndentation = function (context) {
  84. var $tables = $('th.select-all', context).parents('table');
  85. $tables.find('input.form-checkbox').change(function () {
  86. var $rows = $(this).parents('table:first').find('tr');
  87. var row = $(this).parents('tr:first').get(0);
  88. var rowNumber = $rows.index(row);
  89. var rowTotal = $rows.size();
  90. var indentLevel = $(row).find('div.indentation').size();
  91. for (var n = rowNumber + 1; n < rowTotal; n++) {
  92. if ($rows.eq(n).find('div.indentation').size() <= indentLevel) {
  93. break;
  94. }
  95. $rows.eq(n).find('input.form-checkbox').webformProp('checked', this.checked);
  96. }
  97. });
  98. };
  99. /**
  100. * Attach behaviors for Webform results download page.
  101. */
  102. Drupal.webform.downloadExport = function (context) {
  103. if (context === document && Drupal.settings && Drupal.settings.webformExport && document.cookie.match(/webform_export_info=1/)) {
  104. window.location = Drupal.settings.webformExport;
  105. delete Drupal.settings.webformExport;
  106. }
  107. };
  108. /**
  109. * Attach behaviors for Webform conditional administration.
  110. */
  111. Drupal.webform.conditionalAdmin = function (context) {
  112. var $context = $(context);
  113. // Bind to the entire form and allow events to bubble-up from elements. This
  114. // saves a lot of processing when new conditions are added/removed.
  115. $context.find('#webform-conditionals-ajax:not(.webform-conditional-processed)')
  116. .addClass('webform-conditional-processed')
  117. .bind('change', function (e) {
  118. var $target = $(e.target);
  119. if ($target.is('.webform-conditional-source select')) {
  120. Drupal.webform.conditionalSourceChange.apply(e.target);
  121. }
  122. if ($target.is('.webform-conditional-operator select')) {
  123. Drupal.webform.conditionalOperatorChange.apply(e.target);
  124. }
  125. if ($target.is('.webform-conditional-andor select')) {
  126. Drupal.webform.conditionalAndOrChange.apply(e.target);
  127. }
  128. if ($target.is('.webform-conditional-action select')) {
  129. Drupal.webform.conditionalActionChange.apply(e.target);
  130. }
  131. });
  132. // Add event handlers to delete the entire row if the last rule or action is removed.
  133. $context.find('.webform-conditional-rule-remove:not(.webform-conditional-processed)').bind('click', function () {
  134. this.webformRemoveClass = '.webform-conditional-rule-remove';
  135. window.setTimeout($.proxy(Drupal.webform.conditionalRemove, this), 100);
  136. }).addClass('webform-conditional-processed');
  137. $context.find('.webform-conditional-action-remove:not(.webform-conditional-processed)').bind('click', function () {
  138. this.webformRemoveClass = '.webform-conditional-action-remove';
  139. window.setTimeout($.proxy(Drupal.webform.conditionalRemove, this), 100);
  140. }).addClass('webform-conditional-processed');
  141. // Trigger default handlers on the source element, this in turn will trigger
  142. // the operator handlers.
  143. $context.find('.webform-conditional-source select').trigger('change');
  144. // Trigger defaults handlers on the action element.
  145. $context.find('.webform-conditional-action select').trigger('change');
  146. // When adding a new table row, make it draggable and hide the weight column.
  147. if ($context.is('tr.ajax-new-content') && $context.find('.webform-conditional').length === 1) {
  148. Drupal.tableDrag['webform-conditionals-table'].makeDraggable($context[0]);
  149. $context.find('.webform-conditional-weight').closest('td').addClass('tabledrag-hide');
  150. if ($.cookie('Drupal.tableDrag.showWeight') !== '1') {
  151. Drupal.tableDrag['webform-conditionals-table'].hideColumns();
  152. }
  153. $context.removeClass('ajax-new-content');
  154. }
  155. };
  156. /**
  157. * Event callback for the remove button next to an individual rule.
  158. */
  159. Drupal.webform.conditionalRemove = function () {
  160. // See if there are any remaining rules in this element.
  161. var rowCount = $(this).parents('.webform-conditional:first').find(this.webformRemoveClass).length;
  162. if (rowCount <= 1) {
  163. var $tableRow = $(this).parents('tr:first');
  164. var $table = $('#webform-conditionals-table');
  165. if ($tableRow.length && $table.length) {
  166. $tableRow.remove();
  167. Drupal.webform.restripeTable($table[0]);
  168. }
  169. }
  170. };
  171. /**
  172. * Event callback to update the list of operators after a source change.
  173. */
  174. Drupal.webform.conditionalSourceChange = function () {
  175. var source = $(this).val();
  176. var dataType = Drupal.settings.webform.conditionalValues.sources[source]['data_type'];
  177. var $operator = $(this).parents('.webform-conditional-rule:first').find('.webform-conditional-operator select');
  178. // Store a the original list of all operators for all data types in the select
  179. // list DOM element.
  180. if (!$operator[0]['webformConditionalOriginal']) {
  181. $operator[0]['webformConditionalOriginal'] = $operator[0].innerHTML;
  182. }
  183. // Reference the original list to create a new list matching the data type.
  184. var $originalList = $($operator[0]['webformConditionalOriginal']);
  185. var $newList = $originalList.filter('optgroup[label=' + dataType + ']');
  186. var newHTML = $newList[0].innerHTML;
  187. // Update the options and fire the change event handler on the list to update
  188. // the value field, only if the options have changed. This avoids resetting
  189. // existing selections.
  190. if (newHTML != $operator.html()) {
  191. $operator.html(newHTML);
  192. }
  193. // Trigger the change in case the source component changed from one select
  194. // component to another.
  195. $operator.trigger('change');
  196. };
  197. /**
  198. * Event callback to update the value field after an operator change.
  199. */
  200. Drupal.webform.conditionalOperatorChange = function () {
  201. var source = $(this).parents('.webform-conditional-rule:first').find('.webform-conditional-source select').val();
  202. var dataType = Drupal.settings.webform.conditionalValues.sources[source]['data_type'];
  203. var operator = $(this).val();
  204. var $value = $(this).parents('.webform-conditional-rule:first').find('.webform-conditional-value');
  205. var name = $value.find('input, select, textarea').attr('name');
  206. var originalValue = false;
  207. // Given the dataType and operator, we can determine the form key.
  208. var formKey = Drupal.settings.webform.conditionalValues.operators[dataType][operator]['form'];
  209. var formSource = typeof Drupal.settings.webform.conditionalValues.forms[formKey] == 'undefined' ? false : source;
  210. // On initial request, save the default field as printed on the original page.
  211. if (!$value[0]['webformConditionalOriginal']) {
  212. $value[0]['webformConditionalOriginal'] = $value[0].innerHTML;
  213. originalValue = $value.find('input:first').val();
  214. }
  215. // On changes to an existing operator, check if the form key is different
  216. // (and any per-source form, such as a select option list) before replacing
  217. // the form with an identical version.
  218. else if ($value[0]['webformConditionalFormKey'] == formKey && $value[0]['webformConditionalFormSource'] == formSource) {
  219. return;
  220. }
  221. // Store the current form key for checking the next time the operator changes.
  222. $value[0]['webformConditionalFormKey'] = formKey;
  223. $value[0]['webformConditionalFormSource'] = formSource;
  224. // If using the default (a textfield), restore the original field.
  225. if (formKey === 'default') {
  226. $value[0].innerHTML = $value[0]['webformConditionalOriginal'];
  227. }
  228. // If the operator does not need a source value (i.e. is empty), hide it.
  229. else if (formKey === false) {
  230. $value[0].innerHTML = '<input type="text" value="" style="display: none;" >';
  231. }
  232. // If there is a per-source form for this operator (e.g. option lists), use
  233. // the specialized value form.
  234. else if (typeof Drupal.settings.webform.conditionalValues.forms[formKey] == 'object') {
  235. $value[0].innerHTML = Drupal.settings.webform.conditionalValues.forms[formKey][source];
  236. }
  237. // Otherwise all the sources use a generic field (e.g. a text field).
  238. else {
  239. $value[0].innerHTML = Drupal.settings.webform.conditionalValues.forms[formKey];
  240. }
  241. // Set the name attribute to match the original placeholder field.
  242. var $firstElement = $value.find('input, select, textarea').filter(':first');
  243. $firstElement.attr('name', name);
  244. if (originalValue) {
  245. $firstElement.val(originalValue);
  246. }
  247. };
  248. /**
  249. * Event callback to make sure all group and/or operators match.
  250. */
  251. Drupal.webform.conditionalAndOrChange = function () {
  252. var rid = this.getAttribute('data-rid');
  253. var text = $(this).find('option:selected').text();
  254. $(this).parents('.webform-conditional:first').find('.webform-conditional-andor div[data-rid="' + rid + '"]').text(text);
  255. };
  256. /**
  257. * Event callback to show argument only for appropriate actions.
  258. */
  259. Drupal.webform.conditionalActionChange = function () {
  260. var action = $(this).val();
  261. var $argument = $(this).parents('.webform-conditional-condition:first').find('.webform-conditional-argument input');
  262. var isShown = $argument.is(':visible');
  263. switch (action) {
  264. case 'show':
  265. case 'require':
  266. if (isShown) {
  267. $argument.hide();
  268. }
  269. break;
  270. case 'set':
  271. if (!isShown) {
  272. $argument.show();
  273. }
  274. break;
  275. }
  276. };
  277. /**
  278. * Triggers a change event when a label receives a click.
  279. *
  280. * When the browser automatically selects a radio button when it's label is
  281. * clicked, the FAPI states jQuery code doesn't receive an event. This function
  282. * ensures that automatically-selected radio buttons keep in sync with the
  283. * FAPI states.
  284. */
  285. Drupal.webform.radioLabelAutoClick = function (context) {
  286. $('label').once('webform-label').click(function () {
  287. $(this).prev('input:radio').change();
  288. });
  289. };
  290. /**
  291. * Make a prop shim for jQuery < 1.9.
  292. */
  293. $.fn.webformProp = $.fn.webformProp || function (name, value) {
  294. if (value) {
  295. return $.fn.prop ? this.prop(name, true) : this.attr(name, true);
  296. }
  297. else {
  298. return $.fn.prop ? this.prop(name, false) : this.removeAttr(name);
  299. }
  300. };
  301. })(jQuery);