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.

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