node-sword-interface 1.0.41 → 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.
|
@@ -10,9 +10,9 @@ jobs:
|
|
|
10
10
|
- uses: actions/checkout@v2
|
|
11
11
|
- uses: actions/setup-node@v2
|
|
12
12
|
with:
|
|
13
|
-
node-version: '
|
|
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: '
|
|
28
|
+
node-version: '18'
|
|
29
29
|
|
|
30
30
|
- run: npm install --arch=ia32
|
|
31
31
|
- run: npm run doc
|
package/package.json
CHANGED
|
@@ -101,21 +101,24 @@ inline bool isDisallowedCharacter(char c) {
|
|
|
101
101
|
return disallowedPunctuation.find(c) != string::npos;
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
129
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
141
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
-
|
|
170
|
-
|
|
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
|
-
|
|
173
|
-
|
|
203
|
+
// Split the search term into individual words
|
|
204
|
+
vector<string> searchWords = StringHelper::split(lowerCaseSearchTerm, " ");
|
|
174
205
|
|
|
175
|
-
|
|
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
|
|
178
|
-
string
|
|
213
|
+
// Make the verse text lower case if case sensitivity is not required
|
|
214
|
+
string lowerCaseVerseText = verseText;
|
|
179
215
|
if (!isCaseSensitive) {
|
|
180
|
-
std::transform(
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
std::
|
|
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
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
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
|
-
|
|
302
|
+
int flags = getSearchFlags(isCaseSensitive, useExtendedVerseBoundaries);
|
|
230
303
|
|
|
231
|
-
|
|
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
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
306
|
+
if (searchScope != SearchScope::BIBLE) {
|
|
307
|
+
scopeKey = this->getScopeKey(module, searchScope);
|
|
308
|
+
scope = &scopeKey;
|
|
309
|
+
}
|
|
243
310
|
|
|
244
|
-
|
|
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;
|