<commit_before>$('.dropdown-toggle').dropdown();

if ($('#messages').attr('data-show')) {
	$('#messages').modal();
}

function countProperties(obj) {
	var count = 0;
	for(var prop in obj) {
		if(obj.hasOwnProperty(prop))
			++count;
	}
	return count;
}

var goReadAppModule = angular.module('goReadApp', ['ui.sortable'])
	.filter('encodeURI', function() {
		return encodeURIComponent;
	}).config(function($locationProvider) {
		return $locationProvider.html5Mode(true);
	});
goReadAppModule.controller('GoreadCtrl', function($scope, $http, $timeout, $window, $location) {
	$scope.loading = 0;
	$scope.contents = {};
	$scope.opts = {
		folderClose: {},
		nav: true,
		expanded: false,
		mode: 'unread',
		sort: 'newest',
		hideEmpty: false,
		scrollRead: false
	};

	$scope.sortableOptions = {
		stop: function() {
			$scope.uploadOpml();
		}
	};

	$scope.importOpml = function() {
		$scope.shown = 'feeds';
		$scope.loading++;
		$('#import-opml-form').ajaxForm(function() {
			$('#import-opml-form')[0].reset();
			$scope.loaded();
			$scope.showMessage("OPML import is happening. It can take a minute. Don't reorganize your feeds until it's completed importing. Refresh to see its progress.");
		});
	};

	$scope.loaded = function() {
		$scope.loading--;
	};

	$scope.http = function(method, url, data) {
		return $http({
			method: method,
			url: url,
			data: $.param(data || ''),
			headers: {'Content-Type': 'application/x-www-form-urlencoded'}
		});
	};

	$scope.addSubscription = function(e) {
		if (!$scope.addFeedUrl) {
			return false;
		}
		var btn = $(e.target);
		btn.button('loading')
		$scope.loading++;
		var f = $('#add-subscription-form');
		$scope.http('POST', f.attr('data-url'), {
			url: $scope.addFeedUrl
		}).then(function() {
			$scope.addFeedUrl = '';
			btn.button('reset')
			// I think this is needed due to the datastore's eventual consistency.
			// Without the delay we only get the feed data with no story data.
			$timeout(function() {
				$scope.refresh($scope.loaded);
			}, 250);
		}, function(data) {
			if (data.data) {
				alert(data.data);
			}
			$scope.loading--;
			btn.button('reset')
		});
	};

	$scope.procStory = function(xmlurl, story, read) {
		story.read = read;
		story.feed = $scope.xmlurls[xmlurl];
		story.guid = xmlurl + '|' + story.Id;
		if (!story.Title) {
			story.Title = '(title unknown)';
		}
		var today = new Date().toDateString();
		var d = new Date(story.Date * 1000);
		story.dispdate = moment(d).format(d.toDateString() == today ? "h:mm a" : "MMM D, YYYY");
	};

	$scope.clear = function() {
		$scope.feeds = [];
		$scope.numfeeds = 0;
		$scope.stories = [];
		$scope.unreadStories = {};
		$scope.last = 0;
		$scope.xmlurls = {};
	};

	$scope.update = function() {
		$scope.updateFolders();
		$scope.updateUnread();
		$scope.updateStories();
		$scope.updateTitle();
	};

	$scope.updateFolders = function() {
		_.each($scope.feeds, function(f, i) {
			if (f.Outline) {
				_.each(f.Outline, function(s) {
					s.folder = f.Title;
				});
			} else {
				delete f.folder;
			}
		});
	};

	$scope.refresh = function(cb) {
		$scope.loading++;
		$scope.shown = 'feeds';
		$scope.resetScroll();
		delete $scope.currentStory;
		$http.get($('#refresh').attr('data-url-feeds'))
			.success(function(data) {
				$scope.clear();
				$scope.feeds = data.Opml || $scope.feeds;
				$scope.icons = data.Icons;
				$scope.opts = data.Options ? JSON.parse(data.Options) : $scope.opts;

				var loadStories = function(feed) {
					$scope.numfeeds++;
					$scope.xmlurls[feed.XmlUrl] = feed;
					var stories = data.Stories[feed.XmlUrl] || [];
					for(var i = 0; i < stories.length; i++) {
						$scope.procStory(feed.XmlUrl, stories[i], false);
						if ($scope.last < stories[i].Date) {
							$scope.last = stories[i].Date;
						}
						stories[i].canUnread = true;
						$scope.stories.push(stories[i]);
						$scope.unreadStories[stories[i].guid] = true;
					}
				};

				for(var i = 0; i < $scope.feeds.length; i++) {
					var f = $scope.feeds[i];

					if (f.XmlUrl) {
						loadStories(f);
					} else if (f.Outline) {  // check for empty groups
						for(var j = 0; j < f.Outline.length; j++) {
							loadStories(f.Outline[j]);
							$scope.xmlurls[f.Outline[j].XmlUrl].folder = f.Title;
						}
					}
				}

				if (typeof cb === 'function') cb();
				$scope.loaded();
				$scope.update();
				$scope.resetLimit();
				setTimeout($scope.applyGetFeed);
			})
			.error(function() {
				if (typeof cb === 'function') cb();
				$scope.loaded();
			});
	};

	$scope.updateTitle = function() {
		var ur = $scope.unread['all'] || 0;
		document.title = 'go read' + (ur != 0 ? ' (' + ur + ')' : '');
	};

	$scope.setCurrent = function(i, noClose, isClick, $event, noOpen) {
		$scope.adTall = false;
		$scope.adBottom = false;
		var middleClick = $event && $event.which == 2;
		if ($event && !middleClick) {
			$event.preventDefault();
		}
		if (isClick && $scope.storyCollapse) {
			$scope.storyCollapse = false;
		}
		else if (!middleClick && !noClose && i == $scope.currentStory) {
			delete $scope.currentStory;
			$location.path('/');
			return;
		}
		var story = $scope.dispStories[i];
		$scope.getContents(story);
		if (i > 0) {
			$scope.getContents($scope.dispStories[i - 1]);
		}
		if (i < $scope.dispStories.length - 2) {
			$scope.getContents($scope.dispStories[i + 1]);
		}
		if ($scope.currentStory != i) {
			$timeout(function() {
				se = $('#storydiv' + i);
				var eTop = se.offset().top;
				if (!isClick || eTop < 0 || eTop > $('#story-list').height() || (isClick && !middleClick && !noOpen && $scope.opts.expanded)) {
					se[0].scrollIntoView();
				}
				$scope.adTall = se.width() > 650 + 10 + 160 && !$scope.collapsed;
				$scope.adBottom = !$scope.adTall && !$scope.collapsed;
			});<commit_msg>Prevent ad clearing when clicking on open story<commit_after>$('.dropdown-toggle').dropdown();

if ($('#messages').attr('data-show')) {
	$('#messages').modal();
}

function countProperties(obj) {
	var count = 0;
	for(var prop in obj) {
		if(obj.hasOwnProperty(prop))
			++count;
	}
	return count;
}

var goReadAppModule = angular.module('goReadApp', ['ui.sortable'])
	.filter('encodeURI', function() {
		return encodeURIComponent;
	}).config(function($locationProvider) {
		return $locationProvider.html5Mode(true);
	});
goReadAppModule.controller('GoreadCtrl', function($scope, $http, $timeout, $window, $location) {
	$scope.loading = 0;
	$scope.contents = {};
	$scope.opts = {
		folderClose: {},
		nav: true,
		expanded: false,
		mode: 'unread',
		sort: 'newest',
		hideEmpty: false,
		scrollRead: false
	};

	$scope.sortableOptions = {
		stop: function() {
			$scope.uploadOpml();
		}
	};

	$scope.importOpml = function() {
		$scope.shown = 'feeds';
		$scope.loading++;
		$('#import-opml-form').ajaxForm(function() {
			$('#import-opml-form')[0].reset();
			$scope.loaded();
			$scope.showMessage("OPML import is happening. It can take a minute. Don't reorganize your feeds until it's completed importing. Refresh to see its progress.");
		});
	};

	$scope.loaded = function() {
		$scope.loading--;
	};

	$scope.http = function(method, url, data) {
		return $http({
			method: method,
			url: url,
			data: $.param(data || ''),
			headers: {'Content-Type': 'application/x-www-form-urlencoded'}
		});
	};

	$scope.addSubscription = function(e) {
		if (!$scope.addFeedUrl) {
			return false;
		}
		var btn = $(e.target);
		btn.button('loading')
		$scope.loading++;
		var f = $('#add-subscription-form');
		$scope.http('POST', f.attr('data-url'), {
			url: $scope.addFeedUrl
		}).then(function() {
			$scope.addFeedUrl = '';
			btn.button('reset')
			// I think this is needed due to the datastore's eventual consistency.
			// Without the delay we only get the feed data with no story data.
			$timeout(function() {
				$scope.refresh($scope.loaded);
			}, 250);
		}, function(data) {
			if (data.data) {
				alert(data.data);
			}
			$scope.loading--;
			btn.button('reset')
		});
	};

	$scope.procStory = function(xmlurl, story, read) {
		story.read = read;
		story.feed = $scope.xmlurls[xmlurl];
		story.guid = xmlurl + '|' + story.Id;
		if (!story.Title) {
			story.Title = '(title unknown)';
		}
		var today = new Date().toDateString();
		var d = new Date(story.Date * 1000);
		story.dispdate = moment(d).format(d.toDateString() == today ? "h:mm a" : "MMM D, YYYY");
	};

	$scope.clear = function() {
		$scope.feeds = [];
		$scope.numfeeds = 0;
		$scope.stories = [];
		$scope.unreadStories = {};
		$scope.last = 0;
		$scope.xmlurls = {};
	};

	$scope.update = function() {
		$scope.updateFolders();
		$scope.updateUnread();
		$scope.updateStories();
		$scope.updateTitle();
	};

	$scope.updateFolders = function() {
		_.each($scope.feeds, function(f, i) {
			if (f.Outline) {
				_.each(f.Outline, function(s) {
					s.folder = f.Title;
				});
			} else {
				delete f.folder;
			}
		});
	};

	$scope.refresh = function(cb) {
		$scope.loading++;
		$scope.shown = 'feeds';
		$scope.resetScroll();
		delete $scope.currentStory;
		$http.get($('#refresh').attr('data-url-feeds'))
			.success(function(data) {
				$scope.clear();
				$scope.feeds = data.Opml || $scope.feeds;
				$scope.icons = data.Icons;
				$scope.opts = data.Options ? JSON.parse(data.Options) : $scope.opts;

				var loadStories = function(feed) {
					$scope.numfeeds++;
					$scope.xmlurls[feed.XmlUrl] = feed;
					var stories = data.Stories[feed.XmlUrl] || [];
					for(var i = 0; i < stories.length; i++) {
						$scope.procStory(feed.XmlUrl, stories[i], false);
						if ($scope.last < stories[i].Date) {
							$scope.last = stories[i].Date;
						}
						stories[i].canUnread = true;
						$scope.stories.push(stories[i]);
						$scope.unreadStories[stories[i].guid] = true;
					}
				};

				for(var i = 0; i < $scope.feeds.length; i++) {
					var f = $scope.feeds[i];

					if (f.XmlUrl) {
						loadStories(f);
					} else if (f.Outline) {  // check for empty groups
						for(var j = 0; j < f.Outline.length; j++) {
							loadStories(f.Outline[j]);
							$scope.xmlurls[f.Outline[j].XmlUrl].folder = f.Title;
						}
					}
				}

				if (typeof cb === 'function') cb();
				$scope.loaded();
				$scope.update();
				$scope.resetLimit();
				setTimeout($scope.applyGetFeed);
			})
			.error(function() {
				if (typeof cb === 'function') cb();
				$scope.loaded();
			});
	};

	$scope.updateTitle = function() {
		var ur = $scope.unread['all'] || 0;
		document.title = 'go read' + (ur != 0 ? ' (' + ur + ')' : '');
	};

	$scope.setCurrent = function(i, noClose, isClick, $event, noOpen) {
		var middleClick = $event && $event.which == 2;
		if ($event && !middleClick) {
			$event.preventDefault();
		}
		if (isClick && $scope.storyCollapse) {
			$scope.storyCollapse = false;
		}
		else if (!middleClick && !noClose && i == $scope.currentStory) {
			delete $scope.currentStory;
			$location.path('/');
			return;
		}
		var story = $scope.dispStories[i];
		$scope.getContents(story);
		if (i > 0) {
			$scope.getContents($scope.dispStories[i - 1]);
		}
		if (i < $scope.dispStories.length - 2) {
			$scope.getContents($scope.dispStories[i + 1]);
		}
		if ($scope.currentStory != i) {
			$scope.adTall = false;
			$scope.adBottom = false;
			$timeout(function() {
				se = $('#storydiv' + i);
				var eTop = se.offset().top;
				if (!isClick || eTop < 0 || eTop > $('#story-list').height() || (isClick && !middleClick && !noOpen && $scope.opts.expanded)) {
					se[0].scrollIntoView();
				}
				$scope.adTall = se.width() > 650 + 10 + 160 && !$scope.collapsed;
				$scope.adBottom = !$scope.adTall && !$scope.collapsed;
			});
{'fraction_non_alphanumeric': 0.19872347766085907, 'fraction_numerical': 0.0056925996204933585, 'mean_word_length': 3.2010869565217392, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '65001', 'n_tokens_mistral': 4965, 'n_tokens_neox': 4202, 'n_words': 813}