Browse Source

Okay added new files for Rule-making pages

Nathan Schneider 4 years ago
parent
commit
8378407a59
4 changed files with 525 additions and 0 deletions
  1. 465 0
      _layouts/build.html
  2. 9 0
      rule-builder.md
  3. 13 0
      rule-picker.md
  4. 38 0
      rule-writer.md

+ 465 - 0
_layouts/build.html

@@ -0,0 +1,465 @@
+---
+layout: default
+# This is where most of the code for CommunityRule lives
+# Follow comments below in various sections for further explanation
+---
+
+<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);
+  }
+  function drop(ev) {
+      ev.preventDefault();
+      var target = ev.target;
+      // Prevents dropping into text field
+      if (target.id == "drag-directions") {
+          target = target.parentElement;
+      }
+      // 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 = module.cloneNode(true);
+          // Add a corresponding text field
+          var name = module.innerHTML.replace(/(<([^>]+)>)/ig,'');
+          var query = "Explain how the <strong>" + name + "</strong> module works.";
+          addField(query);
+      }
+      // append name
+      module.id += "-dropped";
+      // display the deletion button
+      module.children[2].style.display = "inline";
+      // pop it in!
+      target.appendChild(module);
+      // be sure the dummy text is gone
+      document.getElementById("drag-directions").style.display = "none";
+      // Send field contents to console to aid in database stuff
+      console.log(document.getElementById("module-input"));   
+  }
+
+  // Adds a new question field
+  function addField(content) {
+      // if input is null, add an empty field to the list
+      // include a field removal button
+      var destination = document.getElementById("builder-field");
+      if (content == null) { content = ""; }     
+      var output = '\n<div><a onclick="this.parentNode.remove()" id="delete-module"><img src="{% link assets/tabler_icons/x.svg %}" align="right"/></a>';
+      output += '<span class="question">' + content + '</span>';
+      output += '<p contenteditable="true" class="editable output" id="custom-field"></p></div>\n';
+      destination.innerHTML += output;
+  }
+  // end BUILDER functions
+  
+  // 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) { // switch to preview mode
+          editMode = false;
+          classDisplayAll("section","block");
+          classDisplayAll("button","none");
+          classDisplayAll("question","none");
+          var editableFields = document.getElementsByClassName("editable");  
+          // de-editable-ize the editable fields
+          for (var i = 0; i < editableFields.length; i++) {
+              editableFields[i].contentEditable = "false";
+              editableFields[i].style.borderStyle = "none";
+              // Remove empty fields entirely
+              var content = editableFields[i].innerHTML;
+              content = content.replace(/(<([^>]+)>)/ig,''); // strips stray tags
+              if (content === "") {
+                  editableFields[i].style.display = "none";
+              }   
+          }
+          // Remove headers of empty sections
+          // Inefficient! Might be merged with the above iteration
+          var sections = document.getElementsByClassName("section");
+          for (var i = 0; i < sections.length; i++) {
+              var sectionQuestions = sections[i].getElementsByClassName("editable");
+              var blanks = 0;
+              for (var x = 0; x < sectionQuestions.length; x++) {
+                  var content = sectionQuestions[x].innerHTML;
+                  content = content.replace(/(<([^>]+)>)/ig,''); // strips stray tags
+                  if (content == "") { blanks++; }
+                  if (blanks == sectionQuestions.length) {
+                      var headerID = "header-s" + (i + 1);
+                      document.getElementById(headerID).style.display = "none";
+                  }
+              }
+          }
+          // Handle links
+          // TKTK
+          // Handle author link
+          var authorName = document.getElementById("author-text").value;
+          var authorURL = document.getElementById("author-url").value;
+          if (authorName != "") {
+              document.getElementById("authorship-words").style.display = "inline";
+              if (authorURL != "") { // both author and URL present                
+                  document.getElementById("authorship-result").innerHTML = "<a href='" + authorURL +"'>" + authorName + "</a>";
+                  document.getElementById("authorship-result").style.display = "inline";
+              } else { // only authorName present
+                  document.getElementById("authorship-result").innerHTML = authorName;
+                  document.getElementById("authorship-result").style.display = "inline";
+              }
+          } else {
+              document.getElementById("authorship").style.display = "none";
+          }
+          // Finally, change button name
+          document.getElementById("editToggle").innerHTML = "Customize";
+      } else { // Switch to editMode
+          editMode = true;
+          classDisplayAll("button","block");
+          classDisplayAll("question","block");
+          classDisplayAll("editable","block");
+          classDisplayAll("header","block");
+          classDisplayAll("section","none");
+          classDisplayAll("link-text","inline");
+          classDisplayAll("link-url","inline");
+          // link handling TKTK
+          // author handling
+          document.getElementById("authorship-result").style.display = "none";
+          document.getElementById("authorship-words").style.display = "none";
+          document.getElementById("authorship").style.display = "block";
+          // make all editable fields visible
+          var editableFields = document.getElementsByClassName("editable");
+          for (var i = 0; i < editableFields.length; i++) {
+              editableFields[i].style.borderStyle = "none none dashed none";
+              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) {
+          editMode = true;
+          toggleEditMode(); // turns off editMode
+          classDisplayAll("site-nav","none");
+          classDisplayAll("post-header","none");
+          classDisplayAll("site-footer","none");
+          document.getElementById("attribution").style.display = "block";
+          document.getElementById("toggleDisplayMode").style.display = "inline-block";
+          document.getElementById("publishRule").style.display = "none";
+          document.getElementById("trash").style.display = "inline-block";
+          displayMode = true;
+      } else {
+          toggleEditMode() // turns on editMode
+          classDisplayAll("site-nav","block");
+          classDisplayAll("post-header","block");
+          classDisplayAll("site-footer","block");
+          document.getElementById("attribution").style.display = "none";
+          document.getElementById("toggleDisplayMode").style.display = "none";
+          document.getElementById("publishRule").style.display = "inline-block";
+          document.getElementById("trash").style.display = "none";
+          displayMode = false;
+      }
+  }
+
+  // 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 = content.replace(/(<([^>]+)>)/ig,''); // strips stray tags
+      // Now, begin adding other elements
+      var elements = document.getElementsByClassName('output');
+      for (var i = 1; i < elements.length; i++) {
+          var thisBit = elements[i].innerHTML;
+          thisBit = thisBit.replace(/(<([^>]+)>)/ig,''); // strips stray tags
+          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, /create/?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';
+      // TKTK: Check if ruleID exists; while yes, replace and repeat
+      var rule = [{
+          ruleID: timeID,
+          timestamp: dateTime,
+      }];
+      var fields = document.getElementsByClassName("editable");
+      for (var i = 0; i < fields.length; i++) {
+          var key = fields[i].id;
+          var value = "";
+          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;
+      }
+      const store = new SteinStore(
+          "https://api.steinhq.com/v1/storages/5e8b937ab88d3d04ae0816a5"
+      );
+      store.append("rules", rule).then(data => {
+          window.open("/create/?r=" + timeID, "_self", false);
+      });
+  }
+
+  // displayRule(ID)
+  // Displays content based on ID
+  function displayRule(ID) {
+      const store = new SteinStore(
+          "https://api.steinhq.com/v1/storages/5e8b937ab88d3d04ae0816a5"
+      );     
+      store.read("rules", { search: { ruleID: ID } }).then(data => {
+          // only runs when we have the data from Goog:
+          var rule = data[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;
+              }
+          }
+          // 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
+
+  
+  // 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
+      var rID = urlParams.get('r');
+      displayRule(rID);
+  } else {
+      // Otherwise, open in editMode as default
+      var editMode = true;
+      // switch out of editMode in special cases
+      window.onload = function() {
+          if ((window.location.href.indexOf("/templates/") != -1) ||
+              (window.location.href.indexOf("/about/") != -1)) {
+              toggleEditMode();
+          }
+      }
+  }  
+  
+</script>
+
+<article class="post">
+
+  <header class="post-header">
+    <h1 class="post-title" id="title">      
+        {{ page.title }}
+    </h1>
+
+    <button class="pushButton" id="editToggle" onclick="toggleEditMode()">
+      Preview</button>
+
+  <div class="post-content">    
+    {{ content }}
+  </div>
+
+  </header>
+  
+  <div id="rulebox">
+
+    <span class="question">What is the community’s name?</span>
+    <h1 contenteditable="true" class="editable output" id="communityname">{{ page.community-name }}</h1>
+
+    <!-- BUILDER -->
+    <div ondrop="drop(event)" ondragover="allowDrop(event)"
+         class="modulebox" id="module-input">
+      <span class="question" id="drag-directions">
+        Drag modules from the icon at right</span>      
+      <div class="button">
+        <img src="{% link assets/tabler_icons/tool.svg %}" title="Modules" />
+        <div class="tooltiptext" id="module-menu">
+          <!-- Customizable module -->
+          <span class="module" id="module-custom"
+                draggable="true" ondragstart="drag(event)">
+			<input contenteditable="true" placeholder="Custom..." />
+            <img src="{% link assets/tabler_icons/bulb.svg %}"
+                 draggable="false" />
+		    <a onclick="this.parentNode.remove()" id="delete-module"
+			   style="display:none">
+			  <img src="{% link assets/tabler_icons/x.svg %}" /></a>
+		  </span>
+		  <!-- Load preset modules from _data/modules.csv -->
+		  {% for module in site.data.modules %}
+		  <span class="module fixed" id="module-{{ module.id }}"
+			  draggable="true" ondragstart="drag(event)">
+		    <span>{{ module.name }}</span>
+		  <a target="_blank" href="{{ module.url }}">
+			<img src="{% link assets/tabler_icons/link.svg %}"
+				 draggable="false" /></a>
+		  <a onclick="this.parentNode.remove()" id="delete-module"
+			 style="display:none">
+			<img src="{% link assets/tabler_icons/x.svg %}" /></a>
+		  </span>
+          {% endfor %}
+        </div>
+      </div>
+    </div>
+    <div id="builder-field">
+    </div>
+
+  <div id="authorship" class="linkbox">
+    <span id="authorship-words">Created by</span>
+    <input contenteditable="true" class="editable link-text" id="author-text" placeholder="Created by" />
+    <span class="link-divider"><img src="{% link assets/tabler_icons/pencil.svg %}" title="Add link" /></span>
+    <input contenteditable="true" class="editable link-url" id="author-url" placeholder="Creator URL (http://, https://)" type="url" pattern="http://.*|https://.*" />
+    <span id="authorship-result"></span>
+  </div>
+  
+  </div><!--#rulebox-->
+ 
+  <div id="attribution" style="display:none;">
+    <br />
+    <p><a href="https://communityrule.info">
+        <img src="https://communityrule.info{% link assets/CommunityRule-derived-000000.svg %}" alt="CommunityRule derived"></a></p>
+    <p id="dateTime"></p>
+    <p>Created with <a href="https://communityrule.info">CommunityRule</a><br />
+      <a href="https://creativecommons.org/licenses/by-sa/4.0/">Creative Commons BY-SA</a></p>
+    <p><strong>The Publish feature is experimental. Rules may be removed without notice</strong></p>
+  </div>
+  <div id="attributionMD" style="display:none;">
+---
+
+[![CommunityRule derived](https://communityrule.info{% link assets/CommunityRule-derived-000000.svg %})](https://communityrule.info)
+
+[Creative Commons BY-SA](https://creativecommons.org/licenses/by-sa/4.0/)</div>
+  
+<!--  
+  <button class="pushButton" id="publishRule" onclick="publishRule()"
+    title="Add to the public Library">Publish</button>
+  <button class="pushButton" id="toggleDisplayMode" onclick="toggleDisplayMode()"
+    title="Edit this Rule into a new one">Fork</button>
+  <button class="pushButton" onclick="textOutput()"
+    title="Download this Rule as a Markdown text file">Export</button>
+  <button class="pushButton" id="trash" onclick="deleteRule()">
+    <img src="{% link assets/tabler_icons/trash.svg %}" title="Rule deletion request" />
+  </button>
+  <button class="pushButton"
+          onclick="javascript:location.href='https://www.colorado.edu/lab/medlab/content/communityrule-user-feedback'">
+    Feedback
+  </button>
+-->
+
+  <span class="question">Publish and Export are not yet available for the RuleBuilder</span>
+  
+</article>
+
+

+ 9 - 0
rule-builder.md

@@ -0,0 +1,9 @@
+---
+layout: build
+title: RuleBuilder
+permalink: /rule-builder/
+icon: 
+
+---
+
+Build your Rule by dragging modules in the area below.

+ 13 - 0
rule-picker.md

@@ -0,0 +1,13 @@
+---
+layout: page
+title: RulePicker
+permalink: /rule-picker/
+---
+
+{% for rule in site.templates %}
+<h2>
+    <img src="{{ site.baseurl }}{{ rule.icon }}" />
+    <a href="{{ site.baseurl }}{{ rule.url }}">{{ rule.title }}</a>
+</h2>
+<p>{{ rule.structure }}</p>
+{% endfor %}

+ 38 - 0
rule-writer.md

@@ -0,0 +1,38 @@
+---
+layout: rule
+title: RuleWriter
+permalink: /rule-writer/
+icon: 
+
+community-name: 
+
+# BASICS
+structure: 
+mission:
+values:
+legal: 
+
+# PARTICIPANTS
+membership:
+removal:
+roles:
+limits: 
+
+# POLICY
+rights: 
+decision: 
+implementation: 
+oversight: 
+
+# PROCESS
+deliberation: 
+access: 
+economics: 
+grievances: 
+
+# EVOLUTION
+records: 
+modification: 
+---
+
+Use the questions below to start a rule from scratch. Answer questions in complete sentences, as the questions disappear.