diff --git a/package.json b/package.json
index fc352ee..0c2eb78 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"reflect-metadata": "^0.1.0",
"rtts_assert": "2.0.0-alpha.34",
"rx": "^2.5.3",
+ "firebase": "^2.2.4",
"zone.js": "^0.5.2"
},
"devDependencies": {
@@ -68,6 +69,7 @@
"url-loader": "^0.5.5",
"webpack": "^1.10.5",
"webpack-dev-server": "^1.10.1",
- "webpack-stream": "^2.1.0"
+ "webpack-stream": "^2.1.0",
+ "moment": "^2.10.6"
}
}
diff --git a/src/app/components/example-modules/example-modules.ts b/src/app/components/example-modules/example-modules.ts
index 2876b5b..c023a34 100644
--- a/src/app/components/example-modules/example-modules.ts
+++ b/src/app/components/example-modules/example-modules.ts
@@ -7,6 +7,7 @@ import {RouteConfig, routerDirectives} from 'angular2/router';
import {Tictactoe} from './tictactoe-module/tictactoe';
import {Search} from './search-module/search';
import {Memory} from './memory-module/memory';
+import {HackerNews} from './hn-module/hn';
// View
let template = require('./example-modules.html');
let styles = require('./example-modules.css')
@@ -17,7 +18,8 @@ let styles = require('./example-modules.css')
{ path: '/', redirectTo: '/search' },
{ path: '/search', as: 'search', component: Search },
{ path: '/tictactoe', as: 'tictactoe', component: Tictactoe },
- { path: '/memory', as: 'memory', component: Memory }
+ { path: '/memory', as: 'memory', component: Memory },
+ { pathL '/hn', as: 'hn', component: HackerNews }
])
@View({
directives: [ routerDirectives, CSSClass ],
diff --git a/src/app/components/example-modules/hn-module/components/hn-item/hn-item.ts b/src/app/components/example-modules/hn-module/components/hn-item/hn-item.ts
new file mode 100644
index 0000000..ea157d8
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/components/hn-item/hn-item.ts
@@ -0,0 +1,70 @@
+import { Component, View, For, If, Switch, SwitchWhen, SwitchDefault } from 'angular2/angular2';
+import { HNApi } from '../../services/hn-api';
+import { timeAgo } from '../../../../../services/time';
+import { DomainPipe } from './pipes/domain.pipe';
+
+let view = require('./views/hn-item.html');
+let styles = require('./styles/hn-item.less');
+
+var hnApi;
+
+@Component({
+ selector: 'hn-item',
+ viewBindings: [HNApi],
+ properties: {
+ newItemId: 'itemId',
+ newLoadChildren: 'loadChildren',
+ newTopLevel: 'topLevel'
+ }
+})
+@View({
+ template: view,
+ styles: [ styles ],
+ directives: [
+ For,
+ If,
+ Switch,
+ SwitchWhen,
+ SwitchDefault
+ ]
+})
+export class HNItem {
+ data: Object;
+ domainPipe: Object;
+ loadChildren: boolean;
+ topLevel: boolean;
+ itemId;
+ timeAgo;
+
+
+ constructor(hnApiInstance: HNApi) {
+ this.domainPipe = DomainPipe.transform;
+
+ // Default value.
+ this.loadChildren = true;
+
+ // Make accessible in other methods.
+ hnApi = hnApiInstance;
+
+ this.timeAgo = timeAgo;
+ }
+
+ set newItemId(itemId) {
+ this.itemId = itemId;
+ this.fetchData();
+ }
+
+ set newLoadChildren(loadChildren) {
+ this.loadChildren = loadChildren === 'true';
+ }
+
+ set newTopLevel(topLevel) {
+ this.topLevel = topLevel === 'true';
+ }
+
+ fetchData() {
+ hnApi.fetchItem(this.itemId).then(data => {
+ this.data = data;
+ });
+ }
+}
diff --git a/src/app/components/example-modules/hn-module/components/hn-item/pipes/domain.ts b/src/app/components/example-modules/hn-module/components/hn-item/pipes/domain.ts
new file mode 100644
index 0000000..e3110fb
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/components/hn-item/pipes/domain.ts
@@ -0,0 +1,11 @@
+import { Pipes } from 'angular2/angular2';
+
+export class DomainPipe extends Pipes {
+ static transform(input) {
+ if (!input) {
+ return '';
+ }
+ var domain = input.split('/')[2];
+ return domain ? domain.replace('www.', '') : domain;
+ }
+}
diff --git a/src/app/components/example-modules/hn-module/components/hn-item/styles/hn-item.less b/src/app/components/example-modules/hn-module/components/hn-item/styles/hn-item.less
new file mode 100644
index 0000000..08e5b2e
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/components/hn-item/styles/hn-item.less
@@ -0,0 +1,33 @@
+hn-item {
+ display: block;
+ margin: 10px 0;
+}
+
+.hnItem-title {
+ font-size: 10pt;
+ color: #828282;
+}
+
+.hnItem > section {
+ font-size: 7pt;
+ &,
+ a:link,
+ a:visited {
+ color: #828282;
+ }
+ a:hover {
+ text-decoration: underline;
+ }
+}
+
+.hnItem--comment {
+ margin: 15px 0;
+}
+
+.hnItem--coment-content {
+ margin-top: 5px;
+}
+
+.hnItem--comment-children {
+ margin-left: 50px;
+}
diff --git a/src/app/components/example-modules/hn-module/components/hn-item/views/hn-item.html b/src/app/components/example-modules/hn-module/components/hn-item/views/hn-item.html
new file mode 100644
index 0000000..d893a78
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/components/hn-item/views/hn-item.html
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+ {{timeAgo(data.time)}}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/components/example-modules/hn-module/directives/home.ts b/src/app/components/example-modules/hn-module/directives/home.ts
new file mode 100644
index 0000000..86512d5
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/directives/home.ts
@@ -0,0 +1,28 @@
+import { Component, View, For } from 'angular2/angular2';
+import { HNApi } from '../services/hn-api';
+import { HNItem } from '../components/hn-item/hn-item';
+
+let view = require('../views/home.html');
+let styles = require('../styles/hn.less');
+
+@Component({
+ selector: 'page-home',
+ viewBindings: HNApi
+})
+@View({
+ directives: [
+ For,
+ HNItem
+ ],
+ styles: [ styles ],
+ template: view
+})
+export class HomePage {
+ topStories: Array;
+
+ constructor(public hnApi: HNApi) {
+ hnApi.fetchTopStories().then(() => {
+ this.topStories = hnApi.topStories;
+ });
+ }
+}
diff --git a/src/app/components/example-modules/hn-module/directives/item.ts b/src/app/components/example-modules/hn-module/directives/item.ts
new file mode 100644
index 0000000..e4494a0
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/directives/item.ts
@@ -0,0 +1,27 @@
+import { Component, View, For } from 'angular2/angular2';
+import { HNApi } from '../services/hn-api';
+import { HNItem } from '../../components/hn-item';
+
+let view = require('../views/item.html');
+let styles = require('../styles/hn.less');
+
+@Component({
+ selector: 'page-item',
+ viewBindings: HNApi
+})
+@View({
+ directives: [
+ For,
+ HNItem
+ ],
+ template: view,
+ styles: [ styles ]
+})
+export class ItemPage {
+ childrenIds: Array;
+ itemId;
+
+ constructor(public hnApi: HNApi, id) {
+ this.itemId = id;
+ }
+}
diff --git a/src/app/components/example-modules/hn-module/directives/user.ts b/src/app/components/example-modules/hn-module/directives/user.ts
new file mode 100644
index 0000000..9da63f9
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/directives/user.ts
@@ -0,0 +1,30 @@
+import { Component, View, For, If } from 'angular2/angular2';
+import { HNApi } from '../services/hn-api';
+import { timeAgo } from '../../../../../services/time';
+import { HNItem } from '../components/hn-item/hn-item';
+
+let view = require('../views/user.html');
+let styles = require('../styles/hn.less');
+
+@Component({
+ selector: 'page-user',
+ viewBindings: HNApi
+})
+@View({
+ directives: [
+ For,
+ If,
+ HNItem
+ ],
+ styles: [ styles ],
+ template: view
+})
+export class UserPage {
+ showSubmissions: boolean;
+ timeAgo;
+
+ constructor(hnApi: HNApi) {
+ this.timeAgo = timeAgo;
+ this.showSubmissions = false;
+ }
+}
diff --git a/src/app/components/example-modules/hn-module/hn.ts b/src/app/components/example-modules/hn-module/hn.ts
new file mode 100644
index 0000000..0292b84
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/hn.ts
@@ -0,0 +1,35 @@
+//import {zone} from 'zone.js';
+//window.zone = window.Zone = zone;
+
+let styles = require('./styles/hn.less');
+let view = require('/.views/hn.html');
+
+import {RouteConfig} from 'angular2/router';
+import {Component, View} from 'angular2/angular2';
+import {HomePage} from './directives/home'
+import {ItemPage} from './directives/item'
+import {UserPage} from './directives/user'
+import {HNItem} from './components/hn-item/hn-item';
+
+
+@Component({
+ selector: 'hacker-news'
+})
+@RouteConfig([
+ { path: '/', redirectTo: '/hn' },
+ { path: '/hn', as: 'hn', component: HackerNews },
+ { path: '/hn-item', as: 'hn-item', component: HNItem },
+])
+@View({
+ template: view,
+ styles: [ styles ],
+ directives: [
+ HomePage,
+ ItemPage,
+ UserPage
+ ]
+})
+export class HackerNews {
+
+}
+
diff --git a/src/app/components/example-modules/hn-module/services/hn-api.ts b/src/app/components/example-modules/hn-module/services/hn-api.ts
new file mode 100644
index 0000000..d2650d5
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/services/hn-api.ts
@@ -0,0 +1,78 @@
+import {Firebase} from 'firebase/firebase';
+import {Injectable} from 'angular2/angular2';
+
+const connection = new Firebase('https://hacker-news.firebaseio.com/v0/');
+
+@Injectable()
+export class HNApi {
+ itemStore: Object;
+ userStore: Object;
+ topStories: Array;
+
+ constructor() {
+ this.itemStore = {};
+ this.userStore = {};
+ }
+
+ fetchTopStories() {
+ return new Promise((resolve) => {
+ HNApi.topStoriesRef().once('value', snapshot => {
+ this.topStories = snapshot.val().splice(0, 20);
+
+ resolve(this.topStories);
+ });
+ });
+ }
+
+ fetchItems(items = []) {
+ return new Promise(resolve => {
+ let promises = [];
+
+ items.forEach(itemId => {
+ promises.push(new Promise((resolveItem) => {
+ HNApi.itemRef(itemId).on('value', value => {
+ this.itemStore[itemId] = value.val();
+
+ resolveItem(this.itemStore[itemId]);
+ });
+ }));
+ });
+
+ Promise.all(promises).then(resolve);
+ });
+ }
+
+ fetchItem(item) {
+ if (!item) {
+ return Promise.reject(item);
+ }
+
+ return this.fetchItems([item]).then(data => data[0]);
+ }
+
+ fetchUser(userId) {
+ if (!userId) {
+ return Promise.reject(userId);
+ }
+
+ return new Promise(resolve => {
+ HNApi.userRef(userId).on('value', value => {
+ this.userStore[userId] = value.val();
+
+ resolve(this.userStore[userId]);
+ });
+ });
+ }
+
+ static topStoriesRef() {
+ return connection.child('topstories/');
+ }
+
+ static itemRef(itemId) {
+ return connection.child('item/' + itemId);
+ }
+
+ static userRef(userId) {
+ return connection.child('user/' + userId);
+ }
+}
diff --git a/src/app/components/example-modules/hn-module/styles/hn.less b/src/app/components/example-modules/hn-module/styles/hn.less
new file mode 100644
index 0000000..72aff87
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/styles/hn.less
@@ -0,0 +1,115 @@
+input { font-family:Courier; font-size:10pt; color:#000000; }
+input[type=submit] { font-family:Verdana, Geneva, sans-serif; }
+textarea { font-family:Courier; font-size:10pt; color:#000000; }
+
+a:link { color:#000000; text-decoration:none; }
+a:visited { color:#828282; text-decoration:none; }
+
+.default { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:#828282; }
+.admin { font-family:Verdana, Geneva, sans-serif; font-size:8.5pt; color:#000000; }
+
+.adtitle { font-family:Verdana, Geneva, sans-serif; font-size: 9pt; color:#828282; }
+.yclinks { font-family:Verdana, Geneva, sans-serif; font-size: 8pt; color:#828282; }
+.pagetop { font-family:Verdana, Geneva, sans-serif; font-size: 10pt; color:#222222; }
+.comhead { font-family:Verdana, Geneva, sans-serif; font-size: 8pt; color:#828282; }
+.comment { font-family:Verdana, Geneva, sans-serif; font-size: 9pt; }
+.dead { font-family:Verdana, Geneva, sans-serif; font-size: 9pt; color:#dddddd; }
+
+.comment a:link, .comment a:visited { text-decoration:underline;}
+.dead a:link, .dead a:visited { color:#dddddd; }
+.pagetop a:visited { color:#000000;}
+.topsel a:link, .topsel a:visited { color:#ffffff; }
+
+.comhead a:link, .subtext a:visited { color:#828282; }
+.comhead a:hover { text-decoration:underline; }
+
+.default p { margin-top: 8px; margin-bottom: 0px; }
+
+.pagebreak {page-break-before:always}
+
+pre { overflow: auto; padding: 2px; max-width:600px; }
+pre:hover {overflow:auto}
+
+
+/**
+ * Real Styles
+ */
+
+.u-pointer {
+ cursor: pointer;
+}
+
+body {
+ font-family: Verdana, Geneva, sans-serif;
+ font-size: 10pt;
+ color: #828282;
+}
+
+.bodyContainer {
+ margin: 0 auto;
+ width: 85%;
+ max-width: 1280px;
+ background: #F6F6EF;
+}
+
+.headerBar {
+ background: #FF6600;
+}
+
+.userDetail {
+ margin: 10px 20px;
+}
+
+.userDetail-item {
+
+ &:first-of-type {
+ margin-top: 30px;
+ }
+}
+
+.itemDetail {
+ margin: 10px 20px;
+}
+
+.itemDetail-item {
+
+ &:first-of-type {
+ margin-top: 30px;
+ }
+}
+
+hn-item {
+ display: block;
+ margin: 10px 0;
+}
+
+.hnItem-title {
+ font-size: 10pt;
+ color:#828282;
+}
+
+.hnItem > section {
+ font-size: 7pt;
+
+ &,
+ a:link,
+ a:visited {
+ color: #828282;
+ }
+
+ a:hover {
+ text-decoration: underline;
+ }
+}
+
+.hnItem--comment {
+ margin: 15px 0;
+}
+
+.hnItem--coment-content {
+ margin-top: 5px;
+}
+
+.hnItem--comment-children {
+ margin-left: 50px;
+}
\ No newline at end of file
diff --git a/src/app/components/example-modules/hn-module/views/footer-bar.html b/src/app/components/example-modules/hn-module/views/footer-bar.html
new file mode 100644
index 0000000..cc139c9
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/views/footer-bar.html
@@ -0,0 +1,10 @@
+
diff --git a/src/app/components/example-modules/hn-module/views/header-bar.html b/src/app/components/example-modules/hn-module/views/header-bar.html
new file mode 100644
index 0000000..29b8a38
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/views/header-bar.html
@@ -0,0 +1,19 @@
+
diff --git a/src/app/components/example-modules/hn-module/views/hn.html b/src/app/components/example-modules/hn-module/views/hn.html
new file mode 100644
index 0000000..e34488b
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/views/hn.html
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/app/components/example-modules/hn-module/views/home.html b/src/app/components/example-modules/hn-module/views/home.html
new file mode 100644
index 0000000..2792aac
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/views/home.html
@@ -0,0 +1,7 @@
+
diff --git a/src/app/components/example-modules/hn-module/views/item.html b/src/app/components/example-modules/hn-module/views/item.html
new file mode 100644
index 0000000..be651b7
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/views/item.html
@@ -0,0 +1,6 @@
+
diff --git a/src/app/components/example-modules/hn-module/views/user.html b/src/app/components/example-modules/hn-module/views/user.html
new file mode 100644
index 0000000..47ffb2d
--- /dev/null
+++ b/src/app/components/example-modules/hn-module/views/user.html
@@ -0,0 +1,18 @@
+
diff --git a/src/app/components/example-modules/search-module/services/github.ts b/src/app/components/example-modules/search-module/services/github.ts
index 890338c..4ccd7ae 100644
--- a/src/app/components/example-modules/search-module/services/github.ts
+++ b/src/app/components/example-modules/search-module/services/github.ts
@@ -25,7 +25,6 @@ export class Github implements ISearchable {
}
-
export var githubInjectables = [
bind(Github).toClass(Github)
];
diff --git a/src/app/services/time.ts b/src/app/services/time.ts
new file mode 100644
index 0000000..ff789c8
--- /dev/null
+++ b/src/app/services/time.ts
@@ -0,0 +1,5 @@
+import {moment} from 'moment/moment';
+
+export function timeAgo(time) {
+ return moment(time * 1000).fromNow();
+}