Thursday, March 31, 2011 at 1:05 AM.

river2Suite.viewJsonNews3

on viewJsonNews3 (adrcal=nil, version=3, adruser=nil) {
	<<Changes
		<<3/29/11; 5:40:49 PM by DW
			<<Respect the users' preference for max items on the home page.
		<<3/24/11; 1:02:48 PM by DW
			<<Call river2Suite.userFollows to determine if a user follows a feed. This also takes into account whether or not the feed was in a list.
		<<3/24/11; 11:00:50 AM by DW
			<<Fix bug where this script would fail if the user had no items visible in his river.
		<<3/24/11; 9:46:08 AM by DW
			<<Pass adrdata^.prefs.maxDaysNews to mainresponder.calendar.visitReverseChronologic to limit the number of days we're willing to go back in time.
		<<3/22/11; 5:22:16 PM by DW
			<<New optional param, adruser. If non-nil we use that address, instead of getting it out of the pagetable. Makes it possible for us to be called without having to set up a pagetable.
		<<2/13/11; 4:34:16 PM by DW
			<<Run text through xml.entityencode so that characters with high bits set are encoded. 
		<<12/30/10; 2:23:06 PM by DW
			<<Rewrite of river2Suite.viewJsonNews to not use json.decompile, so we have better control of the JSON that's generated. This is the third version.
		<<12/18/10; 7:06:55 PM by DW
			<<New optional param, version, defaults to 1 -- the original format. In version 2, each "updatedFeed" has exactly one item it. You'll never see a list where an item could be. This means the info is repeated, so the file is a little fatter. You should still group updates from a single feed that happened at the same time. But now that grouping is not reflected in the structure. 
		<<12/7/10; 10:15:28 AM by DW
			<<Add a docs value to the metadata section, pointing to a Scripting News article that explains what's going on here.
		<<12/5/10; 3:07:42 PM by DW
			<<Return a JSON representation of the news page.
	local (adrdata = river2suite.init (), startticks = clock.ticks (), maxitems);
	bundle { //set adruser if it's nil, 3/22/11 by DW
		if adruser == nil {
			try {
				local (pta = html.getpagetableaddress ());
				adruser = pta^.adruser}}};
	bundle { //set maxitems, 3/29/11 by DW
		maxitems = adrdata^.prefs.maxItemsHomePage;
		if adruser != nil {
			if adruser^.prefs.maxRiverItems < maxitems {
				maxitems = adruser^.prefs.maxRiverItems}}};
	local (flhomepage = true, ct = 0, errorstring = "", flcache = true, jsontext = "", indentlevel = 0, lastFeedUrl, flFirstItemAtLevel, flAtLeastOneItem = false);
	on add (s) {
		<<if indentlevel < 0
			<<indentlevel = 0
		jsontext = jsontext + string.filledstring ("\t", indentlevel) + s + "\r"};
		<<scratchpad.jsontext = jsontext //debugging
	on encode (s) {
		s = string.multiplereplaceall (s, @json.data.replaceTable);
		s = xml.entityencode (s); //2/13/11 by DW
		return (s)};
	add ("{"); indentlevel++;
	add ("\"updatedFeeds\": {"); indentlevel++;
	add ("\"updatedFeed\": ["); indentlevel++;
	lastFeedUrl = ""; //force the first item to create a new updated feed table
	on visit (adritem) {
		if typeof (adritem^) != tabletype { //for processing podcasts and picture calender items
			local (name = nameof (adritem^));
			local (adrday = mainresponder.calendar.getdayaddress (@adrdata^.river, adritem^));
			adritem = @adrday^.[name]};
		<<bundle //check type and flags, possibly return, 9/9/09 by DW
			<<if defined (adritem^.type)
				<<if flhomepage //9/15/09 by DW
					<<if not adrdata^.prefs.flPodcastsOnHomePage
						<<if adritem^.type.flPodcast
							<<return (true)
					<<if not adrdata^.prefs.flPhotosOnHomePage
						<<if adritem^.type.flPhoto
							<<return (true)
		bundle { //don't display items from feeds that have been unsubbed, 9/15/09 by DW
			if not defined (adrdata^.feeds.[adritem^.feedurl]) { //9/15/09 by DW
				return (true)}};
		bundle { //display the item in a try, 1/2/10 by DW
			try {
				local (title = adritem^.title, feedurl = adritem^.feedurl, link, flTitleEndsWithPunc);
				local (flduplicatedescription = title == adritem^.description); //10/1/10 by DW
				bundle { //set flTitleEndsWithPunc, 5/20/10 by DW
					local (s = string.trimwhitespace (title), x = sizeof (s));
					if x > 0 {
						local (ch = s [x]);
						flTitleEndsWithPunc = (ch == '!') or (ch == '?')}
					else {
						flTitleEndsWithPunc = false}};
				local (itemnum = number (nameof (adritem^)), itemname = "item" + itemnum);
				local (namelink = "<a name=\"" + itemname + "\"></a>");
				if adruser != nil { //12/2/09 by DW
					
					if not river2Suite.userFollows (adruser, feedurl) { //3/24/11 by DW
						return (true)};
					};
					<<if not defined (adruser^.feeds.[feedurl]) //user isn't following this feed
						<<return (true)
				local (adrfeed = @adrdata^.feeds.[feedurl]);
				if not defined (adrfeed^) { //8/17/09 by DW -- feed has been deleted
					return (true)};
				bundle { //if feedurl changed, add a new "updatedFeed"
					if feedurl != lastfeedurl {
						local (adrfeedinfo = @adrfeed^.feedinfo);
						if lastfeedurl != "" {
							add ("]"); indentlevel--;
							add ("},"); indentlevel--};
						add ("{"); indentlevel++;
						add ("\"feedUrl\": \"" + feedurl + "\",");
						add ("\"websiteUrl\": \"" + adrfeedinfo^.link + "\",");
						add ("\"feedTitle\": \"" + adrfeedinfo^.title + "\",");
						add ("\"whenLastUpdate\": \"" + date.netstandardstring (timeCreated (adritem)) + "\",");
						add ("\"item\": ["); indentlevel++;
						flFirstItemAtLevel = true;
						lastfeedurl = feedurl}};
				bundle { //output one item
					bundle { //put out a comma if it's not the first item at this level
						if not flFirstItemAtLevel {
							add (",")};
						flFirstItemAtLevel = false};
					flAtLeastOneItem = true; //3/24/11 by DW
					add ("{"); indentlevel++;
					bundle { //add body
						if (not defined (adritem^.body)) or (not flcache) {
							if flduplicatedescription { //10/1/10 by DW
								adritem^.body = ""}
							else {
								adritem^.body = searchengine.stripmarkup (adritem^.description)};
							if sizeof (adritem^.body) > adrdata^.prefs.maxBodyLength {
								adritem^.body = string.mid (adritem^.body, 1, adrdata^.prefs.maxBodyLength);
								while adritem^.body [sizeof (adritem^.body)] != " " {
									adritem^.body = string.delete (adritem^.body, sizeof (adritem^.body), 1)};
								adritem^.body = string.delete (adritem^.body, sizeof (adritem^.body), 1);
								adritem^.body = adritem^.body + "..."}};
						add ("\"body\": \"" + encode (adritem^.body) + "\",")};
					add ("\"permaLink\": \"" + adritem^.permalink + "\",");
					add ("\"pubDate\": \"" + date.netstandardstring (adritem^.pubDate) + "\",");
					add ("\"title\": \"" + encode (adritem^.title) + "\",");
					add ("\"link\": \"" + adritem^.link + "\",");
					bundle { //comments
						if sizeof (adritem^.comments) > 0 {
							add ("\"comments\": \"" + adritem^.comments + "\",")}};
					bundle { //enclosure
						if defined (adritem^.enclosure) {
							add ("\"enclosure\": ["); indentlevel++;
							add ("{"); indentlevel++;
							add ("\"url\": \"" + adritem^.enclosure.url + "\",");
							add ("\"type\": \"" + adritem^.enclosure.type + "\",");
							add ("\"length\": \"" + adritem^.enclosure.length + "\"");
							add ("}"); indentlevel--;
							add ("],"); indentlevel--}};
					bundle { //thumbnail
						if defined (adritem^.thumbnail) { //9/7/09 by DW
							add ("\"thumbnail\": ["); indentlevel++;
							add ("{"); indentlevel++;
							add ("\"url\": \"" + adritem^.thumbnail.url + "\",");
							add ("\"width\": \"" + adritem^.thumbnail.width + "\",");
							add ("\"height\": \"" + adritem^.thumbnail.height + "\"");
							add ("}"); indentlevel--;
							add ("],"); indentlevel--}};
					add ("\"id\": \"" + nameof (adritem^) + "\"");
					add ("}"); indentlevel--}}
			else {
				return (false)}};
		return (++ct <= maxitems)};
	if adrcal == nil {
		adrcal = @adrdata^.river};
	mainresponder.calendar.visitReverseChronologic (adrcal, @visit, maxDays:adrdata^.prefs.maxDaysNews);
	if flAtLeastOneItem { //3/24/11 by DW
		add ("]"); indentlevel--; //close off last item list
		add ("}"); indentlevel--}; //close off last updatedFeed
	add ("]"); indentlevel--; //close off updatedFeed
	add ("},"); indentlevel--; //close off updatedFeeds
	bundle { //add metadata
		local (now = clock.now ());
		add ("\"metadata\": {"); indentlevel++;
		add ("\"docs\": \"" + "http://scripting.com/stories/2010/12/06/innovationRiverOfNewsInJso.html" + "\",");
		add ("\"whenGMT\": \"" + date.netstandardstring (now) + "\",");
		add ("\"whenLocal\": \"" + now + " Eastern" + "\",");
		add ("\"version\": \"" + version + "\",");
		add ("\"secs\": \"" + string.formatdouble (double (clock.ticks () - startticks) / 60) + "\"");
		add ("}"); indentlevel--};
	add ("}"); indentlevel--;
	<<wp.newtextobject (jsontext, @scratchpad.jsontext3)
	return (jsontext)};
bundle { //test code
	local (pt);
	new (tabletype, @pt); pt.adruser = nil;
	html.setpagetableaddress (@pt);
	local (tc = clock.ticks ());
	viewJsonNews3 (adruser:@config.river2.users.adam);
	dialog.alert (clock.ticks () - tc)}



This listing is for code that runs in the OPML Editor environment. I created these listings because I wanted the search engines to index it, so that when I want to look up something in my codebase I don't have to use the much slower search functionality in my object database. Dave Winer.