From 40092dff62e1e3a394bbbc716b5eb76f3bc6719e Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Tue, 15 May 2012 07:48:36 -0700 Subject: [PATCH 01/10] Add wUp directive for handling keydowns I had to move almost all the logic into the root controller, which is not nice, but I will refractor it into a service later... --- FinalProject/index.html | 3 +- FinalProject/js/app.js | 220 +++++++++++++++++++--------------------- 2 files changed, 109 insertions(+), 114 deletions(-) diff --git a/FinalProject/index.html b/FinalProject/index.html index f28f330..a5b1874 100755 --- a/FinalProject/index.html +++ b/FinalProject/index.html @@ -30,7 +30,8 @@ - + diff --git a/FinalProject/js/app.js b/FinalProject/js/app.js index f46ee6a..46d4de7 100755 --- a/FinalProject/js/app.js +++ b/FinalProject/js/app.js @@ -80,7 +80,7 @@ function DataController($scope, $http, $filter) { // Load items from the server after we've loaded everything from the local // data store. $scope.getItemsFromServer(); - + //$scope.clearFilter(); // Show all items by default. }); }; @@ -179,6 +179,33 @@ function DataController($scope, $http, $filter) { return $scope.allItems.filter(function(val, i) { return val.starred }).length; }; + // Advances to the next item. + $scope.navDown = function(opt_delta) { + var delta = opt_delta || 1; + var selectedItem = $scope.selectedItem(); + var index = selectedItem.item != null ? selectedItem.index + delta : 0; + + if (0 <= index && index < $scope.items.length) { + $scope.selectItem(index); + } + }; + + // Goes back to the previous item. + $scope.navUp = function() { + $scope.navDown(-1); + }; + + // Click handler to toggle the selected items star status + $scope.toggleStar = function() { + $scope.$parent.toggleStar(); + }; + + // Click handler to toggle the selected items read status + $scope.toggleRead = function() { + $scope.$parent.toggleRead(); + }; + + // Click handler to mark all as read $scope.markAllRead = function() { // Iterate through all items, and set read=true in the data controller // then set read=true in the data store. @@ -188,6 +215,16 @@ function DataController($scope, $http, $filter) { }); }; + $scope.selectedItem = function() { + for (var i = 0, item; item = $scope.items[i]; ++i) { + if (item.selected == true) { + return {item: item, index: i}; + } + } + return {item: null, index: null}; + }; + + $scope.clearFilter = function() { $scope.items = $scope.allItems; }; @@ -198,36 +235,16 @@ function DataController($scope, $http, $filter) { }); }; - // Fetch items when the constructor is called. - $scope.getItemsFromDataStore(); -} - -DataController.$inject = ['$scope', '$http', '$filter']; // For JS compilers. - - -function ItemsController($scope) { //{, $location) { - // A special observer that will watch for when the 'selectedItem' is updated - // and ensure that we scroll into a view so that the selected item is visible - // in the summary list view. - $scope.$watch('selectedItem().index', function(newVal, oldVal, scope) { - // TODO: Performing scrolling like this doesn't seem very Angular-ly. - // Need the setTimeout to prevent race condition with item being selected. - if (newVal != null) { - window.setTimeout(function() { - var curScrollPos = $('.summaries').scrollTop(); - var itemTop = $('.summary.active').offset().top - 60; - $('.summaries').animate({'scrollTop': curScrollPos + itemTop}, 200); - }, 0); - } - }); - - $scope.selectedItem = function() { - for (var i = 0, item; item = $scope.$parent.items[i]; ++i) { - if (item.selected == true) { - return {item: item, index: i}; - } + $scope.handleSpace = function() { + var itemHeight = $('.entry.active').height() + 60; + var winHeight = $(window).height(); + var curScroll = $('.entries').scrollTop(); + var scroll = curScroll + winHeight; + if (scroll < itemHeight) { + $('.entries').scrollTop(scroll); + } else { + $scope.navDown(); } - return {item: null, index: null}; }; $scope.hasNext = function() { @@ -235,9 +252,9 @@ function ItemsController($scope) { //{, $location) { if (selectedItem.index == null) { return true; } - return selectedItem.index < $scope.$parent.items.length - 1; + return selectedItem.index < $scope.items.length - 1; }; - + $scope.hasPrev = function() { var selectedItem = $scope.selectedItem(); if (selectedItem.index == null) { @@ -256,7 +273,7 @@ function ItemsController($scope) { //{, $location) { } if (opt_idx != undefined) { - selectedItem = $scope.$parent.items[opt_idx]; + selectedItem = $scope.items[opt_idx]; selectedItem.selected = true; } else { this.item.selected = true; @@ -273,18 +290,7 @@ function ItemsController($scope) { //{, $location) { //history.pushState(item.get('item_id'), 'title', url + item_url); }; - // Advances to the next item. - $scope.next = function(opt_delta) { - var delta = opt_delta || 1; - - var selectedItem = $scope.selectedItem(); - $scope.selectItem(selectedItem.item != null ? selectedItem.index + delta : 0); - }; - // Goes back to the previous item. - $scope.prev = function() { - $scope.next(-1); - }; // Toggles or sets the read state with an optional boolean $scope.toggleRead = function(opt_read) { @@ -303,6 +309,33 @@ function ItemsController($scope) { //{, $location) { var key = selectedItem.item_id; store.toggleStar(key, star); }; + + // Fetch items when the constructor is called. + $scope.getItemsFromDataStore(); +} + +DataController.$inject = ['$scope', '$http', '$filter']; // For JS compilers. + + +function ItemsController($scope) { //{, $location) { + // A special observer that will watch for when the 'selectedItem' is updated + // and ensure that we scroll into a view so that the selected item is visible + // in the summary list view. + $scope.$watch('selectedItem().index', function(newVal, oldVal, scope) { + // TODO: Performing scrolling like this doesn't seem very Angular-ly. + // Need the setTimeout to prevent race condition with item being selected. + if (newVal != null) { + window.setTimeout(function() { + var curScrollPos = $('.summaries').scrollTop(); + var itemTop = $('.summary.active').offset().top - 60; + $('.summaries').animate({'scrollTop': curScrollPos + itemTop}, 200); + }, 0); + } + }); + + + + } ItemsController.$inject = ['$scope'];//, '$location']; // For JS compilers. @@ -310,7 +343,7 @@ ItemsController.$inject = ['$scope'];//, '$location']; // For JS compilers. // Top Menu/Nav Bar function NavBarController($scope) {//, $injector) { - //$injector.invoke(ItemsController, this, {$scope: $scope}); + //$injector.invoke(ItemsController, this, {$scope: $scope}); // Click handler for menu bar $scope.showAll = function() { @@ -345,86 +378,47 @@ NavBarController.$inject = ['$scope']; // For JS compilers. function NavControlsView($scope) { - // Click handler for up/previous button - $scope.navUp = function() { - $scope.$parent.prev(); - }; - - // Click handler for down/next button - $scope.navDown = function() { - $scope.$parent.next(); - }; - - // Click handler to toggle the selected items star status - $scope.toggleStar = function() { - $scope.$parent.toggleStar(); - }; - - // Click handler to toggle the selected items read status - $scope.toggleRead = function() { - $scope.$parent.toggleRead(); - }; - - // Click handler to mark all as read - $scope.markAllRead = function() { - $scope.$parent.markAllRead(); - }; - // Click handler for refresh $scope.refresh = function() { $scope.getItemsFromServer(); }; } +wReader.directive('wUp', function() { + return function(scope, elm, attr) { + elm.bind('keydown', function(e) { + switch (e.keyCode) { + case 34: // PgDn + case 39: // right arrow + case 40: // down arrow + case 74: // j + return scope.$apply(attr.wDown); + + case 32: // Space + e.preventDefault(); + return scope.$apply(attr.wSpace); + + case 33: // PgUp + case 37: // left arrow + case 38: // up arrow + case 75: // k + return scope.$apply(attr.wUp); + + case 85: // U + return scope.$apply(attr.wRead); + + case 72: // H + return scope.$apply(attr.wStar); + } + }); + }; +}); -function handleSpace() { - var itemHeight = $('.entry.active').height() + 60; - var winHeight = $(window).height(); - var curScroll = $('.entries').scrollTop(); - var scroll = curScroll + winHeight; - if (scroll < itemHeight) { - $('.entries').scrollTop(scroll); - } else { - $('.controls button[ng-click="navDown()"]').click(); - } -}; - -function handleBodyKeyDown(e) { - switch (e.keyCode) { - case 34: // PgDn - case 39: // right arrow - case 40: // down arrow - case 74: // j - $('.controls button[ng-click="navDown()"]').click(); // TODO: figure out angular way - break; - - case 32: // Space - handleSpace(); - e.preventDefault(); - break; - - case 33: // PgUp - case 37: // left arrow - case 38: // up arrow - case 75: // k - $('.controls button[ng-click="navUp()"]').click(); - break; - - case 85: // U - $('.controls button[ng-click="toggleRead()"]').click(); - break; - - case 72: // H - $('.controls button[ng-click="toggleStar()"]').click(); - break; - } -} function handlePopState(e) { //console.log("Pop State", e); } -document.body.addEventListener('keydown', handleBodyKeyDown, false); window.addEventListener('popstate', handlePopState, false); document.addEventListener('DOMContentLoaded', function(e) { From c672cbd3f5e64f37d3419754b461c7397a97dc87 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Tue, 15 May 2012 11:05:50 -0700 Subject: [PATCH 02/10] Refactor the model into a service --- FinalProject/index.html | 45 ++--- FinalProject/js/app.js | 319 +++--------------------------------- FinalProject/js/services.js | 249 ++++++++++++++++++++++++++++ 3 files changed, 292 insertions(+), 321 deletions(-) create mode 100644 FinalProject/js/services.js diff --git a/FinalProject/index.html b/FinalProject/index.html index a5b1874..660611d 100755 --- a/FinalProject/index.html +++ b/FinalProject/index.html @@ -30,12 +30,12 @@ - - +
-
+
- +
- - + - - +
-
@@ -95,7 +95,7 @@
-
+
{{item.pub_name}} @@ -110,22 +110,22 @@

{{item.title}}

-
+
-
{{selectedItem().item.pub_date | formattedDate}}
+
{{items.selected.pub_date | formattedDate}}
- - - + + +
-

{{selectedItem().item.title}}

- {{selectedItem().item.pub_author}} - {{selectedItem().item.pub_name}} +

{{items.selected.title}}

+ {{items.selected.pub_author}} - {{items.selected.pub_name}}
-

+

-
+
Sad Panda

Nothing selected.

@@ -160,10 +160,11 @@

About wReader

+ - + - @@ -125,7 +125,7 @@

{{items.selected.title}}

-
+
Sad Panda

Nothing selected.

@@ -161,6 +161,7 @@

About wReader

+ diff --git a/FinalProject/js/app.js b/FinalProject/js/app.js index b65ae81..9c66819 100755 --- a/FinalProject/js/app.js +++ b/FinalProject/js/app.js @@ -1,27 +1,7 @@ -var wReader = angular.module('wReader', ['wReader.filters', 'wReader.services']); +var wReader = angular.module('wReader', ['wReader.filters', 'wReader.services', 'wReader.directives']); - // config(['$locationProvider', function($locationProvider) { - // $locationProvider.html5Mode(true).hashPrefix('!'); - // }]); - // config(['$routeProvider', function($routeProvider) { - // //$routeProvider.when('/view1', {template: 'partials/partial1.html', controller: MyCtrl1}); - // //$routeProvider.when('/view2', {template: 'partials/partial2.html', controller: MyCtrl2}); - // $routeProvider.otherwise({redirectTo: '/'}); - // }]); -// wReader.factory('itemsService', function() { -// var items = []; - -// return { -// addItem: function(item) { -// items.push(item); -// } -// }; -// }); - - - -function DataController($scope, items, scroll) { +function AppController($scope, items, scroll) { $scope.items = items; @@ -56,7 +36,7 @@ function DataController($scope, items, scroll) { }); } -DataController.$inject = ['$scope', 'items', 'scroll']; // For JS compilers. +AppController.$inject = ['$scope', 'items', 'scroll']; // For JS compilers. // Top Menu/Nav Bar @@ -83,43 +63,6 @@ NavBarController.$inject = ['$scope', 'items']; // For JS compilers. -wReader.directive('wUp', function() { - return function(scope, elm, attr) { - elm.bind('keydown', function(e) { - switch (e.keyCode) { - case 34: // PgDn - case 39: // right arrow - case 40: // down arrow - case 74: // j - return scope.$apply(attr.wDown); - - case 32: // Space - e.preventDefault(); - return scope.$apply(attr.wSpace); - - case 33: // PgUp - case 37: // left arrow - case 38: // up arrow - case 75: // k - return scope.$apply(attr.wUp); - - case 85: // U - return scope.$apply(attr.wRead); - - case 72: // H - return scope.$apply(attr.wStar); - } - }); - }; -}); - - -function handlePopState(e) { - //console.log("Pop State", e); -} - -window.addEventListener('popstate', handlePopState, false); - document.addEventListener('DOMContentLoaded', function(e) { //On mobile devices, hide the address bar window.scrollTo(0); diff --git a/FinalProject/js/directives.js b/FinalProject/js/directives.js new file mode 100644 index 0000000..f15b9e4 --- /dev/null +++ b/FinalProject/js/directives.js @@ -0,0 +1,32 @@ +var directives = angular.module('wReader.directives', []); + + +directives.directive('wUp', function() { + return function(scope, elm, attr) { + elm.bind('keydown', function(e) { + switch (e.keyCode) { + case 34: // PgDn + case 39: // right arrow + case 40: // down arrow + case 74: // j + return scope.$apply(attr.wDown); + + case 32: // Space + e.preventDefault(); + return scope.$apply(attr.wSpace); + + case 33: // PgUp + case 37: // left arrow + case 38: // up arrow + case 75: // k + return scope.$apply(attr.wUp); + + case 85: // U + return scope.$apply(attr.wRead); + + case 72: // H + return scope.$apply(attr.wStar); + } + }); + }; +}); diff --git a/FinalProject/js/filters.js b/FinalProject/js/filters.js index 60fe232..9cda877 100644 --- a/FinalProject/js/filters.js +++ b/FinalProject/js/filters.js @@ -1,13 +1,15 @@ -/* http://docs-next.angularjs.org/api/angular.module.ng.$filter */ - -angular.module('wReader.filters', []). - filter('formattedDate', function() { - return function(d) { - return d ? moment(d).fromNow() : ''; - } - }). - filter('formattedFullDate', function() { - return function(d) { - return d ? moment(d).format('MMMM Do YYYY, h:mm a') : ''; - } - }); +var filters = angular.module('wReader.filters', []); + + +filters.filter('formattedDate', function() { + return function(d) { + return d ? moment(d).fromNow() : ''; + } +}); + + +filters.filter('formattedFullDate', function() { + return function(d) { + return d ? moment(d).format('MMMM Do YYYY, h:mm a') : ''; + } +}); From f42fdfc36b8be121380fecf68498e17d6e6845b7 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Tue, 15 May 2012 11:50:24 -0700 Subject: [PATCH 05/10] Reindex selected item when filter change --- FinalProject/js/services.js | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/FinalProject/js/services.js b/FinalProject/js/services.js index b8b473e..40ae820 100644 --- a/FinalProject/js/services.js +++ b/FinalProject/js/services.js @@ -180,29 +180,17 @@ services.factory('items', ['$http', 'store', 'filterFilter', function($http, sto }, - selectItem: function(opt_idx) { + selectItem: function(idx) { // Unselect previous selection. if (items.selected) { items.selected.selected = false; } - if (opt_idx != undefined) { - items.selected = items.filtered[opt_idx]; - items.selectedIdx = opt_idx; - items.selected.selected = true; - } else { - // this.item.selected = true; - // selectedItem = this.item; - } + items.selected = items.filtered[idx]; + items.selectedIdx = idx; + items.selected.selected = true; items.toggleRead(true); - - //TODO: Update the address bar - //$location.hash(selectedItem.item_id) - - //var url = location.origin + location.pathname + ''; - //var item_url = "" + item.get('item_id'); - //history.pushState(item.get('item_id'), 'title', url + item_url); }, @@ -236,11 +224,28 @@ services.factory('items', ['$http', 'store', 'filterFilter', function($http, sto items.filtered = filter(items.all, function(item) { return item[key] === value; }); + items.reindexSelectedItem(); }, clearFilter: function() { items.filtered = items.all; + items.reindexSelectedItem(); + }, + + + reindexSelectedItem: function() { + if (items.selected) { + var idx = items.filtered.indexOf(items.selected); + + if (idx === -1) { + items.selected.selected = false; + items.selected = null; + items.selectedIdx = null; + } else { + items.selectedIdx = idx; + } + } } }; From a6cc2e8b2e564e5a5eb9662b0f6c66be1bbde316 Mon Sep 17 00:00:00 2001 From: Vojta Jina Date: Tue, 15 May 2012 13:00:11 -0700 Subject: [PATCH 06/10] Move count methods to service as well --- FinalProject/index.html | 8 ++++---- FinalProject/js/app.js | 16 ---------------- FinalProject/js/services.js | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/FinalProject/index.html b/FinalProject/index.html index ed387b4..d246ee1 100755 --- a/FinalProject/index.html +++ b/FinalProject/index.html @@ -46,13 +46,13 @@