node-sword-interface 1.0.38 → 1.0.40
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.
- package/index.js +16 -5
- package/package.json +1 -1
- package/src/napi_module/index.js +0 -0
- package/src/napi_module/module_search_worker.cpp +2 -1
- package/src/napi_module/module_search_worker.hpp +4 -1
- package/src/napi_module/node_sword_interface.cpp +17 -3
- package/src/napi_module/node_sword_interface.hpp +1 -0
- package/src/sword_backend/module_search.cpp +73 -32
- package/src/sword_backend/module_search.hpp +2 -1
package/index.js
CHANGED
|
@@ -331,6 +331,14 @@ class NodeSwordInterface {
|
|
|
331
331
|
return this.nativeInterface.enableMarkup();
|
|
332
332
|
}
|
|
333
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Disable available markup (like Strongs, foot notes, etc.)
|
|
336
|
+
* This influences the output for getChapterText, getBookText and getBibleText.
|
|
337
|
+
*/
|
|
338
|
+
disableMarkup() {
|
|
339
|
+
return this.nativeInterface.disableMarkup();
|
|
340
|
+
}
|
|
341
|
+
|
|
334
342
|
/**
|
|
335
343
|
* Enables rendering of Strongs elements with non-breaking spaces.
|
|
336
344
|
*/
|
|
@@ -584,15 +592,17 @@ class NodeSwordInterface {
|
|
|
584
592
|
* @param {String} searchScope - Options: BIBLE, OT, NT
|
|
585
593
|
* @param {Boolean} isCaseSensitive - Whether the search is case sensitive
|
|
586
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.
|
|
587
596
|
* @return {Promise}
|
|
588
597
|
*/
|
|
589
598
|
getModuleSearchResults(moduleCode,
|
|
590
599
|
searchTerm,
|
|
591
|
-
progressCB=undefined,
|
|
592
|
-
searchType="phrase",
|
|
593
|
-
searchScope="BIBLE",
|
|
594
|
-
isCaseSensitive=false,
|
|
595
|
-
useExtendedVerseBoundaries=false
|
|
600
|
+
progressCB = undefined,
|
|
601
|
+
searchType = "phrase",
|
|
602
|
+
searchScope = "BIBLE",
|
|
603
|
+
isCaseSensitive = false,
|
|
604
|
+
useExtendedVerseBoundaries = false,
|
|
605
|
+
filterOnWordBoundaries = false) {
|
|
596
606
|
|
|
597
607
|
if (progressCB === undefined) {
|
|
598
608
|
progressCB = function(progress) {};
|
|
@@ -606,6 +616,7 @@ class NodeSwordInterface {
|
|
|
606
616
|
searchScope,
|
|
607
617
|
isCaseSensitive,
|
|
608
618
|
useExtendedVerseBoundaries,
|
|
619
|
+
filterOnWordBoundaries,
|
|
609
620
|
progressCB,
|
|
610
621
|
function(searchResults) { resolve(searchResults); });
|
|
611
622
|
} catch (error) {
|
package/package.json
CHANGED
|
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
|
|
|
@@ -90,6 +90,7 @@ Napi::Object NodeSwordInterface::Init(Napi::Env env, Napi::Object exports)
|
|
|
90
90
|
InstanceMethod("getModuleDescription", &NodeSwordInterface::getModuleDescription),
|
|
91
91
|
InstanceMethod("getLocalModule", &NodeSwordInterface::getLocalModule),
|
|
92
92
|
InstanceMethod("enableMarkup", &NodeSwordInterface::enableMarkup),
|
|
93
|
+
InstanceMethod("disableMarkup", &NodeSwordInterface::disableMarkup),
|
|
93
94
|
InstanceMethod("enableStrongsWithNbsp", &NodeSwordInterface::enableStrongsWithNbsp),
|
|
94
95
|
InstanceMethod("getRawModuleEntry", &NodeSwordInterface::getRawModuleEntry),
|
|
95
96
|
InstanceMethod("getReferenceText", &NodeSwordInterface::getReferenceText),
|
|
@@ -527,6 +528,16 @@ Napi::Value NodeSwordInterface::enableMarkup(const Napi::CallbackInfo& info)
|
|
|
527
528
|
return info.Env().Undefined();
|
|
528
529
|
}
|
|
529
530
|
|
|
531
|
+
Napi::Value NodeSwordInterface::disableMarkup(const Napi::CallbackInfo& info)
|
|
532
|
+
{
|
|
533
|
+
lockApi();
|
|
534
|
+
Napi::Env env = info.Env();
|
|
535
|
+
Napi::HandleScope scope(env);
|
|
536
|
+
this->_textProcessor->disableMarkup();
|
|
537
|
+
unlockApi();
|
|
538
|
+
return info.Env().Undefined();
|
|
539
|
+
}
|
|
540
|
+
|
|
530
541
|
Napi::Value NodeSwordInterface::enableStrongsWithNbsp(const Napi::CallbackInfo& info)
|
|
531
542
|
{
|
|
532
543
|
lockApi();
|
|
@@ -749,6 +760,7 @@ Napi::Value NodeSwordInterface::getModuleSearchResults(const Napi::CallbackInfo&
|
|
|
749
760
|
ParamType::string, // searchScope
|
|
750
761
|
ParamType::boolean, // isCaseSensitive
|
|
751
762
|
ParamType::boolean, // useExtendedVerseBoundaries
|
|
763
|
+
ParamType::boolean, // filterOnWordBoundaries
|
|
752
764
|
ParamType::function, // progressCallback
|
|
753
765
|
ParamType::function); // final Callback
|
|
754
766
|
|
|
@@ -758,8 +770,9 @@ Napi::Value NodeSwordInterface::getModuleSearchResults(const Napi::CallbackInfo&
|
|
|
758
770
|
string searchScopeString = string(info[3].As<Napi::String>());
|
|
759
771
|
Napi::Boolean isCaseSensitive = info[4].As<Napi::Boolean>();
|
|
760
772
|
Napi::Boolean useExtendedVerseBoundaries = info[5].As<Napi::Boolean>();
|
|
761
|
-
Napi::
|
|
762
|
-
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>();
|
|
763
776
|
SearchType searchType = SearchType::multiWord;
|
|
764
777
|
|
|
765
778
|
if (searchTypeString == "phrase") {
|
|
@@ -802,7 +815,8 @@ Napi::Value NodeSwordInterface::getModuleSearchResults(const Napi::CallbackInfo&
|
|
|
802
815
|
searchType,
|
|
803
816
|
searchScope,
|
|
804
817
|
isCaseSensitive,
|
|
805
|
-
useExtendedVerseBoundaries
|
|
818
|
+
useExtendedVerseBoundaries,
|
|
819
|
+
filterOnWordBoundaries); // Pass the new parameter
|
|
806
820
|
this->_currentModuleSearchWorker->Queue();
|
|
807
821
|
return env.Undefined();
|
|
808
822
|
}
|
|
@@ -70,6 +70,7 @@ private:
|
|
|
70
70
|
Napi::Value getLocalModule(const Napi::CallbackInfo& info);
|
|
71
71
|
|
|
72
72
|
Napi::Value enableMarkup(const Napi::CallbackInfo& info);
|
|
73
|
+
Napi::Value disableMarkup(const Napi::CallbackInfo& info);
|
|
73
74
|
Napi::Value enableStrongsWithNbsp(const Napi::CallbackInfo& info);
|
|
74
75
|
|
|
75
76
|
Napi::Value getRawModuleEntry(const Napi::CallbackInfo& info);
|
|
@@ -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
|
-
//
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
if (
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
|