diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..69fad35 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e717f5e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1f309e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +bower_components/ +.sass-cache/ +.idea/ +.tmp/ +dist/ diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..f230768 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,17 @@ +{ + "strict": true, + "bitwise": true, + "curly": true, + "eqeqeq": true, + "latedef": false, + "noarg": true, + "undef": true, + "unused": true, + "validthis": true, + "jasmine": true, + "globals": { + "angular": false, + "inject": false, + "module": false + } +} diff --git a/.yo-rc.json b/.yo-rc.json new file mode 100644 index 0000000..963d216 --- /dev/null +++ b/.yo-rc.json @@ -0,0 +1,70 @@ +{ + "generator-gulp-angular": { + "version": "0.12.1", + "props": { + "angularVersion": "~1.4.0", + "angularModules": [ + { + "key": "animate", + "module": "ngAnimate" + }, + { + "key": "cookies", + "module": "ngCookies" + }, + { + "key": "touch", + "module": "ngTouch" + }, + { + "key": "sanitize", + "module": "ngSanitize" + } + ], + "jQuery": { + "key": "none" + }, + "resource": { + "key": "restangular", + "module": "restangular" + }, + "router": { + "key": "ui-router", + "module": "ui.router" + }, + "ui": { + "key": "bootstrap", + "module": null + }, + "bootstrapComponents": { + "key": "none", + "module": null + }, + "cssPreprocessor": { + "key": "node-sass", + "extension": "scss" + }, + "jsPreprocessor": { + "key": "none", + "extension": "js", + "srcExtension": "js" + }, + "htmlPreprocessor": { + "key": "none", + "extension": "html" + }, + "foundationComponents": { + "name": null, + "version": null, + "key": null, + "module": null + }, + "paths": { + "src": "src", + "dist": "dist", + "e2e": "e2e", + "tmp": ".tmp" + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7784325 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# Dialoga App + +# Project Decisions + +- [generator-gulp-angular](https://github.com/Swiip/generator-gulp-angular) +- Angular + - angular-animate + - angular-cookies + - angular-touch + - angular-sanitize + - angular-ui-router + - restangular +- gulp (default task: serve) +- JS old style (no CoffeeScript or ES6 or ...) +- HTML pure (no JADE or HBS or ...) +- Bootstrap CSS only (without JS files) diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..5cce9b5 --- /dev/null +++ b/bower.json @@ -0,0 +1,23 @@ +{ + "name": "dialoga", + "version": "0.0.0", + "dependencies": { + "angular-animate": "~1.4.0", + "angular-cookies": "~1.4.0", + "angular-touch": "~1.4.0", + "angular-sanitize": "~1.4.0", + "angular-ui-router": "~0.2.15", + "jquery": "~2.1.4", + "restangular": "~1.5.1", + "bootstrap-sass-official": "~3.3.4", + "animate.css": "~3.3.0", + "angular": "~1.4.0", + "modernizr": "~2.8.3" + }, + "devDependencies": { + "angular-mocks": "~1.4.0" + }, + "resolutions": { + "angular": "~1.4.0" + } +} diff --git a/e2e/.jshintrc b/e2e/.jshintrc new file mode 100644 index 0000000..5540069 --- /dev/null +++ b/e2e/.jshintrc @@ -0,0 +1,10 @@ +{ + "extends": "../.jshintrc", + "globals": { + "browser": false, + "element": false, + "by": false, + "$": false, + "$$": false + } +} diff --git a/e2e/main.po.js b/e2e/main.po.js new file mode 100644 index 0000000..0f0428c --- /dev/null +++ b/e2e/main.po.js @@ -0,0 +1,15 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var MainPage = function() { + this.jumbEl = element(by.css('.jumbotron')); + this.h1El = this.jumbEl.element(by.css('h1')); + this.imgEl = this.jumbEl.element(by.css('img')); + this.thumbnailEls = element(by.css('body')).all(by.repeater('awesomeThing in main.awesomeThings')); +}; + +module.exports = new MainPage(); diff --git a/e2e/main.spec.js b/e2e/main.spec.js new file mode 100644 index 0000000..ef2e5c1 --- /dev/null +++ b/e2e/main.spec.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('The main view', function () { + var page; + + beforeEach(function () { + browser.get('/index.html'); + page = require('./main.po'); + }); + + it('should include jumbotron with correct data', function() { + expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); + expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); + expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); + }); + + it('should list more than 5 awesome things', function () { + expect(page.thumbnailEls.count()).toBeGreaterThan(5); + }); + +}); diff --git a/gulp/.jshintrc b/gulp/.jshintrc new file mode 100644 index 0000000..072135c --- /dev/null +++ b/gulp/.jshintrc @@ -0,0 +1,4 @@ +{ + "extends": "../.jshintrc", + "node": true +} diff --git a/gulp/build.js b/gulp/build.js new file mode 100644 index 0000000..37d2b09 --- /dev/null +++ b/gulp/build.js @@ -0,0 +1,94 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var $ = require('gulp-load-plugins')({ + pattern: ['gulp-*', 'main-bower-files', 'uglify-save-license', 'del'] +}); + +gulp.task('partials', function () { + return gulp.src([ + path.join(conf.paths.src, '/app/**/*.html'), + path.join(conf.paths.tmp, '/serve/app/**/*.html') + ]) + .pipe($.minifyHtml({ + empty: true, + spare: true, + quotes: true + })) + .pipe($.angularTemplatecache('templateCacheHtml.js', { + module: 'dialoga', + root: 'app' + })) + .pipe(gulp.dest(conf.paths.tmp + '/partials/')); +}); + +gulp.task('html', ['inject', 'partials'], function () { + var partialsInjectFile = gulp.src(path.join(conf.paths.tmp, '/partials/templateCacheHtml.js'), { read: false }); + var partialsInjectOptions = { + starttag: '', + ignorePath: path.join(conf.paths.tmp, '/partials'), + addRootSlash: false + }; + + var htmlFilter = $.filter('*.html'); + var jsFilter = $.filter('**/*.js'); + var cssFilter = $.filter('**/*.css'); + var assets; + + return gulp.src(path.join(conf.paths.tmp, '/serve/*.html')) + .pipe($.inject(partialsInjectFile, partialsInjectOptions)) + .pipe(assets = $.useref.assets()) + .pipe($.rev()) + .pipe(jsFilter) + .pipe($.ngAnnotate()) + .pipe($.uglify({ preserveComments: $.uglifySaveLicense })).on('error', conf.errorHandler('Uglify')) + .pipe(jsFilter.restore()) + .pipe(cssFilter) + .pipe($.replace('../../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/', '../fonts/')) + .pipe($.csso()) + .pipe(cssFilter.restore()) + .pipe(assets.restore()) + .pipe($.useref()) + .pipe($.revReplace()) + .pipe(htmlFilter) + .pipe($.minifyHtml({ + empty: true, + spare: true, + quotes: true, + conditionals: true + })) + .pipe(htmlFilter.restore()) + .pipe(gulp.dest(path.join(conf.paths.dist, '/'))) + .pipe($.size({ title: path.join(conf.paths.dist, '/'), showFiles: true })); +}); + +// Only applies for fonts from bower dependencies +// Custom fonts are handled by the "other" task +gulp.task('fonts', function () { + return gulp.src($.mainBowerFiles()) + .pipe($.filter('**/*.{eot,svg,ttf,woff,woff2}')) + .pipe($.flatten()) + .pipe(gulp.dest(path.join(conf.paths.dist, '/fonts/'))); +}); + +gulp.task('other', function () { + var fileFilter = $.filter(function (file) { + return file.stat.isFile(); + }); + + return gulp.src([ + path.join(conf.paths.src, '/**/*'), + path.join('!' + conf.paths.src, '/**/*.{html,css,js,scss}') + ]) + .pipe(fileFilter) + .pipe(gulp.dest(path.join(conf.paths.dist, '/'))); +}); + +gulp.task('clean', function (done) { + $.del([path.join(conf.paths.dist, '/'), path.join(conf.paths.tmp, '/')], done); +}); + +gulp.task('build', ['html', 'fonts', 'other']); diff --git a/gulp/conf.js b/gulp/conf.js new file mode 100644 index 0000000..a8dec41 --- /dev/null +++ b/gulp/conf.js @@ -0,0 +1,41 @@ +/** + * This file contains the variables used in other gulp files + * which defines tasks + * By design, we only put there very generic config values + * which are used in several places to keep good readability + * of the tasks + */ + +var gutil = require('gulp-util'); + +/** + * The main paths of your project handle these with care + */ +exports.paths = { + src: 'src', + dist: 'dist', + tmp: '.tmp', + e2e: 'e2e' +}; + +/** + * Wiredep is the lib which inject bower dependencies in your project + * Mainly used to inject script tags in the index.html but also used + * to inject css preprocessor deps and js files in karma + */ +exports.wiredep = { + exclude: [/jquery/, /bootstrap.js$/, /bootstrap-sass-official\/.*\.js/, /bootstrap\.css/], + directory: 'bower_components' +}; + +/** + * Common implementation for an error handler of a Gulp plugin + */ +exports.errorHandler = function(title) { + 'use strict'; + + return function(err) { + gutil.log(gutil.colors.red('[' + title + ']'), err.toString()); + this.emit('end'); + }; +}; diff --git a/gulp/e2e-tests.js b/gulp/e2e-tests.js new file mode 100644 index 0000000..3a66702 --- /dev/null +++ b/gulp/e2e-tests.js @@ -0,0 +1,38 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +// Downloads the selenium webdriver +gulp.task('webdriver-update', $.protractor.webdriver_update); + +gulp.task('webdriver-standalone', $.protractor.webdriver_standalone); + +function runProtractor (done) { + var params = process.argv; + var args = params.length > 3 ? [params[3], params[4]] : []; + + gulp.src(path.join(conf.paths.e2e, '/**/*.js')) + .pipe($.protractor.protractor({ + configFile: 'protractor.conf.js', + args: args + })) + .on('error', function (err) { + // Make sure failed tests cause gulp to exit non-zero + throw err; + }) + .on('end', function () { + // Close browser sync server + browserSync.exit(); + done(); + }); +} + +gulp.task('protractor', ['protractor:src']); +gulp.task('protractor:src', ['serve:e2e', 'webdriver-update'], runProtractor); +gulp.task('protractor:dist', ['serve:e2e-dist', 'webdriver-update'], runProtractor); diff --git a/gulp/inject.js b/gulp/inject.js new file mode 100644 index 0000000..f1189f9 --- /dev/null +++ b/gulp/inject.js @@ -0,0 +1,36 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var $ = require('gulp-load-plugins')(); + +var wiredep = require('wiredep').stream; +var _ = require('lodash'); + +gulp.task('inject', ['scripts', 'styles'], function () { + var injectStyles = gulp.src([ + path.join(conf.paths.tmp, '/serve/app/**/*.css'), + path.join('!' + conf.paths.tmp, '/serve/app/vendor.css') + ], { read: false }); + + var injectScripts = gulp.src([ + path.join(conf.paths.src, '/app/**/*.module.js'), + path.join(conf.paths.src, '/app/**/*.js'), + path.join('!' + conf.paths.src, '/app/**/*.spec.js'), + path.join('!' + conf.paths.src, '/app/**/*.mock.js') + ]) + .pipe($.angularFilesort()).on('error', conf.errorHandler('AngularFilesort')); + + var injectOptions = { + ignorePath: [conf.paths.src, path.join(conf.paths.tmp, '/serve')], + addRootSlash: false + }; + + return gulp.src(path.join(conf.paths.src, '/*.html')) + .pipe($.inject(injectStyles, injectOptions)) + .pipe($.inject(injectScripts, injectOptions)) + .pipe(wiredep(_.extend({}, conf.wiredep))) + .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve'))); +}); diff --git a/gulp/scripts.js b/gulp/scripts.js new file mode 100644 index 0000000..7a3e536 --- /dev/null +++ b/gulp/scripts.js @@ -0,0 +1,17 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +gulp.task('scripts', function () { + return gulp.src(path.join(conf.paths.src, '/app/**/*.js')) + .pipe($.jshint()) + .pipe($.jshint.reporter('jshint-stylish')) + .pipe(browserSync.reload({ stream: true })) + .pipe($.size()) +}); diff --git a/gulp/server.js b/gulp/server.js new file mode 100644 index 0000000..7c03f18 --- /dev/null +++ b/gulp/server.js @@ -0,0 +1,63 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); +var browserSyncSpa = require('browser-sync-spa'); + +var util = require('util'); + +var proxyMiddleware = require('http-proxy-middleware'); + +function browserSyncInit(baseDir, browser) { + browser = browser === undefined ? 'default' : browser; + + var routes = null; + if(baseDir === conf.paths.src || (util.isArray(baseDir) && baseDir.indexOf(conf.paths.src) !== -1)) { + routes = { + '/bower_components': 'bower_components' + }; + } + + var server = { + baseDir: baseDir, + routes: routes + }; + + /* + * You can add a proxy to your backend by uncommenting the line bellow. + * You just have to configure a context which will we redirected and the target url. + * Example: $http.get('/users') requests will be automatically proxified. + * + * For more details and option, https://github.com/chimurai/http-proxy-middleware/blob/v0.0.5/README.md + */ + // server.middleware = proxyMiddleware('/users', {target: 'http://jsonplaceholder.typicode.com', proxyHost: 'jsonplaceholder.typicode.com'}); + + browserSync.instance = browserSync.init({ + startPath: '/', + server: server, + browser: browser + }); +} + +browserSync.use(browserSyncSpa({ + selector: '[ng-app]'// Only needed for angular apps +})); + +gulp.task('serve', ['watch'], function () { + browserSyncInit([path.join(conf.paths.tmp, '/serve'), conf.paths.src]); +}); + +gulp.task('serve:dist', ['build'], function () { + browserSyncInit(conf.paths.dist); +}); + +gulp.task('serve:e2e', ['inject'], function () { + browserSyncInit([conf.paths.tmp + '/serve', conf.paths.src], []); +}); + +gulp.task('serve:e2e-dist', ['build'], function () { + browserSyncInit(conf.paths.dist, []); +}); diff --git a/gulp/styles.js b/gulp/styles.js new file mode 100644 index 0000000..d6d4bfe --- /dev/null +++ b/gulp/styles.js @@ -0,0 +1,46 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +var $ = require('gulp-load-plugins')(); + +var wiredep = require('wiredep').stream; +var _ = require('lodash'); + +gulp.task('styles', function () { + var sassOptions = { + style: 'expanded' + }; + + var injectFiles = gulp.src([ + path.join(conf.paths.src, '/app/**/*.scss'), + path.join('!' + conf.paths.src, '/app/index.scss') + ], { read: false }); + + var injectOptions = { + transform: function(filePath) { + filePath = filePath.replace(conf.paths.src + '/app/', ''); + return '@import "' + filePath + '";'; + }, + starttag: '// injector', + endtag: '// endinjector', + addRootSlash: false + }; + + + return gulp.src([ + path.join(conf.paths.src, '/app/index.scss') + ]) + .pipe($.inject(injectFiles, injectOptions)) + .pipe(wiredep(_.extend({}, conf.wiredep))) + .pipe($.sourcemaps.init()) + .pipe($.sass(sassOptions)).on('error', conf.errorHandler('Sass')) + .pipe($.autoprefixer()).on('error', conf.errorHandler('Autoprefixer')) + .pipe($.sourcemaps.write()) + .pipe(gulp.dest(path.join(conf.paths.tmp, '/serve/app/'))) + .pipe(browserSync.reload({ stream: true })); +}); diff --git a/gulp/unit-tests.js b/gulp/unit-tests.js new file mode 100644 index 0000000..5e6aa75 --- /dev/null +++ b/gulp/unit-tests.js @@ -0,0 +1,25 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var karma = require('karma'); + +function runTests (singleRun, done) { + karma.server.start({ + configFile: path.join(__dirname, '/../karma.conf.js'), + singleRun: singleRun, + autoWatch: !singleRun + }, function() { + done(); + }); +} + +gulp.task('test', ['scripts'], function(done) { + runTests(true, done); +}); + +gulp.task('test:auto', ['watch'], function(done) { + runTests(false, done); +}); diff --git a/gulp/watch.js b/gulp/watch.js new file mode 100644 index 0000000..16eb324 --- /dev/null +++ b/gulp/watch.js @@ -0,0 +1,39 @@ +'use strict'; + +var path = require('path'); +var gulp = require('gulp'); +var conf = require('./conf'); + +var browserSync = require('browser-sync'); + +function isOnlyChange(event) { + return event.type === 'changed'; +} + +gulp.task('watch', ['inject'], function () { + + gulp.watch([path.join(conf.paths.src, '/*.html'), 'bower.json'], ['inject']); + + gulp.watch([ + path.join(conf.paths.src, '/app/**/*.css'), + path.join(conf.paths.src, '/app/**/*.scss') + ], function(event) { + if(isOnlyChange(event)) { + gulp.start('styles'); + } else { + gulp.start('inject'); + } + }); + + gulp.watch(path.join(conf.paths.src, '/app/**/*.js'), function(event) { + if(isOnlyChange(event)) { + gulp.start('scripts'); + } else { + gulp.start('inject'); + } + }); + + gulp.watch(path.join(conf.paths.src, '/app/**/*.html'), function(event) { + browserSync.reload(event.path); + }); +}); diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..17e211e --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,30 @@ +/** + * Welcome to your gulpfile! + * The gulp tasks are splitted in several files in the gulp directory + * because putting all here was really too long + */ + +'use strict'; + +var gulp = require('gulp'); +var wrench = require('wrench'); + +/** + * This will load all js or coffee files in the gulp directory + * in order to load all gulp tasks + */ +wrench.readdirSyncRecursive('./gulp').filter(function(file) { + return (/\.(js|coffee)$/i).test(file); +}).map(function(file) { + require('./gulp/' + file); +}); + + +/** + * Default task clean temporaries directories and launch the + * main optimization build task + */ +gulp.task('default', ['clean'], function () { + // gulp.start('build'); + gulp.start('serve'); +}); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..ccdef8a --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,74 @@ +'use strict'; + +var path = require('path'); +var conf = require('./gulp/conf'); + +var _ = require('lodash'); +var wiredep = require('wiredep'); + +function listFiles() { + var wiredepOptions = _.extend({}, conf.wiredep, { + dependencies: true, + devDependencies: true + }); + + return wiredep(wiredepOptions).js + .concat([ + path.join(conf.paths.src, '/app/**/*.module.js'), + path.join(conf.paths.src, '/app/**/*.js'), + path.join(conf.paths.src, '/**/*.spec.js'), + path.join(conf.paths.src, '/**/*.mock.js'), + path.join(conf.paths.src, '/**/*.html') + ]); +} + +module.exports = function(config) { + + var configuration = { + files: listFiles(), + + singleRun: true, + + autoWatch: false, + + frameworks: ['jasmine', 'angular-filesort'], + + angularFilesort: { + whitelist: [path.join(conf.paths.src, '/**/!(*.html|*.spec|*.mock).js')] + }, + + ngHtml2JsPreprocessor: { + stripPrefix: 'src/', + moduleName: 'dialoga' + }, + + browsers : ['PhantomJS'], + + plugins : [ + 'karma-phantomjs-launcher', + 'karma-angular-filesort', + 'karma-jasmine', + 'karma-ng-html2js-preprocessor' + ], + + preprocessors: { + 'src/**/*.html': ['ng-html2js'] + } + }; + + // This block is needed to execute Chrome on Travis + // If you ever plan to use Chrome and Travis, you can keep it + // If not, you can safely remove it + // https://github.com/karma-runner/karma/issues/1144#issuecomment-53633076 + if(configuration.browsers[0] === 'Chrome' && process.env.TRAVIS) { + configuration.customLaunchers = { + 'chrome-travis-ci': { + base: 'Chrome', + flags: ['--no-sandbox'] + } + }; + configuration.browsers = ['chrome-travis-ci']; + } + + config.set(configuration); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..795d11b --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "dialoga", + "version": "0.0.0", + "dependencies": {}, + "scripts": { + "test": "gulp test" + }, + "devDependencies": { + "gulp": "~3.9.0", + "gulp-autoprefixer": "~2.3.1", + "gulp-angular-templatecache": "~1.6.0", + "del": "~1.2.0", + "lodash": "~3.9.3", + "gulp-csso": "~1.0.0", + "gulp-filter": "~2.0.2", + "gulp-flatten": "~0.0.4", + "gulp-jshint": "~1.11.0", + "gulp-load-plugins": "~0.10.0", + "gulp-size": "~1.2.1", + "gulp-uglify": "~1.2.0", + "gulp-useref": "~1.2.0", + "gulp-util": "~3.0.5", + "gulp-ng-annotate": "~1.0.0", + "gulp-replace": "~0.5.3", + "gulp-rename": "~1.2.2", + "gulp-rev": "~5.0.0", + "gulp-rev-replace": "~0.4.2", + "gulp-minify-html": "~1.0.3", + "gulp-inject": "~1.3.1", + "gulp-protractor": "~1.0.0", + "gulp-sourcemaps": "~1.5.2", + "gulp-sass": "~2.0.1", + "gulp-angular-filesort": "~1.1.1", + "main-bower-files": "~2.8.0", + "merge-stream": "~0.1.7", + "jshint-stylish": "~2.0.0", + "wiredep": "~2.2.2", + "karma": "~0.12.36", + "karma-jasmine": "~0.3.5", + "karma-phantomjs-launcher": "~0.2.0", + "karma-angular-filesort": "~0.1.0", + "karma-ng-html2js-preprocessor": "~0.1.2", + "concat-stream": "~1.5.0", + "require-dir": "~0.3.0", + "browser-sync": "~2.7.12", + "browser-sync-spa": "~1.0.2", + "http-proxy-middleware": "~0.0.5", + "chalk": "~1.0.0", + "uglify-save-license": "~0.4.1", + "wrench": "~1.5.8" + }, + "engines": { + "node": ">=0.10.0" + } +} diff --git a/protractor.conf.js b/protractor.conf.js new file mode 100644 index 0000000..f2db101 --- /dev/null +++ b/protractor.conf.js @@ -0,0 +1,27 @@ +'use strict'; + +var paths = require('./.yo-rc.json')['generator-gulp-angular'].props.paths; + +// An example configuration file. +exports.config = { + // The address of a running selenium server. + //seleniumAddress: 'http://localhost:4444/wd/hub', + //seleniumServerJar: deprecated, this should be set on node_modules/protractor/config.json + + // Capabilities to be passed to the webdriver instance. + capabilities: { + 'browserName': 'chrome' + }, + + baseUrl: 'http://localhost:3000', + + // Spec patterns are relative to the current working directly when + // protractor is called. + specs: [paths.e2e + '/**/*.js'], + + // Options to be passed to Jasmine-node. + jasmineNodeOpts: { + showColors: true, + defaultTimeoutInterval: 30000 + } +}; diff --git a/src/app/components/errorHandler/errorHandler.service.js b/src/app/components/errorHandler/errorHandler.service.js new file mode 100644 index 0000000..ce822d9 --- /dev/null +++ b/src/app/components/errorHandler/errorHandler.service.js @@ -0,0 +1,20 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .service('ErrorService', ErrorService); + + /** @ngInject */ + function ErrorService(){ + var service = { + paramRequired: paramRequired + }; + + return service; + + function paramRequired(paramName){ + return 'param required: ' + paramName; + } + } +})(); diff --git a/src/app/components/navbar/navbar.directive.js b/src/app/components/navbar/navbar.directive.js new file mode 100644 index 0000000..f3f330c --- /dev/null +++ b/src/app/components/navbar/navbar.directive.js @@ -0,0 +1,33 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .directive('appNavbar', appNavbar); + + /** @ngInject */ + function appNavbar() { + var directive = { + restrict: 'E', + templateUrl: 'app/components/navbar/navbar.html', + scope: { + creationDate: '=' + }, + controller: NavbarController, + controllerAs: 'vm', + bindToController: true + }; + + return directive; + + /** @ngInject */ + function NavbarController($log) { + $log.debug('NavbarController'); + // var vm = this; + + // "vm.creation" is avaible by directive option "bindToController: true" + // vm.relativeDate = moment(vm.creationDate).fromNow(); + } + } + +})(); diff --git a/src/app/components/navbar/navbar.html b/src/app/components/navbar/navbar.html new file mode 100644 index 0000000..565a551 --- /dev/null +++ b/src/app/components/navbar/navbar.html @@ -0,0 +1,24 @@ + diff --git a/src/app/components/navbar/navbar.scss b/src/app/components/navbar/navbar.scss new file mode 100644 index 0000000..9be162b --- /dev/null +++ b/src/app/components/navbar/navbar.scss @@ -0,0 +1,8 @@ +.navbar-brand { + height: auto; +} + +.navbar-nav > li > a { + padding-top: 30px; + padding-bottom: 30px; +} diff --git a/src/app/components/programa/programa.directive.js b/src/app/components/programa/programa.directive.js new file mode 100644 index 0000000..81a6847 --- /dev/null +++ b/src/app/components/programa/programa.directive.js @@ -0,0 +1,54 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .directive('programaBox', programaBox); + + /** @ngInject */ + function programaBox(ProgramaService, $log) { + + /** @ngInject */ + function ProgramaController() { + $log.debug('ProgramaController'); + + var vm = this; + + $log.debug('this.programa', vm.programa); + vm.proposal = vm.programa; + } + + ProgramaController.prototype.getCategory = function () { + return this.proposal.categories[0]; + }; + ProgramaController.prototype.getCategoryName = function () { + return this.getCategory().name; + }; + + ProgramaController.prototype.getImageUrl = function () { + return 'http://login.dialoga.gov.br/image_uploads/dialoga/0000/0053/requalif_redim.jpg'; + }; + ProgramaController.prototype.getImageAlt = function () { + return 'TODO: descrição da imagem.'; + }; + + ProgramaController.prototype.showContent = function () { + $log.debug('TODO: showContent()'); + }; + + var directive = { + restrict: 'E', + templateUrl: 'app/components/programa/programa.html', + scope: { + programa: '=programa' + }, + controller: ProgramaController, + controllerAs: 'vm', + bindToController: true + }; + + + return directive; + } + +})(); diff --git a/src/app/components/programa/programa.html b/src/app/components/programa/programa.html new file mode 100644 index 0000000..d77a033 --- /dev/null +++ b/src/app/components/programa/programa.html @@ -0,0 +1,6 @@ +
Caminho para uma educação de qualidade.
", + image: {url: "/image_uploads/dialoga/0000/0140/valorizacao_professor.jpg"}, + categories: [{name: "[category]", id: -1, slug: "[category-slug]", image: null}], + author: '[author]', + position: -1, // ? + profile: { // ? + id: -1, + identifier: '[profile.identifier]', + name: '[profile.name]', + }, + setting: { // ? + author_name: '[setting.author_name]', + comment_paragraph_plugin_activate: false + }, + children: [], // ? + tag_list: [] + }; + } + + /** + * Get a list of articles + * @param {Number} limit per_page (default is 30) + * @return {Array} a list of articles + */ + function getArticles (limit) { + if (!limit) { + limit = 30; + } + + return $http.get(endpoint.articles) + .then(handleSuccess) + .catch(handleError); + } + + /** + * Get a task by id + * @param {Number} id of task + * @return {Object} the wanted task or a new one. + */ + function getProposal (id) { + if (!id) { + throw new Error(ErrorService.paramRequired('id')); + } + + return $http.get(endpoint.tasks) + .then(handleSuccess) + .catch(handleError); + } + + // --- + // PRIVATE METHODS + // --- + + /** + * Transform the successful response, unwrapping the application data + * from the API response payload. + * + * @param {Object} response from the server. + * @return {Object} the data unwrapped. + */ + function handleSuccess (response){ + return response.data; + } + + /** + * Transform the error response, unwrapping the application data from + * the API response payload. + * + * @param {Object} error from the server. + * @return {Promise} promise rejection called. + */ + function handleError (error){ + + $log.error('XHR Failed on ProgramaService.\n' + angular.toJson(error.data, true)); + + // The API response from the server should be returned in a + // nomralized format. However, if the request was not handled by the + // server (or what not handles properly - ex. server error), then we + // may have to normalize it on our end, as best we can. + if ( !angular.isObject( error.data ) || !error.data.message) { + return( $q.reject( 'An unknown error occurred.' ) ); + } + + // Otherwise, use expected error message. + return $q.reject(error.data.message); + } + } +})(); diff --git a/src/app/index.config.js b/src/app/index.config.js new file mode 100644 index 0000000..2fb0c76 --- /dev/null +++ b/src/app/index.config.js @@ -0,0 +1,16 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .config(config); + + /** @ngInject */ + function config($logProvider) { + // Enable log + $logProvider.debugEnabled(true); + + // Set options third-party lib + } + +})(); diff --git a/src/app/index.constants.js b/src/app/index.constants.js new file mode 100644 index 0000000..fc8f857 --- /dev/null +++ b/src/app/index.constants.js @@ -0,0 +1,12 @@ +/* global Modernizr:false */ +(function() { + 'use strict'; + + angular + .module('dialoga') + .constant('private_token', null) + .constant('Modernizr', Modernizr) + // .constant('key', value) + ; + +})(); diff --git a/src/app/index.module.js b/src/app/index.module.js new file mode 100644 index 0000000..ad96e04 --- /dev/null +++ b/src/app/index.module.js @@ -0,0 +1,7 @@ +(function() { + 'use strict'; + + angular + .module('dialoga', ['ngAnimate', 'ngCookies', 'ngTouch', 'ngSanitize', 'restangular', 'ui.router']); + +})(); diff --git a/src/app/index.route.js b/src/app/index.route.js new file mode 100644 index 0000000..32228d8 --- /dev/null +++ b/src/app/index.route.js @@ -0,0 +1,65 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .config(routeConfig); + + /** @ngInject */ + function routeConfig($stateProvider, $urlRouterProvider) { + $stateProvider + .state('inicio', { + url: '/', + views: { + 'header': { templateUrl: 'app/partials/header/header.html' }, + 'main': { + templateUrl: 'app/partials/inicio/inicio.html', + controller: 'InicioController', + controllerAs: 'inicio' + }, + 'footer': { templateUrl: 'app/partials/footer/footer.html' } + } + }) + .state('programas', { + url: '/programas', + views: { + 'header': { templateUrl: 'app/partials/header/header.html' }, + 'main': { + templateUrl: 'app/partials/programas/programas.html', + controller: 'ProgramasController', + controllerAs: 'programas' + }, + 'footer': { templateUrl: 'app/partials/footer/footer.html' } + } + }) + .state('sobre', { + url: '/sobre', + views: { + 'header': { templateUrl: 'app/partials/header/header.html' }, + 'main': { + templateUrl: 'app/partials/article/article.html', + controller: 'ArticleController', + controllerAs: 'article' + }, + 'footer': { templateUrl: 'app/partials/footer/footer.html' } + } + }) + .state('termos-de-uso', { + url: '/termos-de-uso', + controller: 'ArticleController', + views: { + 'header': { templateUrl: 'app/partials/header/header.html' }, + 'main': { + templateUrl: 'app/partials/article/article.html', + controller: 'ArticleController', + controllerAs: 'article' + }, + 'footer': { templateUrl: 'app/partials/footer/footer.html' } + } + }) + ; + + $urlRouterProvider.otherwise('/'); + } + +})(); diff --git a/src/app/index.run.js b/src/app/index.run.js new file mode 100644 index 0000000..07b9f3e --- /dev/null +++ b/src/app/index.run.js @@ -0,0 +1,18 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .run(runBlock); + + /** @ngInject */ + function runBlock($log) { + + $log.debug('runBlock end'); + + window.skipToContent = function () { + console.log('TODO: skipToContent'); + }; + } + +})(); diff --git a/src/app/index.scss b/src/app/index.scss new file mode 100644 index 0000000..36f0d64 --- /dev/null +++ b/src/app/index.scss @@ -0,0 +1,41 @@ +/** + * If you want to override some bootstrap variables, you have to change values here. + * The list of variables are listed here bower_components/bootstrap-sass-official/assets/stylesheets/bootstrap/_variables.scss + */ +$navbar-inverse-link-color: #5AADBB; +$icon-font-path: "../../bower_components/bootstrap-sass-official/assets/fonts/bootstrap/"; + +/** + * Do not remove this comments bellow. It's the markers used by wiredep to inject + * sass dependencies when defined in the bower.json of your dependencies + */ +// bower:scss +// endbower + +.browsehappy { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +$barra-theme: ("green": #00420c, "yellow": #2c66ce, "blue": #0042b1); + +.skip-links a:focus { + background-color: #fff !important; + opacity: 1; + z-index: 2; +} + +#footer-brasil { + background: none repeat scroll 0% 0% map-get($barra-theme, "blue"); + padding: 1em 0px; + max-width: 100%; +} + +/** + * Do not remove this comments bellow. It's the markers used by gulp-inject to inject + * all your sass files automatically + */ +// injector +// endinjector diff --git a/src/app/partials/article/article.controller.js b/src/app/partials/article/article.controller.js new file mode 100644 index 0000000..ff5a605 --- /dev/null +++ b/src/app/partials/article/article.controller.js @@ -0,0 +1,28 @@ +(function() { + 'use strict'; + + angular + .module('dialoga') + .controller('ArticleController', ArticleController); + + /** @ngInject */ + function ArticleController($state, $log) { + $log.debug('ArticleController'); + + var vm = this; + + vm.page = $state.current.name; + + switch ( $state.current.name ) { + case 'sobre': + break; + case 'termos-de-uso': + break; + default: + $log.debug('$state.current.name', $state.current.name); + break; + } + + // page = $state.is('sobre'); + } +})(); diff --git a/src/app/partials/article/article.controller.spec.js b/src/app/partials/article/article.controller.spec.js new file mode 100644 index 0000000..32746ac --- /dev/null +++ b/src/app/partials/article/article.controller.spec.js @@ -0,0 +1,15 @@ +(function() { + 'use strict'; + + describe('controllers', function(){ + + beforeEach(module('dialoga')); + + // it('should define more than 5 awesome things', inject(function($controller) { + // var vm = $controller('SobreController'); + + // // expect(angular.isArray(vm.awesomeThings)).toBeTruthy(); + // // expect(vm.awesomeThings.length > 5).toBeTruthy(); + // })); + }); +})(); diff --git a/src/app/partials/article/article.html b/src/app/partials/article/article.html new file mode 100644 index 0000000..35c87bf --- /dev/null +++ b/src/app/partials/article/article.html @@ -0,0 +1,5 @@ +