From c88d9b55d7d5f7fe9eccfafba0631ed802b88f4a Mon Sep 17 00:00:00 2001
From: Frank Steinberg <steinberg@ibr.cs.tu-bs.de>
Date: Sun, 6 Oct 2019 22:06:03 +0200
Subject: [PATCH] First steps towards a web online translation editor.

---
 Makefile                          |   7 +-
 web/bjcp-styleguide.css           | 158 +++++++++++++++++++++
 web/edit.css                      |  43 ++++++
 web/edit.js                       |  64 +++++++++
 web/pell.css                      |  27 ++++
 web/pell.js                       | 224 ++++++++++++++++++++++++++++++
 xsl/bjcp-2015-styleguide-html.xsl | 217 ++++++++---------------------
 7 files changed, 579 insertions(+), 161 deletions(-)
 create mode 100644 web/bjcp-styleguide.css
 create mode 100644 web/edit.css
 create mode 100644 web/edit.js
 create mode 100644 web/pell.css
 create mode 100644 web/pell.js

diff --git a/Makefile b/Makefile
index f2f2388..b2b6a64 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@
 DEFILES		= $(shell ls de/*.xml)
 FIXFILES	= $(shell ls fix/*.xml)
 
-default: bjcp-2015-styleguide-orig.xml bjcp-2015-styleguide-de.xml bjcp-2015-styleguide-orig.html bjcp-2015-styleguide-de.html
+default: bjcp-2015-styleguide-orig.xml bjcp-2015-styleguide-de.xml bjcp-2015-styleguide-orig.html bjcp-2015-styleguide-de.html bjcp-2015-styleguide-de-edit.html
 
 cache/2015_Guidelines_Beer.docx:
 	@if [ ! -d cache ] ; then mkdir cache ; fi
@@ -32,6 +32,10 @@ bjcp-2015-styleguide-de.html: xsl/bjcp-2015-styleguide-html.xsl bjcp-2015-styleg
 	@xsltproc xsl/bjcp-2015-styleguide-html.xsl bjcp-2015-styleguide-de.xml > bjcp-2015-styleguide-de.html
 	@echo "built $@"
 
+bjcp-2015-styleguide-de-edit.html: xsl/bjcp-2015-styleguide-html.xsl bjcp-2015-styleguide-de.xml
+	@xsltproc --stringparam edit yes xsl/bjcp-2015-styleguide-html.xsl bjcp-2015-styleguide-de.xml > bjcp-2015-styleguide-de-edit.html
+	@echo "built $@"
+
 format:
 	@for f in de/*.xml ; do xmllint --format $$f | sed -e 's/ standalone="yes"//' > cache/tmp.xml ; cmp -s cache/tmp.xml $$f ; if [ $$? -ne 0 ] ; then cat cache/tmp.xml > $$f ; echo "reformatted $$f" ; fi ; rm cache/tmp.xml ; done
 
@@ -50,6 +54,7 @@ clean:
 	@rm -f bjcp-2015-styleguide-de.xml
 	@rm -f bjcp-2015-styleguide-orig.html
 	@rm -f bjcp-2015-styleguide-de.html
+	@rm -f bjcp-2015-styleguide-de-edit.html
 	@echo "cleanup done"
 
 distclean: clean
diff --git a/web/bjcp-styleguide.css b/web/bjcp-styleguide.css
new file mode 100644
index 0000000..cef49fb
--- /dev/null
+++ b/web/bjcp-styleguide.css
@@ -0,0 +1,158 @@
+
+styleguide { 
+  font-family: Helvetica, Arial, Geneva, sans-serif;
+  font-size: 8pt;
+}
+
+styleguide chapter table {
+  font-size: 8pt;
+}
+
+styleguide p.list-item {
+  list-style-type: decimal;
+}
+
+styleguide category, styleguide subcategory {
+  font-size: 9pt;
+  margin-top:1em;
+  margin-bottom:1em;
+  margin-left:0em;
+  display: list-item;
+  list-style-position: inside;
+}
+
+styleguide p {
+  margin-top:1em;
+}
+
+styleguide b {
+  font-weight: bold;
+}
+
+styleguide i {
+  font-style: italic;
+}
+
+styleguide u {
+  text-decoration: underline;
+}
+
+styleguide category:before, styleguide subcategory:before {
+  content: attr(id);
+}
+styleguide name {
+  display: inline-block;
+  margin-bottom:1em;
+}
+styleguide description {
+  font-size: 8pt;
+  margin-left:1em;
+  display: block;
+}
+styleguide description p {
+  margin-top: 0;
+}
+
+styleguide overall-impression:before {
+  content: "Overall Impression: ";
+}
+styleguide aroma:before {
+  content: "Aroma: ";
+}
+styleguide appearance:before {
+  content: "Appearance: ";
+}
+styleguide flavor:before {
+  content: "Flavor: ";
+}
+styleguide mouthfeel:before {
+  content: "Mouthfeel: ";
+}
+styleguide comments:before {
+  content: "Comments: ";
+}
+styleguide history:before {
+  content: "History: ";
+}
+styleguide characteristic-ingredients:before {
+  content: "Characteristic Ingredients: ";
+}
+styleguide style-comparison:before {
+  content: "Style Comparison: ";
+}
+styleguide entry-instructions:before {
+  content: "Entry Instructions: ";
+}
+styleguide specs:before {
+  content: "Vital Statistics: ";
+}
+styleguide commercial-examples:before {
+  content: "Commercial Examples: ";
+}
+styleguide tags:before {
+  content: "Tags: ";
+}
+styleguide strength-classifications:before {
+  content: "Strength Classifications: ";
+}
+
+styleguide overall-impression,
+styleguide aroma,
+styleguide appearance,
+styleguide flavor,
+styleguide mouthfeel,
+styleguide comments,
+styleguide history,
+styleguide characteristic-ingredients,
+styleguide style-comparison,
+styleguide specs,
+styleguide entry-instructions,
+styleguide commercial-examples,
+styleguide tags,
+styleguide strength-classifications {
+  position: relative;
+  margin-left: 1em;
+  display: block;
+  font-size: 8pt;
+}
+styleguide overall-impression:before,
+styleguide aroma:before,
+styleguide appearance:before,
+styleguide flavor:before,
+styleguide mouthfeel:before,
+styleguide comments:before,
+styleguide history:before,
+styleguide characteristic-ingredients:before,
+styleguide style-comparison:before,
+styleguide specs:before,
+styleguide entry-instructions:before,
+styleguide commercial-examples:before,
+styleguide tags:before,
+styleguide strength-classifications:before {
+  font-weight: bold;
+}
+
+styleguide specs div.ibu:before {
+  content: "IBU: ";
+}
+styleguide specs div.srm:before {
+  content: "SRM: ";
+}
+styleguide specs div.og:before {
+  content: "OG: ";
+}
+styleguide specs div.fg:before {
+  content: "FG: ";
+}
+styleguide specs div.abv:before {
+  content: "ABV: ";
+}
+styleguide specs div {
+  display: inline-block;
+}
+styleguide specs div * {
+  border: 1px solid;
+  display: inline-block;
+  width: 9em;
+}
+
diff --git a/web/edit.css b/web/edit.css
new file mode 100644
index 0000000..5d9a0af
--- /dev/null
+++ b/web/edit.css
@@ -0,0 +1,43 @@
+
+.pell-content{
+  border-bottom: 1px solid black;
+}
+
+div#editor button[title="save"],
+div#editor button[title="cancel"] {
+  width: 70px;
+}
+div#editor {
+  display: none;
+  position: fixed;
+  width: 100%;
+  top: 0; 
+  left: 0;
+  right: 0;
+  text-align: center;
+  font-family: Helvetica, Arial, Geneva, sans-serif;
+  font-size: 8pt;
+}
+div#editor-inner {
+  text-align: left;
+  margin: 50 auto;
+  padding: 5px;
+  width: 80%;
+  z-index: 2;
+  border: 2px solid black;
+  background: #ddd;
+}
+div#editor-inner input {
+  background: white;
+}
+div#markup {
+  font-family: courier;
+  font-size: 7pt;
+}
+div#render {
+  font-size: 7pt;
+}
+
+*[onclick] {
+  cursor: pointer;
+}
diff --git a/web/edit.js b/web/edit.js
new file mode 100644
index 0000000..5ad2141
--- /dev/null
+++ b/web/edit.js
@@ -0,0 +1,64 @@
+
+const editor = document.getElementById("editor");
+const pell = window.pell;
+const pelleditor = document.getElementById("pelleditor");
+const markup = document.getElementById("markup");
+
+pell.init({
+  element: pelleditor,
+  actions: [
+    'bold',
+    'italic',
+    'underline',
+    {
+      name: 'link',
+      result: () => {
+        const idref = window.prompt('Enter the target category ID');
+        if (idref) {
+          var selection = document.getSelection();
+          if (selection == "") {
+            text = window.prompt('Enter link text (optional)');
+            pell.exec('insertHTML', '<a href="#' + idref + '">' + text + '</a>');
+          } else {
+            //pell.exec('insertHTML', '<a idref="#' + idref + '">' + text + '</a>');
+            pell.exec('createLink', "#" + idref);
+          }
+        }
+      }
+    },
+    {
+      name: 'save',
+      icon: '<div style="background-color:pink;">save</div>',
+      title: 'save',
+      result: () => {
+        editor.style.display = "none";
+      }
+    },
+    {
+      name: 'cancel',
+      icon: '<div style="background-color:pink;">cancel</div>',
+      title: 'cancel',
+      result: () => {
+        editor.style.display = "none";
+      }
+    },
+  ],
+  onChange: (html) => {
+    x = html;
+    x = x.replace(/&nbsp;/g, " ");
+    x = x.replace(/<div>/g, " ");
+    x = x.replace(/<\/div>/g, " ");
+    x = x.replace(/<br>/g, " ");
+    x = x.replace(/  */g, " ");
+    markup.innerText = x;
+    render.innerHTML = x;
+  }
+})
+
+function doedit(elem) {
+  pelleditor.content.innerHTML = elem.innerHTML;
+  editor.style.display = "block";
+  markup.innerText = elem.innerHTML;
+  render.innerHTML = elem.innerHTML;
+}
+
diff --git a/web/pell.css b/web/pell.css
new file mode 100644
index 0000000..5d6916b
--- /dev/null
+++ b/web/pell.css
@@ -0,0 +1,27 @@
+.pell {
+  border: 1px solid rgba(10, 10, 10, 0.1);
+  box-sizing: border-box; }
+
+.pell-content {
+  background: white;
+  box-sizing: border-box;
+  /*height: 200px;*/
+  outline: 0;
+  /*overflow-y: auto;*/
+  padding: 10px; }
+
+.pell-actionbar {
+  background-color: #ddd;
+  border-bottom: 1px solid rgba(10, 10, 10, 0.1); }
+
+.pell-button {
+  background-color: transparent;
+  border: none;
+  cursor: pointer;
+  height: 30px;
+  outline: 0;
+  width: 30px;
+  vertical-align: bottom; }
+
+.pell-button-selected {
+  background-color: #F0F0F0; }
diff --git a/web/pell.js b/web/pell.js
new file mode 100644
index 0000000..36e2c22
--- /dev/null
+++ b/web/pell.js
@@ -0,0 +1,224 @@
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+	typeof define === 'function' && define.amd ? define(['exports'], factory) :
+	(factory((global.pell = {})));
+}(this, (function (exports) { 'use strict';
+
+var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+
+var defaultParagraphSeparatorString = 'defaultParagraphSeparator';
+var formatBlock = 'formatBlock';
+var addEventListener = function addEventListener(parent, type, listener) {
+  return parent.addEventListener(type, listener);
+};
+var appendChild = function appendChild(parent, child) {
+  return parent.appendChild(child);
+};
+var createElement = function createElement(tag) {
+  return document.createElement(tag);
+};
+var queryCommandState = function queryCommandState(command) {
+  return document.queryCommandState(command);
+};
+var queryCommandValue = function queryCommandValue(command) {
+  return document.queryCommandValue(command);
+};
+
+var exec = function exec(command) {
+  var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
+  return document.execCommand(command, false, value);
+};
+
+var defaultActions = {
+  bold: {
+    icon: '<b>B</b>',
+    title: 'Bold',
+    state: function state() {
+      return queryCommandState('bold');
+    },
+    result: function result() {
+      return exec('bold');
+    }
+  },
+  italic: {
+    icon: '<i>I</i>',
+    title: 'Italic',
+    state: function state() {
+      return queryCommandState('italic');
+    },
+    result: function result() {
+      return exec('italic');
+    }
+  },
+  underline: {
+    icon: '<u>U</u>',
+    title: 'Underline',
+    state: function state() {
+      return queryCommandState('underline');
+    },
+    result: function result() {
+      return exec('underline');
+    }
+  },
+  strikethrough: {
+    icon: '<strike>S</strike>',
+    title: 'Strike-through',
+    state: function state() {
+      return queryCommandState('strikeThrough');
+    },
+    result: function result() {
+      return exec('strikeThrough');
+    }
+  },
+  heading1: {
+    icon: '<b>H<sub>1</sub></b>',
+    title: 'Heading 1',
+    result: function result() {
+      return exec(formatBlock, '<h1>');
+    }
+  },
+  heading2: {
+    icon: '<b>H<sub>2</sub></b>',
+    title: 'Heading 2',
+    result: function result() {
+      return exec(formatBlock, '<h2>');
+    }
+  },
+  paragraph: {
+    icon: '&#182;',
+    title: 'Paragraph',
+    result: function result() {
+      return exec(formatBlock, '<p>');
+    }
+  },
+  quote: {
+    icon: '&#8220; &#8221;',
+    title: 'Quote',
+    result: function result() {
+      return exec(formatBlock, '<blockquote>');
+    }
+  },
+  olist: {
+    icon: '&#35;',
+    title: 'Ordered List',
+    result: function result() {
+      return exec('insertOrderedList');
+    }
+  },
+  ulist: {
+    icon: '&#8226;',
+    title: 'Unordered List',
+    result: function result() {
+      return exec('insertUnorderedList');
+    }
+  },
+  code: {
+    icon: '&lt;/&gt;',
+    title: 'Code',
+    result: function result() {
+      return exec(formatBlock, '<pre>');
+    }
+  },
+  line: {
+    icon: '&#8213;',
+    title: 'Horizontal Line',
+    result: function result() {
+      return exec('insertHorizontalRule');
+    }
+  },
+  link: {
+    icon: '&#128279;',
+    title: 'Link',
+    result: function result() {
+      var url = window.prompt('Enter the link URL');
+      if (url) exec('createLink', url);
+    }
+  },
+  image: {
+    icon: '&#128247;',
+    title: 'Image',
+    result: function result() {
+      var url = window.prompt('Enter the image URL');
+      if (url) exec('insertImage', url);
+    }
+  }
+};
+
+var defaultClasses = {
+  actionbar: 'pell-actionbar',
+  button: 'pell-button',
+  content: 'pell-content',
+  selected: 'pell-button-selected'
+};
+
+var init = function init(settings) {
+  var actions = settings.actions ? settings.actions.map(function (action) {
+    if (typeof action === 'string') return defaultActions[action];else if (defaultActions[action.name]) return _extends({}, defaultActions[action.name], action);
+    return action;
+  }) : Object.keys(defaultActions).map(function (action) {
+    return defaultActions[action];
+  });
+
+  var classes = _extends({}, defaultClasses, settings.classes);
+
+  var defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div';
+
+  var actionbar = createElement('div');
+  actionbar.className = classes.actionbar;
+  appendChild(settings.element, actionbar);
+
+  var content = settings.element.content = createElement('div');
+  content.contentEditable = true;
+  content.className = classes.content;
+  content.oninput = function (_ref) {
+    var firstChild = _ref.target.firstChild;
+
+    if (firstChild && firstChild.nodeType === 3) exec(formatBlock, '<' + defaultParagraphSeparator + '>');else if (content.innerHTML === '<br>') content.innerHTML = '';
+    settings.onChange(content.innerHTML);
+  };
+  content.onkeydown = function (event) {
+    if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {
+      setTimeout(function () {
+        return exec(formatBlock, '<' + defaultParagraphSeparator + '>');
+      }, 0);
+    }
+  };
+  appendChild(settings.element, content);
+
+  actions.forEach(function (action) {
+    var button = createElement('button');
+    button.className = classes.button;
+    button.innerHTML = action.icon;
+    button.title = action.title;
+    button.setAttribute('type', 'button');
+    button.onclick = function () {
+      return action.result() && content.focus();
+    };
+
+    if (action.state) {
+      var handler = function handler() {
+        return button.classList[action.state() ? 'add' : 'remove'](classes.selected);
+      };
+      addEventListener(content, 'keyup', handler);
+      addEventListener(content, 'mouseup', handler);
+      addEventListener(button, 'click', handler);
+    }
+
+    appendChild(actionbar, button);
+  });
+
+  if (settings.styleWithCSS) exec('styleWithCSS');
+  exec(defaultParagraphSeparatorString, defaultParagraphSeparator);
+
+  return settings.element;
+};
+
+var pell = { exec: exec, init: init };
+
+exports.exec = exec;
+exports.init = init;
+exports['default'] = pell;
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+})));
diff --git a/xsl/bjcp-2015-styleguide-html.xsl b/xsl/bjcp-2015-styleguide-html.xsl
index 2855688..12483af 100644
--- a/xsl/bjcp-2015-styleguide-html.xsl
+++ b/xsl/bjcp-2015-styleguide-html.xsl
@@ -7,6 +7,10 @@
 
 
 
+  <xsl:param name="edit">no</xsl:param>
+
+
+
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
 
 
@@ -17,169 +21,45 @@
 	<xsl:element name="title">
 	  <xsl:text>BJCP 2015 Styleguide</xsl:text>
 	</xsl:element>
-	<xsl:element name="style">
-
-styleguide { 
-  font-family: Helvetica, Arial, Geneva, sans-serif;
-  font-size: 8pt;
-}
-
-styleguide chapter table {
-  font-size: 8pt;
-}
-
-styleguide p.list-item {
-  list-style-type: decimal;
-}
-
-styleguide category, styleguide subcategory {
-  font-size: 9pt;
-  margin-top:1em;
-  margin-bottom:1em;
-  margin-left:0em;
-  display: list-item;
-  list-style-position: inside;
-}
-
-styleguide p {
-  margin-top:1em;
-}
-
-styleguide b {
-  font-weight: bold;
-}
-
-styleguide i {
-  font-style: italic;
-}
-
-styleguide u {
-  text-decoration: underline;
-}
-
-styleguide category:before, styleguide subcategory:before {
-  content: attr(id);
-}
-styleguide name {
-  display: inline-block;
-  margin-bottom:1em;
-}
-styleguide description {
-  font-size: 8pt;
-  margin-left:1em;
-  display: block;
-}
-styleguide description p {
-  margin-top: 0;
-}
-
-styleguide overall-impression:before {
-  content: "Overall Impression: ";
-}
-styleguide aroma:before {
-  content: "Aroma: ";
-}
-styleguide appearance:before {
-  content: "Appearance: ";
-}
-styleguide flavor:before {
-  content: "Flavor: ";
-}
-styleguide mouthfeel:before {
-  content: "Mouthfeel: ";
-}
-styleguide comments:before {
-  content: "Comments: ";
-}
-styleguide history:before {
-  content: "History: ";
-}
-styleguide characteristic-ingredients:before {
-  content: "Characteristic Ingredients: ";
-}
-styleguide style-comparison:before {
-  content: "Style Comparison: ";
-}
-styleguide entry-instructions:before {
-  content: "Entry Instructions: ";
-}
-styleguide specs:before {
-  content: "Vital Statistics: ";
-}
-styleguide commercial-examples:before {
-  content: "Commercial Examples: ";
-}
-styleguide tags:before {
-  content: "Tags: ";
-}
-styleguide strength-classifications:before {
-  content: "Strength Classifications: ";
-}
-
-styleguide overall-impression,
-styleguide aroma,
-styleguide appearance,
-styleguide flavor,
-styleguide mouthfeel,
-styleguide comments,
-styleguide history,
-styleguide characteristic-ingredients,
-styleguide style-comparison,
-styleguide specs,
-styleguide entry-instructions,
-styleguide commercial-examples,
-styleguide tags,
-styleguide strength-classifications {
-  position: relative;
-  margin-left: 1em;
-  display: block;
-  font-size: 8pt;
-}
-styleguide overall-impression:before,
-styleguide aroma:before,
-styleguide appearance:before,
-styleguide flavor:before,
-styleguide mouthfeel:before,
-styleguide comments:before,
-styleguide history:before,
-styleguide characteristic-ingredients:before,
-styleguide style-comparison:before,
-styleguide specs:before,
-styleguide entry-instructions:before,
-styleguide commercial-examples:before,
-styleguide tags:before,
-styleguide strength-classifications:before {
-  font-weight: bold;
-}
-
-styleguide specs div.ibu:before {
-  content: "IBU: ";
-}
-styleguide specs div.srm:before {
-  content: "SRM: ";
-}
-styleguide specs div.og:before {
-  content: "OG: ";
-}
-styleguide specs div.fg:before {
-  content: "FG: ";
-}
-styleguide specs div.abv:before {
-  content: "ABV: ";
-}
-styleguide specs div {
-  display: inline-block;
-}
-styleguide specs div * {
-  border: 1px solid;
-  display: inline-block;
-  width: 9em;
-}
-
+	<xsl:element name="link">
+	  <xsl:attribute name="rel">stylesheet</xsl:attribute>
+	  <xsl:attribute name="type">text/css</xsl:attribute>
+	  <xsl:attribute name="href">bjcp-styleguide.css</xsl:attribute>
 	</xsl:element>
+	<xsl:element name="link">
+	  <xsl:attribute name="rel">stylesheet</xsl:attribute>
+	  <xsl:attribute name="type">text/css</xsl:attribute>
+	  <xsl:attribute name="href">pell.css</xsl:attribute>
+	</xsl:element>
+	<xsl:if test="not($edit = 'no')">
+	  <xsl:element name="link">
+	    <xsl:attribute name="rel">stylesheet</xsl:attribute>
+	    <xsl:attribute name="type">text/css</xsl:attribute>
+	    <xsl:attribute name="href">edit.css</xsl:attribute>
+	  </xsl:element>
+	</xsl:if>
       </xsl:element>
       <xsl:element name="body">
 	<xsl:apply-templates select="." mode="copy"/>
+	<xsl:if test="not($edit = 'no')">
+	  <div id="editor">
+	    <div id="editor-inner">
+	      Your Author ID: <input type="text" name="author" id="author" />
+	      <div id="pelleditor"></div>
+	    </div>
+	    <div>Markup:<div id="markup"></div></div>
+	    <div>Preview:<div id="render"></div></div>
+	  </div>
+	  <div/>
+	  <xsl:element name="script">
+	    <xsl:attribute name="src">pell.js</xsl:attribute>
+	    <xsl:text> </xsl:text>
+	  </xsl:element>
+	  <xsl:element name="script">
+	    <xsl:attribute name="src">edit.js</xsl:attribute>
+	    <xsl:text> </xsl:text>
+	  </xsl:element>
+	</xsl:if>
       </xsl:element>
     </xsl:element>
   </xsl:template>
@@ -257,6 +137,9 @@ styleguide specs div * {
     <xsl:variable name="href">
       <xsl:value-of select="@href"/>
     </xsl:variable>
+    <xsl:variable name="idref">
+      <xsl:value-of select="translate(@href,'#','')"/>
+    </xsl:variable>
     <xsl:element name="{local-name(.)}">
       <xsl:attribute name="href">
 	<xsl:value-of select="$href"/>
@@ -266,7 +149,7 @@ styleguide specs div * {
 	  <xsl:value-of select="./text()"/>
 	</xsl:when>
 	<xsl:otherwise>
-	  <xsl:value-of select="//bjcp:styleguide//*[@id=$href]/bjcp:name"/>
+	  <xsl:value-of select="//bjcp:styleguide//bjcp:*[@id=$idref]/bjcp:name"/>
 	</xsl:otherwise>
       </xsl:choose>
     </xsl:element>
@@ -283,6 +166,20 @@ styleguide specs div * {
 
 
 
+  <xsl:template match="bjcp:name|bjcp:description|bjcp:overall-impression|bjcp:aroma|bjcp:appearance|bjcp:flavor|bjcp:mouthfeel|bjcp:comments|bjcp:history|bjcp:characteristic-ingredients|bjcp:style-comparison|bjcp:entry-instructions|bjcp:commercial-examples" mode="copy">
+    <xsl:element name="{local-name(.)}">
+      <xsl:apply-templates select="@*" mode="copy"/>
+      <xsl:if test="not($edit = 'no')">
+	<xsl:attribute name="onclick">
+	  <xsl:text>doedit(this);</xsl:text>
+	</xsl:attribute>
+      </xsl:if>
+      <xsl:apply-templates mode="copy"/>
+    </xsl:element>
+  </xsl:template>
+
+
+
   <xsl:template match="@id" mode="copy">
     <xsl:attribute name="id">
       <xsl:choose>
-- 
GitLab