├── public ├── api │ ├── StubResponses │ ├── resources.json │ └── dashboard │ │ ├── 1.json │ │ └── 2.json ├── css │ └── style.css ├── index.html └── lib │ └── css3tips │ └── css3-tips.css ├── .bowerrc ├── src ├── page │ ├── apps │ │ ├── AppsPage.html │ │ ├── AppsPage.ts │ │ └── AppsPage.js │ ├── IPage.d.ts │ ├── dashboard │ │ ├── DashboardPage.less │ │ ├── DashboardPage.html │ │ ├── DashboardPage.ts │ │ └── DashboardPage.js │ ├── home │ │ ├── HomePage.html │ │ ├── HomePage.less │ │ ├── HomePage.ts │ │ └── HomePage.js │ └── error │ │ ├── ErrorPage.html │ │ ├── ErrorPage.ts │ │ └── ErrorPage.js ├── parts │ ├── header-nav │ │ ├── HeaderNav.html │ │ ├── HeaderNav.less │ │ ├── HeaderNav.js │ │ └── HeaderNav.ts │ ├── widget │ │ ├── Widget.html │ │ ├── Widget.ts │ │ ├── Widget.js │ │ ├── Widget.less │ │ ├── WidgetHolder.ts │ │ ├── WidgetHolder.js │ │ └── WidgetHolder.less │ ├── side-nav │ │ ├── SideNav.html │ │ ├── SideNav.ts │ │ ├── SideNav.js │ │ └── SideNav.less │ ├── MenuItem.js │ ├── MenuItem.ts │ └── directory-tree │ │ ├── DirectoryTree.less │ │ ├── DirectoryTree.html │ │ ├── DirectoryTree.js │ │ └── DirectoryTree.ts ├── references.d.ts ├── package.json ├── tsd.json ├── webpack.config.js ├── Application.ts └── Application.js ├── init.sh ├── .gitignore ├── package.json ├── dashboard.iml ├── bower.json ├── README.md └── server.js /public/api/StubResponses: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "public/lib" 3 | } -------------------------------------------------------------------------------- /src/page/apps/AppsPage.html: -------------------------------------------------------------------------------- 1 |

{{title}}

2 | -------------------------------------------------------------------------------- /src/page/IPage.d.ts: -------------------------------------------------------------------------------- 1 | interface IPage{ 2 | title:string; 3 | component:string; 4 | icon?:string; 5 | } -------------------------------------------------------------------------------- /src/page/dashboard/DashboardPage.less: -------------------------------------------------------------------------------- 1 | .dashboard-page{ 2 | .control{ 3 | margin: 0 10px; 4 | } 5 | } -------------------------------------------------------------------------------- /src/page/home/HomePage.html: -------------------------------------------------------------------------------- 1 |

{{title}}

2 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | npm install 2 | bower install 3 | 4 | pushd src 5 | npm install 6 | tsd reinstall 7 | webpack 8 | popd 9 | -------------------------------------------------------------------------------- /src/page/home/HomePage.less: -------------------------------------------------------------------------------- 1 | .home-page{ 2 | dl{ 3 | dt{ 4 | span{ 5 | min-width: 6em; 6 | display: inline-block; 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /src/page/error/ErrorPage.html: -------------------------------------------------------------------------------- 1 |

{{code}}

2 |

{{message}}

3 | back -------------------------------------------------------------------------------- /src/parts/header-nav/HeaderNav.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | {{$root.pages[$root.page].title}} 10 | 11 | -------------------------------------------------------------------------------- /src/parts/header-nav/HeaderNav.less: -------------------------------------------------------------------------------- 1 | header-nav{ 2 | display:block; 3 | background:#2b3d51; 4 | color: #ccc; 5 | 6 | .btn{ 7 | &:hover{ 8 | color:white; 9 | } 10 | } 11 | 12 | ul{ 13 | margin:0; 14 | padding:0; 15 | list-style:none; 16 | } 17 | } -------------------------------------------------------------------------------- /src/parts/widget/Widget.html: -------------------------------------------------------------------------------- 1 |
2 | {{title}} 3 |
4 |
5 | body 6 |
7 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /node_modules 3 | /src/node_modules 4 | /src/typings 5 | /public/lib/jquery 6 | /public/lib/knockout 7 | /public/lib/knockout.punches 8 | /public/lib/knockout-else 9 | /public/lib/knockout-es5 10 | /public/lib/bootstrap 11 | /public/lib/fontawesome 12 | /public/lib/gridster 13 | /public/lib/jquery-ui 14 | /public/js/bundle.js -------------------------------------------------------------------------------- /src/page/dashboard/DashboardPage.html: -------------------------------------------------------------------------------- 1 |

{{title}}

2 |
3 | {{#if: message}} 4 | 5 | {{/if}} 6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /public/api/resources.json: -------------------------------------------------------------------------------- 1 | { 2 | "disks":[ 3 | { 4 | "filesystem": "/dev/vda1", 5 | "used":33505, 6 | "available": 436789 7 | }, 8 | { 9 | "filesystem": "/dev/vdb", 10 | "used":188160, 11 | "available": 78187440 12 | } 13 | ], 14 | "memory":{ 15 | "used":311800, 16 | "free":708616 17 | }, 18 | "swap":{ 19 | "used":123456, 20 | "free":2064376 21 | } 22 | } -------------------------------------------------------------------------------- /src/references.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | 5 | interface KnockoutStatic{ 6 | punches:any; 7 | track:any; 8 | } 9 | interface JQuery{ 10 | gridster:any; 11 | } 12 | 13 | declare var KnockoutElse:{ 14 | init:any; 15 | }; 16 | 17 | /* 18 | declare class Router{ 19 | addRoute:any; 20 | errors:any; 21 | run:any; 22 | } 23 | */ 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node server" 7 | }, 8 | "dependencies": { 9 | "body-parser": "~1.8.1", 10 | "cookie-parser": "~1.3.3", 11 | "debug": "~2.0.0", 12 | "express": "~4.9.0", 13 | "jade": "~1.6.0", 14 | "morgan": "~1.3.0", 15 | "serve-favicon": "~2.1.3" 16 | }, 17 | "devDependencies": { 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/parts/side-nav/SideNav.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dashboard.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashboard", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "ignore": [ 6 | "**/.*", 7 | "node_modules", 8 | "bower_components", 9 | "public/js/lib", 10 | "test", 11 | "tests" 12 | ], 13 | "dependencies": { 14 | "bootstrap": "~3.2.0", 15 | "jquery": "~2.1.1", 16 | "fontawesome": "~4.2.0", 17 | "knockout": "~3.2.0", 18 | "knockout.punches": "~0.5.0", 19 | "knockout-es5": "*", 20 | "knockout-else": "~1.0.11", 21 | "jquery-ui": "~1.11.2", 22 | "gridster": "~0.5.6" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/parts/MenuItem.js: -------------------------------------------------------------------------------- 1 | /// 2 | var MenuItem = (function () { 3 | function MenuItem(arg1, arg2, arg3) { 4 | if (typeof arg1 == "string") { 5 | this.label = arg1; 6 | this.icon = arg2; 7 | this.href = arg3; 8 | } 9 | else { 10 | this.label = arg1.title; 11 | this.icon = arg1.icon; 12 | this.href = arg2; 13 | } 14 | ko.track(this); 15 | } 16 | return MenuItem; 17 | })(); 18 | module.exports = MenuItem; 19 | -------------------------------------------------------------------------------- /src/parts/header-nav/HeaderNav.js: -------------------------------------------------------------------------------- 1 | var HeaderNav = (function () { 2 | function HeaderNav(sideNav) { 3 | this.sideNav = sideNav; 4 | ko.track(this); 5 | } 6 | return HeaderNav; 7 | })(); 8 | require('./HeaderNav.less'); 9 | ko.components.register('header-nav', { 10 | template: require('./HeaderNav.html'), 11 | viewModel: { 12 | createViewModel: function (params, componentInfo) { 13 | return params instanceof HeaderNav ? params : ko.unwrap(params.option); 14 | } 15 | } 16 | }); 17 | module.exports = HeaderNav; 18 | -------------------------------------------------------------------------------- /public/api/dashboard/1.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "First", 3 | "widgets": [ 4 | {"type": "text","data": "A","size_x": 1,"size_y":1,"col":1,"row":1}, 5 | {"type": "text","data": "B","size_x": 1,"size_y":1,"col":2,"row":1}, 6 | {"type": "text","data": "C","size_x": 1,"size_y":1,"col":3,"row":1}, 7 | {"type": "text","data": "D","size_x": 1,"size_y":3,"col":4,"row":1}, 8 | {"type": "text","data": "E","size_x": 1,"size_y":1,"col":1,"row":2}, 9 | {"type": "text","data": "F","size_x": 1,"size_y":1,"col":2,"row":2}, 10 | {"type": "text","data": "G","size_x": 3,"size_y":1,"col":1,"row":3} 11 | ] 12 | } -------------------------------------------------------------------------------- /public/api/dashboard/2.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Second", 3 | "widgets": [ 4 | {"type": "text","data": "1","size_x": 1,"size_y":1,"col":1,"row":1}, 5 | {"type": "text","data": "2","size_x": 1,"size_y":1,"col":2,"row":1}, 6 | {"type": "text","data": "3","size_x": 1,"size_y":2,"col":3,"row":1}, 7 | {"type": "text","data": "4","size_x": 1,"size_y":3,"col":4,"row":1}, 8 | {"type": "text","data": "5","size_x": 1,"size_y":1,"col":1,"row":2}, 9 | {"type": "text","data": "6","size_x": 1,"size_y":1,"col":2,"row":2}, 10 | {"type": "text","data": "7","size_x": 3,"size_y":1,"col":1,"row":3} 11 | ] 12 | } -------------------------------------------------------------------------------- /src/page/apps/AppsPage.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | class AppsPage implements IPage{ 4 | public title ="Apps"; 5 | public component="apps-page"; 6 | public icon = "bookmark-o"; 7 | 8 | constructor( 9 | ){ 10 | ko.track(this); 11 | } 12 | } 13 | export = AppsPage; 14 | 15 | 16 | //require('./AppsPage.less'); 17 | ko.components.register('apps-page',{ 18 | template: require('./AppsPage.html'), 19 | viewModel:{ 20 | createViewModel(params, componentInfo){ 21 | return params instanceof AppsPage ? params : params.option; 22 | } 23 | } 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /src/parts/header-nav/HeaderNav.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import MenuItem = require('../MenuItem'); 3 | import SideNav = require('../side-nav/SideNav'); 4 | 5 | class HeaderNav{ 6 | constructor( 7 | private sideNav:SideNav 8 | ){ 9 | ko.track(this); 10 | } 11 | } 12 | export = HeaderNav; 13 | 14 | require('./HeaderNav.less'); 15 | ko.components.register('header-nav',{ 16 | template: require('./HeaderNav.html'), 17 | viewModel:{ 18 | createViewModel(params, componentInfo){ 19 | return params instanceof HeaderNav ? params : ko.unwrap(params.option); 20 | } 21 | } 22 | }); 23 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dashbord_client", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "webpack.config.js", 6 | "author": "MKGaru", 7 | "license": "MIT", 8 | "dependencies": { 9 | "css-loader": "^0.9.0", 10 | "file-loader": "^0.8.1", 11 | "html-loader": "^0.2.3", 12 | "html5-history-api": "^4.1.15", 13 | "imports-loader": "^0.6.3", 14 | "jquery": "^2.1.1", 15 | "less": "^1.7.5", 16 | "less-loader": "^0.7.7", 17 | "page": "^1.3.7", 18 | "style-loader": "^0.8.1", 19 | "sugar": "^1.4.1", 20 | "url-loader": "^0.5.5", 21 | "webpack": "^1.4.10" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/parts/MenuItem.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | class MenuItem{ 4 | public label:string; 5 | public icon:string; 6 | public href:string; 7 | 8 | constructor(page:IPage,href:string); 9 | constructor(label:string, icon: string, href: string); 10 | constructor(arg1,arg2,arg3?) 11 | { 12 | if(typeof arg1 =="string"){ 13 | this.label = arg1; 14 | this.icon = arg2; 15 | this.href = arg3; 16 | }else{ 17 | this.label = (arg1).title; 18 | this.icon = (arg1).icon; 19 | this.href = arg2; 20 | } 21 | ko.track(this); 22 | } 23 | } 24 | 25 | export = MenuItem; -------------------------------------------------------------------------------- /src/parts/side-nav/SideNav.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import MenuItem = require('../MenuItem'); 3 | 4 | class SideNav{ 5 | public isShown = true; 6 | constructor( 7 | public menu:MenuItem[] 8 | ){ 9 | ko.track(this); 10 | } 11 | 12 | public toggle(){ 13 | this.isShown = !this.isShown; 14 | } 15 | } 16 | export = SideNav; 17 | 18 | require('./SideNav.less'); 19 | ko.components.register('side-nav',{ 20 | template: require('./SideNav.html'), 21 | viewModel:{ 22 | createViewModel(params, componentInfo){ 23 | return params instanceof SideNav ? params : ko.unwrap(params.option); 24 | } 25 | } 26 | }); 27 | 28 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body{ 3 | height:100%; 4 | margin:0; 5 | padding:0; 6 | font-family: "ヒラギノ角ゴ Pro W3", "Hiragino Kaku Gothic Pro", "メイリオ", "Meiryo", "MS Pゴシック", "MS P Gothic", sans-serif; 7 | } 8 | body{ 9 | padding-top:30px; 10 | overflow:hidden; 11 | } 12 | body>header-nav{ 13 | margin-top:-30px; 14 | height:30px; 15 | } 16 | body>side-nav{ 17 | float:left; 18 | height: 100%; 19 | } 20 | body>.contents{ 21 | margin-left:15em; 22 | transition: margin-left 250ms; 23 | padding:1em; 24 | height: 100%; 25 | overflow: auto; 26 | } 27 | body>.contents.wide{ 28 | margin-left:3em; 29 | } 30 | 31 | body>.contents>h1{ 32 | margin:0; 33 | } -------------------------------------------------------------------------------- /src/page/apps/AppsPage.js: -------------------------------------------------------------------------------- 1 | /// 2 | var AppsPage = (function () { 3 | function AppsPage() { 4 | this.title = "Apps"; 5 | this.component = "apps-page"; 6 | this.icon = "bookmark-o"; 7 | ko.track(this); 8 | } 9 | return AppsPage; 10 | })(); 11 | //require('./AppsPage.less'); 12 | ko.components.register('apps-page', { 13 | template: require('./AppsPage.html'), 14 | viewModel: { 15 | createViewModel: function (params, componentInfo) { 16 | return params instanceof AppsPage ? params : params.option; 17 | } 18 | } 19 | }); 20 | module.exports = AppsPage; 21 | -------------------------------------------------------------------------------- /src/page/error/ErrorPage.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | class ErrorPage implements IPage{ 4 | public title ="Error"; 5 | public component ="error-page"; 6 | public icon = "warning"; 7 | 8 | public code:number=500; 9 | public message:string = ''; 10 | 11 | constructor( 12 | ){ 13 | ko.track(this); 14 | } 15 | } 16 | export = ErrorPage; 17 | 18 | //require('./ErrorPage.less'); 19 | ko.components.register('error-page',{ 20 | template: require('./ErrorPage.html'), 21 | viewModel:{ 22 | createViewModel(params, componentInfo){ 23 | return params instanceof ErrorPage ? params : params.option; 24 | } 25 | } 26 | }); 27 | 28 | -------------------------------------------------------------------------------- /src/parts/directory-tree/DirectoryTree.less: -------------------------------------------------------------------------------- 1 | directory-tree{ 2 | display:block; 3 | directory-tree { 4 | margin-left: -2em; 5 | } 6 | ul.fa-ul{ 7 | margin-left:2em; 8 | li{ 9 | span.name{ 10 | display:inline-block; 11 | color:darkgreen; 12 | margin-right:1em; 13 | user-select:none; 14 | -webkit-user-select:none; 15 | &:hover{ 16 | text-decoration: underline; 17 | } 18 | } 19 | span.description{ 20 | color: #aaa; 21 | } 22 | 23 | .fa-folder, 24 | .fa-folder-open-o{ 25 | color: darkorange; 26 | &~span.name{ 27 | color: blue; 28 | cursor:pointer; 29 | } 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /src/parts/side-nav/SideNav.js: -------------------------------------------------------------------------------- 1 | var SideNav = (function () { 2 | function SideNav(menu) { 3 | this.menu = menu; 4 | this.isShown = true; 5 | ko.track(this); 6 | } 7 | SideNav.prototype.toggle = function () { 8 | this.isShown = !this.isShown; 9 | }; 10 | return SideNav; 11 | })(); 12 | require('./SideNav.less'); 13 | ko.components.register('side-nav', { 14 | template: require('./SideNav.html'), 15 | viewModel: { 16 | createViewModel: function (params, componentInfo) { 17 | return params instanceof SideNav ? params : ko.unwrap(params.option); 18 | } 19 | } 20 | }); 21 | module.exports = SideNav; 22 | -------------------------------------------------------------------------------- /src/parts/widget/Widget.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | class Widget{ 4 | private element:Element; 5 | constructor( 6 | public title 7 | ){ 8 | ko.track(this); 9 | } 10 | public link(element:Element){ 11 | this.element = element; 12 | //$(element).draggable(); 13 | } 14 | } 15 | export = Widget; 16 | 17 | require('./Widget.less'); 18 | ko.components.register('widget',{ 19 | template: require('./Widget.html'), 20 | viewModel:{ 21 | createViewModel(params, componentInfo){ 22 | var vm:Widget = params instanceof Widget ? params : ko.utils.unwrapObservable(params.option); 23 | vm.link(componentInfo.element); 24 | return vm; 25 | } 26 | } 27 | }); 28 | -------------------------------------------------------------------------------- /src/parts/directory-tree/DirectoryTree.html: -------------------------------------------------------------------------------- 1 |
    2 |
  • 3 | 4 | {{name}} 5 | [{{description}}] 6 |
  • 7 |
  • 8 |
      9 |
    • 10 | {{#if: $data.sub}} 11 | 12 | {{/if}} 13 | {{#else}} 14 | 15 | {{name}} 16 | [{{description}}] 17 | {{/else}} 18 |
    • 19 |
    20 |
  • 21 |
-------------------------------------------------------------------------------- /src/tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "sugar/sugar.d.ts": { 9 | "commit": "bd9a1922e44ddf35953587a92c5caf4242bf6ea5" 10 | }, 11 | "jquery/jquery.d.ts": { 12 | "commit": "bd9a1922e44ddf35953587a92c5caf4242bf6ea5" 13 | }, 14 | "knockout/knockout.d.ts": { 15 | "commit": "7be40b2a8aca2288fe7011cd24959718756a5664" 16 | }, 17 | "jqueryui/jqueryui.d.ts": { 18 | "commit": "0202fa011287e4f8394f01edc6681442dbdeaf51" 19 | }, 20 | "node/node.d.ts": { 21 | "commit": "5e8ac2341f100202415ff39d8f79875b439ac677" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/parts/directory-tree/DirectoryTree.js: -------------------------------------------------------------------------------- 1 | var DirectoryTree = (function () { 2 | function DirectoryTree(name, description, sub) { 3 | this.name = name; 4 | this.description = description; 5 | this.sub = sub; 6 | this.isOpened = true; 7 | ko.track(this); 8 | } 9 | return DirectoryTree; 10 | })(); 11 | require('./DirectoryTree.less'); 12 | ko.components.register('directory-tree', { 13 | template: require('./DirectoryTree.html'), 14 | viewModel: { 15 | createViewModel: function (params, componentInfo) { 16 | return params instanceof DirectoryTree ? params : ko.unwrap(params.option); 17 | } 18 | } 19 | }); 20 | module.exports = DirectoryTree; 21 | -------------------------------------------------------------------------------- /src/page/error/ErrorPage.js: -------------------------------------------------------------------------------- 1 | /// 2 | var ErrorPage = (function () { 3 | function ErrorPage() { 4 | this.title = "Error"; 5 | this.component = "error-page"; 6 | this.icon = "warning"; 7 | this.code = 500; 8 | this.message = ''; 9 | ko.track(this); 10 | } 11 | return ErrorPage; 12 | })(); 13 | //require('./ErrorPage.less'); 14 | ko.components.register('error-page', { 15 | template: require('./ErrorPage.html'), 16 | viewModel: { 17 | createViewModel: function (params, componentInfo) { 18 | return params instanceof ErrorPage ? params : params.option; 19 | } 20 | } 21 | }); 22 | module.exports = ErrorPage; 23 | -------------------------------------------------------------------------------- /src/parts/directory-tree/DirectoryTree.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import MenuItem = require('../MenuItem'); 3 | import SideNav = require('../directory-tree/DirectoryTree'); 4 | 5 | class DirectoryTree{ 6 | public isOpened = true; 7 | constructor( 8 | public name:string, 9 | public description:string, 10 | public sub: {name:string;description:string}[] 11 | ){ 12 | ko.track(this); 13 | } 14 | } 15 | export = DirectoryTree; 16 | 17 | require('./DirectoryTree.less'); 18 | ko.components.register('directory-tree',{ 19 | template: require('./DirectoryTree.html'), 20 | viewModel:{ 21 | createViewModel(params, componentInfo){ 22 | return params instanceof DirectoryTree ? params : ko.unwrap(params.option); 23 | } 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ko-spa-example 2 | ============== 3 | 4 | Typescript( knockout + punches + es5 ) and LESS and WebPack 5 | ------------ 6 | 7 | 8 | [WebPack] 9 |
10 |
knockout.js (ver 3.2 with component)
11 |
with knockout.punches
12 |
with kunockout.es5
13 |
and more
14 |
15 | 16 | Demo 17 | ------ 18 | Legacy: http://mkgaru.github.io/ko_spa_example/6b7d9d9/ 19 | Current: http://mkgaru.github.io/ko_spa_example/63d449f/ (Github's Demo can't page reload. maby 404) 20 | 21 | Usage 22 | ------ 23 |
24 | $ git clone https://github.com/MKGaru/ko-spa-example.git
25 | $ cd ko-spa-example
26 | $ sh init.sh  #init npm , bower , tsd and webpack
27 | $ npm start   #run demo server
28 | 
29 | and, access to http://localhost:5000/ 30 | -------------------------------------------------------------------------------- /src/parts/widget/Widget.js: -------------------------------------------------------------------------------- 1 | /// 2 | var Widget = (function () { 3 | function Widget(title) { 4 | this.title = title; 5 | ko.track(this); 6 | } 7 | Widget.prototype.link = function (element) { 8 | this.element = element; 9 | //$(element).draggable(); 10 | }; 11 | return Widget; 12 | })(); 13 | require('./Widget.less'); 14 | ko.components.register('widget', { 15 | template: require('./Widget.html'), 16 | viewModel: { 17 | createViewModel: function (params, componentInfo) { 18 | var vm = params instanceof Widget ? params : ko.utils.unwrapObservable(params.option); 19 | vm.link(componentInfo.element); 20 | return vm; 21 | } 22 | } 23 | }); 24 | module.exports = Widget; 25 | -------------------------------------------------------------------------------- /src/parts/widget/Widget.less: -------------------------------------------------------------------------------- 1 | widget{ 2 | display:block; 3 | background:white; 4 | border:solid 1px #ccc; 5 | box-shadow: 2px 2px 2px #ddd; 6 | margin: 0 2px 2px 0; 7 | position: relative; 8 | 9 | &>header{ 10 | cursor:move; 11 | } 12 | &>div{ 13 | 14 | } 15 | &>footer{ 16 | position: absolute; 17 | bottom: 0; 18 | left: 0; 19 | right: 0; 20 | } 21 | 22 | 23 | .data-col(@i) when(@i>0){ 24 | &[data-col="@{i}"] { 25 | left: 10 + (@i - 1) *(250 + 10*2px); 26 | } 27 | .data-col(@i - 1); 28 | } 29 | .data-col(5); 30 | 31 | .data-row(@i) when(@i>0){ 32 | &[data-row="@{i}"] { 33 | top: 10 + (@i - 1) *(250 + 10*2px); 34 | } 35 | .data-row(@i - 1); 36 | } 37 | .data-row(25); 38 | 39 | .data-sizey(@i) when(@i>0){ 40 | &[data-sizey="@{i}"] { 41 | height: -10*2 + @i *(250 + 10*2px); 42 | } 43 | .data-sizey(@i - 1); 44 | } 45 | .data-sizey(24); 46 | 47 | .data-sizex(@i) when(@i>0){ 48 | &[data-sizex="@{i}"] { 49 | width: -10*2 + @i *(250 + 10*2px); 50 | } 51 | .data-sizex(@i - 1); 52 | } 53 | .data-sizex(4); 54 | } 55 | -------------------------------------------------------------------------------- /src/parts/side-nav/SideNav.less: -------------------------------------------------------------------------------- 1 | side-nav{ 2 | display:block; 3 | height:100%; 4 | background: #e6e9ee; 5 | ul{ 6 | list-style:none; 7 | padding:0; 8 | margin:0; 9 | width: 15em; 10 | transition: width 250ms; 11 | &.off{ 12 | width: 3em; 13 | } 14 | li{ 15 | transition: background 500ms; 16 | a{ 17 | display:block; 18 | color: #777; 19 | padding: 0.7em; 20 | font-size: 1.15em; 21 | white-space: nowrap; 22 | 23 | &:link, 24 | &:visited{ 25 | color: #777; 26 | } 27 | } 28 | &:hover{ 29 | background: #eeeeee; 30 | a { 31 | color:#222; 32 | text-decoration: none; 33 | } 34 | } 35 | &.active{ 36 | background: #4697ce; 37 | transition: background 100ms ease-out; 38 | a{ 39 | color: white; 40 | text-decoration: non; 41 | } 42 | &:after{ /* caret <| */ 43 | content: ""; 44 | display: block; 45 | width: 0; 46 | height: 0; 47 | border: 0.85em solid transparent; 48 | border-right: 0.85em solid white; 49 | position: relative; 50 | float: right; 51 | top: -2.3em; 52 | pointer-events: none; 53 | } 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dashboard 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var path = require('path'); 3 | var favicon = require('serve-favicon'); 4 | var logger = require('morgan'); 5 | var cookieParser = require('cookie-parser'); 6 | var bodyParser = require('body-parser'); 7 | 8 | 9 | var app = express(); 10 | 11 | // view engine setup 12 | 13 | // uncomment after placing your favicon in /public 14 | //app.use(favicon(__dirname + '/public/favicon.ico')); 15 | app.use(logger('dev')); 16 | app.use(bodyParser.json()); 17 | app.use(bodyParser.urlencoded({ extended: false })); 18 | app.use(cookieParser()); 19 | app.use(express.static(path.join(__dirname, 'public'))); 20 | 21 | app.all('/*', function(req, res){ 22 | res.sendFile(path.join(__dirname,'public/index.html')); 23 | }); 24 | 25 | // catch 404 and forward to error handler 26 | app.use(function(req, res, next) { 27 | var err = new Error('Not Found'); 28 | err.status = 404; 29 | next(err); 30 | }); 31 | 32 | // error handlers 33 | 34 | // development error handler 35 | // will print stacktrace 36 | if (app.get('env') === 'development') { 37 | app.use(function(err, req, res, next) { 38 | res.status(err.status || 500); 39 | res.send(err); 40 | }); 41 | } 42 | 43 | // production error handler 44 | // no stacktraces leaked to user 45 | app.use(function(err, req, res, next) { 46 | res.status(err.status || 500); 47 | res.send(err); 48 | }); 49 | 50 | app.listen(5000); -------------------------------------------------------------------------------- /src/parts/widget/WidgetHolder.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import Widget = require('./Widget'); 3 | 4 | class WidgetHolder{ 5 | private element:Element; 6 | public gridster:any; 7 | constructor( 8 | public widgets:{widget:Widget;size_x:number;size_y:number;row:number;col:number;}[] 9 | ){ 10 | ko.track(this); 11 | } 12 | 13 | public link(element:Element){ 14 | var holder = this; 15 | this.element = element; 16 | 17 | this.gridster = $(this.element).gridster({ 18 | widget_margins: [10, 10], 19 | widget_base_dimensions: [250, 250], 20 | widget_selector: 'widget', 21 | auto_init:true, 22 | autogenerate_stylesheet:false, 23 | resize: { 24 | enabled: true, 25 | handle_append_to:'footer' 26 | }, 27 | draggable:{ 28 | items: 'widget', 29 | handle: 'header' 30 | } 31 | }).data('gridster'); 32 | 33 | 34 | this.widgets.forEach((widget,i)=>{ 35 | holder.gridster.add_widget('', 36 | widget.size_x, 37 | widget.size_y, 38 | widget.col, 39 | widget.row 40 | ) 41 | }); 42 | } 43 | } 44 | export = WidgetHolder; 45 | 46 | require('./WidgetHolder.less'); 47 | ko.components.register('widget-holder',{ 48 | template: '', 49 | viewModel:{ 50 | createViewModel(params, componentInfo){ 51 | var vm:WidgetHolder = params instanceof WidgetHolder ? params : ko.unwrap(params.option); 52 | vm.link(componentInfo.element); 53 | return vm; 54 | } 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /src/parts/widget/WidgetHolder.js: -------------------------------------------------------------------------------- 1 | var WidgetHolder = (function () { 2 | function WidgetHolder(widgets) { 3 | this.widgets = widgets; 4 | ko.track(this); 5 | } 6 | WidgetHolder.prototype.link = function (element) { 7 | var holder = this; 8 | this.element = element; 9 | this.gridster = $(this.element).gridster({ 10 | widget_margins: [10, 10], 11 | widget_base_dimensions: [250, 250], 12 | widget_selector: 'widget', 13 | auto_init: true, 14 | autogenerate_stylesheet: false, 15 | resize: { 16 | enabled: true, 17 | handle_append_to: 'footer' 18 | }, 19 | draggable: { 20 | items: 'widget', 21 | handle: 'header' 22 | } 23 | }).data('gridster'); 24 | this.widgets.forEach(function (widget, i) { 25 | holder.gridster.add_widget('', widget.size_x, widget.size_y, widget.col, widget.row); 26 | }); 27 | }; 28 | return WidgetHolder; 29 | })(); 30 | require('./WidgetHolder.less'); 31 | ko.components.register('widget-holder', { 32 | template: '', 33 | viewModel: { 34 | createViewModel: function (params, componentInfo) { 35 | var vm = params instanceof WidgetHolder ? params : ko.unwrap(params.option); 36 | vm.link(componentInfo.element); 37 | return vm; 38 | } 39 | } 40 | }); 41 | module.exports = WidgetHolder; 42 | -------------------------------------------------------------------------------- /src/webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: 'Application', 6 | output: { 7 | filename: '../public/js/bundle.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | { test: /\.less$/, loader: 'style!css!less' }, 12 | { test: /\.html$/, loader: 'html' }, 13 | { test: /\.svg$/, loader: 'url-loader?mimetype=image/svg+xml' } 14 | /* 15 | { test: /\.woff$/, loader: 'url-loader?mimetype=application/font-woff' }, 16 | { test: /\.eot$/, loader: 'url-loader?mimetype=application/font-woff' }, 17 | { test: /\.ttf$/, loader: 'url-loader?mimetype=application/font-woff' }, 18 | { test: /\.woff(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?limit=10000&minetype=application/font-woff" }, 19 | { test: /\.(ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/, loader: "url-loader?mimetype=application/font-woff" } 20 | */ 21 | ] 22 | }, 23 | resolve: { 24 | root: [ 25 | // We want roots to resolve the app code: 26 | path.resolve('.'), 27 | // and the bower components: 28 | path.resolve('../public/lib') 29 | ], 30 | alias: { 31 | /* 32 | // This one first to match just the entrypoint module. 33 | // We only need this because the module name doesn't match the file name. 34 | myApp$: 'myApp/app', 35 | // This one maps all our modules called 'myApp.something' to the app/js 36 | // directory 37 | myApp: path.resolve('app', 'js'), 38 | // This is also needed because the module name doesn't match the file name 39 | // but we don't need to locate the file because it is a bower component 40 | // with a file name the same as the directory (component) name: 41 | // bower_components/angular-route/angular-route 42 | ngRoute$: 'angular-route' 43 | */ 44 | } 45 | }, 46 | plugins:[ 47 | new webpack.ResolverPlugin( 48 | new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("../bower.json", ["main"]) 49 | ) 50 | ] 51 | }; -------------------------------------------------------------------------------- /src/page/dashboard/DashboardPage.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import Widget = require('../../parts/widget/Widget'); 4 | import WidgetHolder = require('../../parts/widget/WidgetHolder'); 5 | 6 | class DashboardPage implements IPage{ 7 | public title:string = undefined; 8 | public icon = "tachometer"; 9 | public component = "dashboard-page"; 10 | 11 | public id:string=undefined; 12 | public widgetHolder:WidgetHolder = undefined; 13 | 14 | public message=undefined; 15 | constructor(){ 16 | ko.track(this); 17 | } 18 | 19 | public load(id:string){ 20 | this.id = id; 21 | var base = $("base").attr("href"); 22 | var endpoint = "{base}api/dashboard/{id}.json".assign({base:base,id:id}); 23 | var dashboard = this; 24 | 25 | return $.getJSON(endpoint) 26 | .done(function(data){ 27 | //DEMO GithubPageでのテストデモ用に、localstorageから復帰するよ。 28 | var restore = localStorage.getItem(endpoint); 29 | if(restore){ 30 | data = JSON.parse(restore); 31 | } 32 | //~ forDEMO 33 | 34 | dashboard.title = data.title; 35 | dashboard.widgetHolder = new WidgetHolder(data.widgets.map(widget=>({ 36 | widget: new Widget(widget.data), 37 | size_x:widget.size_x, 38 | size_y:widget.size_y, 39 | col:widget.col, 40 | row:widget.row 41 | }))) 42 | }) 43 | } 44 | private save(){ 45 | var widgetHolder = this.widgetHolder; 46 | var serialized = { 47 | title: this.title, 48 | widgets:[] 49 | }; 50 | serialized.widgets = widgetHolder.gridster.serialize().map((widgetLayout,index)=>({ 51 | type:"text", 52 | data: widgetHolder.widgets[index].widget.title, 53 | size_x:widgetLayout.size_x, 54 | size_y:widgetLayout.size_y, 55 | col:widgetLayout.col, 56 | row:widgetLayout.row 57 | })); 58 | 59 | var base = $("base").attr("href"); 60 | var endpoint = "{base}api/dashboard/{id}.json".assign({base:base,id:this.id}); 61 | 62 | //GithubPageでのテストデモ用に、localstorageに退避するよ 63 | localStorage.setItem(endpoint,JSON.stringify(serialized)); 64 | 65 | this.message="Save success."; 66 | 67 | /* 68 | $.ajax({ 69 | type: "post", 70 | url:endpoint, 71 | data: JSON.stringify(serialized), 72 | contentType: 'application/json' 73 | }).done(function(){ 74 | 75 | }).fail(function(){ 76 | 77 | }); 78 | */ 79 | 80 | } 81 | } 82 | export = DashboardPage; 83 | 84 | require('./DashboardPage.less'); 85 | ko.components.register('dashboard-page',{ 86 | template: require('./DashboardPage.html'), 87 | viewModel:{ 88 | createViewModel(params, componentInfo){ 89 | return params instanceof DashboardPage ? params : params.option; 90 | } 91 | } 92 | }); 93 | 94 | -------------------------------------------------------------------------------- /src/Application.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import SideNav = require('parts/side-nav/SideNav'); 3 | import HeaderNav = require('parts/header-nav/HeaderNav'); 4 | import MenuItem = require('parts/MenuItem'); 5 | 6 | import Widget = require('parts/widget/Widget'); 7 | 8 | import AppsPage = require('page/apps/AppsPage'); 9 | import DashboardPage =require('page/dashboard/DashboardPage'); 10 | import HomePage = require('page/home/HomePage'); 11 | import ErrorPage = require('page/error/ErrorPage'); 12 | 13 | require('sugar'); //http://sugarjs.com/api 14 | require('html5-history-api'); //https://github.com/devote/HTML5-History-API 15 | var page = require('page'); //https://github.com/visionmedia/page.js 16 | 17 | class Application{ 18 | public sideNav:SideNav; 19 | public headerNav:HeaderNav; 20 | 21 | public pages:{[name:string]:IPage}; 22 | public page="home"; 23 | public href=""; 24 | 25 | constructor(){ 26 | // Init Page VMs 27 | var pages = this.pages ={ 28 | 'home': new HomePage(), 29 | 'apps': new AppsPage(), 30 | 'dashboard': undefined, //Dynamic Gen 31 | 'error': new ErrorPage() 32 | }; 33 | ko.track(pages); 34 | 35 | // Init Common Parts VM 36 | this.sideNav = new SideNav( 37 | [ 38 | new MenuItem(pages.home, ''), 39 | new MenuItem('1stDashboard','tachometer','dashboard/1'), 40 | new MenuItem('2ndDashboard','tachometer','dashboard/2'), 41 | new MenuItem(pages.apps, 'app'), 42 | new MenuItem('ErrorSample','warning','hogehoge') 43 | ] 44 | ); 45 | this.headerNav = new HeaderNav( 46 | this.sideNav 47 | ); 48 | 49 | ko.track(this); 50 | } 51 | } 52 | export=Application; 53 | 54 | class ApplicationRouter{ 55 | constructor( 56 | private app:Application 57 | ){ 58 | page.base($("base").attr("href")); 59 | 60 | page('*',(ctx,next)=>{ 61 | app.href = ctx.pathname.substr(1); 62 | next(); 63 | }); 64 | page('', (ctx,next)=>{app.page='home'}); 65 | page('dashboard/:id', (ctx,next)=>{ 66 | var dashboardPage = new DashboardPage(); 67 | dashboardPage.load(ctx.params.id) 68 | .done(data=>{ 69 | app.pages['dashboard'] = dashboardPage; 70 | app.page='dashboard'; 71 | }) 72 | .fail(err=>{ 73 | next(); 74 | }); 75 | }); 76 | page('app',(ctx,next)=>{app.page='apps'}); 77 | page('*',(ctx,next)=>{ 78 | var errorPage = (app.pages['error']); 79 | errorPage.code = 404; 80 | errorPage.message = "Route Not Found"; 81 | app.page='error'; 82 | }); 83 | page(); 84 | } 85 | } 86 | 87 | KnockoutElse.init(); // knockout-else : https://github.com/brianmhunt/knockout-else 88 | ko.punches.enableAll(); // knockout-punches : https://github.com/mbest/knockout.punches 89 | 90 | 91 | $(function(){ 92 | var app = new Application(); 93 | //window['app'] = app; //for Console Debug. 94 | new ApplicationRouter(app); 95 | ko.applyBindings(app,$('html')[0]); 96 | }); 97 | 98 | -------------------------------------------------------------------------------- /src/page/home/HomePage.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | import DirectoryTree = require('../../parts/directory-tree/DirectoryTree'); 4 | 5 | class HomePage implements IPage{ 6 | public title = "DirTree"; 7 | public component= "home-page"; 8 | public icon = "folder"; 9 | 10 | 11 | public resources:any =null; 12 | constructor( 13 | ){ 14 | ko.track(this); 15 | } 16 | 17 | public dir = new DirectoryTree("/","",[ 18 | new DirectoryTree("public","Public Web Dir",[ 19 | new DirectoryTree("api","Stub Response Service",[ 20 | {name:"resources.json",description:"Stub Server Status Response"} 21 | ]), 22 | new DirectoryTree("css","Application CSS Files",[ 23 | {name:"style.css",description:"Common CSS File"} 24 | ]), 25 | new DirectoryTree("js","Application JavaScript Files",[ 26 | {name:"bundle.js",description:"(Application and Library) Generated by WebPack"} 27 | ]), 28 | new DirectoryTree("lib","Dependence library (without Webpack)",[ 29 | ]), 30 | {name:"index.html",description:"Common SPA base page."} 31 | ]), 32 | new DirectoryTree("src","Application Build Src",[ 33 | new DirectoryTree("node_modules","Build and dependent library",[ 34 | ]), 35 | new DirectoryTree("page","Contents of SPA",[ 36 | new DirectoryTree("foo","Some page component",[ 37 | {name:"FooPage.html",description:"HTML of Page"}, 38 | {name:"FooPage.less",description:"Style of Page (option)"}, 39 | {name:"FooPage.ts", description:"Script of Page (ViewModel(implements IPage) , component register , style append)"}, 40 | ]), 41 | new DirectoryTree("bar","Some page component",[ 42 | {name:"BarPage.html",description:"HTML of Page"}, 43 | {name:"BarPage.ts", description:"Script of Page (ViewModel(implements IPage) , component register , style append)"}, 44 | ]), 45 | {name:"IPage.d.ts",description:"Page interface define"}, 46 | ]), 47 | new DirectoryTree("parts","Custom element component",[ 48 | new DirectoryTree("baz","Some component",[ 49 | {name:"Baz.html",description:"Component Template"}, 50 | {name:"Baz.less",description:"Style of Component (option)"}, 51 | {name:"Baz.ts", description:"Script of Component (ViewModel , component register , style append)"}, 52 | ]), 53 | ]), 54 | new DirectoryTree("typings","Typescript Definitely Typed(by tsd)",[ 55 | ]), 56 | {name:"Application.ts",description:"Application Main"}, 57 | {name:"package.json",description:"npm package meta"}, 58 | {name:"references.d.ts",description:"Typescript Definitely Reference"}, 59 | {name:"tsd.json",description:"Typescript Definitely Typed meta"}, 60 | {name:"webpack.config.js",description:"WebPack build config"} 61 | ]) 62 | ]); 63 | 64 | } 65 | export = HomePage; 66 | 67 | 68 | require('./HomePage.less'); 69 | ko.components.register('home-page',{ 70 | template: require('./HomePage.html'), 71 | viewModel:{ 72 | createViewModel(params, componentInfo){ 73 | return params instanceof HomePage ? params : params.option; 74 | } 75 | } 76 | }); 77 | 78 | -------------------------------------------------------------------------------- /src/page/dashboard/DashboardPage.js: -------------------------------------------------------------------------------- 1 | /// 2 | var Widget = require('../../parts/widget/Widget'); 3 | var WidgetHolder = require('../../parts/widget/WidgetHolder'); 4 | var DashboardPage = (function () { 5 | function DashboardPage() { 6 | this.title = undefined; 7 | this.icon = "tachometer"; 8 | this.component = "dashboard-page"; 9 | this.id = undefined; 10 | this.widgetHolder = undefined; 11 | this.message = undefined; 12 | ko.track(this); 13 | } 14 | DashboardPage.prototype.load = function (id) { 15 | this.id = id; 16 | var base = $("base").attr("href"); 17 | var endpoint = "{base}api/dashboard/{id}.json".assign({ base: base, id: id }); 18 | var dashboard = this; 19 | return $.getJSON(endpoint).done(function (data) { 20 | //DEMO GithubPageでのテストデモ用に、localstorageから復帰するよ。 21 | var restore = localStorage.getItem(endpoint); 22 | if (restore) { 23 | data = JSON.parse(restore); 24 | } 25 | //~ forDEMO 26 | dashboard.title = data.title; 27 | dashboard.widgetHolder = new WidgetHolder(data.widgets.map(function (widget) { return ({ 28 | widget: new Widget(widget.data), 29 | size_x: widget.size_x, 30 | size_y: widget.size_y, 31 | col: widget.col, 32 | row: widget.row 33 | }); })); 34 | }); 35 | }; 36 | DashboardPage.prototype.save = function () { 37 | var widgetHolder = this.widgetHolder; 38 | var serialized = { 39 | title: this.title, 40 | widgets: [] 41 | }; 42 | serialized.widgets = widgetHolder.gridster.serialize().map(function (widgetLayout, index) { return ({ 43 | type: "text", 44 | data: widgetHolder.widgets[index].widget.title, 45 | size_x: widgetLayout.size_x, 46 | size_y: widgetLayout.size_y, 47 | col: widgetLayout.col, 48 | row: widgetLayout.row 49 | }); }); 50 | var base = $("base").attr("href"); 51 | var endpoint = "{base}api/dashboard/{id}.json".assign({ base: base, id: this.id }); 52 | //GithubPageでのテストデモ用に、localstorageに退避するよ 53 | localStorage.setItem(endpoint, JSON.stringify(serialized)); 54 | this.message = "Save success."; 55 | /* 56 | $.ajax({ 57 | type: "post", 58 | url:endpoint, 59 | data: JSON.stringify(serialized), 60 | contentType: 'application/json' 61 | }).done(function(){ 62 | 63 | }).fail(function(){ 64 | 65 | }); 66 | */ 67 | }; 68 | return DashboardPage; 69 | })(); 70 | require('./DashboardPage.less'); 71 | ko.components.register('dashboard-page', { 72 | template: require('./DashboardPage.html'), 73 | viewModel: { 74 | createViewModel: function (params, componentInfo) { 75 | return params instanceof DashboardPage ? params : params.option; 76 | } 77 | } 78 | }); 79 | module.exports = DashboardPage; 80 | -------------------------------------------------------------------------------- /src/Application.js: -------------------------------------------------------------------------------- 1 | /// 2 | var SideNav = require('parts/side-nav/SideNav'); 3 | var HeaderNav = require('parts/header-nav/HeaderNav'); 4 | var MenuItem = require('parts/MenuItem'); 5 | var AppsPage = require('page/apps/AppsPage'); 6 | var DashboardPage = require('page/dashboard/DashboardPage'); 7 | var HomePage = require('page/home/HomePage'); 8 | var ErrorPage = require('page/error/ErrorPage'); 9 | require('sugar'); //http://sugarjs.com/api 10 | require('html5-history-api'); //https://github.com/devote/HTML5-History-API 11 | var page = require('page'); //https://github.com/visionmedia/page.js 12 | var Application = (function () { 13 | function Application() { 14 | this.page = "home"; 15 | this.href = ""; 16 | // Init Page VMs 17 | var pages = this.pages = { 18 | 'home': new HomePage(), 19 | 'apps': new AppsPage(), 20 | 'dashboard': undefined, 21 | 'error': new ErrorPage() 22 | }; 23 | ko.track(pages); 24 | // Init Common Parts VM 25 | this.sideNav = new SideNav([ 26 | new MenuItem(pages.home, ''), 27 | new MenuItem('1stDashboard', 'tachometer', 'dashboard/1'), 28 | new MenuItem('2ndDashboard', 'tachometer', 'dashboard/2'), 29 | new MenuItem(pages.apps, 'app'), 30 | new MenuItem('ErrorSample', 'warning', 'hogehoge') 31 | ]); 32 | this.headerNav = new HeaderNav(this.sideNav); 33 | ko.track(this); 34 | } 35 | return Application; 36 | })(); 37 | var ApplicationRouter = (function () { 38 | function ApplicationRouter(app) { 39 | this.app = app; 40 | page.base($("base").attr("href")); 41 | page('*', function (ctx, next) { 42 | app.href = ctx.pathname.substr(1); 43 | next(); 44 | }); 45 | page('', function (ctx, next) { 46 | app.page = 'home'; 47 | }); 48 | page('dashboard/:id', function (ctx, next) { 49 | var dashboardPage = new DashboardPage(); 50 | dashboardPage.load(ctx.params.id).done(function (data) { 51 | app.pages['dashboard'] = dashboardPage; 52 | app.page = 'dashboard'; 53 | }).fail(function (err) { 54 | next(); 55 | }); 56 | }); 57 | page('app', function (ctx, next) { 58 | app.page = 'apps'; 59 | }); 60 | page('*', function (ctx, next) { 61 | var errorPage = app.pages['error']; 62 | errorPage.code = 404; 63 | errorPage.message = "Route Not Found"; 64 | app.page = 'error'; 65 | }); 66 | page(); 67 | } 68 | return ApplicationRouter; 69 | })(); 70 | KnockoutElse.init(); // knockout-else : https://github.com/brianmhunt/knockout-else 71 | ko.punches.enableAll(); // knockout-punches : https://github.com/mbest/knockout.punches 72 | $(function () { 73 | var app = new Application(); 74 | //window['app'] = app; //for Console Debug. 75 | new ApplicationRouter(app); 76 | ko.applyBindings(app, $('html')[0]); 77 | }); 78 | module.exports = Application; 79 | -------------------------------------------------------------------------------- /src/parts/widget/WidgetHolder.less: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | base File: 4 | ! gridster.js - v0.5.6 - 2014-09-25 5 | * http://gridster.net/ 6 | * Copyright (c) 2014 ducksboard; Licensed MIT */ 7 | 8 | .transition (@transition) { 9 | -webkit-transition: @transition; 10 | -moz-transition: @transition; 11 | -ms-transition: @transition; 12 | -o-transition: @transition; 13 | transition: @transition; 14 | } 15 | 16 | widget-holder{ 17 | display:block; 18 | position:relative; 19 | &>*{ 20 | margin: 0 auto; 21 | .transition(~"height .4s, width .4s"); 22 | &:not(.dragging){ 23 | .transition(~"top .4s, left .4s, height .4s, width .4s"); 24 | } 25 | } 26 | .gs-w { 27 | z-index: 2; 28 | position: absolute; 29 | } 30 | .preview-holder { 31 | z-index: 1; 32 | position: absolute; 33 | background-color: #ccc; 34 | border-color: #ccc; 35 | opacity: 0.3; 36 | } 37 | .player-revert { 38 | z-index: 10!important; 39 | .transition(~"left .3s, top .3s !important"); 40 | } 41 | .dragging, 42 | .resizing { 43 | z-index: 10!important; 44 | .transition(~"all 0s !important"); 45 | } 46 | 47 | .ready{ 48 | .gs-w:not(.preview-holder) { 49 | .transition(~"opacity .3s, left .3s, top .3s"); 50 | } 51 | .gs-w:not(.preview-holder), 52 | .resize-preview-holder { 53 | .transition(~"opacity .3s, left .3s, top .3s, width .3s, height .3s"); 54 | } 55 | } 56 | 57 | 58 | .gs-resize-handle { 59 | position: absolute; 60 | z-index: 1; 61 | } 62 | 63 | .gs-resize-handle-both { 64 | width: 20px; 65 | height: 20px; 66 | bottom: -8px; 67 | right: -8px; 68 | background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg08IS0tIEdlbmVyYXRvcjogQWRvYmUgRmlyZXdvcmtzIENTNiwgRXhwb3J0IFNWRyBFeHRlbnNpb24gYnkgQWFyb24gQmVhbGwgKGh0dHA6Ly9maXJld29ya3MuYWJlYWxsLmNvbSkgLiBWZXJzaW9uOiAwLjYuMSAgLS0+DTwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+DTxzdmcgaWQ9IlVudGl0bGVkLVBhZ2UlMjAxIiB2aWV3Qm94PSIwIDAgNiA2IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjojZmZmZmZmMDAiIHZlcnNpb249IjEuMSINCXhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHhtbDpzcGFjZT0icHJlc2VydmUiDQl4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjZweCIgaGVpZ2h0PSI2cHgiDT4NCTxnIG9wYWNpdHk9IjAuMzAyIj4NCQk8cGF0aCBkPSJNIDYgNiBMIDAgNiBMIDAgNC4yIEwgNCA0LjIgTCA0LjIgNC4yIEwgNC4yIDAgTCA2IDAgTCA2IDYgTCA2IDYgWiIgZmlsbD0iIzAwMDAwMCIvPg0JPC9nPg08L3N2Zz4='); 69 | background-position: top left; 70 | background-repeat: no-repeat; 71 | cursor: se-resize; 72 | z-index: 20; 73 | } 74 | 75 | .gs-resize-handle-x { 76 | top: 0; 77 | bottom: 13px; 78 | right: -5px; 79 | width: 10px; 80 | cursor: e-resize; 81 | } 82 | 83 | .gs-resize-handle-y { 84 | left: 0; 85 | right: 13px; 86 | bottom: -5px; 87 | height: 10px; 88 | cursor: s-resize; 89 | } 90 | 91 | .gs-w:hover .gs-resize-handle, 92 | .resizing .gs-resize-handle { 93 | opacity: 1; 94 | } 95 | 96 | .gs-resize-handle, 97 | .gs-w.dragging .gs-resize-handle { 98 | opacity: 0; 99 | } 100 | 101 | .gs-resize-disabled .gs-resize-handle { 102 | display: none!important; 103 | } 104 | 105 | [data-max-sizex="1"] .gs-resize-handle-x, 106 | [data-max-sizey="1"] .gs-resize-handle-y, 107 | [data-max-sizey="1"][data-max-sizex="1"] .gs-resize-handle { 108 | display: none !important; 109 | } 110 | 111 | /* Uncomment this if you set helper : "clone" in draggable options */ 112 | /*.gridster .player { 113 | opacity:0; 114 | } 115 | */ 116 | } 117 | -------------------------------------------------------------------------------- /src/page/home/HomePage.js: -------------------------------------------------------------------------------- 1 | /// 2 | var DirectoryTree = require('../../parts/directory-tree/DirectoryTree'); 3 | var HomePage = (function () { 4 | function HomePage() { 5 | this.title = "DirTree"; 6 | this.component = "home-page"; 7 | this.icon = "folder"; 8 | this.resources = null; 9 | this.dir = new DirectoryTree("/", "", [ 10 | new DirectoryTree("public", "Public Web Dir", [ 11 | new DirectoryTree("api", "Stub Response Service", [ 12 | { name: "resources.json", description: "Stub Server Status Response" } 13 | ]), 14 | new DirectoryTree("css", "Application CSS Files", [ 15 | { name: "style.css", description: "Common CSS File" } 16 | ]), 17 | new DirectoryTree("js", "Application JavaScript Files", [ 18 | { name: "bundle.js", description: "(Application and Library) Generated by WebPack" } 19 | ]), 20 | new DirectoryTree("lib", "Dependence library (without Webpack)", [ 21 | ]), 22 | { name: "index.html", description: "Common SPA base page." } 23 | ]), 24 | new DirectoryTree("src", "Application Build Src", [ 25 | new DirectoryTree("node_modules", "Build and dependent library", [ 26 | ]), 27 | new DirectoryTree("page", "Contents of SPA", [ 28 | new DirectoryTree("foo", "Some page component", [ 29 | { name: "FooPage.html", description: "HTML of Page" }, 30 | { name: "FooPage.less", description: "Style of Page (option)" }, 31 | { name: "FooPage.ts", description: "Script of Page (ViewModel(implements IPage) , component register , style append)" }, 32 | ]), 33 | new DirectoryTree("bar", "Some page component", [ 34 | { name: "BarPage.html", description: "HTML of Page" }, 35 | { name: "BarPage.ts", description: "Script of Page (ViewModel(implements IPage) , component register , style append)" }, 36 | ]), 37 | { name: "IPage.d.ts", description: "Page interface define" }, 38 | ]), 39 | new DirectoryTree("parts", "Custom element component", [ 40 | new DirectoryTree("baz", "Some component", [ 41 | { name: "Baz.html", description: "Component Template" }, 42 | { name: "Baz.less", description: "Style of Component (option)" }, 43 | { name: "Baz.ts", description: "Script of Component (ViewModel , component register , style append)" }, 44 | ]), 45 | ]), 46 | new DirectoryTree("typings", "Typescript Definitely Typed(by tsd)", [ 47 | ]), 48 | { name: "Application.ts", description: "Application Main" }, 49 | { name: "package.json", description: "npm package meta" }, 50 | { name: "references.d.ts", description: "Typescript Definitely Reference" }, 51 | { name: "tsd.json", description: "Typescript Definitely Typed meta" }, 52 | { name: "webpack.config.js", description: "WebPack build config" } 53 | ]) 54 | ]); 55 | ko.track(this); 56 | } 57 | return HomePage; 58 | })(); 59 | require('./HomePage.less'); 60 | ko.components.register('home-page', { 61 | template: require('./HomePage.html'), 62 | viewModel: { 63 | createViewModel: function (params, componentInfo) { 64 | return params instanceof HomePage ? params : params.option; 65 | } 66 | } 67 | }); 68 | module.exports = HomePage; 69 | -------------------------------------------------------------------------------- /public/lib/css3tips/css3-tips.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS3 Tips v1.0.1 3 | * 4 | * A stylesheet for creating tooltips without using anything other than CSS3. 5 | * 6 | * created by c.bavota 7 | * released under GPL v2 8 | * 9 | * March 21st, 2014 10 | */ 11 | [data-tips] { 12 | position: relative; 13 | text-decoration: none; 14 | } 15 | 16 | [data-tips]:after, 17 | [data-tips]:before { 18 | position: absolute; 19 | z-index: 100; 20 | opacity: 0; 21 | } 22 | 23 | [data-tips]:after { 24 | content: attr(data-tips); 25 | height: 25px; 26 | line-height: 25px; 27 | padding: 0 10px; 28 | font-size: 12px; 29 | text-align: center; 30 | color: #fff; 31 | background: #222; 32 | border-radius: 5px; 33 | text-shadow: 0 0 5px #000; 34 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.3); 35 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.3); 36 | box-shadow: 0 0 5px rgba(0,0,0,0.3); 37 | white-space: nowrap; 38 | -moz-box-sizing: border-box; 39 | -webkit-box-sizing: border-box; 40 | box-sizing: border-box; 41 | } 42 | 43 | [data-tips]:before { 44 | content: ""; 45 | width: 0; 46 | height: 0; 47 | border-width: 6px; 48 | border-style: solid; 49 | } 50 | 51 | [data-tips]:hover:after, 52 | [data-tips]:hover:before { 53 | opacity: 1; 54 | } 55 | 56 | /* Top tips */ 57 | [data-tips].top-tip:after, 58 | [data-tips].top-tip:before { 59 | -webkit-transition: bottom 0.25s ease-in-out, opacity 0.25s ease-in-out; 60 | -moz-transition: bottom 0.25s ease-in-out, opacity 0.25s ease-in-out; 61 | transition: bottom 0.25s ease-in-out, opacity 0.25s ease-in-out; 62 | bottom: 90%; 63 | left: -9999px; 64 | margin-bottom: 12px; 65 | } 66 | 67 | [data-tips].top-tip:before { 68 | border-color: #222 transparent transparent transparent; 69 | margin-bottom: 0; 70 | } 71 | 72 | [data-tips].top-tip:hover:after, 73 | [data-tips].top-tip:hover:before { 74 | bottom: 100%; 75 | left: 0; 76 | } 77 | 78 | [data-tips].top-tip:hover:before { 79 | left: 15px; 80 | } 81 | 82 | /* Bottom tip */ 83 | [data-tips].bottom-tip:after, 84 | [data-tips].bottom-tip:before { 85 | -webkit-transition: top 0.25s ease-in-out, opacity 0.25s ease-in-out; 86 | -moz-transition: top 0.25s ease-in-out, opacity 0.25s ease-in-out; 87 | transition: top 0.25s ease-in-out, opacity 0.25s ease-in-out; 88 | top: 90%; 89 | left: -9999px; 90 | margin-top: 12px; 91 | } 92 | 93 | [data-tips].bottom-tip:before { 94 | border-color: transparent transparent #222 transparent; 95 | margin-top: 0; 96 | } 97 | 98 | [data-tips].bottom-tip:hover:after, 99 | [data-tips].bottom-tip:hover:before { 100 | top: 100%; 101 | left: 0; 102 | } 103 | 104 | [data-tips].bottom-tip:hover:before { 105 | left: 15px; 106 | } 107 | 108 | /* Right tip */ 109 | [data-tips].right-tip:after, 110 | [data-tips].right-tip:before { 111 | -webkit-transition: left 0.25s ease-in-out, opacity 0.25s ease-in-out; 112 | -moz-transition: left 0.25s ease-in-out, opacity 0.25s ease-in-out; 113 | transition: left 0.25s ease-in-out, opacity 0.25s ease-in-out; 114 | top: -9999px; 115 | left: 96%; 116 | margin-left: 12px; 117 | } 118 | 119 | [data-tips].right-tip:before { 120 | border-color: transparent #222 transparent transparent; 121 | margin-left: 0; 122 | } 123 | 124 | [data-tips].right-tip:hover:after, 125 | [data-tips].right-tip:hover:before { 126 | left: 100%; 127 | top: 0; 128 | } 129 | 130 | [data-tips].right-tip:hover:before { 131 | top: 7px; 132 | } 133 | 134 | /* Left tip */ 135 | [data-tips].left-tip:after, 136 | [data-tips].left-tip:before { 137 | -webkit-transition: right 0.25s ease-in-out, opacity 0.25s ease-in-out; 138 | -moz-transition: right 0.25s ease-in-out, opacity 0.25s ease-in-out; 139 | transition: right 0.25s ease-in-out, opacity 0.25s ease-in-out; 140 | top: -9999px; 141 | right: 96%; 142 | margin-right: 12px; 143 | } 144 | 145 | [data-tips].left-tip:before { 146 | border-color: transparent transparent transparent #222; 147 | margin-right: 0; 148 | } 149 | 150 | [data-tips].left-tip:hover:after, 151 | [data-tips].left-tip:hover:before { 152 | right: 100%; 153 | top: 0; 154 | } 155 | 156 | [data-tips].left-tip:hover:before { 157 | top: 7px; 158 | } --------------------------------------------------------------------------------