diff --git a/.gitignore b/.gitignore index 94f7b77..2a241e7 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.project b/.project new file mode 100644 index 0000000..fa954bc --- /dev/null +++ b/.project @@ -0,0 +1,11 @@ + + + es5-ff-spa-loader + + + + + + + + diff --git a/README.md b/README.md index a93d382..3abfb6e 100644 --- a/README.md +++ b/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 diff --git a/es5-ff-spa-loader.js b/es5-ff-spa-loader.js new file mode 100644 index 0000000..bf30cb3 --- /dev/null +++ b/es5-ff-spa-loader.js @@ -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 diff --git a/example/example-assets.json b/example/example-assets.json new file mode 100644 index 0000000..e4d3138 --- /dev/null +++ b/example/example-assets.json @@ -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" + ] + } +} diff --git a/example/example.js b/example/example.js new file mode 100644 index 0000000..7acc4f2 --- /dev/null +++ b/example/example.js @@ -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'; + }); + var inlineNot = '\n\t\t'; + + 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'); +}); diff --git a/example/package.json b/example/package.json new file mode 100644 index 0000000..32f3b48 --- /dev/null +++ b/example/package.json @@ -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" + } +} diff --git a/example/www_static/css/boot.css b/example/www_static/css/boot.css new file mode 100644 index 0000000..803eb92 --- /dev/null +++ b/example/www_static/css/boot.css @@ -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; +} diff --git a/example/www_static/css/flot.css b/example/www_static/css/flot.css new file mode 100644 index 0000000..76b6b11 --- /dev/null +++ b/example/www_static/css/flot.css @@ -0,0 +1,10 @@ + +.flot-chart { + display: block; + height: 400px; +} + +.flot-chart-content { + width: 100%; + height: 100%; +} diff --git a/example/www_static/css/panel.css b/example/www_static/css/panel.css new file mode 100644 index 0000000..7e97652 --- /dev/null +++ b/example/www_static/css/panel.css @@ -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; +} + + diff --git a/example/www_static/css/style.css b/example/www_static/css/style.css new file mode 100644 index 0000000..745a426 --- /dev/null +++ b/example/www_static/css/style.css @@ -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; +} + diff --git a/example/www_static/js/controller/page-bar.js b/example/www_static/js/controller/page-bar.js new file mode 100644 index 0000000..3f628ac --- /dev/null +++ b/example/www_static/js/controller/page-bar.js @@ -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) { +} diff --git a/example/www_static/js/controller/page-foo.js b/example/www_static/js/controller/page-foo.js new file mode 100644 index 0000000..a5177b2 --- /dev/null +++ b/example/www_static/js/controller/page-foo.js @@ -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) { +} diff --git a/example/www_static/js/controller/page-index.js b/example/www_static/js/controller/page-index.js new file mode 100644 index 0000000..2a3fd23 --- /dev/null +++ b/example/www_static/js/controller/page-index.js @@ -0,0 +1,10 @@ + +pageRouteInit.push(function ($routeProvider, $locationProvider) { + $routeProvider.when('/example-ui', { + templateUrl: '/example-ui/thtml/index', + controller: PageIndex + }); +}); + +function PageIndex($scope, $http) { +} diff --git a/example/www_static/js/example-app.js b/example/www_static/js/example-app.js new file mode 100644 index 0000000..17bb47b --- /dev/null +++ b/example/www_static/js/example-app.js @@ -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}); + }]); + diff --git a/example/www_views/index.ejs b/example/www_views/index.ejs new file mode 100644 index 0000000..03c00a4 --- /dev/null +++ b/example/www_views/index.ejs @@ -0,0 +1,18 @@ + + + + + Loading<%- inlineNot %> + + <%- inline %> + + + diff --git a/example/www_views/thtml/bar.ejs b/example/www_views/thtml/bar.ejs new file mode 100644 index 0000000..0029a6b --- /dev/null +++ b/example/www_views/thtml/bar.ejs @@ -0,0 +1,32 @@ +
+

Bar

+

Welcome to the bar.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
ChairPersonDrinking
seat 1emptynone
seat 2youcoffee
seat 3catwater
+
+
diff --git a/example/www_views/thtml/foo.ejs b/example/www_views/thtml/foo.ejs new file mode 100644 index 0000000..52c464f --- /dev/null +++ b/example/www_views/thtml/foo.ejs @@ -0,0 +1,4 @@ +
+

Foo

+

Welcome to the foo.

+
diff --git a/example/www_views/thtml/index.ejs b/example/www_views/thtml/index.ejs new file mode 100644 index 0000000..c6c6dcc --- /dev/null +++ b/example/www_views/thtml/index.ejs @@ -0,0 +1,4 @@ +
+

Example UI Index

+

Welcome make yourself at home.

+
diff --git a/example/www_views/thtml/layout/footer.ejs b/example/www_views/thtml/layout/footer.ejs new file mode 100644 index 0000000..3ff644a --- /dev/null +++ b/example/www_views/thtml/layout/footer.ejs @@ -0,0 +1,5 @@ + diff --git a/example/www_views/thtml/layout/header.ejs b/example/www_views/thtml/layout/header.ejs new file mode 100644 index 0000000..109a1c9 --- /dev/null +++ b/example/www_views/thtml/layout/header.ejs @@ -0,0 +1,13 @@ + diff --git a/package.json b/package.json new file mode 100644 index 0000000..56e7db7 --- /dev/null +++ b/package.json @@ -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 (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" + } +}