post_wizard.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Post Type Wizard - repeatable custom field rows.
  2. //
  3. // Every row contributes exactly one param_name, param_type, param_label,
  4. // param_placeholder, param_required, param_private and param_indexed, so they
  5. // arrive server side as aligned arrays. This is why 'required' is a <select>
  6. // and not a checkbox -- an unchecked checkbox submits nothing and would shift
  7. // every later row.
  8. //
  9. // 'Index this field' is a real checkbox because it reads like one, and keeps
  10. // the guarantee anyway: the checkbox itself has no name and never submits, and
  11. // the hidden input beside it -- which always submits, exactly once -- is what
  12. // syncIndexedRow keeps in step with it.
  13. function addParam() {
  14. var tpl = document.getElementById('wizard-param-template');
  15. var host = document.getElementById('wizard-params');
  16. if ( tpl === null || host === null ) {
  17. console.log('post wizard param template missing');
  18. return false;
  19. }
  20. host.appendChild(document.importNode(tpl.content, true));
  21. // The row we just added is the last one; set its relation controls to
  22. // match whatever its type select says.
  23. var rows = host.querySelectorAll('.wizard-param-row');
  24. if (rows.length) {
  25. var added = rows[rows.length - 1].querySelector('.param-type');
  26. if (added) {
  27. syncRelationRow(added);
  28. }
  29. }
  30. return false;
  31. }
  32. // The relation selects are only meaningful for a relation field, but they must
  33. // stay in the DOM and keep submitting whatever the type is -- the server zips
  34. // the param_* arrays together positionally, so a row that submits fewer values
  35. // than its neighbours shifts every row after it. Hide, never remove.
  36. function syncRelationRow(select) {
  37. var row = select.closest('.wizard-param-row');
  38. if (!row) {
  39. return false;
  40. }
  41. var relation = row.querySelector('.wizard-relation');
  42. if (relation) {
  43. relation.style.display = select.value === 'relation' ? '' : 'none';
  44. }
  45. return false;
  46. }
  47. // Copy a row's index checkbox into the hidden input that actually submits.
  48. function syncIndexedRow(checkbox) {
  49. var row = checkbox.closest('.wizard-param-row');
  50. if (!row) {
  51. return false;
  52. }
  53. var hidden = row.querySelector('input[name="param_indexed"]');
  54. if (hidden) {
  55. hidden.value = checkbox.checked ? '1' : '0';
  56. }
  57. return false;
  58. }
  59. // Everything the wizard knows about the types which already exist, written into
  60. // the page by post_wizard() as JSON rather than as JS, so that a display
  61. // template full of markup cannot become code.
  62. function wizardTypes() {
  63. var el = document.getElementById('wizard-types');
  64. if (el === null) {
  65. return {};
  66. }
  67. try {
  68. return JSON.parse(el.textContent) || {};
  69. } catch (e) {
  70. console.log('could not read the existing post types: ' + e);
  71. return {};
  72. }
  73. }
  74. function setField(name, value) {
  75. var el = document.querySelector('#postTypeWizard [name="' + name + '"]');
  76. if (!el) {
  77. return;
  78. }
  79. if (el.type === 'checkbox') {
  80. el.checked = value ? true : false;
  81. return;
  82. }
  83. el.value = value === null || value === undefined ? '' : value;
  84. }
  85. // Put the form into the state that produced the picked type, or back to blank
  86. // for 'A new post type'.
  87. function fillFromType(select) {
  88. var types = wizardTypes();
  89. var type = types[select.value];
  90. var host = document.getElementById('wizard-params');
  91. var about = document.getElementById('wizard-type-description');
  92. if (host) {
  93. host.innerHTML = '';
  94. }
  95. if (!type) {
  96. // Back to blank, but leave the canned-field checkboxes as the server
  97. // rendered them -- those are the defaults for a new type.
  98. setField('name', '');
  99. setField('display', '');
  100. setField('title_placeholder', '');
  101. setField('datasource', '');
  102. setField('description', '');
  103. setField('overwrite', false);
  104. if (about) {
  105. about.textContent = '';
  106. }
  107. addParam();
  108. return false;
  109. }
  110. setField('name', type.name);
  111. setField('display', type.display);
  112. setField('title_placeholder', type.title_placeholder);
  113. setField('datasource', type.datasource);
  114. setField('body_form', type.body_form);
  115. setField('description', type.description);
  116. var boxes = ['wrapper', 'inc_post_title', 'inc_post_tags', 'inc_preview',
  117. 'inc_tags', 'inc_aliases', 'inc_attachments'];
  118. for (var i = 0; i < boxes.length; i++) {
  119. setField(boxes[i], type[boxes[i]]);
  120. }
  121. // Saving it under the same name is the whole point of picking it, and
  122. // without this the save is refused with a message about ticking this box.
  123. setField('overwrite', true);
  124. if (about) {
  125. about.textContent = type.description ||
  126. (type.generated ? 'No description recorded for this type yet.'
  127. : 'Written by hand rather than by the wizard, so there may be more to it than this form can show.');
  128. }
  129. var fields = type.fields || [];
  130. for (var f = 0; f < fields.length; f++) {
  131. addParam();
  132. fillParamRow(fields[f]);
  133. }
  134. if (!fields.length) {
  135. addParam();
  136. }
  137. return false;
  138. }
  139. // The row addParam() just appended is the last one.
  140. function fillParamRow(field) {
  141. var rows = document.querySelectorAll('#wizard-params .wizard-param-row');
  142. if (!rows.length) {
  143. return;
  144. }
  145. var row = rows[rows.length - 1];
  146. var set = function (name, value) {
  147. var el = row.querySelector('[name="' + name + '"]');
  148. if (el) {
  149. el.value = value === null || value === undefined ? '' : value;
  150. }
  151. };
  152. set('param_name', field.name);
  153. set('param_type', field.type);
  154. set('param_label', field.label);
  155. set('param_placeholder', field.placeholder);
  156. set('param_required', field.required ? '1' : '0');
  157. set('param_private', field.private ? '1' : '0');
  158. set('param_indexed', field.indexed ? '1' : '0');
  159. set('param_relation_form', field.relation_form);
  160. set('param_relation_mode', field.relation_mode);
  161. // The checkbox has no name -- the hidden input beside it is what submits --
  162. // so it has to be brought into line by hand.
  163. var box = row.querySelector('.wizard-index input[type="checkbox"]');
  164. if (box) {
  165. box.checked = field.indexed ? true : false;
  166. }
  167. var type = row.querySelector('.param-type');
  168. if (type) {
  169. syncRelationRow(type);
  170. }
  171. }
  172. function delParam(button) {
  173. var row = button.closest('.wizard-param-row');
  174. if (row) {
  175. row.parentNode.removeChild(row);
  176. }
  177. return false;
  178. }
  179. // Start with one blank row, otherwise the feature isn't discoverable.
  180. document.addEventListener("DOMContentLoaded", function(event) {
  181. if ( document.getElementById('wizard-params') !== null ) {
  182. addParam();
  183. }
  184. });