PageSourceSearch

http://scripting.com/code/newsworthy/code.js?x=2

js scripting.com collected 2026-10-01 08:31:05 UTC 17,446 bytes, 623 lines download raw bytes

1const myProductName = "Newsworthy", myVersion = "0.4.3";  
2
3
4const theTabs = {
5	scriptingNews: {
6		enabled: true,
7		name: "Dave",
8		description: "Scripting News, Dave Winer's blog, continuously updated since 1994.",
9		type: "blog",
10		icon: "fa fa-book",
11		flJavaScriptDomFeatures: true,
12		getContent: function (callback) {
13			const htmltext = $("#idBodytext").html ();
14			if (htmltext === undefined) { //test version
15				const url = "http://scripting.com/homepage.html";
16				httpRequest (url, undefined, undefined, callback);
17				}
18			else {
19				callback (undefined, htmltext);
20				}
21			}
22		},
23	Linkblog: {
24		enabled: true,
25		name: "Links",
26		description: "Links that Dave thought you might find interesting, or wanted to keep a link to for himself.",
27		type: "linkblog",
28		icon: "fa fa-link",
29		getContent: function (callback) {
30			const feedUrl = "https://dave.linkblog.org/";
31			const url = "https://feeder.scripting.com/returnlinkbloghtml?url=" + encodeURIComponent (feedUrl);
32			httpRequest (url, undefined, undefined, function (err, htmltext) {
33				if (err) {
34					callback (err);
35					}
36				else {
37					htmltext = "<div class=\"divLinkblogContainer\">" + htmltext + "</div>";
38					callback (undefined, htmltext);
39					}
40				});
41			}
42		},
43	all: {
44		enabled: true,
45		name: "News",
46		type: "river",
47		description: "News from feeds Dave subscribes to in FeedLand, in the \"Tech\" category.",
48		icon: "fa fa-newspaper",
49		getContent: function (callback) {
50			setUpRiver ({screenname: "davewiner", catname: "Tech"}, callback);
51			}
52		},
53	about: {
54		enabled: true,
55		name: "About",
56		type: "outline",
57		description: "Notes about this site, in outline form. Not updated often enough. :-)",
58		icon: "fa fa-info-circle",
59		urlAboutOpml: "http://scripting.com/publicfolder/scripting/aboutpage.opml", 
60		getContent: function (callback) {
61			const aboutOutlineTitle = "About Scripting News";
62			opml.read (theTabs.about.urlAboutOpml, undefined, function (err, theOutline) {
63				if (err) {
64					callback (err);
65					}
66				else {
67					const outlineBody = theOutline.opml.body;
68					outlineBody.text = aboutOutlineTitle;
69					const divAboutOutline = $("<div class=\"divAboutOutline\"></div>");
70					const divRenderedOutline = renderOutlineBrowser (outlineBody, false, undefined, undefined, true);
71					divAboutOutline.append (divRenderedOutline);
72					callback (undefined, divAboutOutline);
73					}
74				});
75			}
76		
77		},
78	blog2: {
79		enabled: false,
80		name: "Blog2",
81		type: "blog",
82		icon: "fa fa-wordpress",
83		getContent: function (callback) {
84			getWordPressSiteText (222485322, callback);
85			}
86		},
87	allNews: {
88		enabled: false,
89		name: "All news",
90		description: "News from all the feeds Dave subscribes to in FeedLand.",
91		type: "river",
92		icon: "fa fa-newspaper",
93		getContent: function (callback) {
94			setUpRiver ({screenname: "davewiner", catname: "All"}, callback);
95			}
96		},
97	politicalNews: {
98		enabled: false,
99		name: "Political news",
100		description: "News from feeds Dave subscribes to in FeedLand, in the \"Politics\" category.",
101		type: "river",
102		icon: "fa fa-newspaper",
103		getContent: function (callback) {
104			setUpRiver ({screenname: "davewiner", catname: "Politics"}, callback);
105			}
106		},
107	tech: {
108		enabled: false,
109		name: "Tech",
110		description: "News from feeds Dave subscribes to in FeedLand, in the \"Tech\" category.",
111		type: "river",
112		icon: "fa fa-newspaper",
113		getContent: function (callback) {
114			setUpRiver ({screenname: "davewiner", catname: "Tech"}, callback);
115			}
116		},
117	podcasts: {
118		enabled: false,
119		name: "Podcasts",
120		type: "river",
121		description: "News from feeds Dave subscribes to in FeedLand, in the \"Podcasts\" category.",
122		icon: "fa fa-newspaper",
123		getContent: function (callback) {
124			setUpRiver ({screenname: "davewiner", catname: "Podcasts"}, callback);
125			}
126		},
127	bloggers: {
128		enabled: false,
129		name: "Bloggers",
130		type: "river",
131		description: "News from feeds Dave subscribes to in FeedLand, in the \"Bloggers\" category.",
132		icon: "fa fa-newspaper",
133		getContent: function (callback) {
134			setUpRiver ({screenname: "davewiner", catname: "Bloggers"}, callback);
135			}
136		},
137	dave: {
138		enabled: false,
139		name: "Dave",
140		type: "river",
141		icon: "fa fa-newspaper",
142		getContent: function (callback) {
143			setUpRiver ({screenname: "davewiner", catname: "Dave"}, callback);
144			}
145		},
146	};
147
148const appConsts = {
149	flBlogrollEnabled: true, //3/13/24 by DW
150	urlFeedlandServer: "https://feedland.social/", //2/28/24 by DW
151	urlSocketServer: "wss://feedland.social/",
152	urlFeedListOpml: "https://feedland.social/opml?screenname=davewiner&catname=blogroll",
153	urlFeedlandViewBlogroll: "https://feedland.social/?username=davewiner&catname=blogroll" //3/13/24 by DW
154	}
155
156var globals = {
157	};
158
159function htmlLink (url, text) {
160	return ("<a href=\"" + url + "\" target=\"_blank\">
160" + text + "</a>");
161	}
162function addToolTip (theObject, tipText, placement="right") { //8/24/22 by DW
163	$(theObject).attr ("data-container", "body"); //10/23/22 by DW
164	$(theObject).attr ("data-toggle", "tooltip");
165	$(theObject).attr ("data-placement", placement);
166	$(theObject).attr ("title", tipText);
167	$(theObject).click (function () { //11/1/22 by DW
168		$(theObject).tooltip ("hide");
169		});
170	return (theObject);
171	}
172function neuterMarkup (theString) { //11/17/23 by DW
173	var temp = document.createElement ("div");
174	temp.textContent = theString;
175	return (temp.innerHTML);
176	}
177function equalStrings (s1, s2) {
178	return (stringLower (s1) == stringLower (s2));
179	}
180function getAllUrlParams (searchstring=location.search) { //9/7/22 by DW
181	var s = searchstring;
182	var allparams = new Object ();
183	if (beginsWith (s, "?")) {
184		s = stringDelete (s, 1, 1);
185		}
186	var splits = s.split ("&");
187	splits.forEach (function (item) {
188		const splits = item.split ("="); //9/10/23 by DW
189		const name = splits [0];
190		var val = decodeURIComponent (stringDelete (item, 1, name.length + 1));
191		val = neuterMarkup (val); //11/17/23 by DW
192		allparams [trimWhitespace (name)] = val;
193		});
194	return (allparams);
195	}
196function doRedirect (url) {
197	location.href = url;
198	}
199function httpRequest (url, timeout, headers, callback) { 
200	timeout = (timeout === undefined) ? 30000 : timeout;
201	var jxhr = $.ajax ({ 
202		url: url,
203		dataType: "text", 
204		headers,
205		timeout
206		}) 
207	.success (function (data, status) { 
208		callback (undefined, data);
209		}) 
210	.error (function (status) { 
211		var message;
212		try { //9/18/21 by DW
213			message = JSON.parse (status.responseText).message;
214			}
215		catch (err) {
216			message = status.responseText;
217			}
218		if ((message === undefined) || (message.length == 0)) { //7/22/22 by DW & 8/31/22 by DW
219			message = "There was an error communicating with the server.";
220			}
221		var err = {
222			code: status.status,
223			message
224			};
225		callback (err);
226		});
227	}
228function setUpRiver (riverSpec, callback) {
229	const divRiverContent = $("<div class=\"divRiverContent\"></div>");
230	displayTraditionalRiver (riverSpec, divRiverContent);
231	callback (undefined, divRiverContent)
232	}
233function readOpmlFile (url, outlineTitle, callback) {
234	httpRequest (url, undefined, undefined, callback);
235	}
236
237//wordpress
238	const flUseLocalServer = false;
239	
240	function getServerAddress () {
241		if (flUseLocalServer) {
242			return ("http://localhost:1408/");
243			}
244		else {
245			return ("https://wpidentity.scripting.com/");
246			}
247		}
248	function myGetSitePosts (idsite, callback) {
249		servercall ("wordpressgetsiteposts", {idsite}, true, callback);
250		}
251	function getWordPressSiteText (idsite, callback) {
252		myGetSitePosts (idsite, function (err, thePosts) {
253			if (err) {
254				callback (err);
255				}
256			else {
257				var htmltext = "", indentlevel = 0, lastday = new Date (0), flNotFirstArchivePageDay = false;
258				function add (s) {
259					htmltext += filledString ("\t", indentlevel) + s + "\n";
260					}
261				thePosts.forEach (function (item) {
262					if (!sameDay (lastday, item.whenCreated)) {
263						if (flNotFirstArchivePageDay) {
264							add ("</div>"); indentlevel--;
265							}
266						add ("<div class=\"divArchivePageDay\">"); indentlevel++;
267						flNotFirstArchivePageDay = true;
268						
269						add ("<div class=\"divDayTitle\">" + formatDate (item.whenCreated, "%A, %B %e, %Y") + "</div>");
270						lastday = item.whenCreated;
271						}
272					add ("<div class=\"divTitledItem\">"); indentlevel++;
273					add ("<div class=\"divTitle\">" + item.title + "</div>");
274					add ("<div class=\"divWordpressContent\">" + item.content + "</div>");
275					add ("</div>"); indentlevel--;
276					});
277				if (flNotFirstArchivePageDay) {
278					add ("</div>"); indentlevel--;
279					}
280				callback (undefined, htmltext);
281				}
282			});
283		}
284	
285//tabs
286	function getNthTab (n) {
287		var ix = 0;
288		for (var x in theTabs) {
289			if (ix == n) {
290				return (theTabs [x]);
291				}
292			ix++;
293			}
294		}
295	function buildTabsAsTabs (userOptions) {
296		var options = {
297			nameActiveTab: getNthTab (0).name
298			};
299		for (var x in userOptions) {
300			if (userOptions [x] !== undefined) {
301				options [x] = userOptions [x];
302				}
303			}
304		
305		function displayTabContents (tabRec) {
306			const where = $(".divTabContent");
307			
308			function append (htmltext) {
309				where.append (htmltext);
310				if (getBoolean (tabRec.flJavaScriptDomFeatures)) {
311					setupJavaScriptFeatures ();
312					}
313				}
314			
315			
316			console.log ("displayTabContents: tabRec.name == " + tabRec.name);
317			if (tabRec.getContent === undefined) {
318				where.html (new Date ().toLocaleTimeString ());
319				}
320			else {
321				where.empty ();
322				if (tabRec.savedHtml === undefined) {
323					tabRec.getContent (function (err, theContent) {
324						if (err) {
325							where.html (err.message);
326							}
327						else {
328							tabRec.savedHtml = theContent;
329							append (theContent);
330							}
331						});
332					}
333				else {
334					append (tabRec.savedHtml);
335					}
336				}
337			}
338		function pushState (tabname) {
339			var state = {
340				tabname
341				};
342			history.pushState (state, "", location.pathname + "?tab=" + encodeURIComponent (stringLower (tabname)));
343			}
344		
345		const ulTabs = $(".divScriptingTabs ul");
346		var flFoundTab = false, firstTab = undefined;
347		ulTabs.empty ();
348		for (var x in theTabs) {
349			const item = theTabs [x];
350			if (item.enabled) {
351				const icon = "<i class=\"iTabIcon " + item.icon + "\"></i>";
352				const liTab = $("<li class=\"liTab\"><a data-toggle=\"tab\">" + icon + item.name + "</a></li>");
353				if (firstTab === undefined) {
354					firstTab = {
355						liTab,
356						item
357						}
358					}
359				if (equalStrings (item.name, options.nameActiveTab)) {
360					liTab.addClass ("active");
361					displayTabContents (item);
362					flFoundTab = true;
363					}
364				
365				function handleClick () { //3/8/24 by DW
366					console.log ("handleClick: item.name == " + item.name);
367					$(".liTab").removeClass ("active");
368					liTab.addClass ("active");
369					displayTabContents (item);
370					pushState (item.name);
371					}
372				liTab.on ("pointerdown", handleClick); //3/8/24 by DW
373				
374				ulTabs.append (liTab);
375				}
376			}
377		
378		if ((!flFoundTab) && (firstTab !== undefined)) {
379			firstTab.liTab.addClass ("active");
380			displayTabContents (firstTab.item);
381			}
382		
383		activateToolTips ();
384		window.addEventListener ("popstate", function (ev) {
385			if (ev.state != null) {
386				console.log ("popstate: ev.state.tabname == " + jsonStringify (ev.state.tabname));
387				for (var x in theTabs) {
388					var item = theTabs [x];
389					if (item.name == ev.state.tabname) {
390						displayTabContents (item);
391						}
392					}
393				$(".liTab").removeClass ("active");
394				$(".liTab").each (function () {
395					if ($(this).text () == ev.state.tabname) {
396						$(this).addClass ("active");
397						}
398					});
399				}
400			});
401		}
402//connection to feedland
403	var appPrefs = {
404		urlReaderApp: undefined
405		};
406	function getUsersBlogUrl () { 
407		return (""); 
408		}
409//search button -- 1/17/25 by DW
410	function searchButtonClick () {
411		console.log ("searchButtonClick");
412		$(".divSearchBox button").blur ();
413		const searchterm = $("#idQueryInput").val ()
414		localStorage.lastSearchTerm = searchterm; 
415		const url = "https://daytona.scripting.com/search?q=" + searchterm;
416		console.log ("searchButtonClick: url == " + url);
417		window.open (url);
418		}
419	function initSearchButton () {
420		if (localStorage.lastSearchTerm !== undefined) {
421			$("#idQueryInput").val (localStorage.lastSearchTerm);
422			}
423		$(".divSearchBox").on ("keydown", function (ev) {
424			if (ev.key === "Enter") {
425				searchButtonClick ();
426				event.preventDefault (); 
427				}
428			});
429		}
430
431function everyMinute () {
432	var now = new Date ();
433	if (now.getMinutes () == 0) {
434		console.log ("\neveryMinute: " + now.toLocaleTimeString () + ", v" + myVersion);
435		}
436	riverItemsEveryMinute ();
437	viewLastUpdateString ();
438	$(".spRandomMotto").each (function () { //8/7/19 by DW
439		$(this).text (getRandomSnarkySlogan ());
440		});
441	}
442function everySecond () {
443	riverItemsEverySecond ();
444	$(".spHowLongRunning").each (function () { //8/9/19 by DW
445		$(this).text ("This blog has been running for: " + howLongSinceStartAsString (new Date ("10/7/1994, 12:00 PDT")));
446		});
447	$(".spHowLongUntilThirty").each (function () { //10/6/24 by DW
448		function howlong () {
449			var whenThirty = new Date ("10/7/2024, 12:00 PDT");
450			var now = new Date ();
451			function howLongUntil (now, whenStart) { 
452				function daysInYear (year) {
453					var flLeapYear = ((year % 400) == 0) || ((year % 100) != 0 && ((year % 4) == 0));
454					return ((flLeapYear) ? 366 : 365);
455					}
456				function daysInMonth (month, year) { 
457					return (new Date (year, month, 0).getDate ()); 
458					} 
459				function getnum (num, units) {
460					if (num != 1) {
461						units += "s";
462						}
463					return (num + " " + units);
464					}
465				const ctSecsInDay = 60 * 60 * 24;
466				const ctMilliSecsInDay = 1000 * ctSecsInDay;
467				var theYear = whenStart.getFullYear ();
468				var ctDays = (now - whenStart) / ctMilliSecsInDay;
469				var ctYears = 0;
470				while (true) {
471					if (ctDays <= daysInYear (theYear)) {
472						break;
473						}
474					ctDays -= daysInYear (theYear);
475					ctYears++;
476					theYear++;
477					}
478				
479				var theMonth = 0, ctMonths = 0;
480				while (true) {
481					
482					if (ctDays < daysInMonth (theMonth, theYear)) {
483						break;
484						}
485					ctDays -= daysInMonth (theMonth, theYear);
486					ctMonths++;
487					theMonth++;
488					}
489				
490				const ctWholeDays = Math.floor (ctDays);
491				var ctRemainingSecs = (ctDays - ctWholeDays) * ctSecsInDay;
492				var ctHours = Math.floor (ctRemainingSecs / (60 * 60));
493				ctRemainingSecs -= ctHours * 60 * 60;
494				var ctMinutes = Math.floor (ctRemainingSecs / 60);
495				ctRemainingSecs -= ctMinutes * 60;
496				ctRemainingSecs = Math.floor (ctRemainingSecs);
497				return ({
498					years: ctYears,
499					months: ctMonths,
500					days: ctWholeDays,
501					hours: ctHours,
502					minutes: ctMinutes,
503					seconds: ctRemainingSecs
504					});
505				}
506			
507			var howlong = howLongUntil (whenThirty, now);
508			
509			var s = "";
510			function addnum (num, label) {
511				if (num > 0) {
512					if (num == 1) {
513						label = stringDelete (label, label.length, 1);
514						}
515					s += num + " " + label + ", ";
516					}
517				}
518			addnum (howlong.days, "days");
519			addnum (howlong.hours, "hours");
520			addnum (howlong.minutes, "minutes");
521			addnum (howlong.seconds, "seconds");
522			if (endsWith (s, ", ")) {
523				s = stringDelete (s, s.length - 1, 2);
524				}
525			return (s);
526			}
527		$(this).text (howlong ());
528		});
529	}
530function viewLastUpdateString () { //9/28/17 by DW
531	var whenstring = getFacebookTimeString (config.now, true); //2/25/18 by DW
532	if (beginsWith (whenstring, "Yesterday")) {
533		whenstring = "Yesterday";
534		}
535	$("#idLastScriptingUpdate").html ("Updated: " + whenstring + ".");
536	}
537
538function startBlogroll (callback) {
539	console.log ("startBlogroll");
540	if ($(".divSidebar").css ("display") != "none") {
541		const flQuietMode = false; //7/31/24 by DW
542		const theCheckbox = $(".divBlogrollQuietModeSwitch input"); 
543		if (flQuietMode) { //4/17/24 by DW
544			$(".divBlogrollContainer").css ("width", 220);
545			}
546		theCheckbox.prop ("checked", flQuietMode);
547		
548		const blogrollOptions = {
549			urlFeedListOpml: appConsts.urlFeedListOpml,
550			urlFeedlandViewBlogroll: appConsts.urlFeedlandViewBlogroll,
551			whereToAppend: $(".divBlogrollContainer"), //3/15/24 by DW
552			title: "Dave's Blogroll",
553			flDisplayTitle: true,
554			flQuietMode: flQuietMode,
555			blogrollDisplayedCallback: function () {
556				console.log ("blogrollDisplayedCallback");
557				if (callback !== undefined) {
558					callback ();
559					}
560				}
561			};
562		try { //2/28/24 by DW
563			const theBlogroll = new blogroll (blogrollOptions);
564			}
565		catch (err) {
566			console.log ("startBlogroll: err.message == " + err.message);
567			return;
568			}
569		
570		theCheckbox.change (function () { //4/20/24 by DW
571			if (theCheckbox.is (':checked')) {
572				blogrollOptions.flQuietMode = true;
573				} 
574			else {
575				blogrollOptions.flQuietMode = false;
576				}
577			$(".divBlogrollContainer").empty ();
578			blogroll (blogrollOptions);
579			});
580		}
581	else {
582		if (callback !== undefined) {
583			callback ();
584			}
585		}
586	}
587
588function startup () {
589	console.log ("startup");
590	
591	if (localStorage.wordpressMemory !== undefined) {
592		wordpressMemory = JSON.parse (localStorage.wordpressMemory);
593		}
594	
595	const allparams = getAllUrlParams ();
596	
597	
598	
599	
600	
601	const options = { //11/12/25 by DW
602		nameActiveTab: allparams.tab
603		};
604	buildTabsAsTabs (options);
605	
606	viewLastUpdateString ();
607	
608	if (appConsts.flBlogrollEnabled) { //11/12/25 by DW
609		const whenstart = new Date ();
610		startBlogroll (function () {
611			console.log ("startBlogroll took " + secondsSince (whenstart) + " secs.");
612			});
613		}
614	
615	self.setInterval (everySecond, 1000);
616	runEveryMinute (everyMinute);
617	everyMinute ();
618	
619	
620	initSearchButton (); //1/17/25 by DW
621	
622	hitCounter ();
623	}

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.