diff --git a/README.md b/README.md index 07ac6d1..21fb451 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,171 @@ # EVE - Command line tool +Eve is a super simple command line tool for create HTML5 applications. It compiles into a single bundle JS library and module with Browseryfy. + ## Install ``` -$ npm install eve +$ npm install -g node-eve +``` + +## Usage + +``` + Usage: eve [options] [command] + + Commands: + + init [name] Crea il template di una app + build [options] [type] [unit] Compila l'applicazione + start [options] Esegue l'applicazione + test [options] Testa l'applicazione + extract [options] Estrae le stringhe dall'applicazione e le restituisce in un file per la traduzione + prepare
Util for prepare a section + Options: + + -h, --help output usage information + -V, --version output the version number +``` + + +### eve init [name] + +``` +$ eve init myapp +``` + +Initialize an application structure. + + +### eve build [type] [unit] + +``` +$ eve build // Alias of ... +$ eve build dev // Compile application of type development. Also provide source map file. +$ eve build dist // Compile application of type production. The code is minified. +$ eve build pub // Compile application of type production. The code is minified and, optionally, obfuscated with jScramble. +``` + +### eve start + +``` +$ eve start +``` + +This comand compile the application each time a file is changed. Eve is listening folders: + +- `templates` +- `locales` +- `lib` +- `test` +- `styles` + +Also it starts a web server, so as to allow the application display in browser. The default port is `5000` but if you change it run the command + +``` +$ eve start -p 5001 +``` + + +### eve test + +``` +$ eve test ModuleName +``` + +### eve extract + +``` +$ eve extract -d ./myapp/ -o ./myapp/file.json +``` + +Generate file for translations strings. File oupts are `po`, `json` or `txt`. More informations read the documentation of [ets] (https://github.com/vash15/extract-translate-string). + + +## Packege.json of your app + +``` +{ + "name": "{{APP_NAME}}", + "version": "1.0.0", + "description": "", + "author": "", + "license": "", + "main": "./lib/app.js", + "scripts": { + "start": "eve start" + }, + "eve-language": "es6", + "eve-version": "2.0.0", + "eve-configs-developement": {}, + "eve-configs-production": {}, + "eve-configs": {} +} +``` + +### eve-language + +Identify the language of the source code. Possible values: `js`, `es6` (default is `js`). [Babelify](https://github.com/babel/babelify) is used. + +### eve-version (not implemented yet) + +Check the version of eve required by the project. + +### eve-configs-developement + +Punt into object the variables for developement mode. + +### eve-configs-production + +Punt into object the variables for production mode. + +### eve-configs + +The object is generated based on the compilation mode (`development` or `production`). Into your app require this object for read the variables. + + +## Tips & Tricks + +### File limit + +If `EMFILE` file error is triggered use the command `ulimit -n 2048` to fix it. + +### Set environment variables on Windows + +To set environment variables in Windows use the tool from ***System in Control Panel*** (or by typing `environment` into the search box in start menu). For more details read this discussion on [Stackoverflow](http://stackoverflow.com/a/9250168). + +### Set environment variables on Linux or Mac + + +``` +`nano ~/.profile` + +export JSCRAMBLER_ACCESSKEY= +export JSCRAMBLER_SECRETKEY= ``` -## File limit -If `EMFILE` file is triggered use the command `ulimit -n 2048` to fix it. \ No newline at end of file + +## LICENSE + +The MIT License (MIT) + +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000..67fa185 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,8 @@ + +# Release notes + +## 2.0.0 + +- Added support to ES6 +- Added new package.json `eve-language` +- Removed `eve-components` diff --git a/lib/build.js b/lib/build.js index bd8c3cc..67cb438 100644 --- a/lib/build.js +++ b/lib/build.js @@ -1,68 +1,105 @@ +var _ = require('underscore'); var exorcist = require('exorcist') var browserify = require('browserify'); var fs = require('fs'); var path = require('path'); var UglifyJS = require('uglify-js'); +var jScrambler = require('jscrambler'); +var colors = require('colors'); +var unzip = require('unzip'); +module.exports = function(type, unit, options, done) { -module.exports = function(mode, unit, done) { - var time = process.hrtime(); - var pkg = require(path.resolve('package.json')); + if ( _.isFunction(options) ){ + done = options; + options = {}; + } + + var time = process.hrtime(); + var mode = options.mode || 'mobile'; + var packageJSONFile = path.resolve('package.json') + var pkg = require(packageJSONFile); + + if ( !pkg['eve-configs'] ) + pkg['eve-configs'] = {}; + + type = type || 'dev'; + var configs = pkg['eve-configs']; + var language = pkg['eve-language'] || 'js'; + var isForPublish = false; + var entryJsFile = path.join('lib', 'app.js'); + var outputJsFile = path.join('build', 'assets', 'js', 'bundle.js'); - mode = mode || 'dev'; - var components = pkg['eve-components'] || []; - var entryJsFile; - var outputJsFile; var mapFile; var debug; - switch (mode) { + switch (type) { case 'dist': case 'dev': - entryJsFile = path.join('lib', 'app.js'); - outputJsFile = path.join('build', 'assets', 'js', 'bundle.js'); - debug = mode === 'dev'; + debug = type === 'dev'; + configs.env = debug ? 'development' : 'production'; break; case 'test': - entryJsFile = path.join('test', unit, 'app.js'); + entryJsFile = path.join('test', unit, 'app.js'); outputJsFile = path.join('test', unit, 'bundle.js'); debug = true; + configs.env = 'development'; + break; + case 'pub': + isForPublish = true; + configs.env = 'production'; break; default: - return done(new Error('Unknown mode ' + mode)); + return done(new Error('Unknown type ' + type)); } + if ( _.isObject( pkg[ "eve-configs-" + configs.env ] ) ) { + _.extend( pkg[ "eve-configs" ], pkg[ "eve-configs-" + configs.env ] ); + } + + // Update new package json + fs.writeFileSync(packageJSONFile , JSON.stringify(pkg, null, 4)); + mapFile = outputJsFile + '.map'; var b = browserify({ debug: debug }); - b.transform('brfs', { + if (language === 'es6') { + b.transform('babelify', { + basedir: path.resolve(__dirname, "node_modules"), + presets: ['es2015'], + sourceMapsAbsolute: true, + only: ['lib'] + }); + } + b.transform('browserify-shim', { basedir: path.resolve(__dirname, "node_modules") }); - - components.forEach(function (aComponent) { - b.require(path.resolve(aComponent.path), aComponent.options); + b.transform('node-underscorify', { + basedir: path.resolve(__dirname, "node_modules") + }); + b.transform('debowerify', { + basedir: path.resolve(__dirname, "node_modules") }); - b.require(path.resolve(entryJsFile), { entry: true } ); + b.require(path.resolve(entryJsFile), { entry: true }); b.bundle(function (err, buf) { if (err) return done(err); - done(null, beautifyTime(process.hrtime(time)), mode); + done(null, beautifyTime(process.hrtime(time)), type); }) .on("end", function() { - if ( !debug ){ - setTimeout(function(){ - var src = fs.readFileSync( outputJsFile, { encoding: 'utf8'} ); // buf.toString('utf8'); - var srcMinify = UglifyJS.minify( - src, - { - fromString: true - } - ); + if ( !debug ) { + setTimeout(function() { + var src = fs.readFileSync( outputJsFile, { encoding: 'utf8'} ); + var srcMinify = UglifyJS.minify( src, { fromString: true }); if ( srcMinify ) { - fs.writeFileSync(outputJsFile, srcMinify.code ); + fs.writeFileSync(outputJsFile, srcMinify.code); + + if ( isForPublish ) + publisher(outputJsFile, mode, done); + } else { done(new Error('Minify error')); } @@ -77,7 +114,7 @@ module.exports = function(mode, unit, done) { .pipe(fs.createWriteStream(outputJsFile, 'utf8')); - function beautifyTime(diff) { + function beautifyTime(diff) { var out = ''; if (diff[0] > 0) { out += diff[0]; @@ -91,4 +128,54 @@ module.exports = function(mode, unit, done) { }; -} \ No newline at end of file + function publisher(outputJsFile, mode, done){ + setTimeout(function(){ + + // jScrambler + var accessKey = process.env.JSCRAMBLER_ACCESSKEY; + var secretKey = process.env.JSCRAMBLER_SECRETKEY; + if ( accessKey && secretKey ){ + + console.log(' Obfuscating code with jScrambler...'.gray); + var time = process.hrtime(); + var client = new jScrambler.Client({keys: {accessKey: accessKey,secretKey: secretKey}}); + jScrambler + .uploadCode(client, { + files: [ outputJsFile ], + mode: mode, + remove_comments: '%DEFAULT%', + rename_local: '%DEFAULT%', + whitespace: '%DEFAULT%', + literal_hooking: '20;50', + self_defending: '%DEFAULT%' + }) + .then(function (res) { + return jScrambler.downloadCode(client, res.id); + }) + .then(function (res) { + // + var zipFile = outputJsFile+'.zip'; + fs.writeFileSync(zipFile, res); + fs.unlinkSync(outputJsFile); + + fs.createReadStream(zipFile) + .pipe(unzip.Extract({ path: path.join('.') })) + .on('close', function(){ + fs.unlinkSync(zipFile); + console.log(' Operation completed in '.gray+beautifyTime(process.hrtime(time)).green); + }); + + return true; + }) + .catch(function (error) { + done(new Error(error && error.message ? 'jScrambler... ' + error.message : 'Error while obfuscating the code with jScrambler' ) ); + }); + } + else { + done(new Error('Set JSCRAMBLER_ACCESSKEY and JSCRAMBLER_SECRETKEY on your .profile')); + } + + }, 1000); + } + +} diff --git a/lib/eve.js b/lib/eve.js index 22e854a..71d41ea 100755 --- a/lib/eve.js +++ b/lib/eve.js @@ -11,7 +11,7 @@ program .version(pkg.version); // http://patorjk.com/software/taag/#p=display&f=Slant&t=EVE -var logo = '' + +var logo = '' + ' _______ ________\n' + ' / ____/ | / / ____/\n' + ' / __/ | | / / __/ \n' + @@ -21,18 +21,31 @@ var logo = '' + console.log(logo.cyan); console.log('In case of EMFILE error type: ulimit -n 2048'.gray); +program + .command('init [name]') + .description('Crea il template di una app') + .action(function (name, options) { + var init = require('./init'); + init(name, function (err) { + if (err) return handleError(err, 'Init'); + console.log('App '.gray + name.cyan + ' initialized successfully'.gray + '\u0007'); + // console.log('Type `bower install` to download all dependencies then `eve start` to start the app'.gray'); + }); + }); program - .command('build [mode] [unit]') + .command('build [type] [unit]') + .option('-m --mode ', 'starter,mobile,html5,nodejs') .description('Compila l\'applicazione') - .action(function (mode, unit) { + .action(function (type, unit, options) { var build = require('./build'); - var scss = require('./scss'); - build(mode, unit, function (err, time, mode) { + var scss = require('./scss'); + + build(type, unit, options, function (err, time, type) { if (err) return handleError(err, 'JS'); - console.log('JS builded with mode '.gray + mode.cyan + '...'.gray + time.green + '\u0007'); + console.log('JS builded with type '.gray + type.cyan + '...'.gray + time.green + '\u0007'); }); - scss(mode, unit, function (err, time) { + scss(type, unit, function (err, time) { if (err) return handleError(err, 'SCSS'); console.log('SCSS builded...'.gray + time.green + '\u0007'); }); @@ -44,9 +57,9 @@ program .option('-p --port ', 'Port') .option('-a --app [platform]', 'App platform') .action(function (options) { - var watch = require('./watch'); - var build = require('./build'); - var scss = require('./scss'); + var watch = require('./watch'); + var build = require('./build'); + var scss = require('./scss'); var express = require('./express'); express(options, function (err, port) { @@ -55,7 +68,7 @@ program watch(function (isJs, isCss) { if (isJs) { - build('dev', null, function (err, time, mode) { + build('dev', null, function (err, time, type) { if (err) return handleError(err, 'JS'); prepareApp(function(err, platform) { if (err) return handleError(err, 'CORDOVA'); @@ -97,9 +110,9 @@ program .description('Testa l\'applicazione') .option('-p --port ', 'Port', 5001) .action(function (unit, options) { - var watch = require('./watch'); - var build = require('./build'); - var scss = require('./scss'); + var watch = require('./watch'); + var build = require('./build'); + var scss = require('./scss'); var express = require('./express'); options.rootPath = path.join('test', unit); @@ -109,7 +122,7 @@ program watch(function (isJs, isCss) { if (isJs) { - build('test', unit, function (err, time, mode) { + build('test', unit, function (err, time, type) { if (err) return handleError(err, 'JS'); console.log('JS builded...'.gray + time.green + '\u0007'); }); @@ -153,4 +166,3 @@ function handleError(err, title) { function split(str){ return str.split(","); } - diff --git a/lib/init.js b/lib/init.js new file mode 100644 index 0000000..3b961e4 --- /dev/null +++ b/lib/init.js @@ -0,0 +1,32 @@ +var fs = require('fs'); +var path = require('path'); +var copyDir = require('extended-fs').copyDir; + +module.exports = function (name, done) { + var src = path.join(__dirname, '../resources/app-template'); + var dest = path.join(process.cwd(), name); + // console.log(src); + // console.log(dest); + // console.log(name); + copyDir(src, dest, function (err) { + if (err) return done(err); + // Sotituisce le variabili nei file appena copiati + var vars = { + APP_NAME: name + }; + writeVarsSync(vars, path.join(dest, 'package.json')); + writeVarsSync(vars, path.join(dest, 'bower.json')); + writeVarsSync(vars, path.join(dest, 'build/index.html')); + done(); + }); +}; + +function writeVarsSync (vars, file) { + var content = fs.readFileSync(file); + content = content.toString(); + Object.keys(vars).forEach(function (aVarName) { + var re = new RegExp('{{' + aVarName + '}}', 'g'); + content = content.replace(re, vars[aVarName]); + }); + fs.writeFileSync(file, content); +}; diff --git a/lib/scss.js b/lib/scss.js index 678a8c7..d248683 100644 --- a/lib/scss.js +++ b/lib/scss.js @@ -44,6 +44,30 @@ module.exports = function (mode, unit, done) { done(err); } // includePaths: [ 'lib/', 'mod/' ], + },function(err, result){ + if (err) { + err.toString = function () { + var out = ''; + out += 'line ' + this.line + ', column ' + this.column + ' \n'; + out += this.message + return out; + }; + return done(err); + } + + fs.writeFile(outFile, result.css, function (err) { + if (err) return done(err); + if ( mode == 'dist' ){ + if (fs.existsSync(mapFile)) + fs.unlinkSync(mapFile); + return done(null, beautifyTime(process.hrtime(time))); + } + fs.writeFile(mapFile, result.map, function (err) { + if (err) return done(err); + return done(null, beautifyTime(process.hrtime(time))); + }); + }); + }); function beautifyTime(diff) { diff --git a/lib/watch.js b/lib/watch.js index 72bae54..f022b34 100644 --- a/lib/watch.js +++ b/lib/watch.js @@ -9,10 +9,9 @@ module.exports = function (options, callback) { var match = options.match || /\.(js|json|html|scss|css)$/; - var blackList = ['bundle.js', 'style.css']; + var blackList = ['bundle.js', 'style.css', 'package.json']; var dirsAndFiles = [ - 'package.json', 'templates', 'locales', 'lib', @@ -40,4 +39,4 @@ module.exports = function (options, callback) { var isCss = ['.scss'].indexOf(ext) !== -1; callback(isJs, isCss); } -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 4405414..07c204c 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,52 @@ { - "name": "eve", - "version": "1.0.7", + "name": "node-eve", + "version": "2.0.0", "description": "Eve command line tools for browserify projects", "bin": { "eve": "./lib/eve.js" }, + "repository": { + "type": "git", + "url": "https://github.com/SonoIo/node-eve" + }, + "keywords": [ + "eve", + "command", + "browserify", + "client", + "cli", + "build" + ], + "contributors": [ + { + "name": "Matteo Baggio", + "email": "matteo.baggio@gmail.com" + }, + { + "name": "Michele Belluco", + "email": "michele@belluco.info" + } + ], + "license": "MIT", "dependencies": { + "async": "^1.5.2", + "babelify": "^7.3.0", "brfs": "~1.3.0", - "browserify": "~8.1.3", + "browserify": "~13.0.1", + "browserify-shim": "^3.8.12", "colors": "~1.0.3", "commander": "~2.5.1", - "ets": "^1.0.2", + "debowerify": "^1.5.0", + "ets": "^1.0.3", "exorcist": "~0.1.6", "express": "~4.9.6", - "node-sass": "~2.0.1", - "node-watch": "~0.3.4", - "uglify-js": "~2.4.16" + "extended-fs": "^0.3.3", + "jscrambler": "^0.7.5", + "node-sass": "~3.4.2", + "node-underscorify": "0.0.14", + "node-watch": "^0.3.5", + "uglify-js": "~2.4.16", + "underscore": "^1.8.3", + "unzip": "^0.1.11" } } diff --git a/resources/app-template/.bowerrc b/resources/app-template/.bowerrc new file mode 100644 index 0000000..6d86506 --- /dev/null +++ b/resources/app-template/.bowerrc @@ -0,0 +1,6 @@ +{ + "directory": "bower_components", + "scripts": { + "postinstall": "eve prepare components" + } +} \ No newline at end of file diff --git a/resources/app-template/bower.json b/resources/app-template/bower.json new file mode 100644 index 0000000..326714d --- /dev/null +++ b/resources/app-template/bower.json @@ -0,0 +1,47 @@ +{ + "name": "{{APP_NAME}}", + "version": "1.0.0", + "authors": [], + "description": "", + "moduleType": [ + "node" + ], + "license": "", + "private": true, + "ignore": [ + "**/.*", + "node_modules", + "bower_components", + "test", + "tests" + ], + "dependencies": { + "underscore": "~1.7.0", + "underscore.string": "~2.3.3", + "backbone": "~1.1.2", + "jquery": "~2.1.1", + "backbone.babysitter": "~0.1.5", + "logentries": "~0.0.3", + "page.js": "~1.3.7", + "moment": "~2.8.3", + "backbone.pubsub": "~1.0.0", + "device-utils": "~1.0.2", + "keyboard.js": "~1.0.0", + "validator-js": "~3.19.1", + "pantarhei": "~0.2.1", + "backbone.touch": "https://github.com/vash15/backbone.touch.git", + "backbone.basicauth": "git://github.com/SonoIo/backbone.basicauth.git#master", + "backbone.viewstack": "~1.0.15", + "async": "~0.9.0", + "network-utils": "~2.0.0", + "cache-utils": "~1.0.1", + "context-utils": "~1.0.1", + "translate-utils": "~1.0.1", + "page-utils": "~1.0.2", + "settings-utils": "~1.0.0" + }, + "devDependencies": { + "mocha": "~1.21.4", + "chai": "~1.9.2" + } +} diff --git a/resources/app-template/build/assets/css/empty b/resources/app-template/build/assets/css/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/build/assets/css/empty @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/resources/app-template/build/assets/fonts/empty b/resources/app-template/build/assets/fonts/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/build/assets/fonts/empty @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/resources/app-template/build/assets/img/empty b/resources/app-template/build/assets/img/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/build/assets/img/empty @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/resources/app-template/build/assets/js/empty b/resources/app-template/build/assets/js/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/build/assets/js/empty @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/resources/app-template/build/index.html b/resources/app-template/build/index.html new file mode 100644 index 0000000..a7a59b1 --- /dev/null +++ b/resources/app-template/build/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + + + + {{APP_NAME}} + + + +
+ +
+ + + + \ No newline at end of file diff --git a/resources/app-template/lib/app.js b/resources/app-template/lib/app.js new file mode 100644 index 0000000..29e12fb --- /dev/null +++ b/resources/app-template/lib/app.js @@ -0,0 +1,78 @@ +var pkg = require('../package.json'); + +// Globals +global.env = { + APP_ENV: 'development' // 'production' +}; + +// Backbone +Backbone.$ = $; + +if (global.env.APP_ENV === 'development') { + var devMessage = ' _ _ ' + '\n' + + ' __| | _____ __ _ __ ___ ___ __| | ___ ___ _ __ ' + '\n' + + ' / _` |/ _ \\ \\ / / | \'_ ` _ \\ / _ \\ / _` |/ _ \\ / _ \\| \'_ \\ ' + '\n' + + ' | (_| | __/\\ V / | | | | | | (_) | (_| | __/ | (_) | | | |' + '\n' + + ' \\__,_|\\___| \\_/ |_| |_| |_|\\___/ \\__,_|\\___| \\___/|_| |_|' + '\n' + + ' ' + '\n'; + console.log(devMessage); +} + +// Translations +var translations; +var fs = require('fs'); +var translate = require('translate'); + +try { + translations = { + 'default': { + 'it_IT': JSON.parse(fs.readFileSync('locales/it_IT/default.json', 'utf8')), + 'en_EN': JSON.parse(fs.readFileSync('locales/en_EN/default.json', 'utf8')) + }, + 'errors': { + 'it_IT': JSON.parse(fs.readFileSync('locales/it_IT/errors.json', 'utf8')), + 'en_EN': JSON.parse(fs.readFileSync('locales/en_EN/errors.json', 'utf8')) + } + }; +} +catch (err) { + throw new Error('Failed while parsing locales'); +} + +translate.setTranslations(translations); +translate.setLocale('it_IT'); +require('moment-it_IT'); +global.__ = translate.__; +global.__n = translate.__n; + +// Libraries +var Page = require('page'); +var Device = require('device-utils'); +var Cache = require('cache'); +var PubSub = require('pubsub'); +var Settings = require('settings'); +var ViewStack = require('viewstack'); +var Network = require('network'); + +// Page +var page = new Page(); + +page.use(Device.middleware()); +page.use(Cache.middleware()); +page.use(PubSub.middleware()); +page.use(Settings.middleware()); +page.use(Network.middleware({ + backbone: Backbone +})); +page.use(ViewStack.middleware({ el: "#application" })); + +// Routes +var pages = require('./controllers/pages'); + +page.url({ url: '', name: 'home' }, + pages.home +); + +page.start(); + + diff --git a/resources/app-template/lib/collections/AppCollection.js b/resources/app-template/lib/collections/AppCollection.js new file mode 100644 index 0000000..1567685 --- /dev/null +++ b/resources/app-template/lib/collections/AppCollection.js @@ -0,0 +1,122 @@ +var async = require('async'); +var _ = require('underscore'); +var Backbone = require('backbone'); + + +var AppCollection = module.exports = Backbone.Collection.extend({ + idAttribute: '_id', + serverUrl: env.SERVER_URL, + database: env.DATABASE, + _comparatorType: null, + fetching: false, + + refs: null, + + initialize: function initialize() { + AppCollection.__super__.initialize.apply(this, arguments); + this.previousFetchData = {}; + this.moreToLoad = true; + }, + + // Quando un model viene aggiunto ad una collection allora aggiorna + // le dipendenze del model in modo che sia in grado di aggiornarsi + // anche quando le sue dipendenze cambiano + // _addReference: function(model, options) { + // AppCollection.__super__._addReference.apply(this, arguments); + // model.listenToRefs(); + // }, + // _removeReference: function(model, options) { + // AppCollection.__super__._removeReference.apply(this, arguments); + // model.stopListeningToRefs(); + // }, + + // Carica elementi dal server un po' alla volta + loadMore: function loadMore(options, done) { + if (!this.moreToLoad) { + _.defer(function () { + return done(null, false, 0); + }); + return; + } + + if (!options) + options = {}; + var self = this; + var data = _.defaults(options.data || {}, this.previousFetchData); + var oldLength = this.length; + var offset = oldLength; + + options.limit = options.limit || 20; + options.offset = offset; + options.remove = false; + options.add = offset > 0; + options.reset = offset === 0; + + data.currentPage = Math.floor(offset/options.limit) + 1; + data.pageSize = options.limit; + + // Indica al fetch che questa chiamata è la continuazione + // di quella precedente + options.loadMore = true; + options.data = data; + + options.success = function(collection, response, opt) { + self.fetching = false; + if (done) { + var moreToLoad = self.moreToLoad = collection.length - oldLength == options.limit; + done(null, moreToLoad, collection.length - oldLength, oldLength); + } + }; + options.error = function(collection, response, options) { + self.fetching = false; + if (done) done(new Error(response)); + }; + + this.fetching = true; + this.fetch(options); + }, + + fetch: function fetch(options) { + if (!options.loadMore) { + this.moreToLoad = true; + this.previousFetchData = options ? options.data || {} : {}; + } + if (global.env.APP_DEMO && options && options.data) + delete options.data; + + if (options && options.data && options.data.currentPage) + options.data.currentPage--; + + AppCollection.__super__.fetch.call(this, options); + }, + + fetchAll: function fetchAll(done) { + var self = this; + var canFetch = true; + self.reset(); + async.whilst( + function () { return canFetch; }, + function (next) { + self.loadMore({}, function (err, moreToLoad) { + if (err) return next(err); + canFetch = moreToLoad; + next(); + }); + }, + function (err) { + return done(err); + } + ); + }, + + getRejectedModels: function getRejectedModels() { + var result; + result = this.filter(function (aModel) { + return aModel.hasRejectedPaths(); + }); + return result; + } + +}); + + diff --git a/resources/app-template/lib/controllers/pages.js b/resources/app-template/lib/controllers/pages.js new file mode 100644 index 0000000..98f20f6 --- /dev/null +++ b/resources/app-template/lib/controllers/pages.js @@ -0,0 +1,8 @@ + +var PagesController = module.exports = { + home: function (ctx, next) { + // var view = new HomeView(); + // ctx.viewstack.pushView(view); + console.log('Home!'); + } +}; diff --git a/resources/app-template/lib/models/AppModel.js b/resources/app-template/lib/models/AppModel.js new file mode 100644 index 0000000..2603be6 --- /dev/null +++ b/resources/app-template/lib/models/AppModel.js @@ -0,0 +1,152 @@ +var _ = require('underscore'); +var Backbone = require('backbone'); +var context = require('context'); + + +var AppModel = module.exports = Backbone.Model.extend({ + idAttribute: 'id', + serverUrl: env.SERVER_URL, + database: env.DATABASE, + rejectedPaths: null, + + initialize: function initialize() { + AppModel.__super__.initialize.apply(this, arguments); + // this.listenToRefs(); + this._fetching = false; + }, + + toString: function toString() { + return this.id; + }, + + toSearchString: function toSearchString() { + return this.id; + }, + + listenToRefs: function listenToRefs() { + var self = this; + self.stopListeningToRefs(); + // Verifica l'esistenza di relazioni tra questo model + // e le sue referenze. Nel caso una referenza non esista + // inizializza il model con il solo attributo id valorizzato + // e si mette in ascolto per propagare l'evento change + // dei model in relazione. + if (self.belongsTo) { + var aRef; + var aRefModel; + var aForeignKey; + _.each(self.belongsTo, function (aBelongsTo) { + aRef = self.getRef(aBelongsTo.ref); + aForeignKey = self.attributes[aBelongsTo.foreignKey]; + if (!aForeignKey) return; + aRefModel = aRef.get(aForeignKey); + // Se non esiste il model lo istanzio creandone uno vuoto ma + // con l'ID. Anche in caso di fetch il model riceverà un update + // e l'evento change verrà scatenato. + if (!aRefModel) { + aRefModel = new aRef.model(); + aRefModel.set(aRefModel.idAttribute, aForeignKey); + aRef.add(aRefModel); + } + // Si mette in ascolto e propaga due eventi: + // -> change + // -> change:[aBelongsTo.ref] + self.listenTo(aRefModel, 'change', function (model, options) { + self.onRefChange(model, aBelongsTo, options); + }); + }); + } + }, + + stopListeningToRefs: function stopListeningToRefs() { + // ISSUE: se per qualche motivo il model sta in ascolto su un evento change + // importante qui viene fermato. + this.stopListening(null, 'change'); + }, + + getRef: function getRef(name) { + name = name.toLowerCase(); + if (!context.collections) + // throw new Error('Cannot retrieve ref without a collection'); + return null; + if (!context.collections[name]) + // throw new Error('No ref named "' + name + '" for this model'); + return null; + return context.collections[name]; + }, + + getRefModel: function getRefModel(name, id) { + var ref = this.getRef(name); + if (!ref) return null; + var refModel = ref.get(id); + if (!refModel) return null; + return refModel; + }, + + getStringOfRefModel: function getStringOfRefModel(ref, id) { + var refModel = this.getRefModel(ref, id); + if (refModel) + return refModel.toString(); + return ''; + }, + + onRefChange: function onRefChange(refModel, belongsTo, options) { + if (typeof options === 'undefined') + options = {}; + options.ref = refModel; + this.trigger('change:' + belongsTo.foreignKey, this, options); + this.trigger('change', this, options); + }, + + hasRejectedPaths: function hasRejectedPaths() { + return this.rejectedPaths !== null && this.rejectedPaths.length > 0; + }, + + getRejectedPaths: function getRejectedPaths() { + return this.rejectedPaths; + }, + + setRejectedPaths: function setRejectedPaths(rejectedPaths) { + this.rejectedPaths = rejectedPaths; + }, + + getRejectedValues: function getRejectedValues() { + var self = this; + var paths = this.getRejectedPaths(); + if (paths === null || paths === void 0 || paths.length === 0) return null; + var result = {}; + _.forEach(paths, function (aPath) { + result[aPath] = { + current: self.get(aPath), + previous: self.previous(aPath) + } + }); + return result; + }, + + fetch: function fetch(options) { + options = options ? options : {}; + var success = options.success; + var error = options.error; + var model = this; + model._fetching = true; + options.success = function (resp) { + model._fetching = false; + if (success) success(model, resp, options); + }; + options.error = function (resp) { + model._fetching = false; + if (error) error(model, resp, options); + }; + return AppModel.__super__.fetch.call(this, options); + }, + + isFetching: function isFetching() { + return !!this._fetching; + } + +}); + + + + diff --git a/resources/app-template/lib/utils/InvalidError.js b/resources/app-template/lib/utils/InvalidError.js new file mode 100644 index 0000000..e887649 --- /dev/null +++ b/resources/app-template/lib/utils/InvalidError.js @@ -0,0 +1,8 @@ +var _ = require('underscore'); + +var InvalidError = module.exports = function InvalidError(options) { + _.defaults(this, options, { + field: '', + message: '' + }); +}; \ No newline at end of file diff --git a/resources/app-template/lib/utils/Logger.js b/resources/app-template/lib/utils/Logger.js new file mode 100644 index 0000000..a16ad70 --- /dev/null +++ b/resources/app-template/lib/utils/Logger.js @@ -0,0 +1,79 @@ + +var _ = require('underscore'); + +// Console per i vecchi browser +if (typeof console === 'undefined') { + var f = function () {}; + window.console = { + log:f, info:f, warn:f, debug:f, error:f + }; +} + +var defaultTransport = { + log: function() { + console.log.apply(console, arguments); + }, + warn: function() { + console.warn.apply(console, arguments); + }, + info: function() { + console.info.apply(console, arguments); + }, + error: function() { + console.error.apply(console, arguments); + } +}; + +var Logger = module.exports = function(options) { + this.transports = []; + + if (!options) + options = {}; + + if (!('defaultTransport' in options) || options.defaultTransport) + this.addTransport(defaultTransport); +}; + +Logger.defaultTransport = defaultTransport; + +Logger.prototype.addTransport = function(transport) { + if (typeof transport.log !== 'function' + || typeof transport.warn !== 'function' + || typeof transport.info !== 'function' + || typeof transport.error !== 'function') + throw new Error('A transport have to contains log, warn, info and error function'); + + this.transports.push(transport); + return this; +} + +Logger.prototype._log = function(type, args) { + var aTransport; + for (var i = this.transports.length - 1; i >= 0; i--) { + aTransport = this.transports[i]; + aTransport[type].apply(this, args); + } + return this; +}; + +Logger.prototype.log = function() { return this._log('log', arguments); }; +Logger.prototype.warn = function() { return this._log('warn', arguments); }; +Logger.prototype.info = function() { return this._log('info', arguments); }; +Logger.prototype.error = function() { return this._log('error', arguments); }; + +Logger.prototype.helper = function() { + var self = this; + var helper = function (msg) { + if (msg) { + self.log(msg); + } + return self; + }; + helper.log = _.bind(self.log, self); + helper.warn = _.bind(self.warn, self); + helper.info = _.bind(self.info, self); + helper.error = _.bind(self.error, self); + return helper; +}; + + diff --git a/resources/app-template/lib/utils/ajax.js b/resources/app-template/lib/utils/ajax.js new file mode 100644 index 0000000..ec60d36 --- /dev/null +++ b/resources/app-template/lib/utils/ajax.js @@ -0,0 +1,49 @@ + +var $ = require('jquery'); +var _ = require('underscore'); +var __ = require('translate'); + +var ajax = module.exports = function ajax(url, data, done) { + if (typeof data === 'function') { + done = data; + data = undefined; + } + + if (!url) + throw new Error('Url not defined for ajax request'); + + var method = 'GET'; + + if (data) + method = 'POST'; + + var options = { + dataType: 'json', + type: method, + data: data + }; + + $.ajax(url, options) + .done(function (data, textStatus, jqXHR) { + done(null, data); + }) + .fail(function (jqXHR, textStatus, errorThrown) { + var err; + if (jqXHR.responseJSON) + err = jqXHR.responseJSON; + else if (jqXHR.responseText && jqXHR.responseText != '') + err = new Error(jqXHR.responseText); + else if (errorThrown && errorThrown != '') + err = new Error(errorThrown); + else if (jqXHR.status == 0) + err = new Error(__n('errors', 'Internet disconnected')); + else + err = new Error(__n('errors', 'Unknonwn error')); + done(err); + }); +}; + +ajax.setup = function setup() { + $.ajaxSetup.apply($, arguments); +}; + diff --git a/resources/app-template/lib/views/AppView.js b/resources/app-template/lib/views/AppView.js new file mode 100644 index 0000000..5f1eb00 --- /dev/null +++ b/resources/app-template/lib/views/AppView.js @@ -0,0 +1,130 @@ +var _ = require('underscore'); +var Backbone = require('backbone'); + +var AppView = module.exports = Backbone.View.extend({ + + touchThreshold: 5, + + className: function className() { + return 'view ' + (this.addClass || ''); + }, + + initialize: function initialize(options) { + if (typeof options !== 'undefined') { + if ('context' in options) + this.setContext(options.context); + // Deprecated + else if ('shared' in options) { + console.warn('Deprecated: use context option instead shared'); + this.setContext(options.shared); + } + } + + if (this.getContext() === null) + this.setContext(require('context')); + + // Assegno il nome della classe per quando l'elemento è attivo + this.touchActiveClassName = 'active-state'; + + // Inizializzo l'oggetto che conterrà tutte le subview + this.views = {}; + this.cache = {}; + if (!this.options) + this.options = {}; + + // Va aggiornato, vedi FreeDUCk + // var pubsub = this.getContext().pubsub; + // if (pubsub) + // this.listenTo(this.getContext().pubsub, 'change:locale', this.render); + }, + + // Context + context: null, + getContext: function getContext() { + return this.context; + }, + setContext: function setContext(newContext) { + this.context = newContext; + }, + + // Deprecated + getShared: function getShared() { return this.getContext(); }, + setShared: function setShared(newContext) { return this.setContext(newContext); }, + + // Estende la view base di backbone per implementare il destroy delle view + // refer: http://lostechies.com/derickbailey/2011/09/15/zombies-run-managing-page-transitions-in-backbone-apps/ + destroy: function destroy() { + this.remove(); + this.off(); + if (this.onDestroy) + this.onDestroy(); + }, + + // Helper che setta lo zIndex di una view + setZindex: function setZindex(zIndex) { + this._zIndex = zIndex; + this.$el.css("z-index", zIndex); + return this; + }, + getZindex: function getZindex() { + return this._zIndex; + }, + + // Eventi + addEvents: function addEvents(events) { + this.events = _.defaults(events, this.events || {}); + return this; + }, + + // Cambio animazione css + // Events: oanimationend animationend webkitAnimationEnd + changeAnimation: function changeAnimation( animationName, selector) { + var el; + if ( _.isUndefined(selector) || selector.length == 0 ){ + el = this.$el; + }else{ + el = selector; + } + el.css("animation-name", animationName); + el.css("-webkit-animation-name", animationName); + el.css("-moz-animation-name", animationName); + el.css("-o-animation-name", animationName); + return this; + }, + + // Comportamento di default in caso di errore della view. + // Riporta in alto l'errore in modo che la view padre sappia cosa fare. + onError: function onError(err) { + this.throwError(err); + }, + + throwError: function throwError(err) { + this.trigger('error', err); + }, + + findPlaceholder: function findPlaceholder(name) { + return this.$el.find('[data-placeholder="' + name + '"]'); + }, + + appendAndRenderToPlaceholder: function appendAndRenderToPlaceholder(name, view) { + this.findPlaceholder(name).append(view.el); + view.render(); + return this; + }, + + // Propaga il comando destroy a tutte le subview + onDestroy: function onDestroy() { + _.forEach(this.views, function (aView) { + if (aView instanceof AppView) + aView.destroy(); + }); + }, + + // Metodo alternativo per associare le subview + // http://ianstormtaylor.com/rendering-views-in-backbonejs-isnt-always-simple/ + assign: function (view, selector) { + view.setElement(this.$(selector)).render(); + } + +}); + diff --git a/resources/app-template/lib/views/PageView.js b/resources/app-template/lib/views/PageView.js new file mode 100644 index 0000000..9bb20eb --- /dev/null +++ b/resources/app-template/lib/views/PageView.js @@ -0,0 +1,32 @@ +var _ = require('underscore'); +var fs = require('fs'); +var AppView = require('./AppView'); + +var PageView = module.exports = AppView.extend({ + + className: 'page', + + initialize: function initialize(options) { + PageView.__super__.initialize.call(this, options); + this.options = _.defaults(this.options || {}, options, { + modal: false + }); + }, + + render: function render() { + PageView.__super__.render.call(this); + if (this.options.modal) { + this.$el.addClass('page-modal'); + } + return this; + }, + + onDeactivate: function onDeactivate() { + this.$el.addClass('deactivate'); + }, + + onActivate: function onActivate(firstTime) { + this.$el.removeClass('deactivate'); + } + +}); diff --git a/resources/app-template/locales/en_EN/default.json b/resources/app-template/locales/en_EN/default.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/resources/app-template/locales/en_EN/default.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/resources/app-template/locales/en_EN/errors.json b/resources/app-template/locales/en_EN/errors.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/resources/app-template/locales/en_EN/errors.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/resources/app-template/locales/it_IT/default.json b/resources/app-template/locales/it_IT/default.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/resources/app-template/locales/it_IT/default.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/resources/app-template/locales/it_IT/errors.json b/resources/app-template/locales/it_IT/errors.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/resources/app-template/locales/it_IT/errors.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/resources/app-template/package.json b/resources/app-template/package.json new file mode 100644 index 0000000..1252960 --- /dev/null +++ b/resources/app-template/package.json @@ -0,0 +1,40 @@ +{ + "name": "{{APP_NAME}}", + "version": "1.0.0", + "description": "", + "author": "", + "license": "", + "main": "./lib/app.js", + "scripts": { + "start": "eve start" + }, + "eve-components": [ + { "path": "./bower_components/pantarhei/PantaRhei.js", "options": { "expose": "pantarhei" } }, + { "path": "./bower_components/jquery/dist/jquery.js", "options": { "expose": "jquery" } }, + { "path": "./bower_components/moment/moment.js", "options": { "expose": "moment" } }, + { "path": "./bower_components/moment/locale/it.js", "options": { "expose": "moment-it_IT" } }, + { "path": "./bower_components/moment/locale/en-gb.js", "options": { "expose": "moment-en_EN" } }, + { "path": "./bower_components/underscore/underscore.js", "options": { "expose": "underscore" } }, + { "path": "./bower_components/underscore.string/dist/underscore.string.js", "options": { "expose": "underscore.string" } }, + { "path": "./bower_components/backbone/backbone.js", "options": { "expose": "backbone" } }, + { "path": "./bower_components/backbone.pubsub/lib/pubsub.js", "options": { "expose": "pubsub" } }, + { "path": "./bower_components/backbone.viewstack/lib/viewstack.js", "options": { "expose": "viewstack" } }, + { "path": "./bower_components/backbone.babysitter/lib/backbone.babysitter.js", "options": { "expose": "backbone.babysitter" } }, + { "path": "./bower_components/backbone.touch/backbone.touch.js", "options": { "expose": "backbone.touch" } }, + { "path": "./bower_components/backbone.basicauth/backbone.basicauth.js", "options": { "expose": "backbone.basicauth" } }, + { "path": "./bower_components/device-utils/lib/device.js", "options": { "expose": "device-utils" } }, + { "path": "./bower_components/keyboard.js/lib/keyboard.js", "options": { "expose": "keyboard" } }, + { "path": "./bower_components/validator-js/validator.js", "options": { "expose": "validator" } }, + { "path": "./bower_components/logentries/product/le.js", "options": { "expose": "logentries" } }, + { "path": "./bower_components/async/lib/async.js", "options": { "expose": "async" } }, + { "path": "./bower_components/network-utils/lib/Network.js", "options": { "expose": "network" } }, + { "path": "./bower_components/cache-utils/lib/Cache.js", "options": { "expose": "cache" } }, + { "path": "./bower_components/context-utils/lib/context.js", "options": { "expose": "context" } }, + { "path": "./bower_components/translate-utils/lib/translate.js", "options": { "expose": "translate" } }, + { "path": "./bower_components/page-utils/lib/Page.js", "options": { "expose": "page" } }, + { "path": "./bower_components/settings-utils/lib/Settings.js", "options": { "expose": "settings" } } + ], + "eve-configs-developement": {}, + "eve-configs-production": {}, + "eve-configs": {} +} \ No newline at end of file diff --git a/resources/app-template/styles/animations/empty b/resources/app-template/styles/animations/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/styles/animations/empty @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/resources/app-template/styles/partials/empty b/resources/app-template/styles/partials/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/styles/partials/empty @@ -0,0 +1 @@ +empty \ No newline at end of file diff --git a/resources/app-template/styles/style.scss b/resources/app-template/styles/style.scss new file mode 100644 index 0000000..eea663b --- /dev/null +++ b/resources/app-template/styles/style.scss @@ -0,0 +1,17 @@ + +// Font +// @import url("../fonts/roboto.css"); +// @import url("../fonts/robotoslab.css"); +// @import url("../fonts/robotocondensed.css"); +// @import url("../fonts/ibs/styles.css"); + +// Vendor +@import 'vendor/normalize'; +@import 'vendor/grid'; + +// Animations +// @import 'animations/fadeOutDown'; + +// Partials +// @import 'partials/base'; + diff --git a/resources/app-template/styles/vendor/_grid.scss b/resources/app-template/styles/vendor/_grid.scss new file mode 100755 index 0000000..7af95ca --- /dev/null +++ b/resources/app-template/styles/vendor/_grid.scss @@ -0,0 +1,234 @@ +/** + * Profound Grid + * + * Built in Sass (SCSS) this responsive/adaptive grid supports both fixed and fluid layouts, + * relative and fixed gutters, as well as your choice of semantic markup or generic '.grid-x' classes. + * + * The biggest difference to other grid systems is the use of negative margins for column + * placements, avoiding the subpixel rounding issues that usually break/uglify fluid layouts + * in some browsers. + * + * Nested columns are supported too, without having to reset the grid on each level. + * + * Credits/Inspiration: + * ------------------- + * Semantic Grid: http://www.semantic.gs + * Susy: http://susy.oddbird.net + * Negative Grid: http://chrisplaneta.com/freebies/negativegrid-fluid-css-grid-by-chris-planeta/ + * + * @author Profound Creative Studio + * @url http://www.profoundgrid.com + * @url http://www.weareprofound.com + */ + + //////////////////////////////////////////////////////////////////////////////// +// Defaults (feel free to override) +//////////////////////////////////////////////////////////////////////////////// + +$total_columns: 12; // // +$total_width: 100%; // px|100% // px = fixed, 100% = fluid +$gutter_width: 0; // px|% // px = fixed, % = fluid +$container_margin: 0; // auto|px|% // + +$class_container: 'row'; // // +$class_column: 'col'; // // +$class_push: 'push'; // // + + +//////////////////////////////////////////////////////////////////////////////// +// GRID MATH +//////////////////////////////////////////////////////////////////////////////// + +@function element_width($column_index){ + @if ($total_width == 100%){ + $not_rounded_value: (((100% + $gutter_width)/$total_columns)*$column_index)-$gutter_width; + $not_rounded_value: $not_rounded_value * 100; + $rounded_value: round($not_rounded_value)/100; + @return $rounded_value; + }@else{ + @return ((($total_width + $gutter_width)/$total_columns)*$column_index)-$gutter_width; + } +} + +@function column_width(){ + @if ($total_width == 100%){ + $not_rounded_value: (((100% + $gutter_width)/$total_columns))-$gutter_width; + $not_rounded_value: $not_rounded_value * 100; + $rounded_value: round($not_rounded_value)/100; + @return $rounded_value * 1%; + }@else{ + @return ($total_width - ($gutter_width*($total_columns - 1)))/$total_columns; + } +} + +@function container_width(){ + @if ($total_width == 100%){ + @if ($container_margin == auto){ + @return 100%; + } + @return 100% - 2*$container_margin; + } @else { + @return $total_width; + } +} +//////////////////////////////////////////////////////////////////////////////// +// BASIC MIXINS +//////////////////////////////////////////////////////////////////////////////// + +/** + * Clearfix + */ +@mixin clearfix(){ + overflow: hidden; + *zoom: 1; +} + +/** + * Legacy Clearfix + */ +@mixin legacy-pie-clearfix(){ + *zoom: 1; + + :after { + content: "\0020"; + display: block; + height: 0; + clear: both; + overflow: hidden; + visibility: hidden; + } +} + +/** + * Establish the grid-containing element. + */ +@mixin container(){ + width: container_width(); + margin: 0 $container_margin; +} + +/** + * Align an element to the grid. + */ +@mixin column($_columns_width, $_padding_columns:null){ + float: left; + margin-right: -100%; + $_width_perc: element_width($_columns_width); + width: $_width_perc; + + @if($_padding_columns != null){ + @include push($_padding_columns); + } +} + +/** + * Apply to any column to make it the last one of the current row. + */ +@mixin column_omega(){ + +} + + +//////////////////////////////////////////////////////////////////////////////// +// MARGIN MIXINS +//////////////////////////////////////////////////////////////////////////////// + +@mixin pre(){} +@mixin post(){} +@mixin squish(){} + +@mixin push($_column_index){ + $_width_perc: 0; + @if ($total_width == 100%){ + $_width_perc: ($gutter_width + element_width($_column_index)); + }@else{ + //$_width_perc: (column_width() + $gutter_width)*$_column_index; + $_width_perc: ($gutter_width + element_width($_column_index)); + } + + margin-left: $_width_perc; +} + +@mixin pull(){} + + +//////////////////////////////////////////////////////////////////////////////// +// HELPER CLASSES +//////////////////////////////////////////////////////////////////////////////// + +@mixin generate_helper_classes(){ + + /* Helper Class: Container */ + .#{$class_container}{ + @include container(); + } + + /* Helper Class: Columns */ + @for $i from 1 through $total_columns{ + .#{$class_column}#{$i} { + @include column($i) + } + } + + /* Helper Class: Horizontal Position */ + @for $i from 0 through ($total_columns - 1){ + .#{$class_push}#{$i} { + @include push($i); + } + } + +} + +@mixin generate_grid_positions($_column_selector, $_element_width){ + + $_cols_per_row: floor($total_columns / $_element_width); + + // create rule for each element + @for $i from 1 through $_cols_per_row{ + + #{$_column_selector}:nth-child(#{$_cols_per_row}n+#{$i}){ + @include push((($i - 1)*$_element_width)); + margin-bottom: $gutter_width; + + @if ($i == 1) { + clear: both; + @include clearfix(); + }@else{ + clear: none; + } + } + } +} + +@mixin generate_grid_positions_legacy($_column_selector, $_element_width, $_elements_in_grid:$total_columns){ + + $_cols_per_row: floor($total_columns / $_element_width); + $_current_col:1; + + // create rule for each element + @for $i from 1 through $_elements_in_grid{ + + $selector: #{$_column_selector}; + @for $ii from 2 through $i{ + $selector: #{$selector}+#{$_column_selector}; + } + + + #{$selector}{ + @include push((($_current_col - 1)*$_element_width)); + margin-bottom: $gutter_width; + + @if ($_current_col == 1) { + clear: both; + @include legacy-pie-clearfix(); + }@else{ + clear: none; + } + + $_current_col: $_current_col + 1; + @if ($_current_col > $_cols_per_row){ + $_current_col:1; + } + } + } +} diff --git a/resources/app-template/styles/vendor/normalize.scss b/resources/app-template/styles/vendor/normalize.scss new file mode 100644 index 0000000..59f19b4 --- /dev/null +++ b/resources/app-template/styles/vendor/normalize.scss @@ -0,0 +1,429 @@ +/*! normalize.css v3.0.1 | MIT License | git.io/normalize */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background: transparent; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} + + + + diff --git a/resources/app-template/templates/empty b/resources/app-template/templates/empty new file mode 100644 index 0000000..7b4d68d --- /dev/null +++ b/resources/app-template/templates/empty @@ -0,0 +1 @@ +empty \ No newline at end of file