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.