<title>Knockout Demo</title>
<script type="text/javascript" src="jquery-1.6.4.min.js"></script>
<script type="text/javascript" src="jquery.tmpl.js"></script>
<script type="text/javascript" src="knockout.js"></script>
border: #FFFF00 3px solid;
<script type="text/javascript">
window.fbAsyncInit = function() {
appId : '248684061845315', // App ID
status : true, // check login status
cookie : true, // enable cookies to allow the server to access the session
oauth : true, // enable OAuth 2.0
xfbml : true // parse XFBML
FB.login(function(response) {
if (response.authResponse) {
FB.api('/me', function(response) {
$(document).ready(function() { initApp(response) });
}, {scope: 'user_photos'});
// Load the SDK Asynchronously
var js, id = 'facebook-jssdk'; if (d.getElementById(id)) {return;}
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "https://connect.facebook.net/en_US/all.js";
d.getElementsByTagName('head')[0].appendChild(js);
<div id="resultPane" style="display: none">
<button onclick="location.reload()">Refresh</button>
Photos of <span class="name" data-bind="text: userName"></span>
<select data-bind="options: albums, optionsCaption: 'Tagged Photos', optionsText: 'name', value: currentAlbum"></select>
<div data-bind="visible: photos().length === 0">There are no photos in this album</div>
<ul data-bind="template: {name: 'photosTemplate', foreach: photos, templateOptions: { viewModel: _self }}">
<div data-bind="visible: loadingPhotos">Loading...</div>
<button data-bind="click: function() { currentPage(currentPage()-1); }, enable: currentPage() > 0"><</button>
<button data-bind="click: function() { currentPage(currentPage()+1); }, enable: nextPage">></button>
<button data-bind="click: submitImage, enable: currentPhoto">Submit</button>
<script type="text/html" id="photosTemplate">
<li data-bind="click: function() { $item.viewModel.selectPhoto($data); }"><img data-bind="css: { selected: $item.viewModel.currentPhoto() === $data }" src="${picture}"/></li>
<script type="text/javascript">
loadingPhotos: ko.observable(true),
albums: ko.observableArray(),
photos: ko.observableArray(),
currentPage: ko.observable(0),
nextPage: ko.observable(false),
currentPhoto: ko.observable(undefined),
currentAlbum: ko.observable(undefined),
selectPhoto: function(photo) {
viewModel.currentPhoto(photo);
submitImage: function() {
if (viewModel.currentPhoto()) {
$('#resultImage').attr('src', viewModel.currentPhoto().source);
viewModel._self = viewModel; //Hack for template foreach
ko.dependentObservable(function() {
if (viewModel.currentAlbum()) {
id = viewModel.currentAlbum().id;
viewModel.loadingPhotos(true);
FB.api(id + '/photos', {limit: 11, offset: viewModel.currentPage() * 10}, function(response) {
var truncatedResponse = response.data;
if (truncatedResponse.length > 10) {
truncatedResponse.pop(); //remove the excess item
viewModel.nextPage(true);
viewModel.nextPage(false);
viewModel.photos(response.data);
viewModel.loadingPhotos(false);
ko.dependentObservable(function() {
viewModel.currentAlbum(); //on currentalbum changed
viewModel.currentPage(0);
FB.api('me/albums', function(response) {
viewModel.albums(response.data);
ko.applyBindings(viewModel, $('#container')[0]);