post_wizard.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. if (el.type === 'textarea') {
  84. el.innerText = value;
  85. return;
  86. }
  87. el.value = value === null || value === undefined ? '' : value;
  88. }
  89. // Put the form into the state that produced the picked type, or back to blank
  90. // for 'A new post type'.
  91. function fillFromType(select) {
  92. var types = wizardTypes();
  93. var type = types[select.value];
  94. var host = document.getElementById('wizard-params');
  95. var about = document.getElementById('wizard-type-description');
  96. if (host) {
  97. host.innerHTML = '';
  98. }
  99. if (!type) {
  100. // Back to blank, but leave the canned-field checkboxes as the server
  101. // rendered them -- those are the defaults for a new type.
  102. setField('name', '');
  103. setField('display', '');
  104. setField('title_placeholder', '');
  105. setField('datasource', '');
  106. setField('description', '');
  107. setField('overwrite', false);
  108. if (about) {
  109. about.textContent = '';
  110. }
  111. addParam();
  112. return false;
  113. }
  114. setField('name', type.name);
  115. setField('display', type.display);
  116. setField('title_placeholder', type.title_placeholder);
  117. setField('datasource', type.datasource);
  118. setField('body_form', type.body_form);
  119. setField('description', type.description);
  120. var boxes = ['wrapper', 'inc_post_title', 'inc_post_tags', 'inc_preview',
  121. 'inc_tags', 'inc_aliases', 'inc_attachments'];
  122. for (var i = 0; i < boxes.length; i++) {
  123. setField(boxes[i], type[boxes[i]]);
  124. }
  125. // Saving it under the same name is the whole point of picking it, and
  126. // without this the save is refused with a message about ticking this box.
  127. setField('overwrite', true);
  128. if (about) {
  129. about.textContent = type.description ||
  130. (type.generated ? 'No description recorded for this type yet.'
  131. : 'Written by hand rather than by the wizard, so there may be more to it than this form can show.');
  132. }
  133. var fields = type.fields || [];
  134. for (var f = 0; f < fields.length; f++) {
  135. addParam();
  136. fillParamRow(fields[f]);
  137. }
  138. if (!fields.length) {
  139. addParam();
  140. }
  141. return false;
  142. }
  143. // The row addParam() just appended is the last one.
  144. function fillParamRow(field) {
  145. var rows = document.querySelectorAll('#wizard-params .wizard-param-row');
  146. if (!rows.length) {
  147. return;
  148. }
  149. var row = rows[rows.length - 1];
  150. var set = function (name, value) {
  151. var el = row.querySelector('[name="' + name + '"]');
  152. if (el) {
  153. el.value = value === null || value === undefined ? '' : value;
  154. }
  155. };
  156. set('param_name', field.name);
  157. set('param_type', field.type);
  158. set('param_label', field.label);
  159. set('param_placeholder', field.placeholder);
  160. set('param_required', field.required ? '1' : '0');
  161. set('param_private', field.private ? '1' : '0');
  162. set('param_indexed', field.indexed ? '1' : '0');
  163. set('param_relation_form', field.relation_form);
  164. set('param_relation_mode', field.relation_mode);
  165. // The checkbox has no name -- the hidden input beside it is what submits --
  166. // so it has to be brought into line by hand.
  167. var box = row.querySelector('.wizard-index input[type="checkbox"]');
  168. if (box) {
  169. box.checked = field.indexed ? true : false;
  170. }
  171. var type = row.querySelector('.param-type');
  172. if (type) {
  173. syncRelationRow(type);
  174. }
  175. }
  176. function delParam(button) {
  177. var row = button.closest('.wizard-param-row');
  178. if (row) {
  179. row.parentNode.removeChild(row);
  180. }
  181. return false;
  182. }
  183. // Start with one blank row, otherwise the feature isn't discoverable.
  184. document.addEventListener("DOMContentLoaded", function(event) {
  185. if ( document.getElementById('wizard-params') !== null ) {
  186. addParam();
  187. }
  188. });