node-sword-interface 1.0.39 → 1.0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@ on: push
5
5
  jobs:
6
6
  build-linux:
7
7
  name: Run Linux build
8
- runs-on: ubuntu-20.04
8
+ runs-on: ubuntu-24.04
9
9
  steps:
10
10
  - uses: actions/checkout@v2
11
11
  - uses: actions/setup-node@v2
package/API.md CHANGED
@@ -50,6 +50,7 @@ This is the main class of node-sword-interface and it provides a set of static f
50
50
  * [.isModuleReadable(moduleCode)](#NodeSwordInterface+isModuleReadable) ⇒ <code>Boolean</code>
51
51
  * [.getModuleDescription(moduleCode)](#NodeSwordInterface+getModuleDescription) ⇒ <code>String</code>
52
52
  * [.enableMarkup()](#NodeSwordInterface+enableMarkup)
53
+ * [.disableMarkup()](#NodeSwordInterface+disableMarkup)
53
54
  * [.enableStrongsWithNbsp()](#NodeSwordInterface+enableStrongsWithNbsp)
54
55
  * [.getRawModuleEntry(moduleCode, key)](#NodeSwordInterface+getRawModuleEntry) ⇒ <code>String</code>
55
56
  * [.getReferenceText(moduleCode, key)](#NodeSwordInterface+getReferenceText) ⇒ [<code>VerseObject</code>](#VerseObject)
@@ -67,7 +68,7 @@ This is the main class of node-sword-interface and it provides a set of static f
67
68
  * [.getBookIntroduction(moduleCode, bookCode)](#NodeSwordInterface+getBookIntroduction) ⇒ <code>String</code>
68
69
  * [.moduleHasBook(moduleCode, bookCode)](#NodeSwordInterface+moduleHasBook) ⇒ <code>Boolean</code>
69
70
  * [.getDictModuleKeys(moduleCode)](#NodeSwordInterface+getDictModuleKeys) ⇒ <code>Array.&lt;String&gt;</code>
70
- * [.getModuleSearchResults(moduleCode, searchTerm, progressCB, searchType, searchScope, isCaseSensitive, useExtendedVerseBoundaries)](#NodeSwordInterface+getModuleSearchResults) ⇒ <code>Promise</code>
71
+ * [.getModuleSearchResults(moduleCode, searchTerm, progressCB, searchType, searchScope, isCaseSensitive, useExtendedVerseBoundaries, filterOnWordBoundaries)](#NodeSwordInterface+getModuleSearchResults) ⇒ <code>Promise</code>
71
72
  * [.terminateModuleSearch()](#NodeSwordInterface+terminateModuleSearch)
72
73
  * [.hebrewStrongsAvailable()](#NodeSwordInterface+hebrewStrongsAvailable) ⇒ <code>Boolean</code>
73
74
  * [.greekStrongsAvailable()](#NodeSwordInterface+greekStrongsAvailable) ⇒ <code>Boolean</code>
@@ -311,6 +312,13 @@ Returns the description of a module.
311
312
  Enables available markup (like Strongs, foot notes, etc.)
312
313
  This influences the output for getChapterText, getBookText and getBibleText.
313
314
 
315
+ **Kind**: instance method of [<code>NodeSwordInterface</code>](#NodeSwordInterface)
316
+ <a name="NodeSwordInterface+disableMarkup"></a>
317
+
318
+ ### nodeSwordInterface.disableMarkup()
319
+ Disable available markup (like Strongs, foot notes, etc.)
320
+ This influences the output for getChapterText, getBookText and getBibleText.
321
+
314
322
  **Kind**: instance method of [<code>NodeSwordInterface</code>](#NodeSwordInterface)
315
323
  <a name="NodeSwordInterface+enableStrongsWithNbsp"></a>
316
324
 
@@ -524,7 +532,7 @@ Returns the keys of a dictionary module.
524
532
 
525
533
  <a name="NodeSwordInterface+getModuleSearchResults"></a>
526
534
 
527
- ### nodeSwordInterface.getModuleSearchResults(moduleCode, searchTerm, progressCB, searchType, searchScope, isCaseSensitive, useExtendedVerseBoundaries) ⇒ <code>Promise</code>
535
+ ### nodeSwordInterface.getModuleSearchResults(moduleCode, searchTerm, progressCB, searchType, searchScope, isCaseSensitive, useExtendedVerseBoundaries, filterOnWordBoundaries) ⇒ <code>Promise</code>
528
536
  Returns the results of a module search.
529
537
 
530
538
  **Kind**: instance method of [<code>NodeSwordInterface</code>](#NodeSwordInterface)
@@ -538,6 +546,7 @@ Returns the results of a module search.
538
546
  | searchScope | <code>String</code> | <code>BIBLE</code> | Options: BIBLE, OT, NT |
539
547
  | isCaseSensitive | <code>Boolean</code> | <code>false</code> | Whether the search is case sensitive |
540
548
  | useExtendedVerseBoundaries | <code>Boolean</code> | <code>false</code> | Whether the search should use extended verse boundaries (Two verses instead of one) in case of a multi word search. |
549
+ | filterOnWordBoundaries | <code>Boolean</code> | <code>false</code> | Whether to filter results based on word boundaries. |
541
550
 
542
551
  <a name="NodeSwordInterface+terminateModuleSearch"></a>
543
552
 
package/index.js CHANGED
@@ -592,15 +592,17 @@ class NodeSwordInterface {
592
592
  * @param {String} searchScope - Options: BIBLE, OT, NT
593
593
  * @param {Boolean} isCaseSensitive - Whether the search is case sensitive
594
594
  * @param {Boolean} useExtendedVerseBoundaries - Whether the search should use extended verse boundaries (Two verses instead of one) in case of a multi word search.
595
+ * @param {Boolean} filterOnWordBoundaries - Whether to filter results based on word boundaries.
595
596
  * @return {Promise}
596
597
  */
597
598
  getModuleSearchResults(moduleCode,
598
599
  searchTerm,
599
- progressCB=undefined,
600
- searchType="phrase",
601
- searchScope="BIBLE",
602
- isCaseSensitive=false,
603
- useExtendedVerseBoundaries=false) {
600
+ progressCB = undefined,
601
+ searchType = "phrase",
602
+ searchScope = "BIBLE",
603
+ isCaseSensitive = false,
604
+ useExtendedVerseBoundaries = false,
605
+ filterOnWordBoundaries = false) {
604
606
 
605
607
  if (progressCB === undefined) {
606
608
  progressCB = function(progress) {};
@@ -614,6 +616,7 @@ class NodeSwordInterface {
614
616
  searchScope,
615
617
  isCaseSensitive,
616
618
  useExtendedVerseBoundaries,
619
+ filterOnWordBoundaries,
617
620
  progressCB,
618
621
  function(searchResults) { resolve(searchResults); });
619
622
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-sword-interface",
3
- "version": "1.0.39",
3
+ "version": "1.0.41",
4
4
  "description": "Javascript (N-API) interface to SWORD library",
5
5
  "keywords": [
6
6
  "C++",
@@ -31,7 +31,7 @@
31
31
  "url": "git+https://github.com/ezra-bible-app/node-sword-interface.git"
32
32
  },
33
33
  "devDependencies": {
34
- "jsdoc-to-markdown": "^8.0.3",
34
+ "jsdoc-to-markdown": "^9.1.1",
35
35
  "node-gyp": "^9.1.0"
36
36
  }
37
37
  }
@@ -34,7 +34,7 @@ $headers = @{}
34
34
  $github_token = $env:GITHUB_TOKEN
35
35
 
36
36
  # --- Set the uri for the release
37
- $URI = "https://api.github.com/repos/ezra-bible-app/sword-build-win32/releases/tags/v1.9.0-2025-03-03"
37
+ $URI = "https://api.github.com/repos/ezra-bible-app/sword-build-win32/releases/tags/v1.8.900-2022-11-06"
38
38
 
39
39
  if ($Env:CI -eq "true") {
40
40
  Write-Host "GitHub actions build ... using GITHUB_TOKEN for authentication!"
File without changes
@@ -37,7 +37,8 @@ void ModuleSearchWorker::Execute(const ExecutionProgress& progress)
37
37
  this->_searchType,
38
38
  this->_searchScope,
39
39
  this->_isCaseSensitive,
40
- this->_useExtendedVerseBoundaries);
40
+ this->_useExtendedVerseBoundaries,
41
+ this->_filterOnWordBoundaries); // Pass the parameter
41
42
 
42
43
  if (this->_searchTerminated) {
43
44
  this->_stdSearchResults.clear();
@@ -38,7 +38,8 @@ public:
38
38
  SearchType searchType,
39
39
  SearchScope searchScope,
40
40
  bool isCaseSensitive=false,
41
- bool useExtendedVerseBoundaries=false)
41
+ bool useExtendedVerseBoundaries=false,
42
+ bool filterOnWordBoundaries=false)
42
43
 
43
44
  : ProgressWorker(repoInterface, jsProgressCallback, callback),
44
45
  _searchMutex(searchMutex),
@@ -49,6 +50,7 @@ public:
49
50
  _searchScope(searchScope),
50
51
  _isCaseSensitive(isCaseSensitive),
51
52
  _useExtendedVerseBoundaries(useExtendedVerseBoundaries),
53
+ _filterOnWordBoundaries(filterOnWordBoundaries),
52
54
  _searchTerminated(false) {
53
55
 
54
56
  this->_napiSwordHelper = new NapiSwordHelper(moduleHelper, moduleStore);
@@ -71,6 +73,7 @@ private:
71
73
  SearchScope _searchScope;
72
74
  bool _isCaseSensitive;
73
75
  bool _useExtendedVerseBoundaries;
76
+ bool _filterOnWordBoundaries; // New member variable
74
77
  bool _searchTerminated;
75
78
  };
76
79
 
@@ -760,6 +760,7 @@ Napi::Value NodeSwordInterface::getModuleSearchResults(const Napi::CallbackInfo&
760
760
  ParamType::string, // searchScope
761
761
  ParamType::boolean, // isCaseSensitive
762
762
  ParamType::boolean, // useExtendedVerseBoundaries
763
+ ParamType::boolean, // filterOnWordBoundaries
763
764
  ParamType::function, // progressCallback
764
765
  ParamType::function); // final Callback
765
766
 
@@ -769,8 +770,9 @@ Napi::Value NodeSwordInterface::getModuleSearchResults(const Napi::CallbackInfo&
769
770
  string searchScopeString = string(info[3].As<Napi::String>());
770
771
  Napi::Boolean isCaseSensitive = info[4].As<Napi::Boolean>();
771
772
  Napi::Boolean useExtendedVerseBoundaries = info[5].As<Napi::Boolean>();
772
- Napi::Function jsProgressCallback = info[6].As<Napi::Function>();
773
- Napi::Function callback = info[7].As<Napi::Function>();
773
+ Napi::Boolean filterOnWordBoundaries = info[6].As<Napi::Boolean>();
774
+ Napi::Function jsProgressCallback = info[7].As<Napi::Function>();
775
+ Napi::Function callback = info[8].As<Napi::Function>();
774
776
  SearchType searchType = SearchType::multiWord;
775
777
 
776
778
  if (searchTypeString == "phrase") {
@@ -813,7 +815,8 @@ Napi::Value NodeSwordInterface::getModuleSearchResults(const Napi::CallbackInfo&
813
815
  searchType,
814
816
  searchScope,
815
817
  isCaseSensitive,
816
- useExtendedVerseBoundaries);
818
+ useExtendedVerseBoundaries,
819
+ filterOnWordBoundaries); // Pass the new parameter
817
820
  this->_currentModuleSearchWorker->Queue();
818
821
  return env.Undefined();
819
822
  }
@@ -21,6 +21,8 @@
21
21
  #include <string>
22
22
  #include <vector>
23
23
  #include <algorithm>
24
+ #include <regex>
25
+ #include <sstream>
24
26
 
25
27
  // sword includes
26
28
  #include <swmgr.h>
@@ -32,6 +34,7 @@
32
34
  #include "module_store.hpp"
33
35
  #include "module_helper.hpp"
34
36
  #include "text_processor.hpp"
37
+ #include "string_helper.hpp"
35
38
 
36
39
  /* REGEX definitions from regex.h */
37
40
  /* POSIX `cflags' bits (i.e., information for `regcomp'). */
@@ -93,29 +96,32 @@ ListKey ModuleSearch::getScopeKey(SWModule* module, SearchScope scope)
93
96
  return key;
94
97
  }
95
98
 
99
+ inline bool isDisallowedCharacter(char c) {
100
+ static const string disallowedPunctuation = ",;.:'´‘’\"“”?!()-=<>/";
101
+ return disallowedPunctuation.find(c) != string::npos;
102
+ }
103
+
96
104
  vector<Verse> ModuleSearch::getModuleSearchResults(string moduleName,
97
105
  string searchTerm,
98
106
  SearchType searchType,
99
107
  SearchScope searchScope,
100
108
  bool isCaseSensitive,
101
- bool useExtendedVerseBoundaries)
109
+ bool useExtendedVerseBoundaries,
110
+ bool filterOnWordBoundaries)
102
111
  {
103
112
  this->_currentModuleName = moduleName;
104
113
  SWModule* module = this->_moduleStore.getSearchSwMgr()->getModule(moduleName.c_str());
105
114
  ListKey listKey;
106
115
  SWKey* scope = 0;
107
116
  int flags = 0;
108
- // This holds the text that we will return
109
117
  vector<Verse> searchResults;
110
118
  vector<string> searchResultReferences;
111
119
 
112
120
  if (!isCaseSensitive) {
113
- // for case insensitivity
114
121
  flags |= REG_ICASE;
115
122
  }
116
123
 
117
124
  if (!useExtendedVerseBoundaries) {
118
- // Use strict search boundaries (only search within individual verses). TODO: Make this configurable.
119
125
  flags |= SWModule::SEARCHFLAG_STRICTBOUNDARIES;
120
126
  }
121
127
 
@@ -137,71 +143,106 @@ vector<Verse> ModuleSearch::getModuleSearchResults(string moduleName,
137
143
 
138
144
  if (searchType == SearchType::strongsNumber) {
139
145
  if (!hasStrongs) {
140
- // Return immediately if search type is Strong's, but the module does not have Strong's support
141
146
  return searchResults;
142
147
  }
143
148
 
144
- // Cut out the number from the Strong's key (starting at index 1 until end of string)
145
149
  string strongsNumber = searchTerm.substr(1, searchTerm.size());
146
150
 
147
151
  if (searchTerm[0] == 'H') {
148
-
149
152
  if (this->_textProcessor.moduleHasStrongsPaddedZeroPrefixes(module)) {
150
153
  string paddedStrongsNumber = this->_textProcessor.padStrongsNumber(strongsNumber);
151
154
  searchTerm = "H" + paddedStrongsNumber;
152
-
153
155
  } else if (this->_textProcessor.moduleHasStrongsZeroPrefixes(module)) {
154
- // If the Strong's key is OT we need to insert a zero in front of the key
155
- // This is necessary because the Sword modules with Strong's have a zero in front of the Hebrew Strong's numbers
156
- // Overwrite the searchTerm with an inserted 0
157
156
  searchTerm = "H0" + strongsNumber;
158
157
  }
159
-
160
158
  } else if (searchTerm[0] == 'G') {
161
-
162
159
  if (this->_textProcessor.moduleHasStrongsPaddedZeroPrefixes(module)) {
163
160
  string paddedStrongsNumber = this->_textProcessor.padStrongsNumber(strongsNumber);
164
161
  searchTerm = "G" + paddedStrongsNumber;
165
162
  }
166
163
  }
167
164
 
168
- // from swmodule.h api docs:
169
- // for use with entryAttrib search type to match whole entry to value, e.g., G1234 and not G12345
170
165
  flags |= SWModule::SEARCHFLAG_MATCHWHOLEENTRY;
171
-
172
166
  searchTerm = "Word//Lemma./" + searchTerm;
173
167
  }
174
168
 
175
- map<string, int> absoluteVerseNumbers = this->_moduleHelper.getAbsoluteVerseNumberMap(module);
176
-
177
169
  // Perform search
178
170
  listKey = module->search(searchTerm.c_str(), int(searchType), flags, scope, 0, internalModuleSearchProgressCB);
179
171
 
180
- // Populate searchResults vector
172
+ // Disable markup before filtering on word boundaries
173
+ this->_textProcessor.disableMarkup();
174
+
175
+ vector<string> filteredReferences;
176
+
177
+ // Make the search term lower case if case sensitivity is not required
178
+ string lowerCaseSearchTerm = searchTerm;
179
+ if (!isCaseSensitive) {
180
+ std::transform(lowerCaseSearchTerm.begin(), lowerCaseSearchTerm.end(), lowerCaseSearchTerm.begin(), ::tolower);
181
+ }
182
+
183
+ // Split the search term into individual words
184
+ vector<string> searchWords = StringHelper::split(lowerCaseSearchTerm, " ");
185
+
186
+ // Filter verses based on word boundaries
181
187
  while (!listKey.popError()) {
182
188
  module->setKey(listKey.getElement());
183
-
184
189
  string verseText = this->_textProcessor.getCurrentVerseText(module,
185
190
  hasStrongs,
186
191
  hasInconsistentClosingEndDivs,
187
192
  moduleMarkupIsBroken);
188
193
 
189
- string currentReference = module->getKey()->getShortText();
190
-
191
- if (std::find(searchResultReferences.begin(),
192
- searchResultReferences.end(), // Only accept the result if we do not
193
- currentReference) == searchResultReferences.end()) { // have it yet!
194
-
195
- Verse currentVerse;
196
- currentVerse.reference = module->getKey()->getShortText();
197
- currentVerse.absoluteVerseNumber = absoluteVerseNumbers[currentVerse.reference];
198
- currentVerse.content = verseText;
199
- searchResults.push_back(currentVerse);
200
- searchResultReferences.push_back(currentReference);
194
+ // Make the verse text lower case if case sensitivity is not required
195
+ string lowerCaseVerseText = verseText;
196
+ if (!isCaseSensitive) {
197
+ std::transform(lowerCaseVerseText.begin(), lowerCaseVerseText.end(), lowerCaseVerseText.begin(), ::tolower);
198
+ }
199
+
200
+ if (filterOnWordBoundaries) {
201
+ // Replace disallowed characters with spaces
202
+ std::replace_if(lowerCaseVerseText.begin(), lowerCaseVerseText.end(),
203
+ [](char c) { return isDisallowedCharacter(c); }, ' ');
204
+
205
+ vector<string> words = StringHelper::split(lowerCaseVerseText, " ");
206
+
207
+ // Check if all parts of the search term match any word in the verse
208
+ bool allPartsMatch = true;
209
+ for (const auto& searchWord : searchWords) {
210
+ if (std::find(words.begin(), words.end(), searchWord) == words.end()) {
211
+ allPartsMatch = false;
212
+ break;
213
+ }
214
+ }
215
+
216
+ if (allPartsMatch) {
217
+ filteredReferences.push_back(module->getKey()->getShortText());
218
+ }
219
+ } else {
220
+ filteredReferences.push_back(module->getKey()->getShortText());
201
221
  }
202
222
 
203
223
  listKey++;
204
224
  }
225
+
226
+ // Enable markup again after word boundary filtering
227
+ this->_textProcessor.enableMarkup();
228
+
229
+ map<string, int> absoluteVerseNumbers = this->_moduleHelper.getAbsoluteVerseNumberMap(module);
230
+
231
+ // Populate searchResults vector
232
+ for (const auto& reference : filteredReferences) {
233
+ module->setKey(reference.c_str());
234
+ string verseText = this->_textProcessor.getCurrentVerseText(module,
235
+ hasStrongs,
236
+ hasInconsistentClosingEndDivs,
237
+ moduleMarkupIsBroken);
238
+
239
+ Verse currentVerse;
240
+ currentVerse.reference = reference;
241
+ currentVerse.absoluteVerseNumber = absoluteVerseNumbers[reference];
242
+ currentVerse.content = verseText;
243
+
244
+ searchResults.push_back(currentVerse);
245
+ }
205
246
  }
206
247
 
207
248
  this->_currentModuleName = "";
@@ -60,7 +60,8 @@ public:
60
60
  SearchType searchType=SearchType::multiWord,
61
61
  SearchScope searchScope=SearchScope::BIBLE,
62
62
  bool isCaseSensitive=false,
63
- bool useExtendedVerseBoundaries=false);
63
+ bool useExtendedVerseBoundaries=false,
64
+ bool filterOnWordBoundaries=false);
64
65
 
65
66
  void terminate();
66
67