<!-- Enables dragging on mobile 
https://github.com/Bernardo-Castilho/dragdroptouch -->
<script src="/assets/DragDropTouch.js"></script>

<script>  
  // Enter JavaScript-land!
  // First, some functions, followed by initialization commands

  // Begin BUILDER functions
  // source: https://www.codecanal.com/html5-drag-and-copy/
  function allowDrop(ev) {
      ev.preventDefault();
  }
  function drag(ev) {
      ev.dataTransfer.setData("text", ev.target.id);
      document.getElementById("module-input").classList.add('drag-target');
  }
  function dragEnd(ev) {
      document.getElementById("module-input").classList.remove('drag-target');
  }
  function drop(ev) {
      ev.preventDefault();
      var target = ev.target;
      // First, confirm target location is valid
      function targetCheck () {
          if (!editMode) {
              return false;
          } else if (target.id == "module-input") { 
              return true;
          } else if (!document.getElementById("module-input").contains(target)) {
              // Ignore destinations not in the correct area
              return false;
          } else if (target.id == "drag-directions") {
              // Prevents dropping into dummy text field
              target = target.parentElement;
              return true;
          } else if (target.classList[0] == "module") {
              return true;
          } else {
              // be sure we're adding to module, not its children              
              target = target.parentElement;
              return targetCheck();
          }
      }
      if (!targetCheck()) { return; } 
      // Set up transfer
      var data = ev.dataTransfer.getData("text");
      // Iff module is from the menu clone it
      var module = document.getElementById(data);
      if ((module.parentElement.id == "module-menu") ||
          (module.parentElement.parentElement.id == "module-menu")) {
          // ^ because a subgroup might be parent
          module = module.cloneNode(true);
          var name = null;
          if (module.id == "module-custom") {
              // For custom modules: replace the <input> with text
              name = module.getElementsByTagName("input")[0].value;
              module.getElementsByTagName("input")[0].remove();
              var customText = document.createElement("span");
              customText.id = "module-name";
              customText.append(name);
              module.prepend(customText);
          }
          // append id with unique timestamp
          var nowModule = new Date();
          module.id += "-" + nowModule.getTime();
      }
      // hide the info button
    //   module.children[1].style.display = "none";
      // display the deletion button
    //   module.children[2].style.display = "inline";
      // pop it in!
      target.appendChild(module);
      // add module-field button (using HTML so it saves to Library)
      module.outerHTML = module.outerHTML.replace("id=\"module-name\"",
                               "id=\"module-name\" onclick=\"moduleEditField(this.parentNode.id)\"");      
      // create the module-field
      moduleEditField(module.id);
      // be sure the dummy text is gone
      if (document.contains(document.getElementById("drag-directions"))) {
          document.getElementById("drag-directions").remove();
      }
  }

  // Edits the title field of a given module based on #custom-field
  function moduleTitleEdit(moduleID) {
      var module = document.getElementById(moduleID).children[0];
      var content =
          stripHTML(document.getElementById("custom-field").innerHTML);
      module.title = content;
  }
  
  // Sets up a field for displaying and editing module details
  function moduleEditField(moduleID) {
      var module = document.getElementById(moduleID);
      var moduleName = module.children[0].innerHTML;
      var moduleTitle = module.children[0].title;
      if (editMode) {
          var query = "How does the <span class=\"module\">" + moduleName
              + "</span> module work?";
          var destination = document.getElementById("builder-field");
          if (moduleName == null) { moduleName = ""; }     
          var output = '\n<div id="custom-field-container">';
          output += '<span class="prompt">' + query + '</span>';
          output += '<div class="field-controls"><a onclick="this.parentNode.parentNode.remove()"><img src="{% link assets/tabler_icons/x.svg %}" class="delete-module" /></a></div>';
          output += '<p contenteditable="true" class="editable" id="custom-field" oninput="moduleTitleEdit(\'' + moduleID + '\')">' + moduleTitle + '</p>';
          output += '</div>\n';
          destination.innerHTML = output;
      } else {
          var output = '\n<div id="custom-field-container">';
          output += '<div class="field-controls"><a onclick="this.parentNode.parentNode.remove()"><img src="{% link assets/tabler_icons/x.svg %}" class="delete-module" /></a></div>';
          output += '<p class="editable" id="custom-field">'
              + moduleTitle + '</p>';
      }
  }

  // Tests if the RuleBuilder is empty
  function builderEmpty() {
      var builder = document.getElementById("module-input");
      var childs = builder.children;
      if (builder.getElementsByClassName("module").length > 0) {
          return false;
      } else {
          return true;
      }
  }

  // Turns RuleBuilder contents into an output-ready nested array
  // Returns empty array if no modules
  function builderArray() {
      var modules = document.getElementById("module-input").children;
      // takes an array of children
      // returns an array with all modules in the array, recursively nested
      function iterateArray (childs) {
          var moduleArray = [];
          if (childs.length > 0) {
              for (var i = 0; i < childs.length; i++) {
                  module = childs[i];
                  if (module.classList[0] == "module") {
                      var moduleName = module.children.item("module-name");
                      var moduleData = moduleName.title;
                      var moduleChilds = module.children;
                      moduleArray.push(
                          [stripHTML(moduleName.innerHTML),
                           stripHTML(moduleData),
                           iterateArray(moduleChilds)]);
                  }
              }
          }
          return moduleArray;
      } // end function
      return iterateArray(modules);
  }

  // returns HTML version of Builder content
  function displayBuilderHTML() {
      var output = "";
      var mainArray = builderArray();
      function arrayHTML(thisArray) {
          var thisOutput = "";
          if (thisArray.length > 0) {
              thisOutput += '<ul>\n';
              for (var i = 0; i < thisArray.length; i++) {
                  var item = thisArray[i];
                  thisOutput += '<li><strong>' + item[0] + '</strong> ';
                  thisOutput += item[1] + '</li>\n';
                  if (item[2].length > 0) {
                      thisOutput += arrayHTML(item[2]);
                  }
              }
              thisOutput += '</ul>\n';
          }
          return thisOutput
      }
      if (mainArray.length > 0) {
          output += '<div class="builder-list">\n';
          output += arrayHTML(mainArray) + '\n</div>\n';
      }
      return output;
  }

  // returns Markdown version of Builder content
  function displayBuilderMD() {
      var mainArray = builderArray();
      var indentLevel = 0;
      function arrayMD(thisArray) {
          var thisOutput = "";
          if (thisArray.length > 0) {
              for (var i = 0; i < thisArray.length; i++) {
                  var item = thisArray[i];
                  for (var x = 0; x < indentLevel; x++) {
                      thisOutput += "    ";
                  }
                  thisOutput += "* **" + item[0] + "** ";
                  thisOutput += item[1] + "\n";
                  if (item[2].length > 0) {
                      indentLevel++;                      
                      thisOutput += arrayMD(item[2]);
                      indentLevel--;
                  }
              }

          }
          return thisOutput;
      }
      return arrayMD(mainArray);
  }  
  
  // end RuleBuilder functions

  // Removes all HTML content, replacing line break tags with newlines
  function stripHTML(input) {
      input = input.replace(/<br ?\/?>/ig, "\n").replace(/(<([^>]+)>)/ig,'');
      return input;
  }
  
  // Intercepts the paste event for editable fields and
  // converts the pasted content to plain text,
  // stripping styles and unwanted markup added by programs like Word.
  function handleEditablePaste(event) {
      try {
          var pastedText = event.clipboardData
              ? event.clipboardData.getData("text/plain")
              : window.clipboardData.getData("Text"); // support IE
          var cleanedText = cleanPastedText(pastedText);
          // Pastes the cleaned up text.
          if (document.queryCommandSupported('insertText')) {
              document.execCommand('insertText', false, cleanedText);
          } else { // support IE
              document.execCommand('paste', false, cleanedText);
          }
          event.stopPropagation();
          event.preventDefault();
          return false;
      } catch (err) {
          // If anything goes wrong with browser compatibility,
          // pass the event through without modification.
          return true;
      }
  }

  // Removes junk that comes with pasting from text editors like Word.
  // taken from https://stackoverflow.com/questions/2875027/clean-microsoft-word-pasted-text-using-javascript
  function cleanPastedText(text) {
      return text.replace(/.*<!--.*-->/g, "");
  }


  // toggleVisible(id)
  // Toggles the visibility of a given element by given ID
  function toggleVisible(id) {
      var x = document.getElementById(id);
      if (x.style.display === "none") {
          x.style.display = "block";
      } else {
          x.style.display = "none";
      }
  }

  // classDisplayAll(className, value)
  // Assigns given display value to all elements with a given className
  function classDisplayAll(className, value) {
      var elements = document.getElementsByClassName(className);
      for (var i = 0; i < elements.length; i++) {
          elements[i].style.display = value;
      } 
  }

  // toggleEditMode()
  // Toggles whether editable fields are editable or not
  // and removes editing tools.
  function toggleEditMode() {
      if (editMode === true) {
          disableEditMode();
        } else {
          enableEditMode();
      }
    }

  function disableEditMode() { // switch to preview mode
        editMode = false;

        document.getElementById("rulebox").classList.add('rulebox-preview');
        document.getElementById("rulebox").classList.remove('rulebox-edit');

        var editableFields = document.getElementsByClassName("editable");  
        // de-editable-ize the editable fields
        for (var i = 0; i < editableFields.length; i++) {
            editableFields[i].contentEditable = "false";
            // Remove empty fields entirely
            var content = editableFields[i].innerHTML;
            content = stripHTML(content);
            if (content === "") {
                // editableFields[i].style.display = "none";
            }   
        }

        // RuleBuilder sections
        if (builderEmpty()) {
        } else {
            document.getElementById("builder-field").innerHTML = displayBuilderHTML();
        }
        if (document.contains(document.getElementById("custom-field-container"))) {
            document.getElementById("custom-field-container").remove();
        }

        // Handle author link
        var authorName = document.getElementById("author-text").value;
        var authorURL = document.getElementById("author-url").value;
        if (authorName != "") {
            if (authorURL != "") { // both author and URL present                
                document.getElementById("authorship-result").innerHTML = "<a href='" + authorURL +"'>" + authorName + "</a>";
            } else { // only authorName present
                document.getElementById("authorship-result").innerHTML = authorName;
            }
            document.getElementById("authorship").style.display = "";
        } else {
            document.getElementById("authorship").style.display = "none";
        }
        // Finally, change button name
        document.getElementById("editToggle").innerHTML = "Customize";
    }

  function enableEditMode() { // Switch to editMode
        editMode = true;

        document.getElementById("rulebox").classList.remove('rulebox-preview');
        document.getElementById("rulebox").classList.add('rulebox-edit');

        // RuleBuilder handling
        document.getElementById("builder-field").innerHTML = "";
        // make all editable fields visible
        var editableFields = document.getElementsByClassName("editable");
        for (var i = 0; i < editableFields.length; i++) {
            editableFields[i].contentEditable = "true";
        }
        // Change button name
        document.getElementById("editToggle").innerHTML = "Preview";
    }

  // toggleDisplayMode()
  // toggles full displayMode, the Rule-only display for a published Rule
  // first, initialize variable:
  var displayMode = false;
  function toggleDisplayMode() {
      if (displayMode == false) {
          document.body.classList.add("display_rule");
          editMode = true;
          disableEditMode(); // turns off editMode
          classDisplayAll("site-nav","none");
          classDisplayAll("post-header","none");
          classDisplayAll("site-footer","none");
          document.getElementById("attribution").style.display = "block";
          document.getElementById("fork").style.display = "inline-block";
          document.getElementById("discuss-button").style.display = "inline-block";
          document.getElementById("publishRule").style.display = "none";
          document.getElementById("trash").style.display = "inline-block";
          // Turn on RuleWriter if there's content
          if ("" != document.getElementById("rulewriter").innerHTML) {
              document.getElementById("rulewriter-box").style.display = "inline-block";
          }
          // Finish
          displayMode = true;
      } else {
          document.body.classList.remove("display_rule");
          enableEditMode() // turns on editMode
          classDisplayAll("site-nav","block");
          classDisplayAll("post-header","block");
          classDisplayAll("site-footer","block");
          document.getElementById("attribution").style.display = "none";
          document.getElementById("fork").style.display = "none";
          document.getElementById("discuss-button").style.display = "none";
          document.getElementById("publishRule").style.display = "inline-block";
          document.getElementById("trash").style.display = "none";
          displayMode = false;
      }
      if (document.getElementById("lineage-list").innerHTML != "") {
          document.getElementById("lineage").style.display = "block";
      }
  }

  // textOutput()
  // Produces Markdown rendition of Rule from Export button
  function textOutput() {
      var filename = 'GOVERNANCE.md';
      // First, add title, whether there is one or not
      var content = '# '+ document.getElementById('communityname').innerHTML + '\n\n';
      content = stripHTML(content);
      // Next, add structure field
      var structure = document.getElementById('structure').innerHTML;
      if (structure != "") {
          content += stripHTML(structure) + '\n\n';
      }
      // Add Builder content
      if (!builderEmpty()) {
          content += displayBuilderMD() + "\n\n";          
      }
      // Now, begin adding Writer elements
      var elements = document.getElementsByClassName('output');
      for (var i = 2; i < elements.length; i++) { // start after structure
          var thisBit = elements[i].innerHTML;
          thisBit = stripHTML(thisBit);
          if (thisBit != "") {
              if (elements[i].classList.contains("subhead")) {
                  // Before printing subhead, make sure it's not empty
                  var i2 = i + 1;
                  while ((i2 < elements.length) &&
                         (!(elements[i2].classList.contains("subhead")))) {
                      if (elements[i2].innerHTML != "") {
                          // in this case, it's not empty, so print and move on
                          content += '## ';
                          content += thisBit + '\n\n';
                          break;
                      } else { i2++; }
                  } // won't print anything if a subhead has only empty children
              } else {
                  // Non-subhead elements can just go ahead and print
                  content += thisBit + '\n\n';
              }
          }
      }
      // Add authorship block
      var authorName = document.getElementById("author-text").value;
      var authorURL = document.getElementById("author-url").value;
      var authorshipBlock = "---\n\nCreated by ";
      if (authorName != "") {
          if (authorURL != "") { // both author and URL present                
              authorshipBlock += ("[" + authorName + "](" + authorURL + ")");
          } else { // only authorName present
              authorshipBlock += authorName;
          }
          content += (authorshipBlock + "\n");
      } 
      // Add attribution block
      content += document.getElementById('attributionMD').innerHTML;
      // Starting here, see https://stackoverflow.com/a/33542499
      var blob = new Blob([content], {type: 'text/plain'});
      if(window.navigator.msSaveOrOpenBlob) {
          window.navigator.msSaveBlob(blob, filename);
      }
      else{
          var elem = window.document.createElement('a');
          elem.href = window.URL.createObjectURL(blob);
          elem.download = filename;
          document.body.appendChild(elem);
          elem.click();
          document.body.removeChild(elem);
          URL.revokeObjectURL(); // This needs an arg but I can't figure out what
      }
      var myFile = new Blob([fileContent], {type: 'text/plain'});
      window.URL = window.URL || window.webkitURL; document.getElementById('download').setAttribute('href',window.URL.createObjectURL(myFile));
      document.getElementById('download').setAttribute('download', fileName);
  }

  // BEGIN Publish tools, via SteinHQ.com

  // publishRule()
  // Publishes existing fields to new page, /builder/?rule=[ruleID]
  // Opens new page in Display mode
  function publishRule() {
      // Confirm user knows what they're getting into
      var r = confirm("Publish to the public Library?");
      if (r == false) { return; }
      // Proceed with publication
      var now = new Date();
      // Numerical ID for published Rule
      var timeID = now.getTime();
      // Readable UTC timestamp
      var dateTime = now.getUTCFullYear()+'.'+(now.getUTCMonth()+1)+'.'+now.getUTCDate()
          +' '+now.getUTCHours()+":"+ now.getUTCMinutes()+":"+now.getUTCSeconds()
          + ' UTC';
      // Check if ruleID exists; while yes, replace and repeat
      var rule = [{
          ruleID: timeID,
          timestamp: dateTime,
      }];
      // begin adding data
      // first, RuleBuilder data
      document.getElementById("builder-field").innerHTML = ""; // so it doesn't publish
      if (!builderEmpty()) {
          rule[0]["modules"] = document.getElementById("module-input").innerHTML;
      }
      // next, RuleWriter data
      var fields = document.getElementsByClassName("editable");
      for (var i = 0; i < fields.length; i++) {
          var key = fields[i].id;
          var value = "";
          // including input fields
          if (fields[i].nodeName == "INPUT") { // for <input>
              value = fields[i].value.replace(/(<([^>]+)>)/ig,"");
          } else { // for other fields
              value = fields[i].innerHTML.replace(/(<([^>]+)>)/ig,"");
          }
          rule[0][key] = value;
      }
      // add to lineage (if it is a fork)
      if (rID) {
          rule[0]["lineage"] = document.getElementById("lineage-list").innerHTML;
      }
      // add to database
      const store = new SteinStore(
          "https://api.steinhq.com/v1/storages/5e8b937ab88d3d04ae0816a5"
      );
      store.append("library", rule).then(data => {
          window.open("/create/?r=" + timeID, "_self", false);
      });
  }

  // addLineage
  // Adds the current page to the lineage
  function addLineage() {
      var communityname = document.getElementById("communityname").innerHTML;
      var newLineage = " < " + '<a href="/create/?r=' + rID + '">'
          + communityname + '</a>';
      var oldLineage = document.getElementById("lineage-list").innerHTML;
      document.getElementById("lineage-list").innerHTML = newLineage + oldLineage;
  }

  // fork()
  // Forks the current Rule and updates the derivation lineage
  function fork() {
      document.getElementById("lineage").style.display = "block";
      addLineage();
      toggleDisplayMode();
  }
  
  // displayRule(ID)
  // Displays content based on ID
  function displayRule(ID) {
      const store = new SteinStore(
          "https://api.steinhq.com/v1/storages/5e8b937ab88d3d04ae0816a5"
      );
      (async () => {
      var sheets = ["library","templates"];
      // create empty set of rules (should only get one member)
      var ruleArray = [];
      // read values from all sheets
      for (var i = 0; i < sheets.length; i++) {
          await store.read(sheets[i], { search: { ruleID: ID } }).then(data => {
              // test if there's anything in data
              if (data.length > 0) {
                  ruleArray = data;
              }
          });
      }
      // Only runs the rest if the array has something
      if (ruleArray.length > 0) {
          var rule = ruleArray[0];
          var fields = document.getElementsByClassName("editable");
          for (var i = 0; i < fields.length; i++) {
              var key = fields[i].id;
              var value = rule[key];
              if (typeof value === "undefined") {
                  value = "";
              } else if (key.includes("-")) { // links
                  document.getElementById(key).value = value;
              } else {
                  document.getElementById(key).innerHTML = value;
              }
          }
          // Add Builder content
          document.getElementById("module-input").innerHTML = rule["modules"];
          // Add lineage
          var lineage = rule["lineage"];
          if (typeof lineage === "undefined") {
              document.getElementById("lineage-list").innerHTML = "";
          } else {
              document.getElementById("lineage-list").innerHTML = lineage;
              document.getElementById("lineage").display = "block";
          }
          // Publish timestamp to Rule
          document.getElementById('dateTime').innerHTML = rule['timestamp'];
          // Finish
          displayMode = false;
          toggleDisplayMode();
          document.title = rule['communityname'] + " / CommunityRule";
      }
          })();
  }

  // deleteRule()
  // A temporary placeholder that sends an email requesting rule deletion
  function deleteRule() {
      var urlParamz = new URLSearchParams(window.location.search);      
      var rID = urlParamz.get('r');
      window.open("mailto:medlab@colorado.edu?subject=Delete Rule request ("
                  + rID + ")&body=Please explain your rationale:\n");
  }
  
  // END Publish tools

var rID;
  window.onload = function() {
  // FINALLY, Page loading
  // First, grab the current URL
  var urlParams = new URLSearchParams(window.location.search);
  // Determine if it is a published Rule
  if (urlParams.has('r')) {
      // If so, grab the Rule from database and enter displayMode
      rID = urlParams.get('r');
      document.body.classList.add("display_rule");
      displayRule(rID);
  } else {
      // Otherwise, open in editMode as default
      enableEditMode();
  }
  // eqip editable fields to remove formatting from pasted content    
      var editableElements = document.getElementsByClassName("editable");
      for (var i = 0; i < editableElements.length; i++ ) {
          editableElements[i].addEventListener("paste", handleEditablePaste);
      } 
  }
</script>
