PageSourceSearch

https://www.laparola.net/app/js/windows/scroller.js

js laparola.net collected 2026-09-24 08:55:33 UTC 12,975 bytes, 547 lines download raw bytes

1
2var Scroller = function(node) {
3
4	var
5		wrapper = node.find('.scroller-text-wrapper'),
6		currentTextInfo = null,
7		locationInfo = {},
8		ignoreScrollEvent = false,
9		speedLastPos = null,
10		speedDelta = 0;
11		
12		speedIndicator = $('<div class="scroller-speed" style="z-index: 50; position: absolute; top: 0; left: 0; width: 50; background: black; padding: 5px;color:#fff"></div>')
13							.appendTo(node.parent())
14							.hide();
15	
16
17	var globalTimeout = null;
18
19	function startGlobalTimeout() {
20		if (globalTimeout == null) {
21			setTimeout(triggerGlobalEvent, 10);
22		}
23	}
24
25	function triggerGlobalEvent() {
26
27		ext.trigger('globalmessage', {type: 'globalmessage', target: this, data: {messagetype: 'nav', type: currentTextInfo.type.toLowerCase(), locationInfo: locationInfo}});
28
29		clearTimeout(globalTimeout);
30		globalTimeout = null;
31	}
32
33	node.on('scroll', function() {
34
35		if (ignoreScrollEvent) {
36			return;
37		}
38		//console.log('sendingscroll');
39
40		update_location_info();
41
42		ext.trigger('scroll', {type: 'scroll', target: this, data: {locationInfo: locationInfo}});
43		startGlobalTimeout();
44		
45		start_speed_test();		
46	});
47	
48	
49	var speedInterval = null;
50	
51	function start_speed_test() {
52		if (speedInterval == null) {
53
54			speedInterval = setInterval(checkSpeed, 100);
55		}
56	}
57	
58	function stop_speed_test() {
59		if (speedInterval != null) {
60			clearInterval(speedInterval);
61			speedInterval = null;
62		}		
63		
64	}
65	
66	function checkSpeed() {
67			
68		var speedNewPos = node.scrollTop();
69		if ( speedLastPos != null ){ // && newPos < maxScroll 
70			speedDelta = speedNewPos -  speedLastPos;
71		}
72		speedLastPos = speedNewPos;
73		
74		if (speedDelta === 0) {
75			load_more();
76			stop_speed_test();			
77		}
78				
79/*
80		if (speedDelta != null) {
81			speedIndicator.html(speedDelta);
82		}		
83*/
84	}
85	
86
87
88	// find the top most visible node (verse, page, etc.)
89	// pass it up as an event
90	function update_location_info() {
91
92		// reset info
93		var
94			topOfContentArea = node.offset().top,
95			sectionid = '',
96			fragmentid = '',
97			label = '',
98			labelLong = '',
99			fragmentSelector = currentTextInfo.fragmentSelector,
100			newLocationInfo = null;
101
102		// magic for bibles or books
103		if (typeof fragmentSelector == 'undefined' || fragmentSelector == '') {
104			switch (currentTextInfo.type.toLowerCase()) {
105				case 'videobible':
106				case 'deafbible':
107				case 'bible':
108				case 'commentary':
109					// find top
110					fragmentSelector = '.verse, .v';
111
112					break;
113				case 'book':
114					// find top
115					fragmentSelector = '.page';
116
117					break;
118			}
119		}
120
121
122		var fragments = node.find( fragmentSelector );
123
124		if (fragments.length == 1) {
125			fragments = node.find('.section');
126		}
127
128		// look through all the markers and find the first one that is fully visible
129		fragments.each(function(e) {
130			var fragment = $(this),
131				isFirstVisibleFragment = false;
132
133			// is the top of the fragment at the top of the scroll pane
134			if (fragment.offset().top - topOfContentArea > -2) {
135				isFirstVisibleFragment = true;
136
137				fragmentid = fragment.attr('data-id');
138				var totalFragments = fragment.parent().find('.' + fragmentid);
139
140				// multi-line verses
141				if (totalFragments.length > 1) {
142
143					fragment = totalFragments.first();
144
145					if (fragment.offset().top - topOfContentArea > -2) {
146						isFirstVisibleFragment = true;
147					} else {
148						isFirstVisibleFragment = false;
149					}
150				}
151
152			}
153
154			if (isFirstVisibleFragment) {
155
156				// TODO: are there other fragments with this id
157
158				fragmentid = fragment.attr('data-id');
159
160
161				switch (currentTextInfo.type.toLowerCase()) {
162					case 'videobible':
163					case 'deafbible':
164					case 'bible':
165					case 'commentary':
166						// find top
167						var bibleref = new bible.Reference( fragmentid );
168						bibleref.language = currentTextInfo.lang;
169
170						label = bibleref.toString();
171						labelLong = label +  ' (' + currentTextInfo.abbr + ')';
172
173						break;
174					case 'book':
175						labelLong = label = currentTextInfo.name + ' ' + sectionid;
176
177						break;
178				}
179
180
181				// pass the marker data
182				newLocationInfo = {
183					// verse ID
184					fragmentid: fragment.attr('data-id'),
185
186					sectionid: fragment.hasClass('section') ? fragment.attr('data-id') : fragment.closest('.section').attr('data-id'),
187
188					// extra positioning info
189					offset: topOfContentArea - fragment.offset().top,
190
191					label: label,
192
193					labelLong: labelLong,
194
195					textid: currentTextInfo.id
196
197				};
198				return false;
199			}
200
201			// means "keep looking" :)
202			return true;
203		});
204
205		// found a fragment
206		if (newLocationInfo != null && (locationInfo == null || newLocationInfo.fragmentid != locationInfo.fragmentid)) {
207			ext.trigger('locationchange', {type:'locationchange', target: this, data: newLocationInfo});
208		}
209
210		//console.log('new location', newLocationInfo);
211
212		locationInfo = newLocationInfo;
213	};
214
215
216	/*
217	var load_more_timeout = null;
218	function start_load_more_timeout() {
219
220		if (load_more_timeout === null) {
221
222			load_more_timeout = setTimeout(function() {
223				load_more();
224				clearTimeout( load_more_timeout );
225				load_more_timeout = null;
226			}, 100);
227
228		}
229	}
230	*/
231
232	function load_more() {
233			
234		// measure top and bottom height
235		var
236			fragmentid = null;
237			wrapper_height = wrapper.height(),
238			node_height = node.height(),
239			above_top = node_scrolltop = node.scrollTop(),
240			sections = wrapper.find( '.section' ),
241			sections_count = sections.length,
242			total_height = 0,
243			below_bottom = wrapper_height /*total_height*/ - node_height - node_scrolltop;
244
245		//console.log('load_more', speedDelta, below_bottom < node_height*2, above_top < node_height*2, above_top > node_height*15, sections_count > 4 && below_bottom > node_height*15)
246
247
248		// only load if stopped
249		if (speedDelta == 0) {
250			
251			// add below
252			if (below_bottom < node_height*2) {
253	
254				fragmentid = sections
255								.last() // the last chapter (bottom)
256								.attr( 'data-nextid' );
257
258				//console.log('load prev1', fragmentid);
259	
260				if (fragmentid != null && fragmentid != 'null' && sections.length < 50) {
261					load('next', fragmentid);
262				}
263	
264	
265			}
266	
267			// add above
268			else if (above_top < node_height*2) {
269	
270				fragmentid = sections
271								.first() // the first chapter (top)
272								.attr( 'data-previd' );
273	
274				//console.log('load prev2', fragmentid, sections.length);
275	
276				if (fragmentid != null && fragmentid != 'null' && sections.length < 50) {
277					load('prev',fragmentid);
278				}
279	
280			}
281			
282			
283				
284			// remove above
285			else if (above_top > node_height*50) { // lpnrmw was 15, but sometimes in commentaries (Jonah 1 in TSK) it kept removing the text above immediately after creating it
286				//console.log('remove above', above_top, node_height);
287	
288				if (wrapper.children().length >= 2) {
289	
290					// we're removing the first section, so we need to find the first one and
291					// measure where its first child should appear
292					var first_node_of_second_section = wrapper.find('.section:eq(1)').children().first(),
293						first_node_offset_before = first_node_of_second_section.offset().top;
294	
295					wrapper.find('.section:first').remove();
296	
297					// now, remeasure where the first node appears and adjust the scrolltop
298					var
299						first_node_offset_after = first_node_of_second_section.offset().top,
300						offset_difference = first_node_offset_after - first_node_offset_before;
301						new_scrolltop = node.scrollTop();
302						updated_scrolltop = new_scrolltop - Math.abs(offset_difference);
303	
304					node.scrollTop(updated_scrolltop);
305				}
306			}
307			
308	
309			// remove below
310			else if (sections_count > 4 && below_bottom > node_height*15) {
311				//console.log('remove below', below_bottom, node_height);
312	
313				wrapper.find('.section:last').remove();
314			}
315		} else {
316			
317			
318		}
319				
320	}
321
322	function load(loadType, sectionid, fragmentid) {
323//console.log(loadType, sectionid, fragmentid);
324
325		if (sectionid === 'null' || sectionid === null || sectionid == '') {
326			return;
327		}
328
329		// check if this exists
330		//if ( wrapper.find('[data-id="' + sectionid + '"]').length > 0 ) {
331		if ( wrapper.find('.' + sectionid).length > 0 ) {
332
333			if (fragmentid && fragmentid.trim() != '' && wrapper.find('.' + fragmentid).length > 0) {
334				scrollTo(fragmentid);
335			}
336			return;
337		}
338
339		if (loadType == 'text') {
340			wrapper.html('<div class="loading-indicator" style="height:' + node.height() + 'px;"></div>');
341			node.scrollTop(0);
342		}
343
344		//console.log('load', loadType, sectionid, fragmentid);
345
346		TextLoader.loadSection( currentTextInfo, sectionid, function(content) {
347
348			// check if this exists
349			//if ( wrapper.find('[data-id="' + sectionid + '"]').length > 0 ) {
350			if ( wrapper.find('.' + sectionid).length > 0 ) {
351
352				if (fragmentid && fragmentid.trim() != '' && wrapper.find('.' + fragmentid).length > 0) {
353					scrollTo(fragmentid);
354				}
355				return;
356			}
357
358			ignoreScrollEvent = true;
359
360			switch (loadType) {
361				default:
362				case 'text':
363
364					// clear out and reset
365					wrapper.html('');
366					node.scrollTop(0);
367					wrapper.append(content);
368
369
370
371					// TODO: scrollto fragmentid
372					if (fragmentid) {
373						scrollTo(fragmentid);
374					}
375
376					locationInfo = null;
377
378					update_location_info();
379					//ext.trigger('scroll', {type: 'scroll', target: this, data: {locationInfo: locationInfo}});
380
381					break;
382
383				case 'next':
384
385					wrapper.append(content);
386
387
388					break;
389
390				case 'prev':
391
392					// add to top, measure, then reset scroll position
393					/*
394					var	node_scrolltop_before = node.scrollTop(),
395						first_item = node.find('.section').children().first();
396					
397					if (first_item.length > 0) {
398						var first_item_offset_top_before = first_item.offset().top;
399
400						// add to top and measure
401						wrapper.prepend(content);
402
403						var first_item_offset_top_after = first_item.offset().top,
404							offest_difference = first_item_offset_top_after - first_item_offset_top_before,
405							new_scrolltop = node_scrolltop_before + offest_difference;
406
407						node.scrollTop( Math.abs(new_scrolltop));
408					}
409					*/
410
411					// add to bottom, then move up
412                    //console.log(content);
413
414					var	node_scrolltop_before = node.scrollTop();
415					var wrapper_height_before = wrapper.height();
416					wrapper.append(content);
417					var wrapper_height_after = wrapper.height();
418					wrapper.prepend(content);
419
420					var height_difference = wrapper_height_after - wrapper_height_before,
421						new_scrolltop = node_scrolltop_before + height_difference;
422
423					node.scrollTop( Math.abs(new_scrolltop ));
424
425					break;
426
427			}
428
429			ignoreScrollEvent = false;
430
431			// send load event up to Window/App
432			//console.log('loaded', sectionid);
433			ext.trigger('globalmessage', {type: 'globalmessage',
434											target: this,
435											data: {
436												messagetype: 'textload',
437												texttype: currentTextInfo.type.toLowerCase(),
438												type: currentTextInfo.type.toLowerCase(),
439												textid: currentTextInfo.id,
440												abbr: currentTextInfo.abbr,
441												sectionid: sectionid,
442												fragmentid: fragmentid,
443												content:content
444											}
445										});
446
447			load_more();
448			//start_load_more_timeout();
449		});
450
451	}
452
453	function scrollTo(fragmentid, offset) {
454
455		if (typeof fragmentid == 'undefined') {
456			return;
457		}
458
459
460		// find the fragment
461		var fragment = wrapper.find('.' + fragmentid);
462
463		//console.log('scrollTo', fragmentid, fragment.length);
464
465		// if it exists, we'll move to it
466		if (fragment.length > 0) {
467
468			var
469				// calculate node position
470				paneTop = node.offset().top,
471				scrollTop = node.scrollTop(),
472				nodeTop = fragment.offset().top,
473				nodeTopAdjusted = nodeTop - paneTop + scrollTop;
474
475			// go to it
476			ignoreScrollEvent = true;
477			node.scrollTop(nodeTopAdjusted + (offset || 0));
478			ignoreScrollEvent = false;
479		}
480		// if it's not there, we'll see if we can load it for this book (or bible version)
481		else {
482			// need to load it!
483			//console.log('need to load', fragmentid);
484
485			var sectionid = fragmentid.split('_')[0],
486				hasSection = currentTextInfo != null ? currentTextInfo.sections.indexOf(sectionid) > -1 : false;
487
488			if (hasSection) {
489				load('text', sectionid, fragmentid);
490			}
491
492		}
493	}
494
495	function size(width, height) {
496		node
497			.width(width)
498			.height(height);
499	}
500
501
502	function getTextInfo() {
503		return currentTextInfo;
504	}
505
506	function setTextInfo(textinfo) {
507
508		if (typeof textinfo.stylesheet != 'undefined' ) {
509
510			var styleId = 'style-' + textinfo.id,
511				styleLink = $('#' + styleId);
512
513			if (styleLink.length == 0) {
514				styleLink = $('<link id="' + styleId + '" rel="stylesheet" href="' + sofia.config.baseContentUrl + 'content/texts/' + textinfo.id + '/' + textinfo.stylesheet + '" />')
515								.appendTo($('head'));
516			}
517		}
518
519
520		currentTextInfo = textinfo;
521	}
522
523	function getLocationInfo() {
524		return locationInfo;
525	}
526
527	function setFocus(newHasFocus) {
528		hasFocus = newHasFocus;
529	}
530
531
532	var ext = {
533		load_more: load_more,
534		load: load,
535		size: size,
536		getTextInfo: getTextInfo,
537		setTextInfo: setTextInfo,
538		getLocationInfo: getLocationInfo,
539		scrollTo: scrollTo,
540		setFocus: setFocus
541	};
542
543	ext = $.extend(true, ext, EventEmitter);
544
545	return ext;
546
547};

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.