From bcf562e7caeecf1f3025a8ff3507a9081a03dcf6 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Tue, 21 Nov 2017 20:07:50 +0000
Subject: [PATCH] Add restore locally changed content button

---
 Changelog.md          |   1 +
 build/index.php       | 118 ++++++++++++++++++++++++++++++++----------
 module_index.json     |   4 +-
 modules/page-edit.php |  20 +++++--
 4 files changed, 110 insertions(+), 33 deletions(-)

diff --git a/Changelog.md b/Changelog.md
index 6b6fbbf..55e6521 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -15,6 +15,7 @@ This file holds the changelog for Pepperminty Wiki. This is the master list of t
  - The `history` action now supports `format=json` and `format=csv`
  - Added tags next to the names of pages in the search results
  - Added new `random_page_exclude` setting that allows you to exclude pages from the random action with a (PHP) regular expression
+ - Added "restore locally saved content" button to page editor
  - [module api] Added new `get_page_parent($pagename)` method.
  - [module api] Added new remote file system to download additional required files. Use it with `register_remote_file`
 
diff --git a/build/index.php b/build/index.php
index 0b27a17..b74f3cb 100644
--- a/build/index.php
+++ b/build/index.php
@@ -4020,9 +4020,14 @@ window.addEventListener("load", function(event) {
 	}
 ]);
 
+/**
+ * Holds a collection to methods to manipulate various types of search index.
+ */
 class search
 {
-	// Words that we should exclude from the inverted index
+	/**
+	 * Words that we should exclude from the inverted index
+	 */
 	public static $stop_words = [
 		"a", "about", "above", "above", "across", "after", "afterwards", "again",
 		"against", "all", "almost", "alone", "along", "already", "also",
@@ -4068,6 +4073,12 @@ class search
 		"your", "yours", "yourself", "yourselves"
 	];
 	
+	/**
+	 * Converts a source string into an index of search terms that can be
+	 * merged into an inverted index.
+	 * @param  string $source The source string to index.
+	 * @return array         An index represents the specified string.
+	 */
 	public static function index($source)
 	{
 		$source = html_entity_decode($source, ENT_QUOTES);
@@ -4098,6 +4109,11 @@ class search
 		return $index;
 	}
 	
+	/**
+	 * Converts a source string into a series of raw tokens.
+	 * @param	string	$source	The source string to process.
+	 * @return	array	An array of raw tokens extracted from the specified source string.
+	 */
 	public static function tokenize($source)
 	{
 		$source = strtolower($source);
@@ -4105,11 +4121,21 @@ class search
 		return preg_split("/((^\p{P}+)|(\p{P}*\s+\p{P}*)|(\p{P}+$))|\|/u", $source, -1, PREG_SPLIT_NO_EMPTY);
 	}
 	
+	/**
+	 * Removes (most) markdown markup from the specified string.
+	 * Stripped strings are not suitable for indexing!
+	 * @param	string	$source	The source string to process.
+	 * @return	string			The stripped string.
+	 */
 	public static function strip_markup($source)
 	{
 		return str_replace([ "[", "]", "\"", "*", "_", " - ", "`" ], "", $source);
 	}
 	
+	/**
+	 * Rebuilds the master inverted index and clears the page id index.
+	 * @param	boolean	$output	Whether to send progress information to the user's browser.
+	 */
 	public static function rebuild_invindex($output = true)
 	{
 		global $pageindex, $env, $paths, $settings;
@@ -4158,25 +4184,23 @@ class search
 		self::save_invindex($paths->searchindex, $invindex);
 	}
 	
-	/*
-	 * @summary Sorts an index alphabetically. Will also sort an inverted index.
-	 * 			This allows us to do a binary search instead of a regular
-	 * 			sequential search.
+	/**
+	 * Sorts an index alphabetically. Will also sort an inverted index.
+	 * This allows us to do a binary search instead of a regular
+	 * sequential search.
+	 * @param	array	$index	The index to sort.
 	 */
 	public static function sort_index(&$index)
 	{
 		ksort($index, SORT_NATURAL);
 	}
 	
-	/*
-	 * @summary Compares two *regular* indexes to find the differences between them.
-	 * 
-	 * @param {array} $indexa - The old index.
-	 * @param {array} $indexb - The new index.
-	 * @param {array} $changed - An array to be filled with the nterms of all
-	 * 							 the changed entries.
-	 * @param {array} $removed - An array to be filled with the nterms of all
-	 * 							 the removed entries.
+	/**
+	 * Compares two *regular* indexes to find the differences between them.
+	 * @param	array	$oldindex	The old index.
+	 * @param	array	$newindex	The new index.
+	 * @param	array	$changed	An array to be filled with the nterms of all the changed entries.
+	 * @param	array	$removed	An array to be filled with the nterms of all  the removed entries.
 	 */
 	public static function compare_indexes($oldindex, $newindex, &$changed, &$removed)
 	{
@@ -4193,15 +4217,19 @@ class search
 		}
 	}
 	
-	/*
-	 * @summary Reads in and parses an inverted index.
+	/**
+	 * Reads in and parses an inverted index.
+	 * @param	string	$invindex_filename	The path tp the inverted index to parse.
+	 * @todo	Remove this function and make everything streamable
 	 */
-	// Todo remove this function and make everything streamable
 	public static function load_invindex($invindex_filename) {
 		$invindex = json_decode(file_get_contents($invindex_filename), true);
 		return $invindex;
 	}
-	
+	/**
+	 * Reads in and parses an inverted index, measuring the time it takes to do so.
+	 * @param  string $invindex_filename The path to the file inverted index to parse.
+	 */
 	public static function measure_invindex_load_time($invindex_filename) {
 		global $env;
 		
@@ -4210,8 +4238,12 @@ class search
 		$env->perfdata->searchindex_decode_time = round((microtime(true) - $searchindex_decode_start)*1000, 3);
 	}
 	
-	/*
-	 * @summary Merge an index into an inverted index.
+	/**
+	 * Merge an index into an inverted index.
+	 * @param	array	$invindex	The inverted index to merge into.
+	 * @param	int		$pageid		The id of the page to assign to the index that's being merged.
+	 * @param	array	$index		The regular index to merge.
+	 * @param	array	$removals	An array of index entries to remove from the inverted index. Useful for applying changes to an inverted index instead of deleting and remerging an entire page's index.
 	 */
 	public static function merge_into_invindex(&$invindex, $pageid, &$index, &$removals = [])
 	{
@@ -4262,11 +4294,22 @@ class search
 		}
 	}
 	
+	/**
+	 * Saves the given inverted index back to disk.
+	 * @param	string	$filename	The path to the file to save the inverted index to.
+	 * @param	array	$invindex	The inverted index to save.
+	 */
 	public static function save_invindex($filename, &$invindex)
 	{
 		file_put_contents($filename, json_encode($invindex));
 	}
 	
+	/**
+	 * Searches the given inverted index for the specified search terms.
+	 * @param	string	$query		The search query.
+	 * @param	array	$invindex	The inverted index to search.
+	 * @return	array	An array of matching pages.
+	 */
 	public static function query_invindex($query, &$invindex)
 	{
 		global $settings, $pageindex;
@@ -4368,7 +4411,7 @@ class search
 			
 			// Sort the new list of clump distances
 			sort($clumpDistances);
-			// Calcualate a measureof how clumped the offsets are
+			// Calcualate a measure of how clumped the offsets are
 			$tightClumpLimit = floor((count($clumpDistances) - 1) / 0.25);
 			$tightClumpsMeasure = $clumpDistances[$tightClumpLimit] - $clumpDistances[0];
 			$clumpsRange = $clumpDistances[count($clumpDistances) - 1] - $clumpDistances[0];
@@ -4395,6 +4438,13 @@ class search
 		return $matching_pages;
 	}
 	
+	/**
+	 * Extracts a context string (in HTML) given a search query that could be displayed
+	 * in a list of search results.
+	 * @param	string	$query	The search queary to generate the context for.
+	 * @param	string	$source	The page source to extract the context from.
+	 * @return	string			The generated context string.
+	 */
 	public static function extract_context($query, $source)
 	{
 		global $settings;
@@ -4479,6 +4529,12 @@ class search
 		return implode(" ... ", $contexts);
 	}
 	
+	/**
+	 * Highlights the keywords of a context string.
+	 * @param	string	$query		The query  to use when highlighting.
+	 * @param	string	$context	The context string to highlight.
+	 * @return	string				The highlighted (HTML) string.
+	 */
 	public static function highlight_context($query, $context)
 	{
 		$qterms = self::tokenize($query);
@@ -6165,6 +6221,7 @@ register_module([
 
 			$content .= "<form method='post' name='edit-form' action='index.php?action=preview-edit&page=" . rawurlencode($env->page) . "' class='editform'>
 					<input type='hidden' name='prev-content-hash' value='" . ((isset($old_pagetext)) ? sha1($old_pagetext) : sha1($pagetext)) . "' />
+					<button class='smartsave-restore'>Restore Locally Saved Content</button>
 					<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
 					<pre class='fit-text-mirror'></pre>
 					<input type='text' name='tags' value='" . htmlentities($page_tags, ENT_HTML5 | ENT_QUOTES) . "' 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' />
@@ -6208,16 +6265,25 @@ window.addEventListener("load", function(event) {
 			
 			/// ~~~ Smart saving ~~~ ///
 			// TODO: Add a button to press that restores the content that you were working on before.
-			page_renderer::AddJSSnippet('document.addEventListener("load", function(event) {
+			page_renderer::AddJSSnippet('window.addEventListener("load", function(event) {
+	"use strict";
 	// Smart saving
-	function getSmartSaveKey() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
+	let getSmartSaveKey = function() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
 	// Saving
 	document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
 	// Loading
-	window.addEventListener("load", function(event) {
-		var editor = document.querySelector("textarea[name=content]");
-		if(editor.value.length > 0) return; // Don\'t restore if there\'s data in the editor already
+	var editor = document.querySelector("textarea[name=content]");
+	let smartsave_restore = function() {
 		editor.value = localStorage.getItem(getSmartSaveKey());
+	}
+	
+	if(editor.value.length === 0) // Don\'t restore if there\'s data in the editor already
+		smartsave_restore();
+	
+	document.querySelector(".smartsave-restore").addEventListener("click", function(event) {
+		event.stopPropagation();
+		event.preventDefault();
+		smartsave_restore();
 	});
 });');
 			
diff --git a/module_index.json b/module_index.json
index 6e039d9..dfa4a9b 100755
--- a/module_index.json
+++ b/module_index.json
@@ -104,7 +104,7 @@
         "author": "Starbeamrainbowlabs",
         "description": "Adds proper search functionality to Pepperminty Wiki using an inverted index to provide a full text search engine. If pages don't show up, then you might have hit a stop word. If not, try requesting the `invindex-rebuild` action to rebuild the inverted index from scratch.",
         "id": "feature-search",
-        "lastupdate": 1508071155,
+        "lastupdate": 1511208633,
         "optional": false
     },
     {
@@ -167,7 +167,7 @@
         "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": 1510613807,
+        "lastupdate": 1511294696,
         "optional": false
     },
     {
diff --git a/modules/page-edit.php b/modules/page-edit.php
index 2707b83..3927643 100644
--- a/modules/page-edit.php
+++ b/modules/page-edit.php
@@ -138,6 +138,7 @@ register_module([
 
 			$content .= "<form method='post' name='edit-form' action='index.php?action=preview-edit&page=" . rawurlencode($env->page) . "' class='editform'>
 					<input type='hidden' name='prev-content-hash' value='" . ((isset($old_pagetext)) ? sha1($old_pagetext) : sha1($pagetext)) . "' />
+					<button class='smartsave-restore'>Restore Locally Saved Content</button>
 					<textarea name='content' autofocus tabindex='1'>$pagetext</textarea>
 					<pre class='fit-text-mirror'></pre>
 					<input type='text' name='tags' value='" . htmlentities($page_tags, ENT_HTML5 | ENT_QUOTES) . "' 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' />
@@ -181,16 +182,25 @@ window.addEventListener("load", function(event) {
 			
 			/// ~~~ Smart saving ~~~ ///
 			// TODO: Add a button to press that restores the content that you were working on before.
-			page_renderer::AddJSSnippet('document.addEventListener("load", function(event) {
+			page_renderer::AddJSSnippet('window.addEventListener("load", function(event) {
+	"use strict";
 	// Smart saving
-	function getSmartSaveKey() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
+	let getSmartSaveKey = function() { return document.querySelector("main h1").innerHTML.replace("Creating ", "").replace("Editing ", "").trim(); }
 	// Saving
 	document.querySelector("textarea[name=content]").addEventListener("keyup", function(event) { window.localStorage.setItem(getSmartSaveKey(), event.target.value) });
 	// Loading
-	window.addEventListener("load", function(event) {
-		var editor = document.querySelector("textarea[name=content]");
-		if(editor.value.length > 0) return; // Don\'t restore if there\'s data in the editor already
+	var editor = document.querySelector("textarea[name=content]");
+	let smartsave_restore = function() {
 		editor.value = localStorage.getItem(getSmartSaveKey());
+	}
+	
+	if(editor.value.length === 0) // Don\'t restore if there\'s data in the editor already
+		smartsave_restore();
+	
+	document.querySelector(".smartsave-restore").addEventListener("click", function(event) {
+		event.stopPropagation();
+		event.preventDefault();
+		smartsave_restore();
 	});
 });');
 			
