made example working
This commit is contained in:
parent
76fb554cff
commit
b8b21655cf
49
.gitignore
vendored
49
.gitignore
vendored
|
@ -1 +1,48 @@
|
|||
# ignore
|
||||
#
|
||||
# es5-ff-spa-loader git ignore rules
|
||||
#
|
||||
|
||||
# Ignore npm files
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
# Ignore mocha test data
|
||||
test/data
|
||||
|
||||
# Ignore example data
|
||||
example/node_modules
|
||||
example/npm-debug.log
|
||||
example/www_static/css/lib
|
||||
example/www_static/js/lib
|
||||
|
||||
# Ignore binary files
|
||||
*.o
|
||||
*.class
|
||||
*.old
|
||||
*.bak
|
||||
*.backup
|
||||
*.dat
|
||||
*.data
|
||||
*.gif
|
||||
*.pdf
|
||||
*.doc
|
||||
|
||||
# Ignore ~ backup files.
|
||||
*~
|
||||
|
||||
# Ignore some eclipse files
|
||||
.settings
|
||||
.classpath
|
||||
|
||||
# Ignore netbeans directory
|
||||
nbproject
|
||||
|
||||
# Ignore mac finder files
|
||||
.DS_Store
|
||||
|
||||
# Ignore windows files.
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# Ignore kde dolphin files
|
||||
.directory
|
||||
|
|
11
.project
Normal file
11
.project
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>es5-ff-spa-loader</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
</natures>
|
||||
</projectDescription>
|
40
README.md
40
README.md
|
@ -1 +1,41 @@
|
|||
es5-ff-spa-loader
|
||||
=========
|
||||
|
||||
A javascript library providing server defined loading of resouces for a single page application.
|
||||
|
||||
## Installation
|
||||
|
||||
Use the es5-ff-spa-loader.js in header as script resource or inline in body.
|
||||
|
||||
## Usage
|
||||
|
||||
On index.html
|
||||
|
||||
FFSpaLoader.start({
|
||||
url: '/my/url/for/resources.json',
|
||||
debug: true
|
||||
});
|
||||
|
||||
## Options
|
||||
|
||||
* url = The url to fetch resources list from.
|
||||
* debug = Enable console debug output.
|
||||
* cachePrefix = The cache key prefix. (default: 'sync-')
|
||||
* cacheGetFn = example; 'function(key) {return localStorage.getItem(key);}'
|
||||
* cacheSetFn = example; 'function(key,value) {localStorage.setItem(key,value);}'
|
||||
* cacheDelFn = example; 'function(key) {return localStorage.removeItem(key);}'
|
||||
|
||||
## Example Application
|
||||
|
||||
There is a fully working express example application in the example folder.
|
||||
todo
|
||||
|
||||
## Contributing
|
||||
|
||||
In lieu of a formal styleguide, take care to maintain the existing coding style.
|
||||
Add unit tests for any new or changed functionality. Lint and test your code.
|
||||
|
||||
## Release History
|
||||
|
||||
### 0.1.x
|
||||
* Initial release
|
||||
|
|
299
es5-ff-spa-loader.js
Normal file
299
es5-ff-spa-loader.js
Normal file
|
@ -0,0 +1,299 @@
|
|||
'use strict';
|
||||
/*
|
||||
* Copyright (c) 2015-2016, Willem Cazander
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are permitted provided
|
||||
* that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this list of conditions and the
|
||||
* following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
|
||||
* the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
||||
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
|
||||
// TODO:
|
||||
// conv to req queue
|
||||
// split js/css so css can be done later
|
||||
// set tag.media = 'only you';
|
||||
// add media in resouces
|
||||
// add userAgant match in resouces
|
||||
// NOTE: add ordered inject, for now JS in non-cache mode needs to be aggragated server side
|
||||
// add browser supported
|
||||
|
||||
var FFSpaLoader = (function () {
|
||||
|
||||
var options = {};
|
||||
|
||||
var utilDebug = function (message) {
|
||||
if (options.debug !== true) {
|
||||
return;
|
||||
}
|
||||
console.log('FFSpaLoader.'+message);
|
||||
};
|
||||
|
||||
var utilHttpFetch = function (url, cb) {
|
||||
var startTime = new Date().getTime();
|
||||
var httpRequest = new XMLHttpRequest();
|
||||
httpRequest.onreadystatechange = function() {
|
||||
if (httpRequest.readyState == 4) {
|
||||
utilDebug('utilHttpFetch url: '+url+' done in '+(new Date().getTime()-startTime)+' ms.');
|
||||
cb(httpRequest);
|
||||
}
|
||||
}
|
||||
httpRequest.open('GET', url, true);
|
||||
httpRequest.send();
|
||||
};
|
||||
|
||||
var cacheHasItem = function (resource) {
|
||||
if (options.cacheGetFn === undefined || options.cacheSetFn === undefined || options.cacheDelFn === undefined) {
|
||||
return false;
|
||||
}
|
||||
var result = options.cacheGetFn(options.cachePrefix + resource.hash) !== null;
|
||||
utilDebug('cacheHasItem resource: '+resource.url+' result: '+result);
|
||||
return result;
|
||||
}
|
||||
|
||||
var cacheGetItem = function (resource) {
|
||||
if (options.cacheGetFn === undefined) {
|
||||
throw new Error('no caching');
|
||||
}
|
||||
utilDebug('cacheGetItem resource: '+resource.url);
|
||||
return JSON.parse(options.cacheGetFn(options.cachePrefix + resource.hash));
|
||||
}
|
||||
|
||||
var cachePutRequest = function (resource, httpRequest) {
|
||||
if (options.cacheSetFn === undefined) {
|
||||
throw new Error('no caching');
|
||||
}
|
||||
var cacheKey = options.cachePrefix + resource.hash;
|
||||
var data = httpRequest.responseText;
|
||||
utilDebug('cachePutData url: '+resource.url+' key: '+cacheKey);
|
||||
options.cacheSetFn(cacheKey, JSON.stringify({
|
||||
resource: resource,
|
||||
data: data,
|
||||
dataDate: new Date().getTime()
|
||||
}));
|
||||
|
||||
// Add all cache keys in central list so we can clear the cache item if resources are removed.
|
||||
var cacheListKey = options.cachePrefix + 'cache-list';
|
||||
var cacheListStr = options.cacheGetFn(cacheListKey);
|
||||
if (cacheListStr === null) {
|
||||
cacheListStr = '[]';
|
||||
}
|
||||
var cacheList = JSON.parse(cacheListStr);
|
||||
cacheList.push(cacheKey);
|
||||
options.cacheSetFn(cacheListKey, JSON.stringify(cacheList));
|
||||
}
|
||||
|
||||
var cacheCleanup = function (resources) {
|
||||
if (options.cacheGetFn === undefined || options.cacheSetFn === undefined || options.cacheDelFn === undefined) {
|
||||
return;
|
||||
}
|
||||
var cacheListKey = options.cachePrefix + 'cache-list';
|
||||
var cacheListStr = options.cacheGetFn(cacheListKey);
|
||||
if (cacheListStr === null) {
|
||||
return;
|
||||
}
|
||||
var cacheList = JSON.parse(cacheListStr);
|
||||
|
||||
utilDebug('cacheCleanup TODO cacheList: '+cacheList.length+' resources: '+resources.length);
|
||||
|
||||
// TODO: impl for removes in resource lists
|
||||
}
|
||||
|
||||
var createResource = function (resource, cb) {
|
||||
utilDebug('createResource url: '+JSON.stringify(resource));
|
||||
if (cacheHasItem(resource)) {
|
||||
if (cacheGetItem(resource).resource.hash === resource.hash) {
|
||||
cb();
|
||||
return;
|
||||
}
|
||||
utilDebug('createResource hash mismatch request fetch: '+resource.url);
|
||||
} else {
|
||||
utilDebug('createResource cache miss request fetch: '+resource.url);
|
||||
}
|
||||
utilHttpFetch(resource.url, function(httpRequest) {
|
||||
if (httpRequest.status == 200) {
|
||||
cachePutRequest(resource, httpRequest);
|
||||
} else {
|
||||
console.warn('error loading '+resource.url);
|
||||
}
|
||||
cb();
|
||||
});
|
||||
}
|
||||
|
||||
var createResources = function(resources, cb) {
|
||||
var startTime = new Date().getTime();
|
||||
utilDebug('createResources');
|
||||
var resourceStack = resources;
|
||||
var resourceLoader = function() {
|
||||
resourceStack = resourceStack.slice(1);
|
||||
if (resourceStack.length === 0) {
|
||||
utilDebug('createResources done in '+(new Date().getTime()-startTime)+' ms.');
|
||||
cb();
|
||||
} else {
|
||||
createResource(resourceStack[0],resourceLoader);
|
||||
}
|
||||
};
|
||||
createResource(resourceStack[0],resourceLoader);
|
||||
}
|
||||
|
||||
var injectResources = function(resources, cb) {
|
||||
var startTime = new Date().getTime();
|
||||
utilDebug('injectResources');
|
||||
resources.forEach(function (resource) {
|
||||
var item = cacheGetItem(resource);
|
||||
var tag = null;
|
||||
if (resource.type === 'css') {
|
||||
tag = document.createElement('style');
|
||||
tag.type = 'text/css';
|
||||
} else {
|
||||
tag = document.createElement('script');
|
||||
tag.type = 'text/javascript';
|
||||
}
|
||||
utilDebug('injectResources resource: '+JSON.stringify(resource));
|
||||
|
||||
tag.appendChild(document.createTextNode(item.data));
|
||||
|
||||
//document.getElementsByTagName('head')[0].appendChild(tag);
|
||||
var ref = document.getElementsByTagName('script')[0];
|
||||
ref.parentNode.insertBefore(tag, ref); // note in reverse order
|
||||
});
|
||||
utilDebug('injectResources done in '+(new Date().getTime()-startTime)+' ms.');
|
||||
cb();
|
||||
}
|
||||
|
||||
var injectLinkResources = function(resources, cb) {
|
||||
var startTime = new Date().getTime();
|
||||
utilDebug('injectLinkResources');
|
||||
resources.forEach(function (resource) {
|
||||
utilDebug('injectLinkResources resource: '+JSON.stringify(resource));
|
||||
var tag = null;
|
||||
if (resource.type === 'css') {
|
||||
tag = document.createElement('link');
|
||||
tag.type = 'text/css';
|
||||
tag.rel = 'stylesheet';
|
||||
tag.href = resource.url;
|
||||
}
|
||||
if (resource.type === 'js') {
|
||||
tag = document.createElement('script');
|
||||
tag.type = 'text/javascript';
|
||||
tag.src = resource.url;
|
||||
}
|
||||
if (tag !== null) {
|
||||
var startTime2 = new Date().getTime();
|
||||
document.getElementsByTagName('head')[0].appendChild(tag);
|
||||
utilDebug('inject time in '+(new Date().getTime()-startTime2)+' ms.');
|
||||
//var ref = document.getElementsByTagName('script')[0];
|
||||
//ref.parentNode.insertBefore(tag, ref); // TODO reverser order
|
||||
} else {
|
||||
utilDebug('unknow resource type: '+resource.type);
|
||||
// TODO add err
|
||||
}
|
||||
});
|
||||
utilDebug('injectLinkResources done in '+(new Date().getTime()-startTime)+' ms.');
|
||||
cb();
|
||||
}
|
||||
|
||||
var startDefaults = function () {
|
||||
if (options.url === undefined) {
|
||||
throw new Error('No url defined');
|
||||
}
|
||||
if (options.debug === undefined) {
|
||||
options.debug = false;
|
||||
}
|
||||
if (options.cachePrefix === undefined) {
|
||||
options.cachePrefix = 'sync-';
|
||||
}
|
||||
}
|
||||
|
||||
var startDone = function (startTime) {
|
||||
utilDebug('start done in '+(new Date().getTime()-startTime)+' ms.');
|
||||
if (options.resultFn) {
|
||||
options.resultFn();
|
||||
}
|
||||
}
|
||||
|
||||
var start = function (opt) {
|
||||
if (opt === undefined) {
|
||||
throw new Error('need options');
|
||||
}
|
||||
var startTime = new Date().getTime();
|
||||
options = opt;
|
||||
startDefaults();
|
||||
var injectAsLink = options.cacheGetFn === undefined || options.cacheSetFn === undefined || options.cacheDelFn === undefined;
|
||||
utilDebug('start options: '+JSON.stringify(options)+' injectAsLink: '+injectAsLink);
|
||||
|
||||
|
||||
|
||||
var resources = [];
|
||||
utilHttpFetch(options.url, function(httpRequest) {
|
||||
if (httpRequest.status == 200) {
|
||||
JSON.parse(httpRequest.responseText).data.resources.forEach(function (r) {
|
||||
resources.push(r);
|
||||
utilDebug('build resources add: '+JSON.stringify(r));
|
||||
});
|
||||
if (injectAsLink) {
|
||||
injectLinkResources(resources, function () {
|
||||
startDone(startTime);
|
||||
});
|
||||
} else {
|
||||
createResources(resources, function () {
|
||||
injectResources(resources, function () {
|
||||
cacheCleanup(resources);
|
||||
startDone(startTime);
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
console.log('app err'); // TODO: fix async load error code path
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
var getOrAskUrl = function (opt) {
|
||||
|
||||
};
|
||||
// TODO: getOrAskUrl: getOrAskUrl,
|
||||
|
||||
return {
|
||||
start: start
|
||||
};
|
||||
})();
|
||||
|
||||
var autoStartFFSpaLoader = function () {
|
||||
var scripts = document.getElementsByTagName('SCRIPT');
|
||||
if (scripts && scripts.length>0) {
|
||||
for (var i in scripts) {
|
||||
if (scripts[i].src && scripts[i].src.match(/.*ffSpaLoaderUrl.*/)) {
|
||||
var query = scripts[i].src;
|
||||
var queryParameters = {};
|
||||
query.split('?').forEach(function(part) {
|
||||
var item = part.split("=");
|
||||
queryParameters[item[0]] = decodeURIComponent(item[1]);
|
||||
});
|
||||
// TODO: remove debug here
|
||||
FFSpaLoader.start({
|
||||
debug: true,
|
||||
injectAsLink: true,
|
||||
url: queryParameters.ffSpaLoaderUrl
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
autoStartFFSpaLoader(); // TODO: make private
|
27
example/example-assets.json
Normal file
27
example/example-assets.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"linkMapping" : {
|
||||
"/static/module/bootstrap/": "node_modules/bootstrap/dist/",
|
||||
"/static/module/flot/": "node_modules/flot/",
|
||||
"/static/": "www_static/"
|
||||
},
|
||||
"css": {
|
||||
"linkTarget": "/static/css/lib/assets.css",
|
||||
"linkSources": [
|
||||
"/static/module/bootstrap/css/bootstrap.css"
|
||||
]
|
||||
},
|
||||
"js": {
|
||||
"linkTarget": "/static/js/lib/assets.js",
|
||||
"linkSources": [
|
||||
"/static/js/lib/jquery-2.1.3/jquery.js@http://code.jquery.com/jquery-2.1.3.js",
|
||||
"/static/module/bootstrap/js/bootstrap.js",
|
||||
"/static/module/flot/jquery.flot.js",
|
||||
"/static/module/flot/jquery.flot.resize.js",
|
||||
"/static/module/flot/jquery.flot.pie.js",
|
||||
"/static/js/lib/angularjs-1.4.0-b4/angular.js@https://code.angularjs.org/1.4.0-beta.4/angular.js",
|
||||
"/static/js/lib/angularjs-1.4.0-b4/angular-route.js@https://code.angularjs.org/1.4.0-beta.4/angular-route.js",
|
||||
"/static/js/lib/angularjs-1.4.0-b4/angular-resource.js@https://code.angularjs.org/1.4.0-beta.4/angular-resource.js",
|
||||
"/static/js/lib/angularjs-1.4.0-b4/angular-touch.js@https://code.angularjs.org/1.4.0-beta.4/angular-touch.js"
|
||||
]
|
||||
}
|
||||
}
|
148
example/example.js
Normal file
148
example/example.js
Normal file
|
@ -0,0 +1,148 @@
|
|||
'use strict';
|
||||
|
||||
var express = require('express');
|
||||
var async = require('async');
|
||||
var path = require('path');
|
||||
var assets = require('node-ff-assets');
|
||||
var fs = require('fs');
|
||||
var minify = require('minify');
|
||||
|
||||
function buildAssets(server,callbackDone) {
|
||||
var singleResult = 'false' !== process.env.DEV_ASSETS_SINGLE_RESULT;
|
||||
var assetsConfig = require('./example-assets.json');
|
||||
assets.build({
|
||||
assets: {
|
||||
js: {
|
||||
configCreate: assets.factory.builder.configCreate.fromJSON(assetsConfig,'js'),
|
||||
configFill: function (config, callback) {
|
||||
async.series([
|
||||
assets.factory.lib.async.pushLinkSources(config, '/static/js/', 'www_static/js/'),
|
||||
assets.factory.lib.async.pushLinkSources(config, '/static/js/controller/', 'www_static/js/controller/'),
|
||||
],callback);
|
||||
},
|
||||
},
|
||||
css: {
|
||||
configCreate: assets.factory.builder.configCreate.fromJSON(assetsConfig,'css'),
|
||||
configFill: function (config, callback) {
|
||||
async.series([
|
||||
assets.factory.lib.async.pushLinkSources(config, '/static/css/', 'www_static/css/'),
|
||||
],callback);
|
||||
},
|
||||
},
|
||||
},
|
||||
assemblerCreate: assets.factory.builder.assemblerCreate.readFileRegex(),
|
||||
assemblerFill: function (assembler, callback) {
|
||||
var serverResultKey = 'ff_assets_'+assembler.config.assetType;
|
||||
assembler.on ('log', assets.factory.assembler.event.log.console(assembler.config.assetType));
|
||||
assembler.on ('result', assets.factory.assembler.event.result.objectSet(server,serverResultKey));
|
||||
assembler.config.linkTargetSingleResult = singleResult;
|
||||
callback();
|
||||
},
|
||||
},callbackDone);
|
||||
}
|
||||
|
||||
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(server) {
|
||||
var inline = '';
|
||||
minify(__dirname+'/../es5-ff-spa-loader.js', {}, function(err, data) {
|
||||
inline = '\n\t\t<script>'+data+'</script>';
|
||||
});
|
||||
var inlineNot = '\n\t\t<script src=\"/static/es5-ff-spa-loader.js\"></script>';
|
||||
|
||||
var useInline = true; // or false
|
||||
|
||||
return function (req, res) {
|
||||
res.render('index', {
|
||||
inlineNot: useInline?'':inlineNot,
|
||||
inline: useInline?inline:''
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function sendRedirect() {
|
||||
return function (req, res) {
|
||||
res.redirect('/example-ui');
|
||||
};
|
||||
}
|
||||
|
||||
var stringHash = function (str) {
|
||||
var hash = 31; // prime
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
hash = ((hash<<5)-hash)+str.charCodeAt(i);
|
||||
hash = hash & hash; // keep 32b
|
||||
}
|
||||
return hash;
|
||||
};
|
||||
|
||||
var server = express();
|
||||
buildAssets(server,function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.info('Server assets done.');
|
||||
|
||||
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/flot', express.static(path.join(__dirname,'node_modules/flot')));
|
||||
|
||||
server.get('/static/es5-ff-spa-loader.js', function (req,res) {
|
||||
res.write(fs.readFileSync(__dirname+'/../es5-ff-spa-loader.js', 'utf8'));
|
||||
res.end();
|
||||
});
|
||||
server.get('/static/resources-spa-mobile', function (req,res) {
|
||||
var hashJs = stringHash(fs.readFileSync(__dirname+'/www_static/js/lib/assets.js', 'utf8'));
|
||||
var hashCss = stringHash(fs.readFileSync(__dirname+'/www_static/css/lib/assets.css', 'utf8'));
|
||||
res.json({
|
||||
data: {
|
||||
resources: [
|
||||
{
|
||||
url: '/static/js/lib/assets.js----TODO',
|
||||
type: 'js',
|
||||
hash: hashJs
|
||||
},
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
server.get('/static/resources-spa-web', function (req,res) {
|
||||
var hashJs = stringHash(fs.readFileSync(__dirname+'/www_static/js/lib/assets.js', 'utf8'));
|
||||
var hashCss = stringHash(fs.readFileSync(__dirname+'/www_static/css/lib/assets.css', 'utf8'));
|
||||
res.json({
|
||||
data: {
|
||||
resources: [
|
||||
{
|
||||
url: '/static/js/lib/assets.js',
|
||||
type: 'js',
|
||||
hash: hashJs
|
||||
},
|
||||
{
|
||||
url: '/static/css/lib/assets.css',
|
||||
type: 'css',
|
||||
hash: hashCss
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
server.get('/', sendRedirect());
|
||||
server.get('/example-ui/thtml/*', renderTemplatePath('thtml/'));
|
||||
server.get('/example-ui', renderIndex(server));
|
||||
server.get('/example-ui/*', renderIndex(server)); // must be last; for HTML5 history
|
||||
console.info('Server config done.');
|
||||
|
||||
server.listen(8080);
|
||||
console.info('Server started on port 8080');
|
||||
});
|
18
example/package.json
Normal file
18
example/package.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "es5-ff-spa-loader-example",
|
||||
"description": "es5-ff-spa-loader-example",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node example.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "1.2.x",
|
||||
"bootstrap": "3.3.x",
|
||||
"ejs": "2.3.x",
|
||||
"express": "4.11.x",
|
||||
"flot": "0.8.x",
|
||||
"minify": "^2.0.2",
|
||||
"node-ff-assets": "^0.2.5"
|
||||
}
|
||||
}
|
140
example/www_static/css/boot.css
Normal file
140
example/www_static/css/boot.css
Normal file
|
@ -0,0 +1,140 @@
|
|||
|
||||
body {
|
||||
margin-top: 100px;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
@media ( min-width :768px) {
|
||||
body {
|
||||
margin-top: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
#wrapper {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
#page-wrapper {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 50px;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
@media ( min-width :768px) {
|
||||
#wrapper {
|
||||
padding-left: 225px;
|
||||
}
|
||||
#page-wrapper {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Top Navigation */
|
||||
.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;
|
||||
}
|
||||
|
||||
.huge {
|
||||
font-size: 40px;
|
||||
}
|
10
example/www_static/css/flot.css
Normal file
10
example/www_static/css/flot.css
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
.flot-chart {
|
||||
display: block;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.flot-chart-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
56
example/www_static/css/panel.css
Normal file
56
example/www_static/css/panel.css
Normal file
|
@ -0,0 +1,56 @@
|
|||
|
||||
.panel-green {
|
||||
border-color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green .panel-heading {
|
||||
border-color: #5cb85c;
|
||||
color: #fff;
|
||||
background-color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green a {
|
||||
color: #5cb85c;
|
||||
}
|
||||
|
||||
.panel-green a:hover {
|
||||
color: #3d8b3d;
|
||||
}
|
||||
|
||||
.panel-red {
|
||||
border-color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red .panel-heading {
|
||||
border-color: #d9534f;
|
||||
color: #fff;
|
||||
background-color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red a {
|
||||
color: #d9534f;
|
||||
}
|
||||
|
||||
.panel-red a:hover {
|
||||
color: #b52b27;
|
||||
}
|
||||
|
||||
.panel-yellow {
|
||||
border-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow .panel-heading {
|
||||
border-color: #f0ad4e;
|
||||
color: #fff;
|
||||
background-color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow a {
|
||||
color: #f0ad4e;
|
||||
}
|
||||
|
||||
.panel-yellow a:hover {
|
||||
color: #df8a13;
|
||||
}
|
||||
|
||||
|
44
example/www_static/css/style.css
Normal file
44
example/www_static/css/style.css
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
#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;
|
||||
}
|
||||
|
10
example/www_static/js/controller/page-bar.js
Normal file
10
example/www_static/js/controller/page-bar.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
pageRouteInit.push(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/example-ui/bar', {
|
||||
templateUrl: '/example-ui/thtml/bar',
|
||||
controller: PageFoo
|
||||
});
|
||||
});
|
||||
|
||||
function PageFoo($scope, $http) {
|
||||
}
|
10
example/www_static/js/controller/page-foo.js
Normal file
10
example/www_static/js/controller/page-foo.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
pageRouteInit.push(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/example-ui/foo', {
|
||||
templateUrl: '/example-ui/thtml/foo',
|
||||
controller: PageFoo
|
||||
});
|
||||
});
|
||||
|
||||
function PageFoo($scope, $http) {
|
||||
}
|
10
example/www_static/js/controller/page-index.js
Normal file
10
example/www_static/js/controller/page-index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
pageRouteInit.push(function ($routeProvider, $locationProvider) {
|
||||
$routeProvider.when('/example-ui', {
|
||||
templateUrl: '/example-ui/thtml/index',
|
||||
controller: PageIndex
|
||||
});
|
||||
});
|
||||
|
||||
function PageIndex($scope, $http) {
|
||||
}
|
22
example/www_static/js/example-app.js
Normal file
22
example/www_static/js/example-app.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
'use strict';
|
||||
|
||||
document.title = 'FF-Spa-Loader Example';
|
||||
$('html').attr('ng-app', 'exampleUI');
|
||||
$('html').attr('lang', 'en');
|
||||
|
||||
$(document.createElement('div')).attr('id', 'wrapper').appendTo($('body'));
|
||||
$(document.createElement('div')).attr('ng-include', '\'/example-ui/thtml/layout/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', '\'/example-ui/thtml/layout/footer\'').appendTo($('body'));
|
||||
|
||||
var pageRouteInit = [];
|
||||
angular.module('exampleUI', ['ngRoute']).
|
||||
config(['$routeProvider', '$locationProvider', function($routeProvider, $locationProvider) {
|
||||
for (var i = 0; i < pageRouteInit.length; i++) {
|
||||
pageRouteInit[i]($routeProvider, $locationProvider);
|
||||
}
|
||||
$routeProvider.otherwise({ redirectTo: '/example-ui' });
|
||||
$locationProvider.html5Mode({enabled: true, requireBase: false});
|
||||
}]);
|
||||
|
18
example/www_views/index.ejs
Normal file
18
example/www_views/index.ejs
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<title>Loading</title><%- inlineNot %>
|
||||
</head>
|
||||
<body><%- inline %>
|
||||
<script>
|
||||
FFSpaLoader.start({
|
||||
url: '/static/resources-spa-web',
|
||||
debug: true,
|
||||
cacheGetFn: function(key) { return localStorage.getItem(key); },
|
||||
cacheSetFn: function(key,value) { localStorage.setItem(key,value); },
|
||||
cacheDelFn: function(key) { return localStorage.removeItem(key); }
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
32
example/www_views/thtml/bar.ejs
Normal file
32
example/www_views/thtml/bar.ejs
Normal file
|
@ -0,0 +1,32 @@
|
|||
<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>
|
4
example/www_views/thtml/foo.ejs
Normal file
4
example/www_views/thtml/foo.ejs
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div>
|
||||
<h2>Foo</h2>
|
||||
<p>Welcome to the foo.</p>
|
||||
</div>
|
4
example/www_views/thtml/index.ejs
Normal file
4
example/www_views/thtml/index.ejs
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div>
|
||||
<h2>Example UI Index</h2>
|
||||
<p>Welcome make yourself at home.</p>
|
||||
</div>
|
5
example/www_views/thtml/layout/footer.ejs
Normal file
5
example/www_views/thtml/layout/footer.ejs
Normal file
|
@ -0,0 +1,5 @@
|
|||
<nav class="navbar">
|
||||
<div class="navbar-footer">
|
||||
Example footer
|
||||
</div>
|
||||
</nav>
|
13
example/www_views/thtml/layout/header.ejs
Normal file
13
example/www_views/thtml/layout/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" href="/example-ui/">Home</a>
|
||||
<a class="navbar-brand" href="/example-ui/foo">Foo</a>
|
||||
<a class="navbar-brand" href="/example-ui/bar">Bar</a>
|
||||
</div>
|
||||
</nav>
|
13
package.json
Normal file
13
package.json
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "es5-ff-spa-loader",
|
||||
"version": "0.0.1",
|
||||
"description": "Javascript Single Page Application Loader",
|
||||
"main": "es5-ff-spa-loader.js",
|
||||
"author": "Willem <willem.git.2016@forwardfire.net> (http://forwardfire.net/)",
|
||||
"keywords": ["website","assets","js","css","loader","spa","mobile","resources","sync","cache"],
|
||||
"license": "BSD-2-Clause",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://bitbucket.org/im_ik/es5-ff-spa-loader.git"
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue