From 7035fa66ec7a504e401e2109a961611e16e03a72 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Sun, 1 May 2016 15:06:27 +0100
Subject: [PATCH] Add unified diff to edit conflict page + tweak line height

---
 build/index.php       | 53 +++++++++++++++++++++++++++++++++----------
 module_index.json     |  8 +++----
 modules/page-edit.php | 38 ++++++++++++++++++++++++++-----
 settings.fragment.php | 13 +++++++----
 4 files changed, 85 insertions(+), 27 deletions(-)

diff --git a/build/index.php b/build/index.php
index 7d27f19..a865beb 100644
--- a/build/index.php
+++ b/build/index.php
@@ -290,7 +290,7 @@ $settings->sessionprefix = preg_replace("/[^0-9a-z]/i", "", strtolower($settings
 // A string of css to include. Will be included in the <head> of every page
 // inside a <style> tag. This may also be a url - urls will be referenced via a
 // <link rel='stylesheet' /> tag.
-$settings->css = "body { margin: 2rem 0; font-family: sans-serif; color: #111111; background: #eee8f2; }
+$settings->css = "body { margin: 2rem 0; background: #eee8f2; line-height: 1.45em; color: #111111; font-family: sans-serif; }
 
 nav { display: flex; background-color: #8a62a7; color: #ffa74d;  }
 nav.top { position: absolute; top: 0; left: 0; right: 0; box-shadow: inset 0 -0.6rem 0.8rem -0.5rem rgba(50, 50, 50, 0.5); }
@@ -347,13 +347,12 @@ a.redlink:visited { color: rgb(130, 15, 15); #8b1a1a }
 .search-result > h2 { margin-left: 2rem; }
 
 label:not(.link-display-label) { display: inline-block; min-width: 7rem; }
-input[type=text]:not(.link-display), input[type=password], textarea { margin: 0.5rem 0.8rem; }
+input[type=text]:not(.link-display), input[type=password], textarea { margin: 0.5rem 0; }
 input[type=text], input[type=password], textarea, #search-box { padding: 0.5rem 0.8rem; background: #d5cbf9; border: 0; border-radius: 0.3rem; font-size: 1rem; color: #442772; }
-textarea { min-height: 35rem; font-size: 1.25rem; }
+textarea { min-height: 35rem; line-height: 1.3em; font-size: 1.25rem; }
 textarea, textarea ~ input[type=submit], #search-box { width: calc(100% - 0.3rem); box-sizing: border-box; }
-textarea ~ input[type=submit] { margin: 0.5rem 0.8rem; padding: 0.5rem; font-weight: bolder; }
+textarea ~ input[type=submit] { margin: 0.5rem 0; padding: 0.5rem; font-weight: bolder; }
 .editform input[type=text] { width: calc(100% - 0.3rem); box-sizing: border-box; }
-.editing_message { margin: 0.8rem; }
 
 .file-gallery { margin: 0.5em; padding: 0.5em; list-style-type: none; }
 .file-gallery > li { display: inline-block; min-width: attr(data-gallery-width); padding: 1em; text-align: center; }
@@ -385,6 +384,10 @@ summary { cursor: pointer; }
 .significant { font-weight: bolder; font-size: 1.1rem; }
 .deletion, .deletion > .editor { text-decoration: line-through; }
 
+.highlighted-diff { white-space: pre-wrap; }
+.diff-added { background-color: rgba(31, 171, 36, 0.6); color: rgba(23, 125, 27, 1); }
+.diff-removed { background-color: rgba(255, 96, 96, 0.6); color: rgba(191, 38, 38, 1); }
+
 .newpage::before { content: \"N\"; margin: 0 0.3em 0 -1em; font-weight: bolder; text-decoration: underline dotted; }
 .upload::before { content: \"\\1f845\"; margin: 0 0.1em 0 -1.1em; }
 
@@ -3293,7 +3296,7 @@ register_module([
 
 register_module([
 	"name" => "Page editor",
-	"version" => "0.13.2",
+	"version" => "0.14",
 	"author" => "Starbeamrainbowlabs",
 	"description" => "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
 	"id" => "page-edit",
@@ -3366,7 +3369,7 @@ register_module([
 			<input type='hidden' name='prev-content-hash' value='" . sha1($pagetext) . "' />
 			<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
 			<input type='text' name='tags' value='$page_tags' placeholder='Enter some tags for the page here. Separate them with commas.' title='Enter some tags for the page here. Separate them with commas.' tabindex='2' />
-			<p class='editing_message'>$settings->editing_message</p>
+			<p class='editing-message'>$settings->editing_message</p>
 			<input name='submit-edit' type='submit' value='Save Page' tabindex='3' />
 		</form>";
 			exit(page_renderer::render_main("$title - $settings->sitename", $content));
@@ -3456,16 +3459,42 @@ register_module([
 				{
 					$content .= "<p><strong>Warning: You are not logged in! Your IP address <em>may</em> be recorded.</strong></p>";
 				}
-				$content .= "<p>An edit conflict has arisen because someone else has saved an edit to $env->page since you started editing it. Both texts are shown below. To continue, please merge your changes with the existing content. Note that only the text in the existing content box will be saved.</p>
-				<form method='post' action='index.php?action=save&page=" . rawurlencode($page) . "&action=save' class='editform'>
+				$content .= "<p>An edit conflict has arisen because someone else has saved an edit to $env->page since you started editing it. Both texts are shown below, along the differences between the 2 conflicting revisions. To continue, please merge your changes with the existing content. Note that only the text in the existing content box will be kept when you press the \"Resolve Conflict\" button at the bottom of the page.</p>
+			
+			<form method='post' action='index.php?action=save&page=" . rawurlencode($page) . "&action=save' class='editform'>
 				<h2>Existing content</h2>
-				<textarea name='content' autofocus tabindex='1'>$existingPageData</textarea>
+				<textarea id='original-content' name='content' autofocus tabindex='1'>$existingPageData</textarea>
+				
+				<h2>Differences</h2>
+				<div id='highlighted-diff' class='highlighted-diff'></div>
+				<!--<pre class='highlighted-diff-wrapper'><code id='highlighted-diff'></code></pre>-->
+				
 				<h2>Your content</h2>
-				<textarea>$pagedata</textarea>
+				<textarea id='new-content'>$pagedata</textarea>
 				<input type='text' name='tags' value='" . $_POST["tags"] . "' placeholder='Enter some tags for the page here. Separate them with commas.' title='Enter some tags for the page here. Separate them with commas.' tabindex='2' />
 				<p class='editing_message'>$settings->editing_message</p>
 				<input name='submit-edit' type='submit' value='Resolve Conflict' tabindex='3' />
 			</form>";
+				
+				// Insert a reference to jsdiff to generate the diffs
+				$diffScript = <<<'DIFFSCRIPT'
+window.addEventListener("load", function(event) {
+	var destination = document.getElementById("highlighted-diff"),
+		diff = JsDiff.diffWords(document.getElementById("original-content").value, document.getElementById("new-content").value),
+		output = "";
+	diff.forEach(function(change) {
+		var classes = "token";
+		if(change.added) classes += " diff-added";
+		if(change.removed) classes += " diff-removed";
+		output += `<span class='${classes}'>${change.value}</span>`;
+	});
+	destination.innerHTML = output;
+});
+DIFFSCRIPT;
+
+				$content .= "\n<script src='https://cdnjs.cloudflare.com/ajax/libs/jsdiff/2.2.2/diff.min.js'></script>
+		<script>$diffScript</script>\n";
+				
 				exit(page_renderer::render_main("Edit Conflict - $env->page - $settings->sitename", $content));
 			}
 			
@@ -4159,7 +4188,7 @@ register_module([
 
 register_module([
 	"name" => "Page viewer",
-	"version" => "0.12.1",
+	"version" => "0.13",
 	"author" => "Starbeamrainbowlabs",
 	"description" => "Allows you to view pages. You really should include this one.",
 	"id" => "page-view",
diff --git a/module_index.json b/module_index.json
index c672292..e4525cf 100644
--- a/module_index.json
+++ b/module_index.json
@@ -100,11 +100,11 @@
     },
     {
         "name": "Page editor",
-        "version": "0.13.2",
+        "version": "0.14",
         "author": "Starbeamrainbowlabs",
         "description": "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
         "id": "page-edit",
-        "lastupdate": 1460201129,
+        "lastupdate": 1462111205,
         "optional": false
     },
     {
@@ -172,11 +172,11 @@
     },
     {
         "name": "Page viewer",
-        "version": "0.12.1",
+        "version": "0.13",
         "author": "Starbeamrainbowlabs",
         "description": "Allows you to view pages. You really should include this one.",
         "id": "page-view",
-        "lastupdate": 1462015525,
+        "lastupdate": 1462110488,
         "optional": false
     },
     {
diff --git a/modules/page-edit.php b/modules/page-edit.php
index ab19e10..0e2225f 100644
--- a/modules/page-edit.php
+++ b/modules/page-edit.php
@@ -1,7 +1,7 @@
 <?php
 register_module([
 	"name" => "Page editor",
-	"version" => "0.13.2",
+	"version" => "0.14",
 	"author" => "Starbeamrainbowlabs",
 	"description" => "Allows you to edit pages by adding the edit and save actions. You should probably include this one.",
 	"id" => "page-edit",
@@ -74,7 +74,7 @@ register_module([
 			<input type='hidden' name='prev-content-hash' value='" . sha1($pagetext) . "' />
 			<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
 			<input type='text' name='tags' value='$page_tags' placeholder='Enter some tags for the page here. Separate them with commas.' title='Enter some tags for the page here. Separate them with commas.' tabindex='2' />
-			<p class='editing_message'>$settings->editing_message</p>
+			<p class='editing-message'>$settings->editing_message</p>
 			<input name='submit-edit' type='submit' value='Save Page' tabindex='3' />
 		</form>";
 			exit(page_renderer::render_main("$title - $settings->sitename", $content));
@@ -164,16 +164,42 @@ register_module([
 				{
 					$content .= "<p><strong>Warning: You are not logged in! Your IP address <em>may</em> be recorded.</strong></p>";
 				}
-				$content .= "<p>An edit conflict has arisen because someone else has saved an edit to $env->page since you started editing it. Both texts are shown below. To continue, please merge your changes with the existing content. Note that only the text in the existing content box will be saved.</p>
-				<form method='post' action='index.php?action=save&page=" . rawurlencode($page) . "&action=save' class='editform'>
+				$content .= "<p>An edit conflict has arisen because someone else has saved an edit to $env->page since you started editing it. Both texts are shown below, along the differences between the 2 conflicting revisions. To continue, please merge your changes with the existing content. Note that only the text in the existing content box will be kept when you press the \"Resolve Conflict\" button at the bottom of the page.</p>
+			
+			<form method='post' action='index.php?action=save&page=" . rawurlencode($page) . "&action=save' class='editform'>
 				<h2>Existing content</h2>
-				<textarea name='content' autofocus tabindex='1'>$existingPageData</textarea>
+				<textarea id='original-content' name='content' autofocus tabindex='1'>$existingPageData</textarea>
+				
+				<h2>Differences</h2>
+				<div id='highlighted-diff' class='highlighted-diff'></div>
+				<!--<pre class='highlighted-diff-wrapper'><code id='highlighted-diff'></code></pre>-->
+				
 				<h2>Your content</h2>
-				<textarea>$pagedata</textarea>
+				<textarea id='new-content'>$pagedata</textarea>
 				<input type='text' name='tags' value='" . $_POST["tags"] . "' placeholder='Enter some tags for the page here. Separate them with commas.' title='Enter some tags for the page here. Separate them with commas.' tabindex='2' />
 				<p class='editing_message'>$settings->editing_message</p>
 				<input name='submit-edit' type='submit' value='Resolve Conflict' tabindex='3' />
 			</form>";
+				
+				// Insert a reference to jsdiff to generate the diffs
+				$diffScript = <<<'DIFFSCRIPT'
+window.addEventListener("load", function(event) {
+	var destination = document.getElementById("highlighted-diff"),
+		diff = JsDiff.diffWords(document.getElementById("original-content").value, document.getElementById("new-content").value),
+		output = "";
+	diff.forEach(function(change) {
+		var classes = "token";
+		if(change.added) classes += " diff-added";
+		if(change.removed) classes += " diff-removed";
+		output += `<span class='${classes}'>${change.value}</span>`;
+	});
+	destination.innerHTML = output;
+});
+DIFFSCRIPT;
+
+				$content .= "\n<script src='https://cdnjs.cloudflare.com/ajax/libs/jsdiff/2.2.2/diff.min.js'></script>
+		<script>$diffScript</script>\n";
+				
 				exit(page_renderer::render_main("Edit Conflict - $env->page - $settings->sitename", $content));
 			}
 			
diff --git a/settings.fragment.php b/settings.fragment.php
index 13cc675..daa06dd 100644
--- a/settings.fragment.php
+++ b/settings.fragment.php
@@ -287,7 +287,7 @@ $settings->sessionprefix = preg_replace("/[^0-9a-z]/i", "", strtolower($settings
 // A string of css to include. Will be included in the <head> of every page
 // inside a <style> tag. This may also be a url - urls will be referenced via a
 // <link rel='stylesheet' /> tag.
-$settings->css = "body { margin: 2rem 0; font-family: sans-serif; color: #111111; background: #eee8f2; }
+$settings->css = "body { margin: 2rem 0; background: #eee8f2; line-height: 1.45em; color: #111111; font-family: sans-serif; }
 
 nav { display: flex; background-color: #8a62a7; color: #ffa74d;  }
 nav.top { position: absolute; top: 0; left: 0; right: 0; box-shadow: inset 0 -0.6rem 0.8rem -0.5rem rgba(50, 50, 50, 0.5); }
@@ -344,13 +344,12 @@ a.redlink:visited { color: rgb(130, 15, 15); #8b1a1a }
 .search-result > h2 { margin-left: 2rem; }
 
 label:not(.link-display-label) { display: inline-block; min-width: 7rem; }
-input[type=text]:not(.link-display), input[type=password], textarea { margin: 0.5rem 0.8rem; }
+input[type=text]:not(.link-display), input[type=password], textarea { margin: 0.5rem 0; }
 input[type=text], input[type=password], textarea, #search-box { padding: 0.5rem 0.8rem; background: #d5cbf9; border: 0; border-radius: 0.3rem; font-size: 1rem; color: #442772; }
-textarea { min-height: 35rem; font-size: 1.25rem; }
+textarea { min-height: 35rem; line-height: 1.3em; font-size: 1.25rem; }
 textarea, textarea ~ input[type=submit], #search-box { width: calc(100% - 0.3rem); box-sizing: border-box; }
-textarea ~ input[type=submit] { margin: 0.5rem 0.8rem; padding: 0.5rem; font-weight: bolder; }
+textarea ~ input[type=submit] { margin: 0.5rem 0; padding: 0.5rem; font-weight: bolder; }
 .editform input[type=text] { width: calc(100% - 0.3rem); box-sizing: border-box; }
-.editing_message { margin: 0.8rem; }
 
 .file-gallery { margin: 0.5em; padding: 0.5em; list-style-type: none; }
 .file-gallery > li { display: inline-block; min-width: attr(data-gallery-width); padding: 1em; text-align: center; }
@@ -382,6 +381,10 @@ summary { cursor: pointer; }
 .significant { font-weight: bolder; font-size: 1.1rem; }
 .deletion, .deletion > .editor { text-decoration: line-through; }
 
+.highlighted-diff { white-space: pre-wrap; }
+.diff-added { background-color: rgba(31, 171, 36, 0.6); color: rgba(23, 125, 27, 1); }
+.diff-removed { background-color: rgba(255, 96, 96, 0.6); color: rgba(191, 38, 38, 1); }
+
 .newpage::before { content: \"N\"; margin: 0 0.3em 0 -1em; font-weight: bolder; text-decoration: underline dotted; }
 .upload::before { content: \"\\1f845\"; margin: 0 0.1em 0 -1.1em; }
 
