-
Notifications
You must be signed in to change notification settings - Fork 173
Code Walkthrough
There are three main parts in Droptiles:
- Dashboard
- App Store
- Login, Signup, Settings.
Dashboard is the home page, that shows the tiles.
App Store shows a collection of apps available for users to add on the Dashboard.
Login, Signup, Settings areas are built using ASP.NET and uses ASP.NET Membership provider.
Dashboard comprises of Sections. Each section contanis a collection of tiles. Each box you see are Tiles. Tiles are mini apps. A tile can be of the following type:
- Simple html pages
- A dynamic Javascript mini-App
- Dynamic page
Let's look at the Flickr tile. First of all, all the tiles appearance are defined in the Tiles.js
file, which contains the meta data for all the tiles. For example, the Flickr tile metadata is defined as following:
flickr: function (uniqueId) {
return {
uniqueId: uniqueId,
name: "flickr",
iconSrc: "img/Flickr alt 1.png",
label: "Flickr",
size: "tile-double tile-double-vertical",
color: "bg-color-darken",
appUrl: "http://www.flickr.com/",
cssSrc: ["tiles/flickr/flickr.css"],
scriptSrc: ["tiles/flickr/flickr.js"],
initFunc: "flickr_load"
};
}
Then icon is the default icon shown on the tile while the javascript, css, html are loaded. The horizontal and vertical size of the tile is defined in the size
attribute. Then when clicked, what URL to host in the full screen view is defined in appUrl
. The additional CSS, Javascripts to load are defined in cssSrc
and scriptSrc
. Finally the initFunc
tells what function to invoke once the javascripts are loaded.
The metadat defines how the tile is displayed on the Dashboard. The behavior to load data from Flickr comes from the Flickr.js
file, defined as follows:
function flickr_load(tile, div) {
var url = "http://api.flickr.com/services/feeds/photos_public.gne?lang=en-us&format=json&tags=nature&jsoncallback=?";
$.getJSON(url, function (data) {
var ctr = 0;
$.each(data.items.reverse(), function (i, item) {
if (item.tags.length < 150) {
var sourceSquare = item.media.m;
var sourceOrig = (item.media.m).replace("_m.jpg", ".jpg");
var htmlString = '<div class="flickr_item"> <a target="_blank" href="' + sourceOrig +
'" class="link" title="' + item.title + '">';
htmlString += '<img title="' + item.title +
'" src="' + sourceSquare + '" ';
htmlString += 'alt="' + item.title +
'" />';
htmlString += '</a><div class="flickr_title">' + item.title + '</div>' +
'</div>';
tile.slides.push(htmlString);
ctr = ctr + 1;
}
});
tile.counter(ctr);
});
}
That's it.
The way Dashboard works is:
- First get the list of Sections and the tiles in each section.
- Create Tile boxes as per the definition stored in Tiles.js file.
- For each Tile, see if the Tile has any external Javascript, CSS and html files to load. If yes, then load them.
- Execute the function defined in the
initFunc
. Pass the tile object, the Tile div reference and theinitParams
to it.
The default tiles shown on Dashboard are defined in the same Tiles.js
file as following:
window.DefaultTiles = [
{
name :"Section1",
tiles: [
{ id: "flickr1", name:"flickr" },
{ id: "amazon1", name:"amazon" },
{ id: "news1", name: "news" },
{ id: "weather1", name: "weather" },
{ id: "calendar1", name: "calendar" },
{ id: "feature1", name: "feature" },
{ id: "facebook1", name: "facebook" }
]
},
{
name: "Section2",
tiles: [
{ id: "wikipedia1", name: "wikipedia" },
{ id: "email1", name: "email" },
{ id: "maps1", name: "maps" },
{ id: "angrybirds1", name: "angrybirds" },
{ id: "cuttherope1", name: "cutTheRope" },
{ id: "dynamicTile1", name: "dynamicTile" },
{ id: "buy1", name: "buy" }]
},
{
name: "Section3", tiles: [
{ id: "youtube1", name: "youtube" },
{ id: "ie1", name: "ie" },
]
}
];
Tiles are shown in the exact order they are fined. The name has to be the same name used to define the Tile metadata.
There are various parts of a tile:
The following HTML defines the tile design:
<div class="metro-sections" data-bind="foreach: sections">
<div class="metro-section" data-bind="attr: {id : uniqueId}, foreach: sortedTiles">
<div data-bind="attr: { id: uniqueId, 'class': tileClasses }">
<!-- ko if: tileImage -->
<div class="tile-image">
<img data-bind="attr: { src: tileImage }" src="img/Internet%20Explorer.png" />
</div>
<!-- /ko -->
<!-- ko if: iconSrc -->
<!-- ko if: slides().length == 0 -->
<div data-bind="attr: { 'class': iconClasses }">
<img data-bind="attr: { src: iconSrc }" src="img/Internet%20Explorer.png" />
</div>
<!-- /ko -->
<!-- /ko -->
<div data-bind="foreach: slides">
<div class="tile-content-main">
<div data-bind="html: $data">
</div>
</div>
</div>
<!-- ko if: label -->
<span class="tile-label" data-bind="html: label">Label</span>
<!-- /ko -->
<!-- ko if: counter -->
<span class="tile-counter" data-bind="html: counter">10</span>
<!-- /ko -->
<!-- ko if: subContent -->
<div data-bind="attr: { 'class': subContentClasses }, html: subContent">
subContent
</div>
<!-- /ko -->
</div>
</div>
</div>
The markup is full of KnockoutJS markups which is used to bind the Tile object model to the html markup.
Here's a sequence diagram that shows how the Dashboard loads:
The code in Dashboard.js starts like this:
var viewModel = new DashboardModel("Start", [], window.currentUser, ui, TileBuilders);
$(document).ready(function () {
// Hide the body area until it is fully loaded in order to prevent flickrs
$('#content').css('visibility', 'visible');
// Initiate KnockoutJS binding which creates all the tiles and binds the whole
// UI to viewModel.
ko.applyBindings(viewModel);
// See if user has a previous session where page setup was stored
var cookie = readCookie("p");
if (cookie != null && cookie.length > 0) {
try {
viewModel.loadSectionsFromString(cookie);
} catch (e) {
// Failed to load saved tiles. Load the default tiles.
viewModel.loadSectionsFromString(DefaultTiles);
}
}
else {
// No cookie, load default tiles. Defined in Tiles.js
viewModel.loadSectionsFromString(DefaultTiles);
}
First it tries to read the section and tiles setup from the cookie, if it is saved. If not found in cookie, it loads the default definition.
The code is heavily documented, so you can read the details from there.
The App Store experience is built the same way as the Dashboard. The App Store shows how reusable the Droptiles framework is. It uses the same TheCore.js
to provide the experience. All it does different is instead of Tiles.js
which defines the tile metadata and the default tiles, it has its own AppStoreTiles.js
that defines the Tile metadata and the default tiles to show on the App Store. That's all that differs.
Here's the code in AppStore.js
:
var viewModel = new DashboardModel("App Store", [], window.currentUser, ui, TileBuilders);
$(document).ready(function () {
// Hide the body area until it is fully loaded in order to prevent flickrs
$('#content').css('visibility', 'visible');
// Initiate KnockoutJS binding which creates all the tiles and binds the whole
// UI to viewModel.
ko.applyBindings(viewModel);
viewModel.switchTheme('theme-white');
viewModel.loadSectionsFromString(window.AppStoreTiles);
That's it. The loadSectionsFromString
function takes care of creating the AppStore Tiles. It takes the section and tile configuration in a string serialized format, like this:
Section1~flickr1,flickr.news1,news|Section2~angrybirds1,angrybirds
This is how it is stored in the cookie as well so that next time you visit the Dashboard, it shows you how you have left it.