Added mobile example app
This commit is contained in:
parent
4fbf705ae5
commit
cf364fc418
26 changed files with 187 additions and 61 deletions
125
example/app_server/example.js
Normal file
125
example/app_server/example.js
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
'use strict';
|
||||
|
||||
var httpPort = 9090;
|
||||
var express = require('express');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var fetch = require('fetch');
|
||||
var cors = require('cors');
|
||||
var morgan = require('morgan');
|
||||
var UglifyJS = require("uglify-js");
|
||||
var Hashes = require('jshashes');
|
||||
|
||||
var clientResourcesWeb = [];
|
||||
var clientResources = {
|
||||
js: [],
|
||||
css: [],
|
||||
cssData: []
|
||||
};
|
||||
|
||||
var addClientResource = function(clientResource, resourceType) {
|
||||
clientResources[resourceType].push(clientResource);
|
||||
};
|
||||
|
||||
var fetchHashResource = function(fetchEntry,cb) {
|
||||
var serverUrl = 'http://localhost:'+httpPort;
|
||||
var hashDigest = new Hashes.SHA1;
|
||||
fetch.fetchUrl(serverUrl + fetchEntry.url,function(err, meta, data) {
|
||||
if (err !== null) { return cb(err); }
|
||||
var assetHash = hashDigest.hex(''+data);
|
||||
clientResourcesWeb.push({
|
||||
url: fetchEntry.url,
|
||||
type: fetchEntry.type,
|
||||
hash: assetHash
|
||||
});
|
||||
cb(null);
|
||||
});
|
||||
};
|
||||
|
||||
var fetchHashResources = function(fetchList, cb) {
|
||||
var resourceStack = fetchList;
|
||||
var resourceLoader = function() {
|
||||
resourceStack = resourceStack.slice(1);
|
||||
if (resourceStack.length === 0) {
|
||||
cb(null);
|
||||
} else {
|
||||
fetchHashResource(resourceStack[0],resourceLoader);
|
||||
}
|
||||
};
|
||||
fetchHashResource(resourceStack[0],resourceLoader);
|
||||
};
|
||||
|
||||
var createClientResourceFetchList = function() {
|
||||
var fetchList = [];
|
||||
for (var clientResourceIdxJs in clientResources.js) {
|
||||
var urlJs = clientResources.js[clientResourceIdxJs];
|
||||
fetchList.push({url:urlJs,type:'js'});
|
||||
}
|
||||
for (var clientResourceIdxCss in clientResources.css) {
|
||||
var urlCss = clientResources.css[clientResourceIdxCss];
|
||||
fetchList.push({url:urlCss,type:'css'});
|
||||
}
|
||||
for (var clientResourceIdxCssData in clientResources.cssData) {
|
||||
var urlCssData = clientResources.cssData[clientResourceIdxCssData];
|
||||
fetchList.push({url:urlCssData,type:'cssData'});
|
||||
}
|
||||
return fetchList;
|
||||
};
|
||||
|
||||
function renderTemplatePath(viewPath) {
|
||||
return function (req, res) {
|
||||
res.locals.query = req.query;
|
||||
var qi = req.url.indexOf('?');
|
||||
if (qi === -1) {
|
||||
qi = req.url.length;
|
||||
}
|
||||
res.render(viewPath + req.url.substring(req.route.path.length-1, qi));
|
||||
};
|
||||
}
|
||||
|
||||
function renderIndex() {
|
||||
return function (req, res) {
|
||||
var inlineScript = UglifyJS.minify(__dirname+'/../../es5-ff-spa-loader.js');
|
||||
res.render('index', {
|
||||
inlineScript: inlineScript.code
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Add resources ORDERED per type
|
||||
addClientResource('/static/module/jquery/jquery.js','js');
|
||||
addClientResource('/static/module/angular/angular.js','js');
|
||||
addClientResource('/static/module/angular-route/angular-route.js','js');
|
||||
addClientResource('/static/module/bootstrap/css/bootstrap.css','css');
|
||||
addClientResource('/static/module/bootstrap/js/bootstrap.js','js');
|
||||
addClientResource('/static/css/boot.css','css');
|
||||
addClientResource('/static/css/style.css','css');
|
||||
addClientResource('/static/js/example-app.js','js'); // deps: jquery,angular
|
||||
addClientResource('/static/js/controller/page-bar.js','js'); // deps: example-app.js
|
||||
addClientResource('/static/js/controller/page-foo.js','js');
|
||||
addClientResource('/static/js/controller/page-index.js','js');
|
||||
|
||||
var server = express();
|
||||
server.use(morgan('dev'));
|
||||
server.use(cors({credentials: true, origin: '*'}));
|
||||
server.set('view engine', 'ejs');
|
||||
server.set('views', path.join(__dirname,'www_views'));
|
||||
server.use('/static', express.static(path.join(__dirname,'www_static')));
|
||||
server.use('/static/module/bootstrap', express.static(path.join(__dirname,'../node_modules/bootstrap/dist')));
|
||||
server.use('/static/module/jquery', express.static(path.join(__dirname,'../node_modules/jquery/dist')));
|
||||
server.use('/static/module/angular', express.static(path.join(__dirname,'../node_modules/angular')));
|
||||
server.use('/static/module/angular-route', express.static(path.join(__dirname,'../node_modules/angular-route')));
|
||||
server.get('/static/spa-client-resources', function (req,res) {res.json({data: {resources: clientResourcesWeb}});});
|
||||
server.get('/', function (req, res) {res.redirect('/example-ui');});
|
||||
server.get('/example-ui/thtml/*', renderTemplatePath('thtml/'));
|
||||
server.get('/example-ui', renderIndex());
|
||||
|
||||
server.listen(httpPort);
|
||||
console.info('Server started on port '+httpPort);
|
||||
|
||||
var res = createClientResourceFetchList();
|
||||
fetchHashResources(res, function(err) {
|
||||
if (err !== null) {console.log(err);}
|
||||
console.log('Total assets build: '+clientResourcesWeb.length);
|
||||
});
|
||||
|
||||
119
example/app_server/www_static/css/boot.css
Normal file
119
example/app_server/www_static/css/boot.css
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
body {
|
||||
margin-top: 50px;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
.top-nav {
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
.top-nav>li {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.top-nav>li>a {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
line-height: 20px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.top-nav>li>a:hover, .top-nav>li>a:focus, .top-nav>.open>a, .top-nav>.open>a:hover,
|
||||
.top-nav>.open>a:focus {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
.top-nav>.open>.dropdown-menu {
|
||||
float: left;
|
||||
position: absolute;
|
||||
margin-top: 0;
|
||||
border: 1px solid rgba(0, 0, 0, .15);
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
background-color: #fff;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
|
||||
}
|
||||
|
||||
.top-nav>.open>.dropdown-menu>li>a {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
ul.message-dropdown {
|
||||
padding: 0;
|
||||
max-height: 250px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
li.message-preview {
|
||||
width: 275px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .15);
|
||||
}
|
||||
|
||||
li.message-preview>a {
|
||||
padding-top: 15px;
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
li.message-footer {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
ul.alert-dropdown {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
/* Side Navigation */
|
||||
@media ( min-width :768px) {
|
||||
.side-nav {
|
||||
position: fixed;
|
||||
top: 51px;
|
||||
left: 225px;
|
||||
width: 225px;
|
||||
margin-left: -225px;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
overflow-y: auto;
|
||||
background-color: #222;
|
||||
}
|
||||
.side-nav>li>a {
|
||||
width: 225px;
|
||||
}
|
||||
.side-nav li a:hover, .side-nav li a:focus {
|
||||
outline: none;
|
||||
background-color: #000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.side-nav>li>ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.side-nav>li>ul>li>a {
|
||||
display: block;
|
||||
padding: 10px 15px 10px 38px;
|
||||
text-decoration: none;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.side-nav>li>ul>li>a:hover {
|
||||
color: #fff;
|
||||
}
|
||||
59
example/app_server/www_static/css/style.css
Normal file
59
example/app_server/www_static/css/style.css
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
#wrapper {
|
||||
padding-right: 0px;
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
.side-nav {
|
||||
right: 0px;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.navbar-footer {
|
||||
margin-left: 45%;
|
||||
}
|
||||
|
||||
.navbar-toggle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
background-color: #C110D8;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-brand {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-brand:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-brand:hover {
|
||||
background-color: #0F9A28;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.navbar-inverse {
|
||||
background-color: #C110D8;
|
||||
}
|
||||
|
||||
@media only screen and (-webkit-min-device-pixel-ratio: 1.3),
|
||||
only screen and (-o-min-device-pixel-ratio: 13/10),
|
||||
only screen and (min-resolution: 120dpi) {
|
||||
|
||||
body {
|
||||
font-size:1.8em !important;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1440px) {
|
||||
body {
|
||||
font-size:2em !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
example/app_server/www_static/js/controller/page-bar.js
Normal file
10
example/app_server/www_static/js/controller/page-bar.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
pageRouteInit.push(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/example-ui/bar', {
|
||||
templateUrl: window.serverUrl+'/example-ui/thtml/bar',
|
||||
controller: PageFoo
|
||||
});
|
||||
});
|
||||
|
||||
function PageFoo($scope, $http) {
|
||||
}
|
||||
42
example/app_server/www_static/js/controller/page-foo.js
Normal file
42
example/app_server/www_static/js/controller/page-foo.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
var tpl = '<div><h2>Foo</h2><p>Welcome to the foo.</p></div>';
|
||||
tpl += '<input type=\"button\" class=\"btn btn-default\" ng-click=\"doReload()\" value=\"Reload\"></input>';
|
||||
tpl += '<input type=\"button\" class=\"btn btn-default\" ng-click=\"doClearServerUrl()\" value=\"Clear Server Url\"></input>';
|
||||
tpl += '<input type=\"button\" class=\"btn btn-default\" ng-click=\"doClearCache()\" value=\"Clear Cache\"></input>';
|
||||
tpl += '<p>{{message}}</p>';
|
||||
|
||||
pageRouteInit.push(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/example-ui/foo', {
|
||||
template: tpl,
|
||||
controller: PageFoo
|
||||
});
|
||||
});
|
||||
|
||||
function PageFoo($scope) {
|
||||
$scope.message = '';
|
||||
$scope.doReload = function () {
|
||||
window.location.reload(true);
|
||||
};
|
||||
|
||||
$scope.doClearServerUrl = function () {
|
||||
FFSpaLoader.clearServerUrl(function(err) {
|
||||
if (err) {
|
||||
$scope.message = 'Error: '+err;
|
||||
} else {
|
||||
$scope.message = 'Cleared server url';
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
|
||||
$scope.doClearCache = function () {
|
||||
FFSpaLoader.clearCache(function(err) {
|
||||
if (err) {
|
||||
$scope.message = 'Error: '+err;
|
||||
} else {
|
||||
$scope.message = 'Cleared cache';
|
||||
}
|
||||
$scope.$apply();
|
||||
});
|
||||
};
|
||||
}
|
||||
10
example/app_server/www_static/js/controller/page-index.js
Normal file
10
example/app_server/www_static/js/controller/page-index.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
pageRouteInit.push(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/example-ui', {
|
||||
template: '<div><h2>Example UI Index</h2><p>Welcome make yourself at home.</p></div>',
|
||||
controller: PageIndex
|
||||
});
|
||||
});
|
||||
|
||||
function PageIndex($scope, $http) {
|
||||
}
|
||||
27
example/app_server/www_static/js/example-app.js
Normal file
27
example/app_server/www_static/js/example-app.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
'use strict';
|
||||
document.title = 'FFSpaLoader Example';
|
||||
|
||||
var serverUrl = window.FFServerUrl;
|
||||
console.log('FFExample provided serverUrl \"'+serverUrl+'\"');
|
||||
|
||||
$(document.createElement('div')).attr('id', 'wrapper').appendTo($('body'));
|
||||
$(document.createElement('div')).attr('ng-controller', 'ApplicationController').attr('ng-include', '\''+serverUrl+'/example-ui/thtml/header\'').appendTo($('#wrapper'));
|
||||
$(document.createElement('div')).attr('id', 'page-wrapper').appendTo($('#wrapper'));
|
||||
$(document.createElement('div')).attr('id', 'container-fluid').attr('ng-view', '').appendTo($('#page-wrapper'));
|
||||
$(document.createElement('div')).attr('ng-include', '\''+serverUrl+'/example-ui/thtml/footer\'').appendTo($('body'));
|
||||
|
||||
var pageRouteInit = [];
|
||||
var exampleUI = angular.module('exampleUI', ['ngRoute']).config(
|
||||
['$routeProvider','$locationProvider','$sceDelegateProvider', function
|
||||
($routeProvider , $locationProvider , $sceDelegateProvider) {
|
||||
pageRouteInit.forEach(function(init) { init($routeProvider, $locationProvider); });
|
||||
$sceDelegateProvider.resourceUrlWhitelist(['self',serverUrl+'/**']);
|
||||
$routeProvider.otherwise({ redirectTo: '/example-ui' });
|
||||
$locationProvider.html5Mode({requireBase: false});
|
||||
}]);
|
||||
|
||||
exampleUI.controller('ApplicationController',function($scope,$http,$location) {
|
||||
$scope.goLink = function ( path ) {
|
||||
$location.path( path );
|
||||
};
|
||||
});
|
||||
16
example/app_server/www_views/index.ejs
Normal file
16
example/app_server/www_views/index.ejs
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Loading</title>
|
||||
</head>
|
||||
<body>
|
||||
<script><%- inlineScript %></script>
|
||||
<script>
|
||||
FFSpaLoader.options.debug.enable = true;
|
||||
FFSpaLoader.options.boot.angular.modules.push('exampleUI');
|
||||
FFSpaLoader.options.server.assets = '/static/spa-client-resources';
|
||||
FFSpaLoader.start();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
7
example/app_server/www_views/thtml/bar.ejs
Normal file
7
example/app_server/www_views/thtml/bar.ejs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<div><h2>Bar</h2><p>Welcome to the bar.</p><div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-striped"><tbody>
|
||||
<tr><th>Chair</th><th>Person</th><th>Drinking</th></tr></tbody><tbody>
|
||||
<tr><td>seat 1</td><td>empty</td><td>none</td></tr>
|
||||
<tr><td>seat 2</td><td>you</td><td>coffee</td></tr>
|
||||
<tr><td>seat 3</td><td>cat</td><td>water</td></tr>
|
||||
</tbody></table></div></div>
|
||||
5
example/app_server/www_views/thtml/footer.ejs
Normal file
5
example/app_server/www_views/thtml/footer.ejs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<nav class="navbar">
|
||||
<div class="navbar-footer">
|
||||
Example footer
|
||||
</div>
|
||||
</nav>
|
||||
13
example/app_server/www_views/thtml/header.ejs
Normal file
13
example/app_server/www_views/thtml/header.ejs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<nav class="navbar navbar-inverse navbar-fixed-top" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" ng-click="goLink('/example-ui/')">Home</a>
|
||||
<a class="navbar-brand" ng-click="goLink('/example-ui/foo')">Foo</a>
|
||||
<a class="navbar-brand" ng-click="goLink('/example-ui/bar')">Bar</a>
|
||||
</div>
|
||||
</nav>
|
||||
Loading…
Add table
Add a link
Reference in a new issue