You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

468 lines
18 KiB

  1. //*==============================================================================
  2. //* ** DO NOT REMOVE OR MODIFY THIS COPYRIGHT MESSAGE **
  3. //*
  4. //* Helpware Search 1.1
  5. //* Copyright (c) 2004-2011 The Helpware Group
  6. //* http://helpware.net/FAR/
  7. //* Requires a Modern Browser that supports JavaScript such as Firefox/IE4/GoogleChrome/Opera/Safari/Netscape
  8. //* WARNING: You must purchase a copy of FAR HTML v4 or greater to use this file.
  9. //*
  10. //*==============================================================================
  11. //* 31-May-2005: RWC - Fixed Offby one error in highlighting. First word would not highlight.
  12. //* 10-June-2009: RWC - All files now saved in UTF-8 file format. Search is now Unicode based. Previously ANSI based.
  13. //* - Added reBreakChars to allow multiple text break chars. Was just space char (0x20).
  14. //* 12-Sept-2011: RWC - Search highlighting now works for all browsers (previously just Internet Explorer).
  15. //*
  16. //var SearchFiles = ["index.htm","Search_OzNet.html"...
  17. //var SearchTitles =["Molecular products","OzNet Web"...
  18. //var SearchIndexes = [["0-0",[128,129,256,257,323]]...
  19. // ...,["WATER;",[355,361]],["WATER-CIRCULATOR",[383]],...
  20. //Options
  21. var PARAM_PartialMatchOK = true;
  22. var PARAM_TargetWindow = 'content';
  23. //Globals - SearchResults is an array of Page Indexes
  24. var SearchResults = [];
  25. var gFindList = [];
  26. var gFirstFindCall = true;
  27. //Chars that break words in an entry field //RWC002 - Space, Ideographic Space
  28. var reBreakChars = new RegExp( "[\u0020\u3000]{1}", "gi" );
  29. //------------------------------------------------------------------------------
  30. // Get Operator Type
  31. // text should be Uppercase. Return 0 = normal search text
  32. //------------------------------------------------------------------------------
  33. var OPT_AND = 1;
  34. var OPT_OR = 2;
  35. var OPT_NOT = 3;
  36. function xxGetOpType(text) {
  37. if ((text=="NOT")||(text=="!")) return OPT_NOT;
  38. else if ((text=="AND")||(text== "&")||(text== "+")) return OPT_AND;
  39. else if ((text=="OR")||(text== "|")) return OPT_OR;
  40. else return(0);
  41. }
  42. //----------------------------------------------------------------------------
  43. // ProcessSearchTerms()
  44. //----------------------------------------------------------------------------
  45. // Params
  46. // ss -- string of terms to parse and find
  47. // DefaultOp - Search Operator to default to for each list term (OPT_OR, OPT_AND, OPT_NOT)
  48. // Desc
  49. // Parse ss string --> String list. Default Return.
  50. // Items forced Uppercase (since Database and all calls use uppercase for speed)
  51. // User can insert override 'AND', 'OR', 'NOT' into the list of terms to
  52. // alter how the next item is searched. After that we go back to Defaultop.
  53. // Optimization
  54. // Pass in SearchIndexes + SearchResults arrays (by ref) so no global lookups - speed up loops
  55. //----------------------------------------------------------------------------
  56. function ProcessSearchTerms(ss, DefaultOp) {
  57. //Parse string into array
  58. var items = ss.split(reBreakChars);
  59. //----------------------------------------
  60. // Remove empty list entried due to multiple spaces passed to split()
  61. // Force all items to Uppercase
  62. //----------------------------------------
  63. var c = 0;
  64. for (var i = 0; i < items.length; i++)
  65. if (items[i] != "") { items[c] = items[i].toUpperCase(); c++; }
  66. items.length = c;
  67. var CheckOp = true;
  68. var otype = DefaultOp;
  69. for (var i = 0; i < items.length; i++) {
  70. //----------------------------------------
  71. // Check for Override Operators.
  72. // Don't allow Op override if working with NOT terms
  73. //----------------------------------------
  74. if ((CheckOp) && (DefaultOp != OPT_NOT)) {
  75. otype = xxGetOpType(items[i]);
  76. CheckOp = (otype == 0);
  77. if (CheckOp) otype = DefaultOp;
  78. else continue;
  79. }
  80. CheckOp = true;
  81. //----------------------------------------
  82. // Find Text results ==> SearchResults
  83. //----------------------------------------
  84. if (otype==OPT_OR) FindText_OR(items[i], SearchIndexes, SearchResults);
  85. if (otype==OPT_AND) FindText_AND(items[i], SearchIndexes, SearchResults);
  86. if (otype==OPT_NOT) FindText_DEL(items[i], SearchIndexes, SearchResults);
  87. //build list of find words
  88. if (DefaultOp!=OPT_NOT)
  89. gFindList[gFindList.length] = items[i];
  90. //Clear global flag
  91. gFirstFindCall = false;
  92. }
  93. }
  94. //------------------------------------------------------------------------------
  95. // s1 - Any words (OR) -->> one or more words present in a document is a result. Ie. Get the OR of all word search results.
  96. // s2 - All words (AND) -->> all words must be present in each result document. Ie. Get the AND of all word search results.
  97. // s3 - Not these words (NOT) -->> Only makes sense when used with the above. Knock out Topics containing these words.
  98. // b4 - Partial Word matching is ok - otherwise we match exaclty what is entered
  99. // s5 - target window -- default = 'content'
  100. // ----------------------------------------------
  101. // -- To match similar spellings in a full-text search, select the Match similar words check box.
  102. // eg "add", "adds", and "added".
  103. // -- To search for words in document titles only, select the Search titles only check box.
  104. // -- To highlight words in searched topics
  105. //------------------------------------------------------------------------------
  106. // Notes
  107. // - DoSearch(s1, s2, s3. partial)
  108. // S1 is a string of words separated by spaces. Words are OR'd together
  109. // S2 is a string of words separated by spaces. Words are AND'd together
  110. // S3 is a string of words separated by spaces. Words are Deleted from other results
  111. // - User can override default properties of S1 and S2 by using the following keywords
  112. // "OR","|" the next word is OR'd
  113. // "AND","&","+" the next word is AND'd
  114. // "NOT","!" the next word is removed
  115. //
  116. //------------------------------------------------------------------------------
  117. function DoSearch(s1, s2, s3, b4, s5)
  118. {
  119. //----------------------------------------------------------------------------
  120. // Init
  121. // - Reset First AND call flag. The first time must be an OR.
  122. // - Clear SearchResults list
  123. // - Clear target list control
  124. //----------------------------------------------------------------------------
  125. gFirstFindCall = true;
  126. SearchResults.length = 0;
  127. gFindList.length = 0;
  128. if (document.forms['searchform'].SearchResultList)
  129. document.forms['searchform'].SearchResultList.length = 0;
  130. PARAM_PartialMatchOK = b4;
  131. if (s5 == '') PARAM_TargetWindow = 'content';
  132. else PARAM_TargetWindow = s5;
  133. //----------------------------------------------------------------------------
  134. //1. (OR) Find documents with "Any of these Words" ==> SearchResults
  135. //2. (AND) Find documents with "All these Words" ==> SearchResults
  136. //3. (NOT) SearchResults must NOT files containing these words ==> Remove from SearchResults
  137. //----------------------------------------------------------------------------
  138. ProcessSearchTerms(s1, OPT_OR);
  139. ProcessSearchTerms(s2, OPT_AND);
  140. ProcessSearchTerms(s3, OPT_NOT);
  141. //----------------------------------------------------------------------------
  142. // Display SearchResults
  143. //----------------------------------------------------------------------------
  144. if (SearchResults.length == 0) {
  145. alert("No matches found!");
  146. return(0); }
  147. //Search Results list exists
  148. if (document.forms['searchform'].SearchResultList)
  149. {
  150. //Fill SearchResults List -- 500 item limit same as H1.x and H2.x
  151. for(var i=0;((i<SearchResults.length) && (i<500));i++) {
  152. var new_option = document.createElement('option');
  153. new_option.text = SearchTitles[SearchResults[i]];
  154. new_option.text= new_option.text.replace(/\&amp;/g,'&');
  155. new_option.value = SearchFiles[SearchResults[i]];
  156. document.forms['searchform'].SearchResultList[i]=new_option;
  157. }
  158. //open the first file
  159. // ** Comment this line out if you don't want the first Search result displayed automatically **
  160. OpenResultListDoc();
  161. }
  162. else {
  163. ShowSearchResultsWindow();
  164. }
  165. return(SearchResults.length);
  166. }
  167. //----------------------------------------------------------------------------
  168. // OR -- Add only Unique items to the SearchResults Array
  169. // items - array of Idxs to OR into SearchResults
  170. // SearchResults - Pass in by ref to to optomize global scope access
  171. //----------------------------------------------------------------------------
  172. function OR_WithSearchResults(items, SearchResults) {
  173. var found;
  174. for (var i = 0; i < items.length; i++) {
  175. //Already in list?
  176. found = false;
  177. for (var k = 0; (k < SearchResults.length) && (!found); k++)
  178. if (items[i] == SearchResults[k])
  179. found = true;
  180. //Not in list? Then Add it!
  181. if (!found)
  182. SearchResults[SearchResults.length] = items[i];
  183. }
  184. }
  185. //----------------------------------------------------------------------------
  186. // AND -- Keep only the intersection of items and SearchResults
  187. // items - array of Idxs to AND into SearchResults
  188. // SearchResults - Pass in by ref to to optomize global scope access
  189. //----------------------------------------------------------------------------
  190. function AND_WithSearchResults(items, SearchResults) {
  191. var count = 0;
  192. for (var i = 0; i < SearchResults.length; i++)
  193. for (var k = 0; k < items.length; k++) {
  194. if (items[k] == SearchResults[i]) {
  195. SearchResults[count] = SearchResults[i];
  196. count++;
  197. break;
  198. }
  199. }
  200. SearchResults.length = count;
  201. }
  202. //----------------------------------------------------------------------------
  203. // DEL -- Remove items from SearchResults list
  204. // items - array of Idxs to DEL from SearchResults
  205. // SearchResults - Pass in by ref to to optomize global scope access
  206. //----------------------------------------------------------------------------
  207. function DEL_WithSearchResults(items, SearchResults) {
  208. var count = 0;
  209. var found;
  210. for (var i = 0; i < SearchResults.length; i++) {
  211. //Its Delete Item in the Result list?
  212. found = false;
  213. for (var k = 0; (k < items.length) && (!found); k++)
  214. if (items[k] == SearchResults[i]) {
  215. found = true;
  216. break;
  217. }
  218. //Not Found in delete list? then keep Result
  219. if (!found) {
  220. SearchResults[count] = SearchResults[i];
  221. count++;
  222. }
  223. }
  224. SearchResults.length = count;
  225. }
  226. //----------------------------------------------------------------------------
  227. // Find Database Text
  228. // By this stage all strings are Uppercase
  229. // Optimization
  230. // - String Compare - check length the same, and check first char match before
  231. // going on to actually do a string compare.
  232. // - Pass Global SearchIndexes in instead of accessing out of scope many times
  233. //----------------------------------------------------------------------------
  234. //Find Text (in SearchIndex passed in by ref) and OR matches into SearchResults list
  235. function FindText_OR(SrchText, SearchIndexes, SearchResults)
  236. {
  237. if (PARAM_PartialMatchOK) {
  238. for(var i=0;i<SearchIndexes.length;i++)
  239. if((SearchIndexes[i][0].length >= SrchText.length)
  240. && (SearchIndexes[i][0].indexOf(SrchText) >= 0)) {
  241. OR_WithSearchResults(SearchIndexes[i][1], SearchResults);
  242. }
  243. }
  244. else {
  245. //Not Partial - Fast - Find exact match and break out
  246. for(var i=0;i<SearchIndexes.length;i++)
  247. if((SearchIndexes[i][0].length == SrchText.length)
  248. && (SearchIndexes[i][0] == SrchText)) {
  249. OR_WithSearchResults(SearchIndexes[i][1], SearchResults);
  250. break;
  251. }
  252. }
  253. }
  254. //Find Text (in SearchIndex passed in by ref) and AND matches into SearchResults list
  255. function FindText_AND(SrchText, SearchIndexes, SearchResults)
  256. {
  257. //Optimization: Take copy to minimize global out of scope lookups
  258. var FirstFindCall = gFirstFindCall;
  259. //If 2nd or 3rd... item and No SearchResults then Nothing to AND with
  260. if ((!FirstFindCall) && (SearchResults.length == 0))
  261. return;
  262. var tempList = [];
  263. if (PARAM_PartialMatchOK) {
  264. for(var i=0;i<SearchIndexes.length;i++)
  265. if((SearchIndexes[i][0].length >= SrchText.length)
  266. && (SearchIndexes[i][0].indexOf(SrchText) >= 0))
  267. OR_WithSearchResults(SearchIndexes[i][1], tempList);
  268. }
  269. else {
  270. //Not Partial - Fast - Find exact match and break out
  271. for(var i=0;i<SearchIndexes.length;i++)
  272. if((SearchIndexes[i][0].length == SrchText.length)
  273. && (SearchIndexes[i][0] == SrchText)) {
  274. OR_WithSearchResults(SearchIndexes[i][1], tempList);
  275. //Exact match - we are done
  276. break;
  277. }
  278. }
  279. //Add Results
  280. //1st call wont have results yet -- We must OR into SearchResults as AND would not do nothing
  281. if (tempList.length >= 0) {
  282. if (FirstFindCall)
  283. OR_WithSearchResults(tempList, SearchResults);
  284. else
  285. AND_WithSearchResults(tempList, SearchResults);
  286. }
  287. else
  288. //No Results + not first call -- AND will wipe out all results
  289. if (!FirstFindCall)
  290. SearchResults.length = 0;
  291. }
  292. //Find Text (in SearchIndex passed in by ref) and DELETE matches from SearchResults list
  293. function FindText_DEL(SrchText, SearchIndexes, SearchResults)
  294. {
  295. //first check there is something to delete from
  296. if (SearchResults.length)
  297. for(var i=0;i<SearchIndexes.length;i++)
  298. if((SearchIndexes[i][0].length == SrchText.length)
  299. &&(SearchIndexes[i][0]==SrchText)) {
  300. //Send match words idx array off to be deleted from SearchResults
  301. DEL_WithSearchResults(SearchIndexes[i][1], SearchResults);
  302. //We found the word and its idx array data -- Jobs done
  303. break;
  304. }
  305. }
  306. //------------------------------------------------------------------------------
  307. // Highlight search hits
  308. //------------------------------------------------------------------------------
  309. var targetWin = null;
  310. var highlightStartTag = "<span style='background-color:#FFFF00;color:#222222;'>"; //yellow highlight
  311. var highlightEndTag = "</span>";
  312. //------------------------------------------------------------------------------
  313. // Highlight text by adding HTML tags before and after all occurrences of the search term.
  314. // Acknowledgments: Thank you Sujit Kumar Shah - http://www.sks.com.np/article/14/pure-javascript-search-and-text-highlighting.html
  315. //------------------------------------------------------------------------------
  316. function doHighlight(bodyText, searchTerm)
  317. {
  318. var newText = "";
  319. var i = -1;
  320. var lcSearchTerm = searchTerm.toLowerCase();
  321. var lcBodyText = bodyText.toLowerCase();
  322. while (bodyText.length > 0) {
  323. i = lcBodyText.indexOf(lcSearchTerm, i+1);
  324. if (i < 0) {
  325. newText += bodyText;
  326. bodyText = "";
  327. } else {
  328. // skip anything inside an HTML tag
  329. if (bodyText.lastIndexOf(">", i) >= bodyText.lastIndexOf("<", i)) {
  330. // skip anything inside a <script> block
  331. if (lcBodyText.lastIndexOf("/script>", i) >= lcBodyText.lastIndexOf("<script", i)) {
  332. newText += bodyText.substring(0, i) + highlightStartTag + bodyText.substr(i, searchTerm.length) + highlightEndTag;
  333. bodyText = bodyText.substr(i + searchTerm.length);
  334. lcBodyText = bodyText.toLowerCase();
  335. i = -1;
  336. }
  337. }
  338. }
  339. }
  340. return newText;
  341. }
  342. // Highlight search terms
  343. function HighlightTopic() {
  344. var bodyText = targetWin.document.body.innerHTML;
  345. for(var k = 0; k < gFindList.length; k++) {
  346. bodyText = doHighlight(bodyText, gFindList[k]);
  347. }
  348. targetWin.document.body.innerHTML = bodyText;
  349. }
  350. //------------------------------------------------------------------------------
  351. // Open List item in Browser - Target = PARAM_TargetWindow
  352. //------------------------------------------------------------------------------
  353. function OpenResultListDoc() {
  354. //Something selected in the search result list?
  355. var iSelect = document.forms['searchform'].SearchResultList.selectedIndex;
  356. if (iSelect < 0)
  357. iSelect = 0;
  358. //Open the selected file
  359. if (window.navigator.userAgent.indexOf("Netscape") > 0) {
  360. top.right.location.href = document.forms['searchform'].SearchResultList.options[iSelect].value;
  361. targetWin = top.right.window;
  362. }
  363. else //all other browsers
  364. targetWin = open(document.forms['searchform'].SearchResultList.options[iSelect].value, PARAM_TargetWindow);
  365. //Highlight search hits on a delay
  366. if (targetWin)
  367. setTimeout('HighlightTopic()',1000);
  368. }
  369. //------------------------------------------------------------------------------
  370. // Search Results Window -- called if user does not
  371. // -- 500 item limit same as H1.x and H2.x
  372. //------------------------------------------------------------------------------
  373. function ShowSearchResultsWindow() {
  374. var newWindow = window.open("about:blank", "searchValue", "width=500, height=300, resizable=yes, maximizable=no, status=yes, scrollbars=yes");
  375. newWindow.document.write('<html>\n<head>\n<title>Search Results</title>\n');
  376. newWindow.document.write('</head>\n');
  377. newWindow.document.write('<body>\n');
  378. //Fill SearchResults List
  379. for(var i=0;((i<SearchResults.length) && (i<500));i++) {
  380. //Search Topic Title
  381. var aTitle = SearchTitles[SearchResults[i]];
  382. //URL
  383. var aURL = SearchFiles[SearchResults[i]];
  384. newWindow.document.write('<p>Title: '+ aTitle +'<br>\n');
  385. newWindow.document.write('URL: <a href="'+ aURL +'">'+aURL+'</a></p>\n');
  386. }
  387. newWindow.document.write("</body>\n");
  388. newWindow.document.write("</html>\n");
  389. newWindow.document.close();
  390. // self.name = "main";
  391. }
  392. //------------------------------------------------------------------------------
  393. // Other Script
  394. //------------------------------------------------------------------------------
  395. function CloseNavPane() {
  396. if ((top.content.location == null) || (top.content.location == undefined) || (typeof(top.content.location.href) != "string") || (top.content.location.href == ""))
  397. top.location="index.htm"; //can't work out the current content file - return home
  398. else
  399. top.location=top.content.location;
  400. }
  401. //------------------------------------------------------------------------------
  402. //
  403. //------------------------------------------------------------------------------