├── demos ├── node │ ├── package.json │ └── demo.js └── browser │ ├── styles.css │ ├── worknotes.md │ ├── index.html │ └── code.js ├── snapshots └── august2025 │ ├── readme.md │ └── feedlandsocket.js ├── package ├── package.json └── feedlandsocket.js ├── LICENSE ├── README.md ├── worknotes.md └── source.opml /demos/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "description": "An app that demo's feedlandsocket package.", 4 | "author": "Dave Winer ", 5 | "main": "demo.js", 6 | "license": "MIT", 7 | "version": "0.4.0", 8 | "dependencies" : { 9 | "daveutils": "*", 10 | "feedlandsocket": "*" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /snapshots/august2025/readme.md: -------------------------------------------------------------------------------- 1 | # August 2025 snapshot 2 | 3 | I've done a rewrite to turn it into a package you can use in other apps. That was the right way to go. 4 | 5 | This version preserves the simpler demo app that was here before August 2025. But please don't deploy an app using this code. It had problems that were fixed in the August 2025 release. 6 | 7 | -------------------------------------------------------------------------------- /package/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "feedlandsocket", 3 | "description": "A package and demo app that connects to FeedLand's websocket feature.", 4 | "author": "Dave Winer ", 5 | "main": "feedlandsocket.js", 6 | "license": "MIT", 7 | "version": "0.5.1", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/scripting/feedlandSocket.git" 11 | }, 12 | "files": [ 13 | "feedlandsocket.js", 14 | "demo.js", 15 | "readme.md", 16 | "worknotes.md" 17 | ], 18 | "dependencies" : { 19 | "daveutils": "*", 20 | "ws": "*" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demos/browser/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: white; 3 | } 4 | .divPageBody { 5 | width: 750px; 6 | margin-left: auto; 7 | margin-right: auto; 8 | margin-top: 50px; 9 | margin-bottom: 400px; 10 | min-height: 400px; 11 | font-family: Arial, sans-serif; 12 | font-size: 24px; 13 | } 14 | 15 | .divTitle { 16 | font-size: 22px; 17 | font-weight: bold; 18 | margin-bottom: 10px; 19 | } 20 | .divJsonTextarea { 21 | width: 100%; 22 | height: 400px; 23 | font-family: "Ubuntu"; 24 | font-size: 15px; 25 | line-height: 140%; 26 | border: 1px solid #ccc; 27 | padding: 10px; 28 | background: white; 29 | white-space: pre; 30 | overflow-x: auto; 31 | word-wrap: normal; 32 | tab-size: 6; 33 | margin-bottom: 5px; 34 | } 35 | .divLineBelowText, .divSecondLine { 36 | font-size: 14px; 37 | } 38 | .spFeedlandServer { 39 | cursor: pointer; 40 | } 41 | 42 | /* styles for phone, tablet */ 43 | @media screen and (max-width: 1024px) { 44 | .divPageBody { 45 | width: 95%; 46 | margin-top: 80px; 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /demos/browser/worknotes.md: -------------------------------------------------------------------------------- 1 | #### 9/21/25; 10:58:25 AM by DW 2 | 3 | I want it to start with a blank space where the data goes, we can provide a screen shot in the docs. It's confusing to have something in the box when it says "0 new feed items.:" 4 | 5 | #### 9/6/25; 9:58:55 AM by DW 6 | 7 | code for a feedland socket client, cribbed from the readerland base. 8 | 9 | the goal is to get something that can be included in a browser JS app that gets it a flow of new and updated feed items. 10 | 11 | initially we use the socket handler in feedlandHome, but I think ultimately we want to move that code down to this level so we only have one set of functions for all our apps, including the example app. 12 | 13 | this means moving the recentIds functionality into wsConnectUserToServer. 14 | 15 | #### 5/24/25; 10:44:50 AM by DW 16 | 17 | readerland starts here 18 | 19 | #### 5/5/25; 5:30:56 PM by DW -- bookmarks menu 20 | 21 | i'm going to make a plain and simple portable bookmarks menu, works like the one in drummer 22 | 23 | #### 5/5/25; 9:05:54 AM by DW 24 | 25 | a place to test browser-based code 26 | 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Dave Winer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demos/node/demo.js: -------------------------------------------------------------------------------- 1 | const fs = require ("fs"); 2 | const utils = require ("daveutils"); 3 | const feedlandSocket = require ("feedlandsocket"); 4 | 5 | function handleMessage (theCommand, thePayload) { 6 | function nowstring () { 7 | return (new Date ().toLocaleTimeString ()); 8 | } 9 | function itemToJson (theItem) { 10 | var briefObject = new Object (); 11 | function copyProp (name) { 12 | if (theItem [name] !== undefined) { 13 | briefObject [name] = theItem [name]; 14 | } 15 | } 16 | copyProp ("id"); 17 | copyProp ("feedUrl"); 18 | copyProp ("title"); 19 | copyProp ("pubDate"); 20 | copyProp ("whenReceived"); 21 | return (utils.jsonStringify (briefObject)); 22 | } 23 | function handleNewItem (theItem) { 24 | console.log ("\n" + nowstring () + ": new item == " + itemToJson (theItem)); 25 | } 26 | function handleUpdatedItem (theItem) { 27 | console.log ("\n" + nowstring () + ": updated item == " + itemToJson (theItem)); 28 | } 29 | switch (theCommand) { 30 | case "newItem": 31 | handleNewItem (thePayload.item); 32 | break; 33 | } 34 | } 35 | 36 | var config = { 37 | urlFeedlandSocket: "wss://feedland.com:443/_ws/" //11/3/25 by DW 38 | }; 39 | 40 | utils.readConfig ("config.json", config, function () { 41 | console.log ("\nconfig == " + utils.jsonStringify (config) + "\n"); 42 | const options = { 43 | urlFeedlandSocket: config.urlFeedlandSocket, 44 | handleMessage 45 | }; 46 | feedlandSocket.connect (options); 47 | }); 48 | 49 | -------------------------------------------------------------------------------- /snapshots/august2025/feedlandsocket.js: -------------------------------------------------------------------------------- 1 | const myVersion = "0.4.1", myProductName = "feedlandSocket"; 2 | 3 | const fs = require ("fs"); 4 | const utils = require ("daveutils"); 5 | const websocket = require ("websocket").w3cwebsocket; 6 | 7 | var config = { 8 | urlSocketServer: "wss://feedland.org/" //4/18/23 by DW 9 | }; 10 | 11 | function wsConnectUserToServer (itemReceivedCallback) { 12 | var mySocket = undefined; 13 | function checkConnection () { 14 | if (mySocket === undefined) { 15 | mySocket = new websocket (config.urlSocketServer); 16 | mySocket.onopen = function (evt) { 17 | }; 18 | mySocket.onmessage = function (evt) { 19 | function getPayload (jsontext) { 20 | var thePayload = undefined; 21 | try { 22 | thePayload = JSON.parse (jsontext); 23 | } 24 | catch (err) { 25 | } 26 | return (thePayload); 27 | } 28 | if (evt.data !== undefined) { //no error 29 | var theCommand = utils.stringNthField (evt.data, "\r", 1); 30 | var jsontext = utils.stringDelete (evt.data, 1, theCommand.length + 1); 31 | var thePayload = getPayload (jsontext); 32 | switch (theCommand) { 33 | case "newItem": 34 | itemReceivedCallback (thePayload); 35 | break; 36 | } 37 | } 38 | }; 39 | mySocket.onclose = function (evt) { 40 | mySocket = undefined; 41 | }; 42 | mySocket.onerror = function (evt) { 43 | }; 44 | } 45 | } 46 | setInterval (checkConnection, 1000); 47 | } 48 | 49 | wsConnectUserToServer (function (thePayload) { 50 | console.log (new Date ().toLocaleTimeString () + ": title == " + thePayload.theFeed.title + ", feedUrl == " + thePayload.theFeed.feedUrl); 51 | }); 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # feedlandSocket 2 | 3 | Connects with a FeedLand instance, getting a complete set of new items and updates of feeds as it reads them. 4 | 5 | ### Background 6 | 7 | While FeedLand is reading feeds, it's sending a constant stream of JavaScript objects to each user via websockets. 8 | 9 | This is how it updates feeds in realtime. 10 | 11 | This service is also available to applications running in any environment that supports the standard websockets. 12 | 13 | ### Watch it run 14 | 15 | Here's the demo app that runs in the browser. 16 | 17 | 18 | 19 | You'll see a series of JSON packets flow through a box on the screen. 20 | 21 | Open the JavaScript console for more info. 22 | 23 | ### What's in the repo 24 | 25 | A Node.js package that connects to FeedLand's websocket interface. 26 | 27 | Demo apps for hooking into the socket connection from a browser or from a Node app. 28 | 29 | ### Philosophy 30 | 31 | I think this is the next way news is distributed, a lot simpler than RSS imho. Just open a connection and let the news flow to you. 32 | 33 | You have to be sure the feeds you want are subscribed to on feedland.com, or another FeedLand instance you want to serve as the backend. 34 | 35 | At this point it's a firehose, you get notification of all new items, and updates to items. Over time we probably will have to have more configuration options. 36 | 37 | ### Plugins wanted 38 | 39 | I'd love to see WordPress plugins devs see what they can do with this. 40 | 41 | -------------------------------------------------------------------------------- /demos/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | FeedLand Socket Demo 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 |
 
29 | 31 |
32 | 33 | Source code 34 | 35 | 36 | 37 | Running for 0 seconds. 38 | 39 |
40 |
41 | Server: . 42 |
43 |
44 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /worknotes.md: -------------------------------------------------------------------------------- 1 | #### 11/16/25; 10:03:13 AM by DW 2 | 3 | I think we may be getting too many update messages. Added a console.log when we receive an updatedItem message, showing the feed it came from. 4 | 5 | * wrote a report on feedlanddev. 6 | 7 | If the user enters an empty string, set the pref to undefined, so we use the default url when reloading. 8 | 9 | #### 11/5/25; 8:02:47 AM by DW 10 | 11 | If you didn't specify the url of the socket server in the url param for the demo app, it would try to connect to a server at "null." 12 | 13 | It worked when you came to the demo app from my blog, but if you got it from the readme here, it wouldn't work. 14 | 15 | Need to be more careful Dave. But having a test group there to review each release for stupid mistakes like this would be good too. 16 | 17 | #### 11/3/25; 8:04:39 AM by DW 18 | 19 | Change the default socket server to the one behind feedland.com. 20 | 21 | 22 | 23 | Added new URL param to specify the socket server we'll use. This URL works. 24 | 25 | https://socketdemo.feedland.org/?url=wss://feedland.org/ 26 | 27 | #### 9/22/25; 10:05:32 AM by DW 28 | 29 | Changes to the socket demo app. 30 | 31 | * Cname for the demo page: socketdemo.feedland.org. 32 | 33 | * Time units shouldn't have fractions, so 2.1 seconds should become 2 seconds. 34 | 35 | * We display the address of the socket server. If you click on the address a dialog appears asking for the new address. Enter the new address carefully because you must get it exactly right. Here are the addresses of three servers you can try out: 36 | 37 | 1. wss://feedland.org/ 38 | 39 | 2. wss://feedland.social/ 40 | 41 | 3. wss://feedland.com:443/_ws/ 42 | 43 | * If the title of an item is undefined, erase the title from the previous item. 44 | 45 | #### 9/21/25; 11:00:54 AM by DW 46 | 47 | There's a new example app that runs in the browser. It's time to publish the code via the repo. There's still work to do, but we have to get this out now. 48 | 49 | turned the demo folder to demos. 50 | 51 | moved the node demo to demos:node 52 | 53 | added the new example app to the demos folder at demos:browser 54 | 55 | #### 8/7/25; 8:01:33 AM by DW 56 | 57 | Now it's more than a demo. 58 | 59 | It's now a NPM package, and includes a demo app. 60 | 61 | Changed in dependencies from "websocket" to "ws". 62 | 63 | It filters out repeated id's. FeedLand will send duplicate notices. At some point it will stop doing that, but in the meantime, let's catch them at this level. 64 | 65 | #### 4/18/23; 9:18:03 AM by DW 66 | 67 | Feedland now uses WSS, the secure version of websockets, so this code must change. 68 | 69 | -------------------------------------------------------------------------------- /package/feedlandsocket.js: -------------------------------------------------------------------------------- 1 | const myVersion = "0.5.1", myProductName = "feedlandSocket"; 2 | 3 | const fs = require ("fs"); 4 | const utils = require ("daveutils"); 5 | const websocket = require ("ws"); 6 | 7 | var config = { 8 | urlSocketServer: "wss://feedland.org/" //4/18/23 by DW 9 | }; 10 | 11 | exports.connect = function (userOptions) { //5/25/24 by DW 12 | var options = { 13 | flWebsocketEnabled: true, 14 | urlFeedlandSocket: undefined, 15 | maxRetries: 100, //when we lose a connection, we try to reconnect this many times 16 | ctSecsBetwRetries: 10, 17 | initialCheckTimeout: 100, //5/4/25 by DW 18 | maxSecsBetwNotifications: 10.1, //a notice on the same id less than x secs apart are considered to be the same one 19 | flDebugMessages: true, 20 | handleMessage: function (theCommand, thePayload) { 21 | switch (theCommand) { 22 | case "hello": 23 | console.log ("hello"); 24 | break; 25 | } 26 | } 27 | }; 28 | utils.mergeOptions (userOptions, options); 29 | 30 | var recentIds = new Object (); 31 | function notSeenRecently (id) { 32 | var flSeen = false; 33 | function ageOut () { 34 | var newObject = new Object (); 35 | for (var x in recentIds) { 36 | if (utils.secondsSince (recentIds [x]) <= options.maxSecsBetwNotifications) { 37 | newObject [x] = recentIds [x]; 38 | } 39 | } 40 | recentIds = newObject; 41 | } 42 | ageOut (); //remove expired ids 43 | for (var x in recentIds) { 44 | if (id == x) { 45 | flSeen = true; 46 | } 47 | } 48 | recentIds [id] = new Date (); 49 | return (!flSeen); 50 | } 51 | 52 | var ctRetries = 0, idSocketChecker; 53 | var ctIncomingMessages; //5/25/25 by DW 54 | 55 | if (options.flWebsocketEnabled) { //2/8/23 by DW 56 | var mySocket = undefined; 57 | function checkConnection () { 58 | if (mySocket === undefined) { 59 | mySocket = new websocket (options.urlFeedlandSocket); 60 | if (options.flDebugMessages) { 61 | console.log ("wsConnectUserToServer: socket created"); 62 | } 63 | mySocket.onopen = function (evt) { 64 | ctRetries = 0; //5/1/25 by DW -- we got through 65 | ctIncomingMessages = 0; //5/25/25 by DW -- for debugging 66 | if (options.flDebugMessages) { 67 | console.log ("wsConnectUserToServer: socket connection is open"); 68 | } 69 | }; 70 | mySocket.onmessage = function (evt) { 71 | function getPayload (jsontext) { 72 | var thePayload = undefined; 73 | try { 74 | thePayload = JSON.parse (jsontext); 75 | } 76 | catch (err) { 77 | } 78 | return (thePayload); 79 | } 80 | if (evt.data !== undefined) { //no error 81 | var theCommand = utils.stringNthField (evt.data, "\r", 1); 82 | var jsontext = utils.stringDelete (evt.data, 1, theCommand.length + 1); 83 | var thePayload = getPayload (jsontext); 84 | 85 | var id; //8/7/25 by DW 86 | if (thePayload.item === undefined) { //it's an updatedFeed message, probably 87 | id = thePayload.id; 88 | } 89 | else { 90 | id = thePayload.item.id; 91 | } 92 | 93 | if (notSeenRecently (id)) { 94 | options.handleMessage (theCommand, thePayload); 95 | } 96 | 97 | } 98 | }; 99 | mySocket.onclose = function (evt) { 100 | mySocket = undefined; 101 | if (ctRetries++ >= options.maxRetries) { //5/1/25 by DW 102 | clearInterval (idSocketChecker); 103 | } 104 | }; 105 | mySocket.onerror = function (evt) { 106 | console.log ("wsConnectToServer: socket received an error."); 107 | }; 108 | } 109 | } 110 | setTimeout (function () { //5/4/25 by DW 111 | checkConnection (); 112 | idSocketChecker = setInterval (checkConnection, 1000 * options.ctSecsBetwRetries); 113 | }, options.initialCheckTimeout); 114 | } 115 | } 116 | 117 | 118 | -------------------------------------------------------------------------------- /demos/browser/code.js: -------------------------------------------------------------------------------- 1 | var myVersion = 0.52, myProductName = "socketdemo"; 2 | 3 | const appConsts = { 4 | urlFeedlandSocket: "wss://feedland.com:443/_ws/", 5 | screenname: "scripting", 6 | catname: "socketdemo", 7 | } 8 | 9 | var appPrefs = { 10 | urlFeedlandSocket: undefined 11 | } 12 | 13 | var myFeedland = undefined; 14 | 15 | const whenStart = new Date (); 16 | var ctMessagesReceived = 0; 17 | var flPrefsChanged = false; 18 | 19 | function loadPrefs () { //9/22/25 by DW 20 | if (localStorage.socketdemo !== undefined) { 21 | try { 22 | const jstruct = JSON.parse (localStorage.socketdemo); 23 | for (var x in jstruct) { 24 | appPrefs [x] = jstruct [x]; 25 | } 26 | } 27 | catch (err) { 28 | } 29 | } 30 | } 31 | function prefsChanged () { 32 | flPrefsChanged = true; 33 | } 34 | function savePrefs () { 35 | localStorage.socketdemo = jsonStringify (appPrefs); 36 | } 37 | function getFeedlandAddress () { 38 | var theAddress = getURLParameter ("url"); 39 | if (theAddress == "null") { 40 | theAddress = (appPrefs.urlFeedlandSocket === undefined) ? appConsts.urlFeedlandSocket : appPrefs.urlFeedlandSocket; 41 | } 42 | return (theAddress); 43 | } 44 | function howLongSince (when) { 45 | const secs = secondsSince (when), secsInMinute = 60, secsInHour = secsInMinute * 60, secsInDay = 24 * secsInHour; 46 | function round (num) { 47 | return (Math.round (num)) 48 | } 49 | if (secs < secsInMinute) { 50 | return (round (secs) + " seconds"); 51 | } 52 | else { 53 | if (secs < secsInHour) { 54 | return (round (secs / secsInMinute) + " minutes"); 55 | } 56 | else { 57 | return (round (secs / secsInHour) + " hours"); 58 | } 59 | } 60 | } 61 | function viewJsontext (theObject) { 62 | function formatJsonWithTabs (theObject) { 63 | var jsontext = JSON.stringify (theObject, null, '\t'); 64 | jsontext = jsontext.replace (/^(\t*)\}/gm, '$1\t}'); //move closing braces to align with content inside (add one more tab) 65 | return (jsontext); 66 | } 67 | const jsontext = formatJsonWithTabs (theObject); 68 | $(".divJsonTextarea").text (jsontext); 69 | 70 | const titlestring = (theObject.title === undefined) ? " " : maxStringLength (theObject.title, 60, false, true); 71 | $(".divTitle").text (titlestring); 72 | 73 | } 74 | function nowstring () { 75 | return (new Date ().toLocaleTimeString ()); 76 | } 77 | 78 | function feedlandSockets (userOptions) { //9/6/25 by DW 79 | const socketOptions = { 80 | flWebsocketEnabled: true, 81 | urlFeedlandSocket: undefined, 82 | handleMessage 83 | }; 84 | mergeOptions (userOptions, socketOptions); 85 | 86 | var recentIds = new Object (); 87 | function notSeenRecently (id) { 88 | var flSeen = false; 89 | function ageOut () { 90 | var newObject = new Object (); 91 | for (var x in recentIds) { 92 | if (secondsSince (recentIds [x]) <= readerlandConsts.maxSecsBetwNotifications) { 93 | newObject [x] = recentIds [x]; 94 | } 95 | } 96 | recentIds = newObject; 97 | } 98 | ageOut (); //remove expired ids 99 | for (var x in recentIds) { 100 | if (id == x) { 101 | flSeen = true; 102 | } 103 | } 104 | recentIds [id] = new Date (); 105 | return (!flSeen); 106 | } 107 | 108 | function handleMessage (theCommand, thePayload) { 109 | function getTitle (item) { 110 | if (item.title === undefined) { 111 | return (maxStringLength (stripMarkup (item.description), 35)); 112 | } 113 | else { 114 | return (item.title); 115 | } 116 | } 117 | 118 | if (thePayload.item !== undefined) { //debugging 119 | switch (theCommand) { 120 | case "newItem": 121 | var wpData = ""; 122 | if (thePayload.item.metadata !== undefined) { 123 | if (thePayload.item.metadata.wpSiteId !== undefined) { 124 | wpData = thePayload.item.metadata.wpSiteId + "/" + thePayload.item.metadata.wpPostId; 125 | } 126 | } 127 | console.log (`${nowstring ()} ${theCommand} ${thePayload.item.feedUrl} ${wpData}`); 128 | viewJsontext (thePayload.item); 129 | ctMessagesReceived++; 130 | break; 131 | case "updatedItem": //11/16/25 by DW 132 | console.log (`${nowstring ()} ${theCommand} ${thePayload.item.feedUrl}`); 133 | break; 134 | } 135 | } 136 | } 137 | 138 | wsConnectUserToServer (socketOptions); //5/28/25 by DW 139 | } 140 | 141 | function startup () { 142 | loadPrefs (); 143 | console.log ("startup: appPrefs == " + jsonStringify (appPrefs)); 144 | 145 | function everySecond () { 146 | const items = (ctMessagesReceived == 1) ? "item" : "items"; 147 | $(".spCount").html (ctMessagesReceived + " new " + items + " received."); 148 | $(".spHowLong").text (howLongSince (whenStart)) 149 | if (flPrefsChanged) { 150 | savePrefs (); 151 | flPrefsChanged = false; 152 | } 153 | } 154 | const options = { 155 | urlFeedlandSocket: getFeedlandAddress (), //9/22/25 by DW 156 | } 157 | myFeedland = new feedlandSockets (options); 158 | 159 | ctMessagesReceived = 0; 160 | 161 | $(".spFeedlandServer").text (getFeedlandAddress ()); 162 | $(".spFeedlandServer").click (function () { 163 | console.log ("click"); 164 | function useUrl (url) { 165 | appPrefs.urlFeedlandSocket = url; 166 | $(".spFeedlandServer").text (url); 167 | savePrefs (); //must save immediately 168 | location.reload (); 169 | } 170 | askDialog ("Address of FeedLand socket server:", appPrefs.urlFeedlandSocket, "", function (url, flcancel) { 171 | if (!flcancel) { 172 | url = trimWhitespace (url); 173 | if (url.length == 0) { //11/16/25 by DW 174 | useUrl (undefined); 175 | } 176 | else { 177 | if (beginsWith (url, "wss://")) { 178 | useUrl (url); 179 | } 180 | else { 181 | alertDialog ("Can't set the socket server because it must begin with wss://."); 182 | } 183 | } 184 | } 185 | }); 186 | }); 187 | 188 | self.setInterval (everySecond, 1000); 189 | hitCounter (); //9/7/25 by DW 190 | } 191 | -------------------------------------------------------------------------------- /source.opml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | nodeEditor: feedlandSocket 18 | Tue, 22 Nov 2022 21:59:25 GMT 19 | Sun, 16 Nov 2025 15:23:38 GMT 20 | Dave Winer 21 | http://davewiner.com/ 22 | 1, 2, 3, 4, 7, 21, 24, 31, 44, 46, 53, 57, 65, 70, 71, 72, 79, 83, 97, 102, 113, 115, 121, 122, 124, 127, 128, 131, 145, 148, 154, 158, 163 23 | 1 24 | 71 25 | 587 26 | 902 27 | 1855 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | --------------------------------------------------------------------------------