[Deprecated] Campaign CSS Splitter

Splits the Theming editor into multiple named fields for easier management and adds syntax highlighting

  1. // ==UserScript==
  2. // @name [Deprecated] Campaign CSS Splitter
  3. // @namespace http://tampermonkey.net/
  4. // @version 3
  5. // @description Splits the Theming editor into multiple named fields for easier management and adds syntax highlighting
  6. // @author Salvatos
  7. // @match https://kanka.io/*/campaign_styles/*
  8. // @icon https://www.google.com/s2/favicons?domain=kanka.io
  9. // @grant GM_addStyle
  10. // @grant GM_getResourceText
  11. // @require https://cdn.jsdelivr.net/npm/codemirror@5.62.3/lib/codemirror.min.js
  12. // @require https://cdn.jsdelivr.net/npm/codemirror@5.62.3/mode/css/css.js
  13. // @require https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.56.0/addon/display/autorefresh.min.js
  14. // @resource SYNTAX_CSS https://cdn.jsdelivr.net/npm/codemirror@5.62.3/lib/codemirror.css
  15. // @resource THEME_CSS https://cdn.jsdelivr.net/npm/codemirror@5.62.3/theme/darcula.css
  16. // ==/UserScript==
  17.  
  18. /* To change your CodeMirror theme, check out the theme reference demo to find one you like:
  19. https://codemirror.net/demo/theme.html
  20. And replace the @resource URL above linking to darcula.css with the appropriate file from jsdelivr:
  21. https://www.jsdelivr.com/package/npm/codemirror?path=theme
  22. Then change the variable below to reflect the new theme’s name: */
  23. const cmTheme = "darcula";
  24.  
  25. // Load remote CSS for CodeMirror
  26. GM_addStyle(GM_getResourceText("SYNTAX_CSS"));
  27. GM_addStyle(GM_getResourceText("THEME_CSS"));
  28.  
  29. GM_addStyle(`
  30. #css-split-toggle, #add-css {
  31. display: inline-block;
  32. margin-right: 5px;
  33. font-size: 13px;
  34. padding: 5px;
  35. line-height: 1;
  36. }
  37. .CodeMirror {
  38. border: 2px solid;
  39. margin-bottom: 10px;
  40. resize: vertical;
  41. }
  42. input.css-split {
  43. font-weight: bold;
  44. border: 0;
  45. border-bottom: 1px solid;
  46. }
  47. #css-save {
  48. position: fixed;
  49. top: 0;
  50. left: 50%;
  51. transform: translateX(-50%);
  52. background-color: #d33724;
  53. z-index: 1080;
  54. color: whitesmoke;
  55. padding: 10px;
  56. font-weight: bold;
  57. border-bottom-left-radius: 2px;
  58. border-bottom-right-radius: 2px;
  59. }
  60. `);
  61.  
  62. // Name some useful DOM objects
  63. const formGroup = $('form .form-group:nth-child(2)');
  64. const masterTextarea= $('#css');
  65. const saveButtonText = $('.panel-footer button').html(); // For l10n purposes
  66.  
  67. // Add a toggle button
  68. $(formGroup).append('<button type="button" id="css-split-toggle" field-status="merged">Split CSS sections</button>');
  69. $("#css-split-toggle").click(splitFields);
  70.  
  71. function splitFields() {
  72. // If we're merged (default), we need to split
  73. if ($("#css-split-toggle").attr("field-status") == "merged") {
  74. // Place current value of master textarea in a delimiter/block array
  75. let splitCSS = masterTextarea.val().split(/\/\*†(.*)†\*\//); // Delimiter: "/*† Section name †*/"
  76.  
  77. // Hide the master textarea from view
  78. masterTextarea.hide();
  79.  
  80. // Make a sortable container for our blocks
  81. $('<div id="sortable"></div>').insertBefore("#css-split-toggle");
  82.  
  83. // Prepare a global array of CodeMirror instances
  84. window.cmInstance = new Array();
  85.  
  86. // Push content into discrete blocks
  87. // If we have a delimiter at the top AND content after it, ignore the first array entry since it's empty
  88. let i = (splitCSS[0] == "" && splitCSS[1] !== "") ? 1 : 0;
  89. for (; i < splitCSS.length; i++) {
  90. // Even matches are non-delimiters, create a textarea for each
  91. if (i % 2 == 0) {
  92. // Make the code block ID linear to facilitate iteration later on
  93. let linearid = i/2;
  94.  
  95. // Create and populate textarea
  96. if (i == 0) { // Loose code before the first delimiter, give it a delimiter lest everything fall apart
  97. $('#sortable').append(`
  98. <div class="sortable-item">
  99. <details>
  100. <summary>
  101. <input class="css-split" value="Untitled" size="30" />&nbsp;
  102. <i class="fas fa-arrows-alt ui-sortable-handle" title="Drag and drop to change block ordering"></i>
  103. </summary>
  104. <textarea id="css-` + linearid + `" class="css-split"></textarea>
  105. </details>
  106. </div>`);
  107. }
  108. else { // A delimiter is already set, just append the textarea
  109. $('.sortable-item:last-child details').append('<textarea id="css-' + linearid + '" class="css-split"></textarea>');
  110. }
  111. // Set value
  112. $('#css-' + linearid).val(splitCSS[i].trim());
  113.  
  114. // Create a CodeMirror instance from textarea
  115. cmInstance[linearid] = CodeMirror.fromTextArea($('.sortable-item:last-child details textarea:last-child')[0], {
  116. mode: "css",
  117. autoRefresh: true,
  118. indentWithTabs: true,
  119. lineWrapping: true,
  120. theme: cmTheme
  121. });
  122. }
  123. // Odd matches are delimiters, create a title for each
  124. else {
  125. $('#sortable').append(`
  126. <div class="sortable-item">
  127. <details>
  128. <summary>
  129. <input class="css-split" value="` + splitCSS[i].trim() + `" size="30" />&nbsp;
  130. <i class="fas fa-arrows-alt ui-sortable-handle" title="Drag and drop to change block ordering"></i>
  131. </summary>
  132. </details>
  133. </div>`);
  134. }
  135. }
  136.  
  137. // Switch toggle status
  138. $("#css-split-toggle").attr("field-status", "split");
  139. $("#css-split-toggle").html("Merge CSS sections");
  140.  
  141. // Add warning to Save button and top of screen (todo: actually prevent saving)
  142. $('.panel-footer button').html("MERGE YOUR CSS BEFORE SAVING!");
  143. $('.panel-footer button').prop("disabled", true);
  144. //$('body').append('<div id="css-save">MERGE YOUR CSS BEFORE SAVING!</div>');
  145.  
  146. // Make code blocks sortable via Bootstrap
  147. $("#sortable").sortable({handle: '.fa-arrows-alt'});
  148.  
  149. // Add Section button
  150. $(formGroup).append('<button type="button" id="add-css">Create new section</button>');
  151. $("#add-css").click(function() {
  152. let sectionCounter = $('.sortable-item textarea').length + 1;
  153. $('#sortable').append(`
  154. <div class="sortable-item">
  155. <details>
  156. <summary>
  157. <input class="css-split" value="Untitled" size="30" />&nbsp;
  158. <i class="fas fa-arrows-alt ui-sortable-handle" title="Drag and drop to change block ordering"></i>
  159. </summary>
  160. <textarea id="css-` + sectionCounter + `" class="css-split"></textarea>
  161. </details>
  162. </div>`);
  163.  
  164. // Create a CodeMirror instance from textarea
  165. cmInstance[sectionCounter] = CodeMirror.fromTextArea($('.sortable-item:last-child details textarea:last-child')[0], {
  166. mode: "css",
  167. autoRefresh: true,
  168. indentWithTabs: true,
  169. lineWrapping: true,
  170. theme: cmTheme
  171. });
  172. });
  173. }
  174. // If we're split, we need to merge
  175. else if ($("#css-split-toggle").attr("field-status") == "split") {
  176. // Combine textareas... and delimiters!
  177. let merged = '';
  178.  
  179. $('.css-split').each(function(){
  180. if ($(this).is("textarea")) {
  181. let blockID = $(this).attr('id').split(/-/)[1];
  182.  
  183. // Save CM value to its textarea, then grab it and pad it with line breaks
  184. cmInstance[blockID].save();
  185. merged += '\n' + cmInstance[blockID].getValue() + '\n\n';
  186. }
  187. else if ($(this).is("input")) {
  188. // If we deleted the title, don't add a delimiter; otherwise add the symbols back in
  189. if ($(this).val().trim().length > 0) {
  190. merged += '/*† ' + $(this).val().trim() + ' †*/';
  191. }
  192. }
  193. });
  194.  
  195. // Give the master textarea the combined value and show it after removing the blocks and buttons
  196. masterTextarea.val(merged.trim());
  197. $('.sortable-item, #add-css, #css-save').remove();
  198. masterTextarea.show();
  199.  
  200. // Switch toggle status
  201. $("#css-split-toggle").attr("field-status", "merged");
  202. $("#css-split-toggle").html("Split CSS sections");
  203.  
  204. // Restore Save button (todo: actually prevent saving)
  205. $('.panel-footer button').html(saveButtonText);
  206. $('.panel-footer button').prop("disabled", false);
  207. }
  208. }