PageSourceSearch

https://biblewebapp.com/study-v2/js/docs/plugins.versepopup.js

js biblewebapp.com collected 2026-09-24 19:09:42 UTC 22,321 bytes, 780 lines download raw bytes

1	
2/**
3 * Finds verse references and highlights them
4 *
5 * @author John Dyer (http://j.hn/)
6 */
7 
8docs.plugins.push({
9
10	init: function(docManager) {
11		
12		// insert style
13		$('<style id="verse-popup-style">.tagged-reference { cursor: pointer; color: #779;  }</style>').appendTo($(document.body));
14		
15		// create popup
16		var versePopup = docs.createModal('Verses', 'Verses');
17		versePopup.content.css({'max-height': '150px', 'overflow': 'auto'});
18		
19		// setup events to add media icons
20		docManager.addEventListener('load', function(e) {				
21			findVerses(e.chapter);
22		});
23		
24		function findVerses(chapter) {
25			
26			bibly.ignoreClassName = 'cf';
27			bibly.handleLinks = function(node, reference) {
28				
29				//node.style.fontWeight = 'bold';
30				//node.style.color = '#0ff';
31				node.className = 'tagged-reference';
32				
33				$(node).on('mouseover', function() {
34					
35					// measure position
36					var
37						node = $(this),
38						nodePos = node.offset(),
39						nodeWidth = node.width(),
40						nodeHeight = node.height(),
41						popupWidth = versePopup.window.outerWidth(true),
42						popupHeight = versePopup.window.outerHeight(true),
43						windowWidth = $(window).width(),
44						windowHeight = $(window).height(),
45						// default to below and to the right
46						top = (nodePos.top + nodeHeight + nodeHeight > windowHeight) ? nodePos.top - popupHeight - 5 : nodePos.top + nodeHeight + 4,
47						left = (nodePos.left + popupWidth > windowWidth) ? windowWidth - popupWidth : nodePos.left;			
48					
49					
50					versePopup.title.html(reference.toString());
51					versePopup.show();
52					versePopup.window.css({top: top, left: left});					
53					
54					var chapterUrl = 'content/bibles/' + docManager.documents[0].selector.val() + '/' + reference.osisBookID + '.' + reference.chapter1 + '.html';
55					
56					versePopup.content.html('Loading...');
57					
58					// TODO: get verse text!
59					$.ajax({
60						url: chapterUrl,
61						dataType: 'html',
62						success: function(data) {
63							var verseClasses = [],
64								startVerse = reference.verse1 > 0 ? reference.verse1 : 1,
65								endVerse = reference.verse2 > 0 ? Math.min(reference.verse1+3,reference.verse2) : reference.verse1;
66							
67							for (var i=startVerse; i<=endVerse; i++) {
68								verseClasses.push('.' + reference.osisBookID + '_' + reference.chapter1 + '_' + i);
69							}
70							
71							versePopup.content.empty();
72							
73							var verses = $(data).find( verseClasses.join(',') )
74											.find('span.note, span.cf')
75											.remove()
76											.end();
77							
78							verses.appendTo(versePopup.content);
79							
80						}, error: function() {
81							
82							console.log('error', chapterUrl);
83						}
84					});
85					
86					
87				}).on('mouseout', function() {
88					
89					versePopup.hide();
90					
91				}).on('click', function() {
92					
93					var firstDoc = docManager.documents[0];
94					
95					firstDoc.input.val( reference.toString() );
96					firstDoc.navigateToUserInput();
97				});
98				
99				
100			}
101			
102			//console.log('scan', chapter[0]);
103			
104			bibly.scanForReferences(chapter[0]);
105			
106		}
107	}
108});
109
110
111bible.genNames = function() {
112	var names = [],
113		i = 0,
114		il = bible.DEFAULT_BIBLE.length;
115	for (; i<il; i++) {
116		
117		var osisName = bible.DEFAULT_BIBLE[i];
118		
119		names.push( bible.BOOK_DATA[osisName].names['eng'].join('|') );
120	}
121	
122	return names.join('|');
123};
124
125
126// adapted from old scripturizer.js code
127
128(function() {
129	
130	/* Scripture API methods */
131	var
132		callbackIndex = 100000,
133		jsonpCache = {},
134		jsonp = function(url, callback){  
135		
136			var jsonpName = 'callback' + (callbackIndex++),
137				script = doc.createElement("script"); 
138		
139			win[jsonpName] = function(d) {
140				callback(d);
141			}
142			jsonpCache[url] = jsonpName;
143		
144			url += (url.indexOf("?") > -1 ? '&' : '?') + 'callback=' + jsonpName;			  
145			//url += '&' + new Date().getTime().toString(); // prevent caching        
146						
147			script.setAttribute("src",url);
148			script.setAttribute("type","text/javascript");                
149			body.appendChild(script);
150			
151		},
152		getBibleText = function(refString, callback) {
153			var v = getPopupVersion(),
154				max = bibly.maxVerses,
155				reference = new bible.Reference(refString);
156				
157			// check that it's only 4 verses
158			if (reference.verse1 > 0 && reference.verse2 > 0 && reference.verse2 - reference.verse1 > (max-1)) {
159				reference.verse2 = reference.verse1 + (max-1);
160			} else if (reference.verse1 <= 0 && reference.verse2 <= 0) {
161				reference.verse1 = 1;
162				reference.verse2 = max;
163				reference.chapter2 = -1;
164			}
165					
166			switch (v) {
167				default:
168				case 'NET':
169					jsonp('http://labs.bible.org/api/?passage=' + encodeURIComponent(reference.toString()) + '&type=json', callback);
170					break;
171				case 'KJV':
172				case 'LEB':
173					jsonp('http://api.biblia.com/v1/bible/content/' + v + '.html.json?style=oneVersePerLine&key=' + bibly.bibliaApiKey + '&passage=' + encodeURIComponent(reference.toString()), callback);
174					break;
175				case 'ESV':
176					jsonp('http://www.esvapi.org/crossref/ref.php?reference=' + encodeURIComponent(reference.toString()), callback);
177					break;					
178			} 
179		},		
180		handleBibleText = function(d) {
181			var 
182				v = getPopupVersion(),
183				p = bibly.popup,
184				max = bibly.maxVerses,
185				text = '',
186				className = v + '-version',
187				i,il;
188				
189			switch (v) {
190				default:
191				case 'NET':
192					for (i=0,il=d.length; i<il && i<max; i++) {
193						text += '<span class="bibly_verse_number">' + d[i].verse + '</span>' + d[i].text + ' ';
194					}
195					break;
196				case 'KJV':
197				case 'LEB':
198					className += ' biblia';
199					text = d.text;
200					break;
201				case 'ESV':
202					text = d.content;
203					break;					
204			}
205			
206			p.content.innerHTML = '<div class="' + className + '">
206' + text + '</div>';
207		};
208		
209	/* handler for when a verse node is found */
210	var
211		createBiblyLinks = function(newNode, reference) {
212				newNode.setAttribute('href', reference.toShortUrl() + (bibly.linkVersion != '' ? '.' + bibly.linkVersion : ''));
213				newNode.setAttribute('title', 'Read ' + reference.toString());
214				newNode.setAttribute('rel', reference.toString());
215				newNode.setAttribute('class', bibly.className);
216				if (bibly.newWindow) {
217					newNode.setAttribute('target', '_blank')
218				}
219				
220				if (bibly.enablePopups) {
221					addEvent(newNode,'mouseover', handleLinkMouseOver);
222					addEvent(newNode,'mouseout', handleLinkMouseOut);
223				}		
224		
225		},
226
227		checkPosTimeout,
228		handleLinkMouseOver = function(e) {
229			if (!e) e = win.event;
230			
231			clearPositionTimer();
232						
233			var target = (e.target) ? e.target : e.srcElement,
234				p = bibly.popup,
235				pos = getPosition(target),
236				x = y = 0,
237				v = getPopupVersion();
238				referenceText = target.getAttribute('rel'),
239				viewport = getWindowSize(),
240				scrollPos = getScroll();
241			
242			p.outer.style.display = 'block';
243			p.header.innerHTML = referenceText + ' (' + v + ')';
244			p.content.innerHTML = 'Loading...<br/><br/><br/>';
245			p.footer.innerHTML = getFooter(v);
246			
247			
248			function positionPopup() {
249				x = pos.left - (p.outer.offsetWidth/2) + (target.offsetWidth/2);
250				y = pos.top - p.outer.clientHeight; // for the arrow
251				
252				if (x < 0) {
253					x = 0;
254				} else if (x + p.outer.clientWidth > viewport.width) {
255					x = viewport.width - p.outer.clientWidth - 20;
256				}
257				
258				if (y < 0 || (y < scrollPos.y) ){ // above the screen
259					y = pos.top + target.offsetHeight + 10;
260					p.arrowtop.style.display = 'block';
261					p.arrowtop_border.style.display = 'block';
262					p.arrowbot.style.display = 'none';
263					p.arrowbot_border.style.display = 'none';					
264					
265				} else {
266					y = y-10; 
267					p.arrowtop.style.display = 'none';
268					p.arrowtop_border.style.display = 'none';
269					p.arrowbot.style.display = 'block';
270					p.arrowbot_border.style.display = 'block';					
271				}
272				
273				p.outer.style.top = y + 'px';
274				p.outer.style.left = x + 'px';				
275			}
276			positionPopup();
277			
278			
279			getBibleText(referenceText, function(d) {
280				// handle the various JSON outputs
281				handleBibleText(d);
282				
283				// reposition the popup
284				//y = pos.top - p.outer.clientHeight - 10; // border
285				//p.outer.style.top = y + 'px';
286				positionPopup();
287			});			
288		},
289		handleLinkMouseOut = function(e) {
290			startPositionTimer();
291		},
292		
293		handlePopupMouseOver = function(e) {	
294			clearPositionTimer();
295		},
296		handlePopupMouseOut = function(e) {
297			startPositionTimer();
298		},
299		
300		/* Timer on/off */
301		startPositionTimer = function () {
302			checkPosTimeout = setTimeout(hidePopup, 500);
303		},
304		clearPositionTimer = function() {
305			clearTimeout(checkPosTimeout);
306			delete checkPosTimeout;
307		},
308		hidePopup = function() {
309			var p = bibly.popup;
310			p.outer.style.display = 'none';	
311		},
312		
313		getPosition = function(node) {		
314			var curleft = curtop = curtopscroll = curleftscroll = 0;
315			if (node.offsetParent) {
316				do {
317					curleft += node.offsetLeft;
318					curtop += node.offsetTop;				
319					curleftscroll += node.offsetParent ? node.offsetParent.scrollLeft : 0;
320					curtopscroll += node.offsetParent ? node.offsetParent.scrollTop : 0;
321				} while (node = node.offsetParent);
322			}
323			
324			return {left:curleft,top:curtop,leftScroll:curleftscroll,topScroll:curtopscroll};
325		},
326		getWindowSize= function() {
327			var width = 0, 
328				height = 0;
329			if( typeof( win.innerWidth ) == 'number' ) {
330				// Non-IE
331				width = win.innerWidth;
332				height = win.innerHeight;
333			} else if( doc.documentElement && ( doc.documentElement.clientWidth || doc.documentElement.clientHeight ) ) {
334				//IE 6+ in 'standards compliant mode'
335				width = doc.documentElement.clientWidth;
336				height = doc.documentElement.clientHeight;
337			} else if( body && ( body.clientWidth || body.clientHeight ) ) {
338				//IE 4 compatible
339				width = body.clientWidth;
340				height = body.clientHeight;
341			}
342			
343			return {width:width, height: height};
344		},
345		getScroll = function () {
346			var scrOfX = 0, 
347				scrOfY = 0;
348			if( body && ( body.scrollLeft || body.scrollTop ) ) {
349				//DOM compliant
350				scrOfY = body.scrollTop;
351				scrOfX = body.scrollLeft;
352			} else if( doc.documentElement && ( doc.documentElement.scrollLeft || doc.documentElement.scrollTop ) ) {
353				//IE6 standards compliant mode
354				scrOfY = doc.documentElement.scrollTop;
355				scrOfX = doc.documentElement.scrollLeft;
356			} else if( typeof( win.pageYOffset ) == 'number' ) {
357				//Netscape compliant
358				scrOfY = win.pageYOffset;
359				scrOfX = win.pageXOffset;
360			}
361			
362			return {x: scrOfX, y:scrOfY };
363		};		
364	
365	
366	/* core bibly functions */
367	var bibly = {
368			version: '0.9.2',
369			maxNodes: 500,
370			className: 'bibly_reference',
371			enablePopups: true,
372			popupVersion: 'ESV',
373			linkVersion: '',
374			bibliaApiKey: '436e02d01081d28a78a45d65f66f4416', 
375			autoStart: true,
376			startNodeId: '',
377			maxVerses: 4,
378			newWindow: false,
379			ignoreClassName: 'bibly_ignore',
380			ignoreTags: ['h1','h2','h3','h4'],
381			newTagName: 'A',
382			handleLinks: createBiblyLinks
383		},	
384		win = window,
385		doc = document,
386		body = null,		
387		defaultPopupVersion = 'ESV',
388		allowedPopupVersions = ['NET','ESV','KJV','LEB','DARBY'],
389		bok = bible.genNames(),	
390		
391		
392		
393		// 1 OR 1:1 OR 1:1-2, 100, but not 1000
394		ver = '(' 	+						// 
395					'(1?\\d{1,2})' +		// 1				
396					'([\.:]\\s?(\\d+))?' + 	// :2
397				')' +	
398				'(' + 						
399					'\\s?[-Ð&]\\s?(\\d+)' +	// -3
400					'([\.:]\\s?(\\d+))?' +  // :4
401				')?',
402		
403		// NOT 1. Only 1:1 OR 1:1-2 
404		ver2 = '(1?\\d{1,2})' 					// 1##
405					+ '[\.:]\\s?(\\d+)'
406					+ '(\\s?[-Ð&]\\s?(\\d+))?', // this is needed so verses after semi-colons require a :. Problem John 3:16;
406 2 Cor 3:3 <-- the 2 will be a verse)
407			
408		regexPattern = 	'\\b('+bok+')\.?\\s+' 	// book name with period and/or spaces afterward
409						+ '(' + ver				// verse pattern (1 OR 1:1 OR 1:1-2)
410							+ '('
411							+ '(\\s?,(\\s+)?'+ver+')|' // , VERSE
412							+ '(\\s?;(\\s+)?'+ver2+')' // ; CHAPTER:Verse
413							+ ')*'					// infinite reoccurance
414						+ ')\\b',
415		
416		referenceRegex = new RegExp(regexPattern, 'mi'),
417		verseRegex = new RegExp(ver, 'mi'),
418		alwaysSkipTags = ['a','script','style','textarea'],
419		lastReference = null,
420		textHandler = function(node) {
421			var match = referenceRegex.exec(node.data), 
422				val, 
423				referenceNode, 
424				afterReferenceNode,
425				newLink;
426			
427			// reset this
428			lastReference = null;
429			
430			if (match) {
431				val = match[0];
432				// see https://developer.mozilla.org/en/DOM/text.splitText
433				// split into three parts [node=before][referenceNode][afterReferenceNode]
434				referenceNode = node.splitText(match.index);
435				afterReferenceNode = referenceNode.splitText(val.length);
436				
437				// send the matched text down the 
438				newLink = createLinksFromNode(node, referenceNode);
439				
440				return newLink;
441			} else {
442				return node;
443			}
444		},
445		createLinksFromNode = function(node, referenceNode) {
446			if (referenceNode.nodeValue == null)
447				return referenceNode;
448		
449			// split up match by ; and , characters and make a unique link for each
450			var 
451				newLink,
452				commaIndex = referenceNode.nodeValue.indexOf(','),
453				semiColonIndex = referenceNode.nodeValue.indexOf(';'),
454				separatorIndex = (commaIndex > 0 && semiColonIndex > 0) ? Math.min(commaIndex, semiColonIndex) : Math.max(commaIndex, semiColonIndex),
455				separator,
456				remainder,
457				reference,
458				startRemainder;
459			
460			// if there is a separator ,; then split up into three parts [node][separator][after]
461			if (separatorIndex > 0) {
462				separator = referenceNode.splitText(separatorIndex);
463				
464				startRemainder = 1;
465				while(startRemainder < separator.nodeValue.length && separator.nodeValue.substring(startRemainder,startRemainder+1) == ' ')
466					startRemainder++;
467				
468				remainder = separator.splitText(startRemainder);
469			}	
470			
471			// test if the text matches a real reference
472			refText = referenceNode.nodeValue;	
473			reference = parseRefText(refText);
474			if (typeof reference != 'undefined' && reference.isValid()) {
475				
476				// replace the referenceNode TEXT with an anchor node to bib.ly
477				newNode = node.ownerDocument.createElement(bibly.newTagName);				
478				node.parentNode.replaceChild(newNode, referenceNode);	
479				
480				// this can be overridden for other systems
481				bibly.handleLinks(newNode, reference);
482				
483				newNode.appendChild(referenceNode);
484				
485				// if there was a separator, now parse the stuff after it
486				if (remainder) {				
487					newNode = createLinksFromNode(node, remainder);				
488				}
489				
490				return newNode;
491			} else {
492				// for false matches, return it unchanged
493				return referenceNode;
494			}
495		},
496		parseRefText = function(refText) {
497			
498			var 
499				text = refText,
500				reference = new bible.Reference(text),
501				match = null,
502				p1, p2, p3, p4;
503			
504			if (reference != null && typeof reference.isValid != 'undefined' && reference.isValid()) {
505				
506				lastReference = reference;
507				return reference;
508				
509			} else if (lastReference  != null) {
510				
511				// single verse match (3)
512				match = verseRegex.exec(refText);				
513				if (match) {				
514			
515// "1"			["1", 		"1", 	"1", u, 	u, 		u, 		u, 		u, u]
516// "1:2"		["1:2", 	"1:2", 	"1", ":2", 	"2", 	u, 		u, 		u, u]
517// "1-2"		["1-2", 	"1", 	"1", u, 	u, 		"-2", 	"2", 	u, u]
518// "1:2-3"		["1:2-3", 	"1:2", 	"1", ":2", 	"2", 	"-3", 	"3", 	u, u]
519// "1:2-3:4"	["1:2-3:4", "1:2", 	"1", ":2", 	"2", 	"-3:4", "3", 	":4", "4"]					
520					
521					p1 = parseInt(match[2],10);
522					p2 = parseInt(match[4],10);
523					p3 = parseInt(match[6],10);
524					p4 = parseInt(match[8],10);
525					
526					if (isNaN(p2)) {
527						p2 = 0;
528					}
529					if (isNaN(p3)) {
530						p3 = 0;
531					}
532					if (isNaN(p4)) {
533						p4 = 0;
534					}
535					
536					// single verse (1)
537					if (p1 > 0 && p2 == 0 && p3 == 0 && p4 == 0) {
538											
539						lastReference.verse1 = p1;
540						lastReference.chapter2 = -1;
541						lastReference.verse2 = -1;
542					
543					// 1:2
544					} else if (p1 > 0 && p2 > 0 && p3 == 0 && p4 == 0) {
545						
546						lastReference.chapter1 = p1;
547						lastReference.verse1 = p2;
548						lastReference.chapter2 = -1;
549						lastReference.verse2 = -1;	
550						
551					// 1-2
552					} else if (p1 > 0 && p2 == 0 && p3 > 0 && p4 == 0) {
553						
554						lastReference.verse1 = p1;
555						lastReference.chapter2 = -1;
556						lastReference.verse2 = p3;							
557					
558					// 1:2-3
559					} else if (p1 > 0 && p2 > 0 && p3 > 0 && p4 == 0) {
560						
561						lastReference.chapter1 = p1;
562						lastReference.verse1 = p2;
563						lastReference.chapter2 = -1;
564						lastReference.verse2 = p3;		
565					
566	
567					// 1:2-3:4
568					} else if (p1 > 0 && p2 > 0 && p3 > 0 && p4 > 0) {
569						
570						lastReference.chapter1 = p1;
571						lastReference.verse1 = p2;
572						lastReference.chapter2 = p3;
573						lastReference.verse2 = p4;
574					}
575					
576					
577					return lastReference;
578				}
579			
580				// failure
581				return {
582					refText: refText,
583					toShortUrl: function() {
584						return 'http://bib.ly/' + refText.replace(/\s/ig,'').replace(/:/ig,'.').replace(/Ð/ig,'-');
585					},
586					toString: function() {
587						return refText  + " = Can't parse it";
588					}
589				};				
590			} else {
591				return undefined;
592			}
593		},
594
595		getFooter = function(version) {
596			switch (version) {
597				case 'NET':
598					return '<a href="http://bible.org/">NET Bible¨ copyright ©1996-2006 by Biblical Studies Press, LLC</a>';
599				case 'ESV':
600					return 'English Standard Version. Copyright &copy;2001 by <a href="http://www.crosswaybibles.org">Crossway Bibles</a>';
601				case 'LEB':					
602				case 'KJV':
603					return version + ' powered by <a href="http://biblia.com/">Biblia</a> web services from <a href="http://www.logos.com/">Logos Bible Software</a>';
604				default:
605					return '';
606			}
607		},
608		
609		getPopupVersion = function() {
610			var v = bibly.popupVersion.toUpperCase(), 
611				indexOf=-1, 
612				i=0, 
613				il=allowedPopupVersions.length;
614			
615			// old IEs don't have Array.indexOf
616			for (; i < il; i++) {
617				if (allowedPopupVersions[i].toUpperCase() === v) {
618					indexOf = i;
619					break;
620				}
621			}
622
623			return (indexOf > -1) ? v : defaultPopupVersion;
624		},
625
626		scanForReferences = function(node) {				
627			// build doc
628			traverseDOM(node.childNodes[0], 1, textHandler);		
629		},
630		traverseDOM = function(node, depth, textHandler) {
631			var count = 0,
632				//skipRegex = /^(a|script|style|textarea)$/i,
633				skipRegex = new RegExp('^(' + alwaysSkipTags.concat(bibly.ignoreTags).join('|') + ')$', 'i');
634				
635				
636			while (node && depth > 0) {
637				count++;
638				if (count >= bibly.maxNodes) {
639					setTimeout(function() { traverseDOM(node, depth, textHandler); }, 50);
640					return;
641				}
642
643				switch (node.nodeType) {
644					case 1: // ELEMENT_NODE
645						if (!skipRegex.test(node.tagName.toLowerCase()) && node.childNodes.length > 0 && (bibly.ignoreClassName == '' || node.className.toString().indexOf(bibly.ignoreClassName) == -1)) {											
646							node = node.childNodes[0];
647							depth ++;
648							continue;
649						}
650						break;
651					case 3: // TEXT_NODE
652					case 4: // CDATA_SECTION_NODE
653						node = textHandler(node);
654						break;
655				}
656
657				if (node.nextSibling) {
658					node = node.nextSibling;
659				} else {
660					while (depth > 0) {
661						node = node.parentNode;
662						depth --;
663						if (node.nextSibling) {
664							node = node.nextSibling;
665							break;
666						}
667					}
668				}
669			}
670		},
671		addEvent = function(obj,name,fxn) {
672			if (obj.attachEvent) {
673				obj.attachEvent('on' + name, fxn);
674			} else if (obj.addEventListener) {
675				obj.addEventListener(name, fxn, false);
676			} else {
677				var __ = obj['on' + name];
678				obj['on' + name] = function() {
679				   fxn();
680					__();
681				};
682			}			
683		},		
684		isStarted = false,
685		extend = function() {
686			// borrowed from ender
687			var options, name, src, copy, 
688				target = arguments[0] || {},
689				i = 1,
690				length = arguments.length;	
691
692			// Handle case when target is a string or something (possible in deep copy)
693			if ( typeof target !== "object" && typeof target !== "function" ) {
694				target = {};
695			}
696
697			for ( ; i < length; i++ ) {
698				// Only deal with non-null/undefined values
699				if ( (options = arguments[ i ]) != null ) {
700					// Extend the base object
701					for ( name in options ) {
702						src = target[ name ];
703						copy = options[ name ];
704
705						// Prevent never-ending loop
706						if ( target === copy ) {
707							continue;
708						}
709
710						if ( copy !== undefined ) {
711							target[ name ] = copy;
712						}
713					}
714				}
715			}
716
717			// Return the modified object
718			return target;				
719		},
720		startBibly = function() {
721			
722			if (isStarted)
723				return;				
724			isStarted = true;
725			
726			doc = document;
727			body = doc.body;
728			
729			// create popup
730			var p = bibly.popup = {
731					outer: doc.createElement('div')
732				}, 
733				parts = ['header','content','footer','arrowtop_border','arrowtop','arrowbot_border','arrowbot'],
734				i,
735				il,
736				div,
737				name,
738				node = null;
739				
740			p.outer.className = 'bibly_popup_outer';
741			// build all the parts	
742			for (i=0,il=parts.length; i<il; i++) {
743				name = parts[i];
744				div = doc.createElement('div');
745				div.className = 'bibly_popup_' + name;
746				p.outer.appendChild(div);
747				p[name] = div;
748			}
749			
750			body.appendChild(p.outer);	
751			
752			addEvent(p.outer,'mouseover',handlePopupMouseOver);
753			addEvent(p.outer,'mouseout',handlePopupMouseOut);
754
755			if (bibly.autoStart) {
756				if (bibly.startNodeId != '') {
757					node = doc.getElementById(bibly.startNodeId);
758				}
759				
760				if (node == null) {
761					node = body;
762				}
763				
764				scanForReferences(node);
765			}
766		};
767
768	// super cheater version of DOMoade
769	// do whatever happens first
770	addEvent(doc,'DOMContentLoaded',startBibly);
771	addEvent(win,'load',startBibly);	
772	
773	if (typeof window.bibly != 'undefined') 
774		bibly = extend(bibly, window.bibly);
775	
776	// export
777	bibly.startBibly = startBibly;
778	bibly.scanForReferences = scanForReferences;
779	win.bibly = bibly;	
780})();

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.