1 /******************************************************************************
2  *
3  *  gbfwordjs.cpp -	SWFilter descendant for ???
4  *
5  * $Id: gbfwordjs.cpp 3515 2017-11-01 11:38:09Z scribe $
6  *
7  * Copyright 2005-2013 CrossWire Bible Society (http://www.crosswire.org)
8  *	CrossWire Bible Society
9  *	P. O. Box 2528
10  *	Tempe, AZ  85280-2528
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU General Public License as published by the
14  * Free Software Foundation version 2.
15  *
16  * This program is distributed in the hope that it will be useful, but
17  * WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * General Public License for more details.
20  *
21  */
22 
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <gbfwordjs.h>
26 #include <swmodule.h>
27 #include <ctype.h>
28 #include <utilstr.h>
29 #include <versekey.h>
30 
31 
32 SWORD_NAMESPACE_START
33 
34 namespace {
35 
36 	static const char oName[] = "Word Javascript";
37 	static const char oTip[]  = "Toggles Word Javascript data";
38 
oValues()39 	static const StringList *oValues() {
40 		static const SWBuf choices[3] = {"Off", "On", ""};
41 		static const StringList oVals(&choices[0], &choices[2]);
42 		return &oVals;
43 	}
44 }
45 
46 
GBFWordJS()47 GBFWordJS::GBFWordJS() : SWOptionFilter(oName, oTip, oValues()) {
48 
49      defaultGreekLex   = 0;
50      defaultHebLex     = 0;
51      defaultGreekParse = 0;
52      defaultHebParse   = 0;
53      mgr               = 0;
54 }
55 
56 
~GBFWordJS()57 GBFWordJS::~GBFWordJS() {
58 }
59 
60 
processText(SWBuf & text,const SWKey * key,const SWModule * module)61 char GBFWordJS::processText(SWBuf &text, const SWKey *key, const SWModule *module) {
62 	if (option) {
63 		char token[2112]; // cheese.  Fix.
64 		int tokpos = 0;
65 		bool intoken = false;
66 		int word = 1;
67 		char val[128];
68 		char wordstr[5];
69 		unsigned int textStart = 0, lastAppendLen = 0, textEnd = 0;
70 		SWBuf tmp;
71 		bool newText = false;
72 		bool needWordOut = false;
73 		AttributeValue *wordAttrs = 0;
74 		SWBuf modName = (module)?module->getName():"";
75 		SWBuf wordSrcPrefix = modName;
76 
77 		const SWBuf orig = text;
78 		const char * from = orig.c_str();
79 		VerseKey *vkey = 0;
80 		if (key) {
81 			vkey = SWDYNAMIC_CAST(VerseKey, key);
82 		}
83 
84 		for (text = ""; *from; from++) {
85 			if (*from == '<') {
86 				intoken = true;
87 				tokpos = 0;
88 				token[0] = 0;
89 				token[1] = 0;
90 				token[2] = 0;
91 				textEnd = (unsigned int)text.length();
92 				continue;
93 			}
94 			if (*from == '>') {	// process tokens
95 				intoken = false;
96 				if (*token == 'W' && (token[1] == 'G' || token[1] == 'H')) {	// Strongs
97 					strcpy(val,token+1);
98 					if (atoi((!isdigit(*val))?val+1:val) < 5627) {
99 						// normal strongs number
100 						sprintf(wordstr, "%03d", word++);
101 						needWordOut = (word > 2);
102 						wordAttrs = &(module->getEntryAttributes()["Word"][wordstr]);
103 						(*wordAttrs)["Lemma"] = val;
104 	//printf("Adding: [\"Word\"][%s][\"Strongs\"] = %s\n", wordstr, val);
105 						tmp = "";
106 						tmp.append(text.c_str()+textStart, (int)(textEnd - textStart));
107 						(*wordAttrs)["Text"] = tmp;
108 						text.append("</span>");
109 						SWBuf ts;
110 						ts.appendFormatted("%d", textStart);
111 						(*wordAttrs)["TextStart"] = ts;
112 	//printf("Adding: [\"Word\"][%s][\"Text\"] = %s\n", wordstr, tmp.c_str());
113 						newText = true;
114 					}
115 					else {
116 						// verb morph
117 						if (wordAttrs) {
118 							(*wordAttrs)["Morph"] = val;
119 						}
120 	//printf("Adding: [\"Word\"][%s][\"Morph\"] = %s\n", wordstr, val);
121 					}
122 
123 				}
124 				if (*token == 'W' && token[1] == 'T') {	// Morph
125 					if (token[2] == 'G' || token[2] == 'H') {
126 						strcpy(val, token+2);
127 					}
128 					else strcpy(val, token+1);
129 					if (wordAttrs) {
130 						(*wordAttrs)["Morph"] = val;
131 						(*wordAttrs)["MorphClass"] = "StrongsMorph";
132 					}
133 					newText = true;
134 				}
135 				// if not a strongs token, keep token in text
136 				text += '<';
137 				text += token;
138 				text += '>';
139 				if (needWordOut) {
140 					char wstr[11];
141 					sprintf(wstr, "%03d", word-2);
142 					AttributeValue *wAttrs = &(module->getEntryAttributes()["Word"][wstr]);
143 					needWordOut = false;
144 					SWBuf strong = (*wAttrs)["Lemma"];
145 					SWBuf morph = (*wAttrs)["Morph"];
146 					SWBuf morphClass = (*wAttrs)["MorphClass"];
147 					SWBuf wordText = (*wAttrs)["Text"];
148 					SWBuf textSt = (*wAttrs)["TextStart"];
149 					if (strong.size()) {
150 						char gh = 0;
151 						gh = isdigit(strong[0]) ? 0:strong[0];
152 						if (!gh) {
153 							if (vkey) {
154 								gh = vkey->getTestament() ? 'H' : 'G';
155 							}
156 						}
157 						else strong << 1;
158 
159 						SWModule *sLex = 0;
160 						SWModule *sMorph = 0;
161 						if (gh == 'G') {
162 							sLex = defaultGreekLex;
163 							sMorph = defaultGreekParse;
164 						}
165 						if (gh == 'H') {
166 							sLex = defaultHebLex;
167 							sMorph = defaultHebParse;
168 						}
169 						SWBuf lexName = "";
170 						if (sLex) {
171 							// we can pass the real lex name in, but we have some
172 							// aliases in the javascript to optimize bandwidth
173 							lexName = sLex->getName();
174 							if (lexName == "StrongsGreek")
175 								lexName = "G";
176 							if (lexName == "StrongsHebrew")
177 								lexName = "H";
178 						}
179 						SWBuf wordID;
180 						if (vkey) {
181 							// optimize for bandwidth and use only the verse as the unique entry id
182 							wordID.appendFormatted("%d", vkey->getVerse());
183 						}
184 						else {
185 							wordID = key->getText();
186 						}
187 						for (unsigned int i = 0; i < wordID.size(); i++) {
188 							if ((!isdigit(wordID[i])) && (!isalpha(wordID[i]))) {
189 								wordID[i] = '_';
190 							}
191 						}
192 						wordID.appendFormatted("_%s%d", wordSrcPrefix.c_str(), atoi(wstr));
193 						if (textSt.size()) {
194 							int textStr = atoi(textSt.c_str());
195 							textStr += lastAppendLen;
196 							SWBuf spanStart = "";
197 
198 
199 
200 							if (!sMorph) sMorph = 0;	// to pass unused warning for now
201 /*
202 							if (sMorph) {
203 								SWBuf popMorph = "<a onclick=\"";
204 								popMorph.appendFormatted("p(\'%s\',\'%s\','%s','');\" >%s</a>", sMorph->getName(), morph.c_str(), wordID.c_str(), morph.c_str());
205 								morph = popMorph;
206 							}
207 */
208 
209 							// 'p' = 'fillpop' to save bandwidth
210 							const char *m = strchr(morph.c_str(), ':');
211 							if (m) m++;
212 							else m = morph.c_str();
213 							spanStart.appendFormatted("<span class=\"clk\" onclick=\"p('%s','%s','%s','%s','','%s');\" >", lexName.c_str(), strong.c_str(), wordID.c_str(), m, modName.c_str());
214 							text.insert(textStr, spanStart);
215 							lastAppendLen = (unsigned int)spanStart.length();
216 						}
217 					}
218 
219 				}
220 				if (newText) {
221 					textStart = (unsigned int)text.length(); newText = false;
222 				}
223 				continue;
224 			}
225 			if (intoken) {
226 				if (tokpos < 2045) {
227 					token[tokpos++] = *from;
228 					// TODO: why is this + 2 ?
229 					token[tokpos+2] = 0;
230 				}
231 			}
232 			else {
233 				text += *from;
234 			}
235 		}
236 
237 		char wstr[11];
238 		sprintf(wstr, "%03d", word-1);
239 		AttributeValue *wAttrs = &(module->getEntryAttributes()["Word"][wstr]);
240 		needWordOut = false;
241 		SWBuf strong = (*wAttrs)["Lemma"];
242 		SWBuf morph = (*wAttrs)["Morph"];
243 		SWBuf morphClass = (*wAttrs)["MorphClass"];
244 		SWBuf wordText = (*wAttrs)["Text"];
245 		SWBuf textSt = (*wAttrs)["TextStart"];
246 		if (strong.size()) {
247 			char gh = 0;
248 			gh = isdigit(strong[0]) ? 0:strong[0];
249 			if (!gh) {
250 				if (vkey) {
251 					gh = vkey->getTestament() ? 'H' : 'G';
252 				}
253 			}
254 			else strong << 1;
255 
256 			SWModule *sLex = 0;
257 			if (gh == 'G') {
258 				sLex = defaultGreekLex;
259 			}
260 			if (gh == 'H') {
261 				sLex = defaultHebLex;
262 			}
263 			SWBuf lexName = "";
264 			if (sLex) {
265 				// we can pass the real lex name in, but we have some
266 				// aliases in the javascript to optimize bandwidth
267 				lexName = sLex->getName();
268 				if (lexName == "StrongsGreek")
269 					lexName = "G";
270 				if (lexName == "StrongsHebrew")
271 					lexName = "H";
272 			}
273 			SWBuf wordID;
274 			if (vkey) {
275 				// optimize for bandwidth and use only the verse as the unique entry id
276 				wordID.appendFormatted("%d", vkey->getVerse());
277 			}
278 			else {
279 				wordID = key->getText();
280 			}
281 			for (unsigned int i = 0; i < wordID.size(); i++) {
282 				if ((!isdigit(wordID[i])) && (!isalpha(wordID[i]))) {
283 					wordID[i] = '_';
284 				}
285 			}
286 			wordID.appendFormatted("_%s%d", wordSrcPrefix.c_str(), atoi(wstr));
287 			if (textSt.size()) {
288 				int textStr = atoi(textSt.c_str());
289 				textStr += lastAppendLen;
290 				SWBuf spanStart = "";
291 				// 'p' = 'fillpop' to save bandwidth
292 				const char *m = strchr(morph.c_str(), ':');
293 				if (m) m++;
294 				else m = morph.c_str();
295 				spanStart.appendFormatted("<span class=\"clk\" onclick=\"p('%s','%s','%s','%s','','%s');\" >", lexName.c_str(), strong.c_str(), wordID.c_str(), m, modName.c_str());
296 				text.insert(textStr, spanStart);
297 			}
298 		}
299 	}
300 
301 	return 0;
302 }
303 
304 SWORD_NAMESPACE_END
305