Commits

Michael Granger committed 1a0c677

Checkpoint commit

  • Participants
  • Parent commits 6d99755

Comments (0)

Files changed (6)

data/newznabr/apps/newznabr-rest-service

 
 	param :last_updated, lambda {|string| Time.parse(string) }
 
+
 	#
 	# API documentation
 	#
 	# api_description "A REST service for a NewzNabr index"
 
 
-end # class NewzNabr::Service
+end # class NewzNabr::RESTService
 
 Encoding.default_internal = Encoding::UTF_8
-NewzNabr::Service.run if __FILE__ == $0
+NewzNabr::RESTService.run if __FILE__ == $0
 

data/newznabr/static/js/newznabr.js

 
 
 /*
+    JavaScript Relative Time Helpers
+
+	Copyright (c) 2009 James F. Herdman
+
+	Permission is hereby granted, free of charge, to any person obtaining a copy
+	of this software and associated documentation files (the "Software"), to deal
+	in the Software without restriction, including without limitation the rights
+	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+	copies of the Software, and to permit persons to whom the Software is
+	furnished to do so, subject to the following conditions:
+
+	The above copyright notice and this permission notice shall be included in
+	all copies or substantial portions of the Software.
+
+	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+	THE SOFTWARE.
+*/
+
+/**
+ * Returns a description of this date in relative terms.
+
+ * Examples, where new Date().toString() == "Mon Nov 23 2009 17:36:51 GMT-0500 (EST)":
+ *
+ * new Date().toRelativeTime()
+ * --> 'Just now'
+ *
+ * new Date("Nov 21, 2009").toRelativeTime()
+ * --> '2 days ago'
+ *
+ * new Date("Nov 25, 2009").toRelativeTime()
+ * --> '2 days from now'
+ *
+ * // One second ago
+ * new Date("Nov 23 2009 17:36:50 GMT-0500 (EST)").toRelativeTime()
+ * --> '1 second ago'
+ *
+ * toRelativeTime() takes an optional argument - a configuration object.
+ * It can have the following properties:
+ * - now - Date object that defines "now" for the purpose of conversion.
+ *         By default, current date & time is used (i.e. new Date())
+ * - nowThreshold - Threshold in milliseconds which is considered "Just now"
+ *                  for times in the past or "Right now" for now or the immediate future
+ * - smartDays - If enabled, dates within a week of now will use Today/Yesterday/Tomorrow
+ *               or weekdays along with time, e.g. "Thursday at 15:10:34"
+ *               rather than "4 days ago" or "Tomorrow at 20:12:01"
+ *               instead of "1 day from now"
+ *
+ * If a single number is given as argument, it is interpreted as nowThreshold:
+ *
+ * // One second ago, now setting a now_threshold to 5 seconds
+ * new Date("Nov 23 2009 17:36:50 GMT-0500 (EST)").toRelativeTime(5000)
+ * --> 'Just now'
+ *
+ * // One second in the future, now setting a now_threshold to 5 seconds
+ * new Date("Nov 23 2009 17:36:52 GMT-0500 (EST)").toRelativeTime(5000)
+ * --> 'Right now'
+ *
+ */
+Date.prototype.toRelativeTime = (function() {
+
+  var _ = function(options) {
+    var opts = processOptions(options);
+
+    var now = opts.now || new Date();
+    var delta = now - this;
+    var future = (delta <= 0);
+    delta = Math.abs(delta);
+
+    // special cases controlled by options
+    if (delta <= opts.nowThreshold) {
+      return future ? 'Right now' : 'Just now';
+    }
+    if (opts.smartDays && delta <= 6 * MS_IN_DAY) {
+      return toSmartDays(this, now);
+    }
+
+    var units = null;
+    for (var key in CONVERSIONS) {
+      if (delta < CONVERSIONS[key])
+        break;
+      units = key; // keeps track of the selected key over the iteration
+      delta = delta / CONVERSIONS[key];
+    }
+
+    // pluralize a unit when the difference is greater than 1.
+    delta = Math.floor(delta);
+    if (delta !== 1) { units += "s"; }
+    return [delta, units, future ? "from now" : "ago"].join(" ");
+  };
+
+  var processOptions = function(arg) {
+    if (!arg) arg = 0;
+    if (typeof arg === 'string') {
+      arg = parseInt(arg, 10);
+    }
+    if (typeof arg === 'number') {
+      if (isNaN(arg)) arg = 0;
+      return {nowThreshold: arg};
+    }
+    return arg;
+  };
+
+  var toSmartDays = function(date, now) {
+    var day;
+    var weekday = date.getDay(),
+        dayDiff = weekday - now.getDay();
+    if (dayDiff == 0)       day = 'Today';
+    else if (dayDiff == -1) day = 'Yesterday';
+    else if (dayDiff == 1 && date > now)  day = 'Tomorrow';
+    else                    day = WEEKDAYS[weekday];
+    return day + " at " + date.toLocaleTimeString();
+  };
+
+  var CONVERSIONS = {
+    millisecond: 1, // ms    -> ms
+    second: 1000,   // ms    -> sec
+    minute: 60,     // sec   -> min
+    hour:   60,     // min   -> hour
+    day:    24,     // hour  -> day
+    month:  30,     // day   -> month (roughly)
+    year:   12      // month -> year
+  };
+  var MS_IN_DAY = (CONVERSIONS.millisecond * CONVERSIONS.second * CONVERSIONS.minute * CONVERSIONS.hour * CONVERSIONS.day);
+
+  var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
+
+  return _;
+
+})();
+
+
+
+/*
+ * Wraps up a common pattern used with this plugin whereby you take a String
+ * representation of a Date, and want back a date object.
+ */
+Date.fromString = function(str) {
+  return new Date(Date.parse(str));
+};
+
+
+/*
  * Navmenu
  */
 
 
 // Controllers
 angular.module('newznabr.controllers', ['newznabr.services']).
-	controller('DashboardController', ['$scope', 'Release',
-		function dashboardController($scope, Release) {
+	controller('DashboardController', ['$scope', 'Release', 'Binary',
+		function dashboardController($scope, Release, Binary) {
 			console.log( "Controller for Dashboard running." );
 			$scope.viewname = 'Dashboard';
-			$scope.newest_releases = Release.query({ limit: 15 });
+			// $scope.releases = Release.query({ limit: 15 });
+			$scope.binaries = Binary.query({ limit: 15 });
 		}
 	]).
-	controller('SettingsController', ['$scope', 'Group', 
-		function dashboardController($scope, Group) {
+	controller('SettingsController', ['$scope', '$log', 'Group', 
+		function dashboardController($scope, $log, Group) {
 			console.log( "Controller for Settings running." );
 			$scope.viewname = 'Settings';
-			$scope.groups = Group.query();
 
-			$scope.save = function save( group ) {
-				group.$update();
-			}
+			Group.query(function(groups) {
+				$log.log( "Got %d groups from the service.", groups.length );
+				$scope.activegroups = groups.filter(function(g) { return g.active; } );
+				$log.log( "  Set %d active groups.", $scope.activegroups.length );
+				$scope.inactivegroups = groups.filter(function(g) { return !g.active; } );
+				$log.log( "  Set %d inactive groups.", $scope.inactivegroups.length );
+			});
+
+			$scope.activate = function activate( group ) {
+				group.active = true;
+				group.$update( function() {
+					var i = $scope.inactivegroups.indexOf( group );
+					$scope.inactivegroups.splice( i, 1 );
+					$scope.activegroups.push( group );
+				});
+			};
+			$scope.deactivate = function deactivate( group ) {
+				group.active = false;
+				group.$update( function() {
+					var i = $scope.activegroups.indexOf( group );
+					$scope.activegroups.splice( i, 1 );
+					$scope.inactivegroups.push( group );
+				});
+			};
 		}
 	]);
 
 			} else if ( typeof date != 'Date' ) {
 				// Munge the date into one Firefox doesn't choke on. Ugh.
 				date = date.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1/$2/$3");
-				date = new Date(Date.parse(date));
+				date = Date.fromString( date );
 			}
 
 			return date.toRelativeTime();
 		'newznabr.filters',
 		'newznabr.services',
 		'newznabr.controllers',
-		'navmenu'
+		'navmenu',
+		'ngSanitize'
 	]).
 	config(['$routeProvider', function($routeProvider) {
 		console.debug( "Setting up route provider." );

data/newznabr/static/views/dashboard.html

 
 <div class="row">
 	<div class="large-12 columns">
-		<div id="slider">
-			<img src="http://placehold.it/1000x400&text=[ img 1 ]" />
-		</div>
-    
-		<hr />
+		<form action="#/search" method="get" accept-charset="utf-8">
+			<fieldset class="row collapse">
+			<div class="large-10 columns">
+				<input type="text" placeholder="keywords, titles, etc.">
+			</div>
+			<div class="large-2 columns">
+				<input class="button prefix" type="submit" value="Search" />
+			</div>
+			</fieldset>
+		</form>
 	</div>
 </div>
   
 <!-- Three-up Content Blocks -->
 
 <div class="row">
-	<div class="large-4 columns">
-		<img src="http://placehold.it/400x300&text=[img]" />
-		<h4>This is a content section.</h4>
-		<p>Bacon ipsum dolor sit amet nulla ham qui sint exercitation eiusmod commodo, chuck duis velit. Aute in reprehenderit, dolore aliqua non est magna in labore pig pork biltong. Eiusmod swine spare ribs reprehenderit culpa. Boudin aliqua adipisicing rump corned beef.</p>
+
+	<div class="large-12 columns">
+	<table id="release-results">
+		<thead>
+			<tr>
+			  <th>Name</th>
+			  <th>From</th>
+			  <th>Date</th>
+			  <th>Parts</th>
+			</tr>
+		</thead>
+		<tbody>
+			<tr ng-repeat="binary in binaries">
+			  <td>{{binary.name}}</td>
+			  <td>{{binary.fromname}}</td>
+			  <td>{{binary.date | reldate}}</td>
+			  <td>{{binary.total_parts}}</td>
+			</tr>
+		</tbody>
+	</table>
+	
 	</div>
-    
-	<div class="large-4 columns">
-		<img src="http://placehold.it/400x300&text=[img]" />
-		<h4>This is a content section.</h4>
-		<p>Bacon ipsum dolor sit amet nulla ham qui sint exercitation eiusmod commodo, chuck duis velit. Aute in reprehenderit, dolore aliqua non est magna in labore pig pork biltong. Eiusmod swine spare ribs reprehenderit culpa. Boudin aliqua adipisicing rump corned beef.</p>
-	</div>
-    
-	<div class="large-4 columns">
-		<img src="http://placehold.it/400x300&text=[img]" />
-		<h4>This is a content section.</h4>
-		<p>Bacon ipsum dolor sit amet nulla ham qui sint exercitation eiusmod commodo, chuck duis velit. Aute in reprehenderit, dolore aliqua non est magna in labore pig pork biltong. Eiusmod swine spare ribs reprehenderit culpa. Boudin aliqua adipisicing rump corned beef.</p>
-	</div>
-	
+    	
 </div>
     
 <!-- Call to Action Panel -->

data/newznabr/static/views/settings.html

 
 
 			<form>
-				<fieldset>
-					<legend>Activate</legend>
-					<div class="row checkbox-row" ng-repeat="group in groups">
-						
-						<div class="small-1 column">
-						  <input name="group-{{group.id}}-active" ng-model="group.active" 
-						  	type="checkbox" ng-change="save(group)" />
-						</div>
+				<fieldset class="activated">
+					<legend>Active</legend>
+					<table ng-show="activegroups">
+						<thead><tr><th>Name</th><th>Description</th></tr></thead>
+						<tbody>
+						<tr ng-repeat="group in activegroups | orderBy:'name'"
+							ng-click="deactivate(group)">
+							<td>{{group.name}}</td>
+							<td>{{group.description}}</td>
+						</tr>
+						</tbody>
+					</table>
+				</fieldset>
 
-						<div class="small-11 columns">{{group.name}} {{group | json}}</div>
-					</div>
+				<fieldset class="deactivated">
+					<legend>Deactivated</legend>
+					<table ng-show="inactivegroups">
+						<thead><tr><th>Name</th><th>Description</th></tr></thead>
+						<tbody>
+						<tr ng-repeat="group in inactivegroups | orderBy:'name'"
+							ng-click="activate(group)">
+							<td>{{group.name}}</td>
+							<td>{{group.description}}</td>
+						</tr>
+						</tbody>
+					</table>
 				</fieldset>
 			</form>
 		</div>

data/newznabr/templates/layout.tmpl

 	<script src="/js/vendor/foundation/foundation.topbar.js" defer="defer"></script>
 	<script src="/js/vendor/angular.min.js" defer="defer"></script>
 	<script src="/js/vendor/angular-resource.min.js" defer="defer"></script>
+	<script src="/js/vendor/angular-sanitize.min.js" defer="defer"></script>
 
     <?subscribe jslibs ?>
   

lib/newznabr/release.rb

 		text      :name, null: false, default: ''
 		text      :searchname, null: false, default: ''
 		int       :totalpart, default: 0
-		bigint   :size, null: false, default: 0
+		bigint    :size, null: false, default: 0
 		uuid      :guid, null: false
 		text      :fromname, default: nil
 		float     :completion, null: false, default: 0.0