@@ -0,0 +1,69 @@
'use strict';

var bowerFilesToExclude = require('./tasks/config/bowerFilesToExclude.js');

module.exports = function (config) {
config.set({

basePath: 'client',

frameworks: ['jasmine'],

preprocessors: {
'**/*.html': ['ng-html2js']
},

ngHtml2JsPreprocessor: {
stripPrefix: 'client/',
moduleName: 'templates'
},

plugins: [
'karma-phantomjs-launcher',
'karma-jasmine',
'karma-ng-html2js-preprocessor'
],

files: require('main-bower-files')({
filter: function (path) {
for (var i = 0; i < bowerFilesToExclude.length; i++) {
if (!/\.js$/.test(path) || new RegExp(bowerFilesToExclude[i]).test(path)) { return false; }
}
return true;
}
}).concat([
'bower_components/angular-mocks/angular-mocks.js',
'app.js',
'views/**/*.js',
'views/**/*.html',
'services/**/*.js',
'directives/**/*.js',
'directives/**/*.html',
'filters/**/*.js'
]),

exclude: [
'views/**/*.e2e.js'
],

reporters: ['progress'],

port: 9876,

colors: true,

// possible values:
// config.LOG_DISABLE
// config.LOG_ERROR
// config.LOG_WARN
// config.LOG_INFO
// config.LOG_DEBUG
logLevel: config.LOG_INFO,

autoWatch: false,

browsers: ['PhantomJS'],

singleRun: true
});
};
@@ -0,0 +1,68 @@
{
"name": "teeDesign",
"version": "0.1.0",
"devDependencies": {
"apidoc": "^0.14.0",
"del": "^2.2.0",
"errorhandler": "^1.4.2",
"gulp": "^3.9.0",
"gulp-angular-filesort": "^1.1.1",
"gulp-angular-templatecache": "^1.8.0",
"gulp-autoprefixer": "^3.1.0",
"gulp-bump": "^1.0.0",
"gulp-concat": "^2.6.0",
"gulp-css-url-rebase": "^0.2.0",
"gulp-git": "^1.6.1",
"gulp-inject": "^3.0.0",
"gulp-jscs": "^3.0.2",
"gulp-jscs-stylish": "^1.3.0",
"gulp-jshint": "^2.0.0",
"gulp-minify-css": "^1.2.3",
"gulp-mocha": "^2.2.0",
"gulp-ng-annotate": "^1.1.0",
"gulp-nodemon": "^2.0.6",
"gulp-open": "^1.0.0",
"gulp-plumber": "^1.0.1",
"gulp-protractor": "^2.1.0",
"gulp-rev-all": "0.8.18",
"gulp-sass": "^2.1.1",
"gulp-uglify": "^1.5.1",
"gulp-usemin": "^0.3.16",
"gulp-util": "^3.0.7",
"gulp-wait": "^0.0.2",
"gulp-watch": "^4.3.5",
"jasmine-core": "^2.4.1",
"jshint": "^2.9.1-rc3",
"jshint-stylish": "^2.1.0",
"karma": "^0.13.21",
"karma-jasmine": "^0.3.7",
"karma-ng-html2js-preprocessor": "^0.2.1",
"karma-phantomjs-launcher": "^1.0.0",
"main-bower-files": "^2.11.1",
"phantomjs": "^2.1.3",
"phantomjs-prebuilt": "^2.1.4",
"run-sequence": "^1.1.5",
"sassdoc": "^2.1.19",
"should": "^8.1.1",
"streamqueue": "^1.1.1",
"supertest": "^1.1.0"
},
"dependencies": {
"async": "^1.5.2",
"body-parser": "^1.14.2",
"browser-sync": "^2.5.3",
"chalk": "^1.1.1",
"compression": "^1.6.0",
"cookie-parser": "^1.4.1",
"express": "^4.13.3",
"lodash": "^4.0.0",
"method-override": "^2.3.5",
"morgan": "^1.6.1",
"ripe": "^0.2.1"
},
"scripts": {
"start": "node server/server.js",
"test": "gulp test",
"apidoc": "./node_modules/.bin/apidoc -i ./server -o ./docs/api"
}
}
@@ -0,0 +1,23 @@
exports.config = { // jshint ignore:line

// The version is susceptible to change
seleniumServerJar: './node_modules/gulp-protractor/node_modules/protractor/selenium/selenium-server-standalone-2.45.0.jar',
// chromeDriver: './node_modules/gulp-protractor/node_modules/protractor/selenium/chromedriver',

baseUrl: 'http://localhost:9000',

capabilities: {
browserName: 'chrome'
// chromeOptions: {
// binary: '/usr/bin/google-chrome-stable'
// }
},

framework: 'jasmine',

jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000
}

};
@@ -0,0 +1,9 @@
{
"node": true,
"globals": {
"describe": false,
"it": false,
"afterEach": false,
"beforeEach": false
}
}
@@ -0,0 +1,4 @@
'use strict';

module.exports = {
};
@@ -0,0 +1,13 @@
'use strict';

var path = require('path');
var _ = require('lodash');

var all = {

env: process.env.NODE_ENV || 'development',
root: path.normalize(__dirname + '/../../..'),
port: process.env.PORT || 9000
};

module.exports = _.merge(all, require('./' + all.env + '.js'));
@@ -0,0 +1,5 @@
'use strict';

module.exports = {
ip: process.env.IP || undefined
};
@@ -0,0 +1,4 @@
'use strict';

module.exports = {
};
@@ -0,0 +1,27 @@
'use strict';

var express = require('express');
var compression = require('compression');
var morgan = require('morgan');
var path = require('path');
var bodyParser = require('body-parser');

var config = require('./environment');

module.exports = function (app) {

var env = config.env;

app.set('view engine', 'html');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(compression());
app.use(morgan('dev'));
app.use(express.static(path.join(config.root, 'client')));
app.set('appPath', 'client');

if (env === 'development' || env === 'test') {
app.use(require('errorhandler')());
}

};
@@ -0,0 +1,22 @@
'use strict';

var config = require('./config/environment');

module.exports = function (app) {

// API

app.route('/:url(api|app|bower_components|assets)/*')
.get(function (req, res) {
res.status(404).end();
});

app.route('/*')
.get(function (req, res) {
res.sendFile(
app.get('appPath') + '/index.html',
{ root: config.root }
);
});

};
@@ -0,0 +1,31 @@
'use strict';

var express = require('express');
var chalk = require('chalk');
var config = require('./config/environment');

var app = express();
var server = require('http').createServer(app);

require('./config/express')(app);
require('./routes')(app);

server.listen(config.port, config.ip, function () {

console.log(
chalk.red('\nExpress server listening on port ')
+ chalk.yellow('%d')
+ chalk.red(', in ')
+ chalk.yellow('%s')
+ chalk.red(' mode.\n'),
config.port,
app.get('env')
);

if (config.env === 'development') {
require('ripe').ready();
}

});

module.exports = server;
@@ -0,0 +1,110 @@
'use strict';

/**
* Build task
*/

var gulp = require('gulp');
var path = require('path');
var sq = require('streamqueue');
var runSequence = require('run-sequence');
var del = require('del');
var plumber = require('gulp-plumber');
var usemin = require('gulp-usemin');
var cssRebaseUrls = require('gulp-css-url-rebase');
var autoprefixer = require('gulp-autoprefixer');
var minifyCss = require('gulp-minify-css');
var angularTemplatecache = require('gulp-angular-templatecache');
var concat = require('gulp-concat');
var ngAnnotate = require('gulp-ng-annotate');
var uglify = require('gulp-uglify');
var revAll = require('gulp-rev-all');
var revToExclude = require('./config/revFilesToExclude');

var toDelete = [];

module.exports = function (done) {
runSequence(
['clean:dist', 'sass'],
['usemin', 'copy:dist'],
['scripts', 'cssmin'],
'rev',
'clean:finish',
done);
};

gulp.task('clean:dist', function (done) {
del(['dist/**', '!dist', '!dist/.git{,/**}'])
.then(function () { done(); }).catch(done);
});

gulp.task('clean:finish', function (done) {
del([
'.tmp/**',
'dist/client/app.{css,js}'
].concat(toDelete))
.then(function () { done(); }).catch(done);
});

gulp.task('copy:dist', function () {
var main = gulp.src(['server/**/*', 'package.json'], { base: './' });
var assets = gulp.src('client/assets/**/*', { base: './' });

return sq({ objectMode: true }, main, assets)
.pipe(gulp.dest('dist/'));
});

gulp.task('usemin', ['inject'], function () {
return gulp.src('client/index.html')
.pipe(plumber())
.pipe(usemin({ css: [cssRebaseUrls({ root: 'client' }), 'concat'] }))
.pipe(gulp.dest('dist/client/'));
});

gulp.task('cssmin', function () {
return gulp.src('dist/client/app.css')
.pipe(autoprefixer())
.pipe(minifyCss())
.pipe(gulp.dest('dist/client/'));
});

gulp.task('scripts', function () {
var views = gulp.src('client/views/**/*.html')
.pipe(angularTemplatecache({
root: 'views',
module: 'teeDesign'
}));

var tpls = gulp.src('client/directives/**/*.html')
.pipe(angularTemplatecache({
root: 'directives',
module: 'teeDesign'
}));

var app = gulp.src('dist/client/app.js');

return sq({ objectMode: true }, app, views, tpls)
.pipe(concat('app.js'))
.pipe(ngAnnotate())
.pipe(uglify())
.pipe(gulp.dest('dist/client/'));
});

gulp.task('rev', function () {

var rev = new revAll({
transformFilename: function (file, hash) {
var filename = path.basename(file.path);
if (revToExclude.indexOf(filename) !== -1) {
return filename;
}
toDelete.push(path.resolve(file.path));
var ext = path.extname(file.path);
return path.basename(file.path, ext) + '.' + hash.substr(0, 8) + ext;
}
});

return gulp.src('dist/client/**')
.pipe(rev.revision())
.pipe(gulp.dest('dist/client/'));
});
@@ -0,0 +1,31 @@
'use strict';

/**
* Git versioning and bump
*/

var gulp = require('gulp');
var fs = require('fs');
var bump = require('gulp-bump');
var git = require('gulp-git');

module.exports = {

version: function () {
return gulp.src(['./package.json', './bower.json'])
.pipe(bump({
type: process.argv[3] ? process.argv[3].substr(2) : 'patch'
}))
.pipe(gulp.dest('./'));
},

bump: function () {
fs.readFile('./package.json', function (err, data) {
if (err) { return ; }
return gulp.src(['./package.json', './bower.json'])
.pipe(git.add())
.pipe(git.commit('chore(core): bump to ' + JSON.parse(data).version));
});
}

};
@@ -0,0 +1,5 @@
/**
* Paths which will be ignored by bowerFiles automatic injection
*/

module.exports = [];
@@ -0,0 +1,13 @@
/**
* Files injected into index.html by gulp-inject
* used by tasks inject & watch
*/

module.exports = [
'client/app.js',
'client/animations/*.js',
'client/directives/**/*.js', '!client/directives/**/*.spec.js',
'client/filters/**/*.js', '!client/filters/**/*.spec.js',
'client/services/**/*.js', '!client/services/**/*.spec.js',
'client/views/**/*.js', '!client/views/**/*.spec.js', '!client/views/**/*.e2e.js'
];
@@ -0,0 +1,5 @@
/**
* These filenames will not be revisioned.
*/

module.exports = ['index.html', 'favicon.ico'];
@@ -0,0 +1,45 @@
'use strict';

/**
* Control things.
*/

var gulp = require('gulp');
var fs = require('fs');
var _ = require('lodash');
var async = require('async');
var jshint = require('gulp-jshint');
var jscs = require('gulp-jscs');
var jscsStylish = require('gulp-jscs-stylish');

module.exports = function (done) {

function getConfig (file) {
return _.merge(
JSON.parse(fs.readFileSync('./.jshintrc', 'utf-8')),
JSON.parse(fs.readFileSync(file, 'utf-8'))
);
}

function control (paths, conf) {
return function (done) {
gulp.src(paths)
.pipe(jshint(conf))
.pipe(jshint.reporter('jshint-stylish'))
.on('finish', function () {
gulp.src(paths)
.pipe(jscs())
.on('error', function () {})
.pipe(jscsStylish())
.on('end', done);
});
};
}

async.series([
control(['client/**/*.js', '!client/bower_components/**'], getConfig('./client/.jshintrc')),
control(['server/**/*.js'], getConfig('./server/.jshintrc')),
control(['gulpfile.js', 'tasks/*.js'], getConfig('./server/.jshintrc'))
], done);

};
@@ -0,0 +1,20 @@
'use strict';

/**
* Documentation tasks
*/

var gulp = require('gulp');
var sassdoc = require('sassdoc');
var exec = require('child_process').exec;

exports.sassdoc = function () {
gulp.src('client/styles/**/*.scss')
.pipe(sassdoc({
dest: 'docs/sass'
}));
};

exports.apidoc = function (done) {
exec('npm run apidoc', done);
};
@@ -0,0 +1,26 @@
'use strict';

/**
* Inject css/js files in index.html
*/

var gulp = require('gulp');
var bowerFiles = require('main-bower-files');
var fileSort = require('gulp-angular-filesort');
var inject = require('gulp-inject');

var toInject = require('./config/filesToInject');
var toExclude = require('./config/bowerFilesToExclude');

module.exports = function () {
return gulp.src('client/index.html')
.pipe(inject(gulp.src(bowerFiles(), { read: false }), {
name: 'bower',
relative: 'true',
ignorePath: toExclude
}))
.pipe(inject(
gulp.src(toInject).pipe(fileSort()), { relative: true }
))
.pipe(gulp.dest('client'));
};
@@ -0,0 +1,22 @@
'use strict';

/**
* Preview the build app
*/

var gulp = require('gulp');
var open = require('gulp-open');

var config = require('../server/config/environment');

var openOpts = {
uri: 'http://localhost:' + config.port,
already: false
};

module.exports = function () {
process.env.NODE_ENV = 'production';
require('../dist/server/server');
return gulp.src('dist/client/index.html')
.pipe(open(openOpts));
};
@@ -0,0 +1,16 @@
'use strict';

/**
* Compile sass
*/

var gulp = require('gulp');
var plumber = require('gulp-plumber');
var sass = require('gulp-sass');

module.exports = function () {
return gulp.src('client/styles/app.scss')
.pipe(plumber())
.pipe(sass())
.pipe(gulp.dest('client/styles/css'));
};
@@ -0,0 +1,52 @@
'use strict';

/**
* Serve app. For dev purpose.
*/

var gulp = require('gulp');
var ripe = require('ripe');
var nodemon = require('gulp-nodemon');
var open = require('gulp-open');
var bsync = require('browser-sync');

var config = require('../server/config/environment');

var openOpts = {
uri: 'http://localhost:' + config.port,
already: false
};

module.exports = {

nodemon: function (cb) {
return nodemon({
script: 'server/server.js',
ext: 'js',
ignore: ['client', 'dist', 'node_modules', 'gulpfile.js']
})
.on('start', function () {
if (!openOpts.already) {
openOpts.already = true;
ripe.wait(cb);
} else {
ripe.wait(function () {
bsync.reload({ stream: false });
});
}
});
},

bsync: function () {
bsync.init({
proxy: 'localhost:9000',
browser: process.env.BROWSER || 'google chrome',
online: false,
notify: false,
watchOptions: {
interval: 500
}
});
}

};
@@ -0,0 +1,85 @@
'use strict';

/**
* Test tasks
*/

var gulp = require('gulp');
var util = require('gulp-util');
var chalk = require('chalk');
var protractor = require('gulp-protractor');
var KarmaServer = require('karma').Server;
var plumber = require('gulp-plumber');
var mocha = require('gulp-mocha');

/**
* Log. With options.
*
* @param {String} msg
* @param {Object} options
*/
function log (msg, options) {
options = options || {};
console.log(
(options.padding ? '\n' : '')
+ chalk.yellow(' > ' + msg)
+ (options.padding ? '\n' : '')
);
}

exports.e2eUpdate = protractor.webdriver_update;

exports.e2eTests = function () {
gulp.src('client/views/**/*.e2e.js')
.pipe(protractor.protractor({ configFile: 'protractor.conf.js' }))
.on('error', function (e) {
util.log(e.message);
process.exit(-1);
})
.on('end', function () { process.exit(0); });
};

function testServer (done) {

log('Running server tests...', { padding: true });

gulp.src('server/**/*.spec.js', { read: false })
.pipe(plumber())
.pipe(mocha({ reporter: 'spec' }))
.once('error', function (err) { done(err); })
.once('end', function () { done(0); });
}

function testClient (done) {

log('Running client tests...', { padding: true });

var server = new KarmaServer({
configFile: __dirname + '/../karma.conf.js'
}, done);

server.start();
}

exports.test = function (done) {
process.env.NODE_ENV = 'test';
var arg = process.argv[3] ? process.argv[3].substr(2) : false;
if (arg === 'client') {
return testClient(done);
} else if (arg === 'server') {
return testServer(function (code) {
done(code);
process.exit(code);
});
} else if (arg === false) {
return testClient(function (code) {
if (code) { return done(code); }
testServer(function (code) {
done(code);
process.exit(code);
});
});
} else {
console.log('Wrong parameter [%s], availables : --client, --server', arg);
}
};
@@ -0,0 +1,74 @@
'use strict';

/**
* Watch files, and do things when they changes.
* Recompile scss if needed.
* Reinject files
*/

var gulp = require('gulp');
var bsync = require('browser-sync');
var watch = require('gulp-watch');
var inject = require('gulp-inject');
var plumber = require('gulp-plumber');
var sass = require('gulp-sass');
var bowerFiles = require('main-bower-files');

var toInject = require('./config/filesToInject');
var toExclude = require('./config/bowerFilesToExclude');

module.exports = function () {

gulp.watch('bower.json', function () {
gulp.src('client/index.html')
.pipe(inject(gulp.src(bowerFiles(), { read: false }), {
name: 'bower',
relative: 'true',
ignorePath: toExclude
}))
.pipe(gulp.dest('client'))
.pipe(bsync.reload({ stream: true }));
});

watch([
'client/styles/**/*.scss',
'client/views/**/*.scss',
'client/directives/**/*.scss'
], function () {
gulp.src('client/styles/app.scss')
.pipe(plumber())
.pipe(sass())
.pipe(gulp.dest('client/styles/css'))
.pipe(bsync.reload({ stream: true }));
});

var coreFiles = [
'client/views/**/*.html',
'client/views/**/*.js',
'!client/views/**/*.scss',
'!client/views/**/*.spec.js',
'!client/views/**/*.e2e.js',
'client/directives/**/*.html',
'client/directives/**/*.js',
'!client/directives/**/*.spec.js',
'client/services/**/*.js',
'!client/services/**/*.spec.js',
'client/animations/*.js',
'client/filters/**/*.js',
'!client/filters/**/*.spec.js'
];

var lastInjection = Date.now();

watch(coreFiles, { events: ['add', 'unlink'] }, function () {
if (Date.now() - lastInjection < 100) { return; }
lastInjection = Date.now();
gulp.src('client/index.html')
.pipe(inject(gulp.src(toInject), { relative: true }))
.pipe(gulp.dest('client'));
});

watch(coreFiles, bsync.reload);
watch(['client/index.html', 'client/app.js'], bsync.reload);

};