PageSourceSearch

https://www.biblewebapp.com/js/app-reader.js

js biblewebapp.com collected 2026-10-02 05:13:45 UTC 30,253 bytes, 857 lines download raw bytes

1if (!window.console)
2    window.console = { log: function() { } };
3
4if (!Array.indexOf) {
5    Array.prototype.indexOf = function(obj) {
6        for (var i = 0; i < this.length; i++) {
7            if (this[i] == obj) {
8                return i;
9            }
10        }
11        return -1;
12    }
13}
14
15$(document).ready(function() {
16
17    bible = {};
18
19    bible.Reader = {
20        lang: 'G',
21
22        dialog: null,
23
24        init: function() {
25
26            this.dialog = $('#dialog-container');
27            this.dialogText = $('#dialog-text');
28            this.dialogHeader = $('#dialog-header');
29            //.dialog();
30            //this.dialog.dialog('close');
31
32
33            // reference helper
34            $('#reference').addClass('firstTime').click(function(e) { $(this).removeClass('firstTime'); });
35
36
37            // font choices
38
39            // add font options
40            var options = '';
41            for (var size = 8; size <= 38; size += 2) {
42                options += '<option value="' + size + 'px">' + size + 'px</option>';
43            }
44            $('select.font-size').html(options);
45            $('#font-size-hebrew').val('20px');
46            $('#font-size-greek').val('16px');
47            
48            
49            options = '';
50            for (var size = 80; size <= 500; size += 20) {
51                options += '<option value="' + size + '%">' + size + '%</option>';
52            }
53            $('select.font-lineheight').html(options);
54            $('#font-lineheight-hebrew').val('240%');
55            $('#font-lineheight-greek').val('200%');            
56            
57
58            $('head').append('<style id="injectedFontCss" type="text/css"></style>');
59            this.updateFonts();
60            $('.font-family').keyup(function(e) { bible.Reader.updateFonts(); });
61            $('.font-size, .font-lineheight').change(function(e) { bible.Reader.updateFonts(); });
62
63            // setup verse change		
64            $('#reference').keyup(function(e) {
65                if (e.keyCode == 13) {
66                    e.preventDefault();
67                    bible.Reader.getReference();
68                }
69            });
70            $('#go-button').bind('click', function(e) {
71                    e.preventDefault();
72                    bible.Reader.getReference();
73            });
74
75            // setup word count threshold
76            $('#word-count-threshold, #show-parsing, #show-pos, #show-frequency').change(function(f) {
77                bible.Reader.buildLexicalInfo();
78            });
79
80            // setup parts of speech
81            $('select.pos-rule')
82			.change(function(l) {
83			    bible.Reader.updateStyles();
84			})
85			.html('<option value="">--none--</option>'
86					+ '<option value="c:#910000">Red</option>'
87					+ '<option value="h:#ffcccc">Red (bg)</option>'
88					+ '<option value="c:#ffcc33">Orange</option>'
89					+ '<option value="h:#ffcc33">Orange (bg)</option>'
90					+ '<option value="c:#eeee66">Yellow</option>'
91					+ '<option value="h:#eeee66">Yellow (bg)</option>'
92					+ '<option value="c:#319D2D">Green</option>'
93					+ '<option value="h:#aaff55">Green (bg)</option>'
94					+ '<option value="c:#0000ff">Blue</option>'
95					+ '<option value="h:#55AAFF">Blue (bg)</option>'
96					+ '<option value="c:#AA7FFF">Purple</option>'
97					+ '<option value="h:#AA7FFF">Purple (bg)</option>'
98					+ '<option value="u">Underline</option>'
99					+ '<option value="b">Bold</option>'
100					+ '<option value="i">Italic</option>'
101
102
103
104					);
105
106        },
107
108        updateFonts: function() {
109            /*
110            var cssText =
111                'span.gk { font-family: ' + $('#font-family-greek').val() + '; font-size: ' + $('#font-size-greek').val() + '; line-height: ' + $('#font-lineheight-greek').val() + ';}' +
112                'span.hb { font-family: ' + $('#font-family-hebrew').val() + '; font-size: ' + $('#font-size-hebrew').val() + '; line-height: ' + $('#font-lineheight-hebrew').val() + ';}';
113
114            console.log(cssText);
115            if ($.browser.msie) {
116                $('#injectedFontCss').remove();
117                $('head').append('<style id="injectedFontCss" type="text/css">' + cssText + '</style>');
118            } else {
119                $('#injectedFontCss').html(cssText);
120            }
121            */
122            
123           var lang = (bible.Reader.lang == 'H') ? 'hebrew' : 'greek';
124           $('#bible-text').css(
125							{
126								'font-family':$('#font-family-' + lang).val(),
127								'font-size': $('#font-size-' + lang).val(),
128								'line-height':$('#font-lineheight-' + lang).val(),
129							}
130						);
131
132        },
133
134        wordOver: function(e) {
135            if (!$('#show-popups').attr('checked'))
136                return;
137
138            // $(this).animate({backgroundColor: '#ffff00'}, 500);
139            var o = $(this);
140            o.css('color', '#666');
141
142
143            var sn = o.attr('sn');
144            if (!sn || parseInt(sn) < 1) return
145
146            var pos = o.attr('pos')
147            var inf = o.attr('inf');
148            var d = bible.Reader.dialog;
149
150            d.css('display', 'block');
151            d.css('top', e.pageY + 5);
152            d.css('left', e.pageX + 5);
153            var dt = bible.Reader.dialogText;
154            var dh = bible.Reader.dialogHeader;
155            dh.html('');
156            dt.html('loading...');
157
158            //d.load('scraping/gbs/t-' + sn + '.html');
159            //return;
160
161            bible.Storage.getLexicalEntry(bible.Reader.lang + sn, function(data) {
162                dh.html('<span class="lexical-form ' + ((bible.Reader.lang == 'G') ? 'gk' : 'hb') + '">' + data.LexicalForm + '</span> '
163				+ '<span class="strongs">' + bible.Reader.lang + sn + '</span>');
164
165                dt.html(
166				'<span class="morphology">' + bible.Reader.getPartofSpeech(pos) + ', ' + bible.Reader.getMorphology(pos, inf) + '</span>'
167				+ '<span class="gloss">usage: ' + data.KjvDefinition + '</span>'
168				+ '<div class="outline-of-usage">' + data.OutlineOfUsage + '</dev>');
169                /*
170                var top = e.pageY+5;
171                var left = e.pageX+5;
172                var h = dt.height();
173                var sTop = $(document).scrollTop();
174                var vH= $(document).height();
175       
176			console.log(top , h , vH);
177       
178			if (top + h > vH) {
179                top = (top + h) - vH;
180                }
181				
182			d.css('top', top);
183                d.css('left', left);
184                */
185            });
186        },
187
188        wordOut: function(e) {
189            bible.Reader.dialog.css('display', 'none');
190            /*
191            var o = $(this);
192            var oOff = o.offset();
193            var oW = o.width();
194            var oH = o.height();	
195		
196		function mouseWithin(mX, mY, oX, oY, oW, oH) {
197            console.log(mX, mY, oX, oY, oW, oH);
198            console.log('-', (mX >= oX) , (mX <= oX + oW), (mY >= oY ), ( mY <= oY + oH) );
199			
200			return (mX >= oX && mX <= oX + oW &&
201            mY >= oY && mY <= oY + oH );
202            };
203		
204		setTimeout(function() {
205			
206			var dOff = bible.Reader.dialog.offset();
207            var dW = bible.Reader.dialog.width();
208            var dH = bible.Reader.dialog.height();
209			
210			if ( !(mouseWithin( e.pageX, e.pageY, oOff.left, oOff.top, oW, oH) || mouseWithin( e.pageX, e.pageY, dOff.left, dOff.top, dW, dH) ) ) {
211            bible.Reader.dialog.css('display','none');	
212            }			
213			
214		}, 250);
215            */
216
217            // $(this).stop().css('background-color', '#ffffff');
218            $(this).css('color', '');
219
220        },
221
222
223
224
225        getReference: function() {
226            var ref = $('#reference').val();
227            $('#bible-text').html('Loading...');
228            $('#footnotes').html('');
229
230            bible.Interface.getReference(ref, function(data) {
231								console.log('got some data', data);
232            
233                bible.Storage.setRef(data.reference);
234                $('#reference').val(data.reference);
235                $('#bible-text').html(data.html);
236
237                var book = parseInt(data.book);
238
239                if (book > 0) {
240
241                    bible.Reader.lang = data.lang;
242
243                    // add verse hovers/highlight		
244                    //$('#bible-text span').hover(bible.Reader.wordOver, bible.Reader.wordOut);
245                    $('#bible-text span').mouseover(bible.Reader.wordOver);
246                    $('#bible-text span').mouseout(bible.Reader.wordOut);
247
248                    // text direction for greek and hebrew		
249                    if (bible.Reader.lang == 'H') {
250                        //if (book <= 39) { 
251                        $('#bible-text').removeClass('greek');
252                        $('#bible-text').addClass('hebrew');
253                        //$('#bible-text').css('direction','rtl');
254                    } else {
255                        $('#bible-text').removeClass('hebrew');
256                        $('#bible-text').addClass('greek');
257                        //$('#bible-text').css('direction','ltr');
258                    }
259
260
261                    // RUBY?
262                    if ($('word-count-display').val() == 'above') {
263                        bible.Reader.createRuby();
264                    } else {
265                        bible.Reader.buildLexicalInfo();
266                    }
267                } else {
268                    $('#reference').val(ref);
269                    alert('Invalid reference. Try an alternate spelling.');
270                }
271
272            });
273        },
274
275        partsOfSpeech: {
276            N: 'noun',
277            A: 'adjective',
278            T: 'article',
279            V: 'verb',
280            P: 'personal pronoun',
281            R: 'relative pronoun',
282            C: 'reciprocal pronoun',
283            D: 'demonstrative pronoun',
284            K: 'correlative pronoun',
285            I: 'interrogative pronoun',
286            X: 'indefinite pronoun',
287            Q: 'correlative or interrogative pronoun',
288            F: 'reflexive pronoun',
289            S: 'possessive pronoun',
290            ADV: 'adverb',
291            CONJ: 'conjunction',
292            COND: 'cond',
293            PRT: 'particle',
294            PREP: 'preposition',
295            INJ: 'interjection',
296            ARAM: 'aramaic',
297            HEB: 'hebrew'
298        },
299
300        getPartofSpeech: function(pos) {
301            var full = bible.Reader.partsOfSpeech[pos.toUpperCase()];
302
303            return (full != null) ? full : '?';
304        },
305
306        nounCases: {
307            'N': 'nominative',
308            'V': 'vocative',
309            'G': 'genitive',
310            'D': 'dative',
311            'A': 'accusative'
312        },
313
314        wordNumber: {
315            'S': 'singular',
316            'P': 'plural'
317        },
318
319        wordGender: {
320            'M': 'masculine',
321            'F': 'feminine',
322            'N': 'neuter'
323        },
324
325        wordPerson: {
326            '1': '1st',
327            '2': '2nd',
328            '3': '3rd'
329        },
330
331        verbTenses: {
332            'P': 'present',
333            'I': 'imperfect',
334            'F': 'future',
335            '2F': 'second future',
336            'A': 'aorist',
337            '2A': 'second aorist',
338            'R': 'perfect',
339            '2R': 'second perfect',
340            'L': 'pluperfect',
341            '2L': 'second pluperfect',
342            'X': 'no tense stated'
343        },
344
345        verbVoices: {
346            'A': 'active',
347            'M': 'middle',
348            'P': 'passive',
349            'E': 'middle or passive',
350            'D': 'middle deponent',
351            'O': 'passive deponent',
352            'N': 'middle or passive deponent',
353            'Q': 'impersonal active',
354            'X': 'no voice'
355        },
356
357        verbMoods: {
358            'I': 'indicative',
359            'S': 'subjunctive',
360            'O': 'optative',
361            'M': 'imperative',
362            'N': 'infinitive',
363            'P': 'participle',
364            'R': 'imperative participle'
365        },
366
367        getMorphology: function(pos, info) {
368
369            switch (pos.toUpperCase()) {
370                case 'T':
371                case 'N':
372                    var c = bible.Reader.nounCases[info.substring(0, 1)];
373                    var n = bible.Reader.wordNumber[info.substring(1, 2)];
374                    var g = bible.Reader.wordGender[info.substring(2, 3)];
375                    return c + ', ' + n + ', ' + ((g) ? g : '');
376                case 'A':
377                    var c = bible.Reader.nounCases[info.substring(0, 1)];
378                    var n = bible.Reader.wordNumber[info.substring(1, 2)];
379                    return c + ', ' + n;
380                case 'V':
381                    var t = '';
382                    var rem = ''
383                    if (info.substring(0, 1) == '2') {
384                        t = bible.Reader.verbTenses[info.substring(0, 2)];
385                        rem = info.substring(2);
386                    } else {
387                        t = bible.Reader.verbTenses[info.substring(0, 1)];
388                        rem = info.substring(1);
389                    }
390                    var v = bible.Reader.verbVoices[rem.substring(0, 1)];
391                    var m = bible.Reader.verbMoods[rem.substring(1, 2)];
392
393                    if (rem.length == 2) {
394                        return info + ': ' + t + ', ' + v + ', ' + m;
395                    } else if (rem.length == 5) {
396                        var p = bible.Reader.wordPerson[rem.substring(3, 4)];
397                        var n = bible.Reader.wordNumber[rem.substring(4, 5)];
398                        return t + ', ' + v + ', ' + m + ', ' + p + ', ' + n;
399
400                    } else if (rem.length == 6) {
401                        var c = bible.Reader.nounCases[rem.substring(3, 4)];
402                        var n = bible.Reader.wordNumber[rem.substring(4, 5)];
403                        var g = bible.Reader.wordGender[rem.substring(5, 6)];
404
405                        return t + ', ' + v + ', ' + m + ', ' + c + ', ' + n + ', ' + g;
406                    }
407
408                    //m = bible.Reader.verbMoods[info.substring(2+offset,3+offset)];
409
410
411
412                default:
413                    return info;
414            }
415        },
416
417        createRuby: function() {
418           // var rubyHtml = '';
419           // $('#bible-text span').each(function(
420        },
421
422        buildLexicalInfo: function() {
423            var threshold = $('#word-count-threshold').val();
424
425            // remove all words
426            $('sup.def-up').remove();
427
428            // show loading message
429            $('#footnotes').html("Loading...");
430
431            // add back to selected words
432            if (threshold != '') {
433                var lexicalList = [];
434
435                var tn = parseInt(threshold);
436                var index = 0;
437
438                var showParsing = $('#show-parsing').attr('checked');
439                var showFrequency = $('#show-frequency').attr('checked');
440
441                // go through all the words, 
442                // grab the ones we need to define
443                var wordsNeedingWork = [];
444
445                $('#bible-text span').each(function(f) {
446                    var word = $(this);
447
448                    // filter out verse numbers, articles, and prepositions
449                    if (!word.hasClass('v-num') && !word.hasClass('pos-t') && !word.hasClass('pos-prep')) {
450
451                        var counts = word.attr('wc').split('-');
452                        var tCount = parseInt(counts[0]);
453                        var bCount = parseInt(counts[1]);
454                        var strongsNum = word.attr('sn');
455
456                        // check frequency to see if we need to show this
457                        if ((isNaN(tCount) || tCount <= tn || threshold == 'all') && (parseInt(strongsNum) >
457 0)) {
458
459                            // if we are not showing parsing, check to see if the number can be re-used						
460
461                            var key = strongsNum;
462                            var number = 0;
463                            var skip = false;
464
465                            if (showParsing) {
466                                number = ++index;
467                            } else {
468
469                                number = lexicalList.indexOf(key);
470
471                                if (number != -1) {
472                                    // this number already existed, so use one higher than the index,							
473                                    number++;
474
475                                    // but don't show the definition again
476                                    skip = true;
477                                } else {
478                                    // since this is new, we increase the index
479                                    number = ++index;
480                                    lexicalList.push(key);
481                                    //list += '<li><a name="foot_' + number + '"></a>' + lex + ": " + kjv + '</li>';
482                                }
483                            }
484
485                            // create definition
486                            if (!skip) {
487
488                                var pos = word.attr('pos');
489                                var posInfo = word.attr('inf');
490
491                                wordsNeedingWork.push({
492                                    number: number,
493                                    //lex: lex,
494                                    lex: '',
495                                    strongsNum: strongsNum,
496                                    tCount: tCount,
497                                    bCount: bCount,
498                                    pos: pos,
499                                    posInfo: posInfo,
500                                    englishGlosses: ''
501                                });
502
503
504                            }
505
506                            // create superscript number
507                            // could also use $.append, not sure which style looks better
508                            if (bible.Reader.lang == 'H')
509                                word.before('<sup class="def-up">' + number + '</sup>');
510                            else
511                                word.after('<sup class="def-up">' + number + '</sup>');
512                        }
513                    }
514                });
515
516                console.log('word length', wordsNeedingWork.length);
517
518
519                // get all definitions from our database
520                // when it calls back, draw the list
521                bible.Storage.fillDefinitions(wordsNeedingWork, function(data) {
522
523                    var wordList = data;
524                    var showPartOfSpeech = $('#show-pos').attr('checked');
525                    var showParsing = $('#show-parsing').attr('checked');
526                    var showFrequency = $('#show-frequency').attr('checked');
527
528
529                    var list = '';
530                    for (var wi = 0; wi < wordList.length; wi++) {
531                        var wordData = wordList[wi];
532
533                        list += '<li>' + '<span class="' + ((bible.Reader.lang == 'G') ? 'gk' : 'hb') + '">' + wordData.lex + '</span>, ';
534
535                        if ((showPartOfSpeech || (showParsing && wordData.posInfo != ''))) {
536                            list += ' <span class="parsing-info">';
537                            if (showPartOfSpeech && showParsing)
538                                list += bible.Reader.getPartofSpeech(wordData.pos) + ((wordData.posInfo != '') ? ', ' + bible.Reader.getMorphology(wordData.pos, wordData.posInfo) : '');
539                            else if (showPartOfSpeech && !showParsing)
540                                list += bible.Reader.getPartofSpeech(wordData.pos);
541                            else if (!showPartOfSpeech && showParsing)
542                                list += bible.Reader.getMorphology(wordData.pos, wordData.posInfo);
543
544                            list += '</span>, ';
545                        }
546
547                        list += '' + wordData.englishGlosses +
548						((showFrequency) ? ' (' + wordData.tCount + '-' + wordData.bCount + ')' : '') +
549						'</li>';
550                    }
551
552                    $('#footnotes').html('<ol>
552' + list + '</ol>');
553
554                    // delete this info
555                    wordList = null;
556                    wordsNeedingWork = null;
557
558                });
559            }
560        },
561
562        updateStyles: function() {
563            var styles = '';
564            $('select.pos-rule').each(function(f) {
565                var effect = $(this).val();
566                if (effect != '') {
567                    var ef = effect.substring(0, 1);
568                    var css = '';
569                    switch (ef) {
570                        case 'h':
571                            css = 'background-color: ' + effect.substring(2) + ';';
572                            break;
573                        case 'c':
574                            css = 'color: ' + effect.substring(2) + ';';
575                            break;
576                        case 'b':
577                            css = 'font-weight: bold;';
578                            break;
579                        case 'u':
580                            css = 'text-decoration: underline;';
581                            break;
582                        case 'i':
583                            css = 'font-style: italic;';
584                            break;
585                    }
586
587
588                    styles += '.' + this.id + ' { ' + css + ' } ';
589                }
590            });
591
592            try {
593                $('#custom-styles').html(styles);
594            } catch (ex) { }
595
596        }
597    }
598
599    // STORAGE
600    bible.Storage = {
601        lexicon: null,
602
603        init: function() {
604            bible.Storage.settingsStore = new Persist.Store('settings');
605
606            bible.Storage.lexiconStore = new Persist.Store('lexicon');
607
608            // get lexicon
609            bible.Storage.lexiconStore.get('lexicon', function(ok, data) {
610                if (ok && data != null) {
611                    bible.Storage.lexicon = []; //eval("(" + data + ")");
612                } else {
613                    bible.Storage.lexicon = [];
614                }
615
616                console.log('lexicon', ok, data);
617            });
618        },
619
620        saveLexicon: function() {
621            var x = jQuery.param(bible.Storage.lexicon)
622            bible.Storage.lexiconStore.set('lexicon', x);
623            console.log('lexicon store', bible.Storage.lexicon.length, bible.Storage.lexicon, x);
624        },
625
626        getLexicalEntry: function(sn, callback) {
627            var entry = bible.Storage.lexicon[sn];
628
629            if (entry != null) {
630                callback(entry);
631            } else {
632                bible.Interface.getLexicalEntry(sn, function(data) {
633                    // store
634                    bible.Storage.lexicon[sn] = data;
635
636                    // send				
637                    callback(data);
638                });
639            }
640
641
642        },
643
644        getStrongs: function(word, callback) {
645            // check store
646            bible.Storage.strongsNumStore.get(word, function(ok, data) {
647                // if in store, use it
648                if (ok) {
649                    callback(data);
650                } else {
651                    // else, pull it from ajax
652                    bible.Interface.getStrongs(word, function(data) {
653                        callback(data);
654                    });
655                }
656            });
657        },
658
659        deleteRef: function() {
660            bible.Storage.settingsStore.remove('reference', function(ok, data) { });
661        },
662
663        setRef: function(value) {
664            bible.Storage.settingsStore.set('reference', value);
665        },
666
667        getRef: function(callback) {
668            bible.Storage.settingsStore.get('reference', function(ok, data) { callback(ok, data); });
669        },
670
671        fillDefinitions: function(wordList, callback) {
672
673            // (1) get the list of words not in the database
674            var undefinedWords = [];
675            for (var i = 0; i < wordList.length; i++) {
676
677                var lex = wordList[i].lex;
678                var strongsNum = wordList[i].strongsNum;
679                var key = (lex != '') ? lex : strongsNum;
680
681                var definition = bible.Storage.lexicon[key];
682                if (!definition || definition == null)
683                    undefinedWords.push(key);
684            }
685
686            console.log('word needing', undefinedWords.length, undefinedWords);
687
688            // (4) fill the requested list
689            function fillList() {
690                // fill in this list
691                for (var i = 0; i < wordList.length; i++) {
692
693                    //var lex = wordList[i].lex;
694                    var strongsNum = wordList[i].strongsNum;
695                    var key = bible.Reader.lang + strongsNum; //(lex != '') ? lex : strongsNum;			
696
697                    var definition = bible.Storage.lexicon[key];
698                    console.log('looking', key, definition);
699
700                    if (definition != null) {
701                        wordList[i].englishGlosses = definition.KjvDefinition;
702                        wordList[i].lex = definition.LexicalForm;
703                    } else {
704                        wordList[i].englishGlosses = '(not available)';
705                        wordList[i].lex = '?';
706                    }
707                }
708
709                // (5) run callback
710                callback(wordList);
711            }
712
713            // (2) if we need words, get them via ajax
714            if (undefinedWords.length > 0) {
715
716                var wordQuery = undefinedWords.join(',');
717
718
719                // grab all the words we need
720                bible.Interface.getLexicalList(wordQuery, bible.Reader.lang, function(data) {
721
722                    // (3) fill the database with the ajax words
723                    for (var i = 0; i < data.length; i++) {
724                        var definition = data[i];
725                        var key = bible.Reader.lang + definition.StrongsNumber;
726                        console.log('storing', key, definition);
727                        bible.Storage.lexicon[key] = definition;
728                    }
729
730                    fillList();
731
732                    // store for next time
733                    bible.Storage.saveLexicon();
734
735                });
736            } else {
737                fillList();
738            }
739        }
740    };
741
742    bible.Interface = {
743
744        getLexicalList: function(words, lang, callback) {
745						/*
746            jQuery.get('get.ashx', { a: 'lexlist', d: words, l: lang }, function(data) {
747                callback(data.list);
748            }, 'json');
749						*/
750						
751						jQuery.ajax({
752							url: 'get.ashx', 
753							type: 'GEt',
754							data: { a: 'lexlist', d: words, l: lang }, 
755							success: function(data) {
756								console.log('request complete');
757                callback(data.list);
758							}, 
759							error: function(req, status, error) {
760								console.log('request error', req, status, error );
761							}, 														
762							dataType: 'json'
763						});						
764        },
765
766        getLexicalEntry: function(word, callback) {
767
768						jQuery.ajax({
769							url: 'get.ashx', 
770							type: 'GEt',
771							data: { a: 'lex', d: word }, 
772							success: function(data) {
773								console.log('request complete');
774                callback(data);
775							}, 
776							error: function(req, status, error) {
777								console.log('request error', req, status, error );
778							}, 														
779							dataType: 'json'
780						});			
781
782						/*
783            jQuery.get('get.ashx', { a: 'lex', d: word }, function(data) {
784                callback(data);
785            }, 'json');
786						*/
787        },
788
789        getReference: function(reference, callback) {
790
791						jQuery.ajax({
792							url: 'get.ashx', 
793							type: 'GEt',
794							data: { a: 'ref', d: reference }, 
795							success: function(data) {
796								console.log('request complete');
797                callback(data);
798							}, 
799							error: function(req, status, error) {
800								console.log('request error', req, status, error );
801							}, 							
802							dataType: 'json'
803						});			
804
805						/*
806            jQuery.get('get.ashx', { a: 'ref', d: reference }, function(data) {
807                callback(data);
808            }, 'json');
809						*/
810        }
811
812    };
813
814    bible.utils = {
815        serializeJSON: function(a) {
816            var s = '';
817
818            // Serialize the key/values
819            for (var j in a)
820            // If the value is an array then the key names need to be repeated
821                if (a[j] && a[j].constructor == Array)
822                jQuery.each(a[j], function() {
823                    s.push(encodeURIComponent(j) + "=" + encodeURIComponent(this));
824                });
825            else
826                s.push(encodeURIComponent(j) + "=" + encodeURIComponent(jQuery.isFunction(a[j]) ? a[j]() : a[j]));
827
828            // Return the resulting serialization
829            return s.join("&").replace(/%20/g, "+");
830        },
831
832        deserializeJSON: function(s) {
833            return eval("(" + s + ")");
834        }
835    }
836
837
838    // START UP
839    bible.Reader.init();
840    bible.Storage.init();
841
842    //  init refererence
843    bible.Storage.getRef(function(ok, data) {
844
845        var ref = (!ok || data == null || data.toString() == '' || data.toString().indexOf('Invalid') > -1) ? 'Rom 3:20-25' : data;
846        $('#reference').val(ref);
847
848        bible.Reader.getReference();
849
850        // init styles	
851        $('#pos-v').attr('selectedIndex', '2'); // verbs = red gb
852        $('#pos-n').attr('selectedIndex', '7'); // nouns = green
853        bible.Reader.updateStyles();
854
855    });
856
857});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.