node-sword-interface 1.0.40 → 1.0.42

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,14 +5,14 @@ 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
12
12
  with:
13
- node-version: '14'
13
+ node-version: '18'
14
14
 
15
- - run: sudo apt update && sudo apt install -y libcurl4-gnutls-dev
15
+ - run: sudo apt update && sudo apt install -y libcurl4-gnutls-dev subversion
16
16
  - run: npm install
17
17
  - run: npm run doc
18
18
 
@@ -25,7 +25,7 @@ jobs:
25
25
  - uses: actions/checkout@v2
26
26
  - uses: actions/setup-node@v2
27
27
  with:
28
- node-version: '14'
28
+ node-version: '18'
29
29
 
30
30
  - run: npm install --arch=ia32
31
31
  - run: npm run doc
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-sword-interface",
3
- "version": "1.0.40",
3
+ "version": "1.0.42",
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!"
@@ -101,21 +101,24 @@ inline bool isDisallowedCharacter(char c) {
101
101
  return disallowedPunctuation.find(c) != string::npos;
102
102
  }
103
103
 
104
- vector<Verse> ModuleSearch::getModuleSearchResults(string moduleName,
105
- string searchTerm,
106
- SearchType searchType,
107
- SearchScope searchScope,
108
- bool isCaseSensitive,
109
- bool useExtendedVerseBoundaries,
110
- bool filterOnWordBoundaries)
104
+ bool ModuleSearch::validateSearchParameters(SWModule* module, const string& searchTerm)
105
+ {
106
+ if (module == 0) {
107
+ cerr << "ModuleSearch::getModuleSearchResults: getLocalModule returned zero pointer for " << _currentModuleName << endl;
108
+ return false;
109
+ }
110
+
111
+ if (searchTerm == "") {
112
+ cerr << "ModuleSearch::getModuleSearchResults: cannot work with empty search term!" << endl;
113
+ return false;
114
+ }
115
+
116
+ return true;
117
+ }
118
+
119
+ int ModuleSearch::getSearchFlags(bool isCaseSensitive, bool useExtendedVerseBoundaries)
111
120
  {
112
- this->_currentModuleName = moduleName;
113
- SWModule* module = this->_moduleStore.getSearchSwMgr()->getModule(moduleName.c_str());
114
- ListKey listKey;
115
- SWKey* scope = 0;
116
121
  int flags = 0;
117
- vector<Verse> searchResults;
118
- vector<string> searchResultReferences;
119
122
 
120
123
  if (!isCaseSensitive) {
121
124
  flags |= REG_ICASE;
@@ -125,126 +128,207 @@ vector<Verse> ModuleSearch::getModuleSearchResults(string moduleName,
125
128
  flags |= SWModule::SEARCHFLAG_STRICTBOUNDARIES;
126
129
  }
127
130
 
128
- if (module == 0) {
129
- cerr << "ModuleSearch::getModuleSearchResults: getLocalModule returned zero pointer for " << moduleName << endl;
130
- } else if (searchTerm == "") {
131
- cerr << "ModuleSearch::getModuleSearchResults: cannot work with empty search term!" << endl;
132
- } else {
133
- ListKey scopeKey;
131
+ return flags;
132
+ }
134
133
 
135
- if (searchScope != SearchScope::BIBLE) {
136
- scopeKey = this->getScopeKey(module, searchScope);
137
- scope = &scopeKey;
134
+ string ModuleSearch::prepareStrongsSearchTerm(string searchTerm, SearchType searchType, SWModule* module)
135
+ {
136
+ string strongsNumber = searchTerm.substr(1, searchTerm.size());
137
+
138
+ if (searchTerm[0] == 'H') {
139
+ if (this->_textProcessor.moduleHasStrongsPaddedZeroPrefixes(module)) {
140
+ string paddedStrongsNumber = this->_textProcessor.padStrongsNumber(strongsNumber);
141
+ searchTerm = "H" + paddedStrongsNumber;
142
+ } else if (this->_textProcessor.moduleHasStrongsZeroPrefixes(module)) {
143
+ searchTerm = "H0" + strongsNumber;
138
144
  }
145
+ } else if (searchTerm[0] == 'G') {
146
+ if (this->_textProcessor.moduleHasStrongsPaddedZeroPrefixes(module)) {
147
+ string paddedStrongsNumber = this->_textProcessor.padStrongsNumber(strongsNumber);
148
+ searchTerm = "G" + paddedStrongsNumber;
149
+ }
150
+ }
139
151
 
140
- bool hasStrongs = this->_moduleHelper.moduleHasGlobalOption(module, "Strongs");
141
- bool moduleMarkupIsBroken = this->_moduleHelper.isBrokenMarkupModule(moduleName);
142
- bool hasInconsistentClosingEndDivs = this->_moduleHelper.isInconsistentClosingEndDivModule(moduleName);
143
-
144
- if (searchType == SearchType::strongsNumber) {
145
- if (!hasStrongs) {
146
- return searchResults;
147
- }
148
-
149
- string strongsNumber = searchTerm.substr(1, searchTerm.size());
152
+ return "Word//Lemma./" + searchTerm;
153
+ }
150
154
 
151
- if (searchTerm[0] == 'H') {
152
- if (this->_textProcessor.moduleHasStrongsPaddedZeroPrefixes(module)) {
153
- string paddedStrongsNumber = this->_textProcessor.padStrongsNumber(strongsNumber);
154
- searchTerm = "H" + paddedStrongsNumber;
155
- } else if (this->_textProcessor.moduleHasStrongsZeroPrefixes(module)) {
156
- searchTerm = "H0" + strongsNumber;
157
- }
158
- } else if (searchTerm[0] == 'G') {
159
- if (this->_textProcessor.moduleHasStrongsPaddedZeroPrefixes(module)) {
160
- string paddedStrongsNumber = this->_textProcessor.padStrongsNumber(strongsNumber);
161
- searchTerm = "G" + paddedStrongsNumber;
155
+ bool ModuleSearch::phraseSequenceCheck(const vector<string>& words, const vector<string>& searchWords)
156
+ {
157
+ // For single word searches, sequence doesn't matter
158
+ if (searchWords.size() <= 1) {
159
+ return true;
160
+ }
161
+
162
+ // Iterate through potential starting positions in the verse
163
+ for (size_t startPos = 0; startPos < words.size(); startPos++) {
164
+ // Check if this is a match for the first word
165
+ if (words[startPos] == searchWords[0]) {
166
+ // See if subsequent words match in sequence
167
+ bool sequenceMatch = true;
168
+ for (size_t i = 1; i < searchWords.size(); i++) {
169
+ size_t nextPos = startPos + i;
170
+ // Make sure we don't go beyond the verse text
171
+ if (nextPos >= words.size() || words[nextPos] != searchWords[i]) {
172
+ sequenceMatch = false;
173
+ break;
162
174
  }
163
175
  }
164
-
165
- flags |= SWModule::SEARCHFLAG_MATCHWHOLEENTRY;
166
- searchTerm = "Word//Lemma./" + searchTerm;
176
+
177
+ if (sequenceMatch) {
178
+ return true;
179
+ }
167
180
  }
181
+ }
182
+
183
+ return false;
184
+ }
185
+
186
+ vector<string> ModuleSearch::getSearchResultReferences(SWModule* module, ListKey& listKey,
187
+ const string& searchTerm, SearchType searchType,
188
+ bool isCaseSensitive, bool filterOnWordBoundaries,
189
+ bool hasStrongs, bool hasInconsistentClosingEndDivs,
190
+ bool moduleMarkupIsBroken)
191
+ {
192
+ vector<string> filteredReferences;
193
+
194
+ // Disable markup before filtering on word boundaries
195
+ this->_textProcessor.disableMarkup();
168
196
 
169
- // Perform search
170
- listKey = module->search(searchTerm.c_str(), int(searchType), flags, scope, 0, internalModuleSearchProgressCB);
197
+ // Make the search term lower case if case sensitivity is not required
198
+ string lowerCaseSearchTerm = searchTerm;
199
+ if (!isCaseSensitive) {
200
+ std::transform(lowerCaseSearchTerm.begin(), lowerCaseSearchTerm.end(), lowerCaseSearchTerm.begin(), ::tolower);
201
+ }
171
202
 
172
- // Disable markup before filtering on word boundaries
173
- this->_textProcessor.disableMarkup();
203
+ // Split the search term into individual words
204
+ vector<string> searchWords = StringHelper::split(lowerCaseSearchTerm, " ");
174
205
 
175
- vector<string> filteredReferences;
206
+ while (!listKey.popError()) {
207
+ module->setKey(listKey.getElement());
208
+ string verseText = this->_textProcessor.getCurrentVerseText(module,
209
+ hasStrongs,
210
+ hasInconsistentClosingEndDivs,
211
+ moduleMarkupIsBroken);
176
212
 
177
- // Make the search term lower case if case sensitivity is not required
178
- string lowerCaseSearchTerm = searchTerm;
213
+ // Make the verse text lower case if case sensitivity is not required
214
+ string lowerCaseVerseText = verseText;
179
215
  if (!isCaseSensitive) {
180
- std::transform(lowerCaseSearchTerm.begin(), lowerCaseSearchTerm.end(), lowerCaseSearchTerm.begin(), ::tolower);
216
+ std::transform(lowerCaseVerseText.begin(), lowerCaseVerseText.end(), lowerCaseVerseText.begin(), ::tolower);
181
217
  }
182
218
 
183
- // Split the search term into individual words
184
- vector<string> searchWords = StringHelper::split(lowerCaseSearchTerm, " ");
185
-
186
219
  // Filter verses based on word boundaries
187
- while (!listKey.popError()) {
188
- module->setKey(listKey.getElement());
189
- string verseText = this->_textProcessor.getCurrentVerseText(module,
190
- hasStrongs,
191
- hasInconsistentClosingEndDivs,
192
- moduleMarkupIsBroken);
193
-
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);
220
+ if (filterOnWordBoundaries) {
221
+ // Replace disallowed characters with spaces
222
+ std::replace_if(lowerCaseVerseText.begin(), lowerCaseVerseText.end(),
223
+ [](char c) { return isDisallowedCharacter(c); }, ' ');
224
+
225
+ vector<string> words = StringHelper::split(lowerCaseVerseText, " ");
226
+
227
+ // Check if all parts of the search term match any word in the verse
228
+ bool allPartsMatch = true;
229
+ for (const auto& searchWord : searchWords) {
230
+ if (std::find(words.begin(), words.end(), searchWord) == words.end()) {
231
+ allPartsMatch = false;
232
+ break;
233
+ }
198
234
  }
199
235
 
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, " ");
236
+ bool correctOrder = true;
206
237
 
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
- }
238
+ if (searchType == SearchType::phrase && allPartsMatch) {
239
+ correctOrder = phraseSequenceCheck(words, searchWords);
240
+ }
215
241
 
216
- if (allPartsMatch) {
217
- filteredReferences.push_back(module->getKey()->getShortText());
218
- }
219
- } else {
242
+ if (allPartsMatch && correctOrder) {
220
243
  filteredReferences.push_back(module->getKey()->getShortText());
221
244
  }
222
-
223
- listKey++;
245
+ } else {
246
+ // If not filtering on word boundaries, we simply push the reference
247
+ filteredReferences.push_back(module->getKey()->getShortText());
224
248
  }
225
249
 
226
- // Enable markup again after word boundary filtering
227
- this->_textProcessor.enableMarkup();
250
+ listKey++;
251
+ }
252
+
253
+ // Enable markup again after word boundary filtering
254
+ this->_textProcessor.enableMarkup();
255
+
256
+ return filteredReferences;
257
+ }
258
+
259
+ vector<Verse> ModuleSearch::createVersesFromReferences(SWModule* module, const vector<string>& references,
260
+ bool hasStrongs, bool hasInconsistentClosingEndDivs,
261
+ bool moduleMarkupIsBroken)
262
+ {
263
+ vector<Verse> verses;
264
+ map<string, int> absoluteVerseNumbers = this->_moduleHelper.getAbsoluteVerseNumberMap(module);
265
+
266
+ for (const auto& reference : references) {
267
+ module->setKey(reference.c_str());
268
+ string verseText = this->_textProcessor.getCurrentVerseText(module,
269
+ hasStrongs,
270
+ hasInconsistentClosingEndDivs,
271
+ moduleMarkupIsBroken);
272
+
273
+ Verse currentVerse;
274
+ currentVerse.reference = reference;
275
+ currentVerse.absoluteVerseNumber = absoluteVerseNumbers[reference];
276
+ currentVerse.content = verseText;
277
+
278
+ verses.push_back(currentVerse);
279
+ }
280
+
281
+ return verses;
282
+ }
283
+
284
+ vector<Verse> ModuleSearch::getModuleSearchResults(string moduleName,
285
+ string searchTerm,
286
+ SearchType searchType,
287
+ SearchScope searchScope,
288
+ bool isCaseSensitive,
289
+ bool useExtendedVerseBoundaries,
290
+ bool filterOnWordBoundaries)
291
+ {
292
+ this->_currentModuleName = moduleName;
293
+ SWModule* module = this->_moduleStore.getSearchSwMgr()->getModule(moduleName.c_str());
294
+ ListKey listKey;
295
+ SWKey* scope = 0;
296
+ vector<Verse> searchResults;
297
+
298
+ if (!validateSearchParameters(module, searchTerm)) {
299
+ return searchResults;
300
+ }
228
301
 
229
- map<string, int> absoluteVerseNumbers = this->_moduleHelper.getAbsoluteVerseNumberMap(module);
302
+ int flags = getSearchFlags(isCaseSensitive, useExtendedVerseBoundaries);
230
303
 
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);
304
+ ListKey scopeKey;
238
305
 
239
- Verse currentVerse;
240
- currentVerse.reference = reference;
241
- currentVerse.absoluteVerseNumber = absoluteVerseNumbers[reference];
242
- currentVerse.content = verseText;
306
+ if (searchScope != SearchScope::BIBLE) {
307
+ scopeKey = this->getScopeKey(module, searchScope);
308
+ scope = &scopeKey;
309
+ }
243
310
 
244
- searchResults.push_back(currentVerse);
245
- }
311
+ bool hasStrongs = this->_moduleHelper.moduleHasGlobalOption(module, "Strongs");
312
+ bool moduleMarkupIsBroken = this->_moduleHelper.isBrokenMarkupModule(moduleName);
313
+ bool hasInconsistentClosingEndDivs = this->_moduleHelper.isInconsistentClosingEndDivModule(moduleName);
314
+
315
+ if (searchType == SearchType::strongsNumber && hasStrongs) {
316
+ searchTerm = prepareStrongsSearchTerm(searchTerm, searchType, module);
317
+ flags |= SWModule::SEARCHFLAG_MATCHWHOLEENTRY;
246
318
  }
247
319
 
320
+ // Perform search
321
+ listKey = module->search(searchTerm.c_str(), int(searchType), flags, scope, 0, internalModuleSearchProgressCB);
322
+
323
+ // Get search result references while considering the word boundary filter option
324
+ vector<string> filteredReferences = getSearchResultReferences(module, listKey, searchTerm, searchType,
325
+ isCaseSensitive, filterOnWordBoundaries,
326
+ hasStrongs, hasInconsistentClosingEndDivs,
327
+ moduleMarkupIsBroken);
328
+
329
+ searchResults = createVersesFromReferences(module, filteredReferences, hasStrongs,
330
+ hasInconsistentClosingEndDivs, moduleMarkupIsBroken);
331
+
248
332
  this->_currentModuleName = "";
249
333
 
250
334
  return searchResults;
@@ -67,6 +67,19 @@ public:
67
67
 
68
68
  private:
69
69
  sword::ListKey getScopeKey(sword::SWModule* module, SearchScope scope);
70
+
71
+ bool validateSearchParameters(sword::SWModule* module, const std::string& searchTerm);
72
+ int getSearchFlags(bool isCaseSensitive, bool useExtendedVerseBoundaries);
73
+ std::string prepareStrongsSearchTerm(std::string searchTerm, SearchType searchType, sword::SWModule* module);
74
+ std::vector<std::string> getSearchResultReferences(sword::SWModule* module, sword::ListKey& listKey,
75
+ const std::string& searchTerm, SearchType searchType,
76
+ bool isCaseSensitive, bool filterOnWordBoundaries,
77
+ bool hasStrongs, bool hasInconsistentClosingEndDivs,
78
+ bool moduleMarkupIsBroken);
79
+ std::vector<Verse> createVersesFromReferences(sword::SWModule* module, const std::vector<std::string>& references,
80
+ bool hasStrongs, bool hasInconsistentClosingEndDivs,
81
+ bool moduleMarkupIsBroken);
82
+ bool phraseSequenceCheck(const std::vector<std::string>& words, const std::vector<std::string>& searchWords);
70
83
 
71
84
  ModuleStore& _moduleStore;
72
85
  ModuleHelper& _moduleHelper;