Skip to content

Commit

Permalink
Add wUp directive for handling keydowns
Browse files Browse the repository at this point in the history
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...
  • Loading branch information
vojtajina committed May 15, 2012
1 parent f346129 commit 40092df
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 114 deletions.
3 changes: 2 additions & 1 deletion FinalProject/index.html
Expand Up @@ -30,7 +30,8 @@

<base target="_blank"> <!-- This amazingness opens all links in a new tab. -->
</head>
<body ng-controller="DataController" class="ng-cloak">
<body ng-controller="DataController" class="ng-cloak" w-down="navDown()" w-up="navUp()"
w-read="toggleRead()" w-star="toggleStar()" w-space="handleSpace()">
<!-- Prompt IE 6 users to install Chrome Frame. Remove this if you support IE 6.
chromium.org/developers/how-tos/chrome-frame-getting-started -->
<!--[if lt IE 7]><p class=chromeframe>Your browser is <em>ancient!</em> <a href="http://browsehappy.com/">Upgrade to a different browser</a> or <a href="http://www.google.com/chromeframe/?redirect=true">install Google Chrome Frame</a> to experience this site.</p><![endif]-->
Expand Down
220 changes: 107 additions & 113 deletions FinalProject/js/app.js
Expand Up @@ -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.
});
};
Expand Down Expand Up @@ -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.
Expand All @@ -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;
};
Expand All @@ -198,46 +235,26 @@ 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() {
var selectedItem = $scope.selectedItem();
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) {
Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -303,14 +309,41 @@ 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.
//ItemsController.prototype = Object.create(DataController.prototype);

// 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() {
Expand Down Expand Up @@ -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() {

This comment has been minimized.

Copy link
@esprehn

esprehn May 15, 2012

This is really weird since w-up actually binds lots of keyboard handling that's not "up". I'm not sure this follows best practice you'd want in a demo app. Feels like a hack.

This comment has been minimized.

Copy link
@vojtajina

vojtajina May 16, 2012

Author Owner

Yep, I know :-D

I could either add another directive (say w-keydown) or have five separate directives (but then I would have to register five keydown listeners, which I don't wanna). So I'm changing to first one.

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) {
Expand Down

0 comments on commit 40092df

Please sign in to comment.