diff options
Diffstat (limited to 'doc/ref/csharp/html/SearchHelp.aspx')
-rw-r--r-- | doc/ref/csharp/html/SearchHelp.aspx | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/doc/ref/csharp/html/SearchHelp.aspx b/doc/ref/csharp/html/SearchHelp.aspx new file mode 100644 index 0000000000..6e2a17b6ab --- /dev/null +++ b/doc/ref/csharp/html/SearchHelp.aspx @@ -0,0 +1,233 @@ +<%@ Page Language="C#" EnableViewState="False" %> + +<script runat="server"> +//=============================================================================================================== +// System : Sandcastle Help File Builder +// File : SearchHelp.aspx +// Author : Eric Woodruff (Eric@EWoodruff.us) +// Updated : 05/15/2014 +// Note : Copyright 2007-2015, Eric Woodruff, All rights reserved +// Compiler: Microsoft C# +// +// This file contains the code used to search for keywords within the help topics using the full-text index +// files created by the help file builder. +// +// This code is published under the Microsoft Public License (Ms-PL). A copy of the license should be +// distributed with the code. It can also be found at the project website: https://GitHub.com/EWSoftware/SHFB. This +// notice, the author's name, and all copyright notices must remain intact in all applications, documentation, +// and source files. +// +// Date Who Comments +// ============================================================================================================== +// 06/24/2007 EFW Created the code +// 02/17/2012 EFW Switched to JSON serialization to support websites that use something other than ASP.NET +// such as PHP. +// 05/15/2014 EFW Updated for use with the lightweight website presentation styles +//=============================================================================================================== + +/// <summary> +/// This class is used to track the results and their rankings +/// </summary> +private class Ranking +{ + public string Filename, PageTitle; + public int Rank; + + public Ranking(string file, string title, int rank) + { + Filename = file; + PageTitle = title; + Rank = rank; + } +} + +/// <summary> +/// Render the search results +/// </summary> +/// <param name="writer">The writer to which the results are written</param> +protected override void Render(HtmlTextWriter writer) +{ + JavaScriptSerializer jss = new JavaScriptSerializer(); + string searchText, ftiFile; + char letter; + bool sortByTitle = false; + + jss.MaxJsonLength = Int32.MaxValue; + + // The keywords for which to search should be passed in the query string + searchText = this.Request.QueryString["Keywords"]; + + if(String.IsNullOrEmpty(searchText)) + { + writer.Write("<strong>Nothing found</strong>"); + return; + } + + // An optional SortByTitle option can also be specified + if(this.Request.QueryString["SortByTitle"] != null) + sortByTitle = Convert.ToBoolean(this.Request.QueryString["SortByTitle"]); + + List<string> keywords = this.ParseKeywords(searchText); + List<char> letters = new List<char>(); + List<string> fileList; + Dictionary<string, List<long>> ftiWords, wordDictionary = new Dictionary<string,List<long>>(); + + // Load the file index + using(StreamReader sr = new StreamReader(Server.MapPath("fti/FTI_Files.json"))) + { + fileList = jss.Deserialize<List<string>>(sr.ReadToEnd()); + } + + // Load the required word index files + foreach(string word in keywords) + { + letter = word[0]; + + if(!letters.Contains(letter)) + { + letters.Add(letter); + ftiFile = Server.MapPath(String.Format(CultureInfo.InvariantCulture, "fti/FTI_{0}.json", (int)letter)); + + if(File.Exists(ftiFile)) + { + using(StreamReader sr = new StreamReader(ftiFile)) + { + ftiWords = jss.Deserialize<Dictionary<string, List<long>>>(sr.ReadToEnd()); + } + + foreach(string ftiWord in ftiWords.Keys) + wordDictionary.Add(ftiWord, ftiWords[ftiWord]); + } + } + } + + // Perform the search and return the results as a block of HTML + writer.Write(this.Search(keywords, fileList, wordDictionary, sortByTitle)); +} + +/// <summary> +/// Split the search text up into keywords +/// </summary> +/// <param name="keywords">The keywords to parse</param> +/// <returns>A list containing the words for which to search</returns> +private List<string> ParseKeywords(string keywords) +{ + List<string> keywordList = new List<string>(); + string checkWord; + string[] words = Regex.Split(keywords, @"\W+"); + + foreach(string word in words) + { + checkWord = word.ToLower(CultureInfo.InvariantCulture); + + if(checkWord.Length > 2 && !Char.IsDigit(checkWord[0]) && !keywordList.Contains(checkWord)) + keywordList.Add(checkWord); + } + + return keywordList; +} + +/// <summary> +/// Search for the specified keywords and return the results as a block of HTML +/// </summary> +/// <param name="keywords">The keywords for which to search</param> +/// <param name="fileInfo">The file list</param> +/// <param name="wordDictionary">The dictionary used to find the words</param> +/// <param name="sortByTitle">True to sort by title, false to sort by ranking</param> +/// <returns>A block of HTML representing the search results</returns> +private string Search(List<string> keywords, List<string> fileInfo, + Dictionary<string, List<long>> wordDictionary, bool sortByTitle) +{ + StringBuilder sb = new StringBuilder(10240); + Dictionary<string, List<long>> matches = new Dictionary<string, List<long>>(); + List<long> occurrences; + List<int> matchingFileIndices = new List<int>(), occurrenceIndices = new List<int>(); + List<Ranking> rankings = new List<Ranking>(); + + string filename, title; + string[] fileIndex; + bool isFirst = true; + int idx, wordCount, matchCount; + + foreach(string word in keywords) + { + if(!wordDictionary.TryGetValue(word, out occurrences)) + return "<strong>Nothing found</strong>"; + + matches.Add(word, occurrences); + occurrenceIndices.Clear(); + + // Get a list of the file indices for this match + foreach(long entry in occurrences) + occurrenceIndices.Add((int)(entry >> 16)); + + if(isFirst) + { + isFirst = false; + matchingFileIndices.AddRange(occurrenceIndices); + } + else + { + // After the first match, remove files that do not appear for + // all found keywords. + for(idx = 0; idx < matchingFileIndices.Count; idx++) + if(!occurrenceIndices.Contains(matchingFileIndices[idx])) + { + matchingFileIndices.RemoveAt(idx); + idx--; + } + } + } + + if(matchingFileIndices.Count == 0) + return "<strong>Nothing found</strong>"; + + // Rank the files based on the number of times the words occurs + foreach(int index in matchingFileIndices) + { + // Split out the title, filename, and word count + fileIndex = fileInfo[index].Split('\x0'); + + title = fileIndex[0]; + filename = fileIndex[1]; + wordCount = Convert.ToInt32(fileIndex[2]); + matchCount = 0; + + foreach(string word in keywords) + { + occurrences = matches[word]; + + foreach(long entry in occurrences) + if((int)(entry >> 16) == index) + matchCount += (int)(entry & 0xFFFF); + } + + rankings.Add(new Ranking(filename, title, matchCount * 1000 / wordCount)); + + if(rankings.Count > 99) + break; + } + + // Sort by rank in descending order or by page title in ascending order + rankings.Sort(delegate (Ranking x, Ranking y) + { + if(!sortByTitle) + return y.Rank - x.Rank; + + return x.PageTitle.CompareTo(y.PageTitle); + }); + + // Format the file list and return the results + sb.Append("<ol>"); + + foreach(Ranking r in rankings) + sb.AppendFormat("<li><a href=\"{0}\" target=\"_blank\">{1}</a></li>", r.Filename, r.PageTitle); + + sb.Append("</ol>"); + + if(rankings.Count < matchingFileIndices.Count) + sb.AppendFormat("<p>Omitted {0} more results</p>", matchingFileIndices.Count - rankings.Count); + + return sb.ToString(); +} +</script> |