├── .gitignore ├── .travis.yml ├── LICENCE ├── README.md ├── bower.json ├── gulpfile.js ├── karma.conf.js ├── package.json ├── src ├── app.js ├── config │ └── _default.json ├── index.html ├── locale │ ├── de-de.js │ └── en-gb.js ├── modules │ └── orders │ │ ├── index.js │ │ ├── lang │ │ └── en.json │ │ ├── order │ │ ├── index.js │ │ ├── order.controller.js │ │ └── orders.order.tpl.html │ │ ├── orders.controller.js │ │ ├── orders.scss │ │ ├── orders.service.js │ │ ├── orders.tpl.html │ │ └── orders │ │ ├── index.js │ │ ├── orders.directive.js │ │ └── orders.orders.tpl.html └── scss │ └── app.scss ├── tests └── unit │ └── modules │ └── orders │ └── orders.service.spec.js └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | config/ 3 | !src/config 4 | dist 5 | 6 | # Logs 7 | logs 8 | *.log 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # Commenting this out is preferred by some people, see 29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 30 | node_modules 31 | bower_components/ 32 | 33 | # Users Environment Variables 34 | .lock-wscript 35 | 36 | .DS_Store 37 | 38 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | before_install: 5 | - npm config set ca "" 6 | - npm install -g grunt-cli 7 | - npm install -g bower 8 | # - npm install -g selenium-webdriver 9 | install: 10 | - yes no | bower install 11 | - npm install 12 | before_script: 13 | - export DISPLAY=:99.0 14 | - sh -e /etc/init.d/xvfb start 15 | # - webdriver-manager update --standalone && webdriver-manager start > /dev/null & 16 | # - sleep 20 17 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Matt Richards 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gulp-angular-webpack-seed 2 | 3 | This project is not maintained, contains vulnerable dependencies, leverages legacy libraries and tools (Angular 1.x, node ~0.10) and demonstrates outdated patterns. 4 | 5 | **There are almost certainly better ways to approach this with a modern stack!** 6 | 7 | Proceed with caution & at your own risk. 8 | 9 | --- 10 | 11 | [![Build Status][2]][1] [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 12 | 13 | [1]: https://travis-ci.org/tthew/gulp-angular-webpack-seed 14 | [2]: https://travis-ci.org/tthew/gulp-angular-webpack-seed.svg 15 | 16 | An Angular.js based Single Page Application seed. 17 | 18 | Features: 19 | 20 | - Modular design with the help of Webpack 21 | - Stateful GUI utilising ui.router 22 | - RESTful data consumption with Restangular 23 | - I18n/l10n with angular-translate 24 | - 'Atomic' Modules / Components 25 | - Self-documenting project structure 26 | - Build Environment using Gulp 27 | 28 | ## Prerequisites 29 | 30 | - Node (~0.10) 31 | - Npm 32 | - Gulp 33 | - Bower 34 | 35 | ### Installation 36 | 37 | > npm install && bower install 38 | 39 | #### Running the development server 40 | 41 | > gulp serve 42 | 43 | #### Running the test suite 44 | 45 | > gulp test 46 | 47 | Or like this to run either `unit` or `e2e` tests in isolation: 48 | 49 | # Unit Tests 50 | > gulp test:unit 51 | 52 | # Integration Tests 53 | > gulp test:e2e 54 | 55 | ## Disclaimer 56 | 57 | This is a WIP / Sandbox project and is in **no way** suggested to be any kind of 'best practice'. Expect breaking changes. Use wisely and at your own risk. 58 | 59 | ## Licence 60 | 61 | The MIT License (MIT) 62 | 63 | Copyright (c) 2014 Matt Richards 64 | 65 | Permission is hereby granted, free of charge, to any person obtaining a copy 66 | of this software and associated documentation files (the "Software"), to deal 67 | in the Software without restriction, including without limitation the rights 68 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 69 | copies of the Software, and to permit persons to whom the Software is 70 | furnished to do so, subject to the following conditions: 71 | 72 | The above copyright notice and this permission notice shall be included in 73 | all copies or substantial portions of the Software. 74 | 75 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 76 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 77 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 78 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 79 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 80 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 81 | THE SOFTWARE. 82 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tthew-gulp-angular-webpack-seed", 3 | "version": "0.0.1", 4 | "homepage": "", 5 | "authors": [ 6 | "Matt Richards " 7 | ], 8 | "description": "", 9 | "main": "src/index.html", 10 | "license": "MIT", 11 | "private": true, 12 | "ignore": [ 13 | "**/.*", 14 | "node_modules", 15 | "bower_components", 16 | "test", 17 | "tests" 18 | ], 19 | "dependencies": { 20 | "angular": "~1.3.0", 21 | "angular-mocks": "~1.2.25", 22 | "angular-ui-router": "~0.2.11", 23 | "angular-foundation": "~0.3.1", 24 | "foundation": "~5.4.5", 25 | "angular-animate": "~1.3.0", 26 | "font-awesome": "~4.2.0", 27 | "angular-bindonce": "~0.3.1", 28 | "query-string": "~1.0.0", 29 | "angular-ui-select": "~0.7.0", 30 | "angular-sanitize": "~1.2.26", 31 | "contentful": "~0.1.2", 32 | "ng-contentful": "~0.1.2", 33 | "showdown": "~0.3.1", 34 | "angular-markdown-filter": "~1.0.0", 35 | "angularjs": "~1.3.0", 36 | "restangular": "~1.4.0", 37 | "lodash": "~2.4.1", 38 | "angular-translate": "~2.4.2", 39 | "bootstrap-sass-official": "~3.3.0" 40 | }, 41 | "resolutions": { 42 | "angular": "~1.3.0", 43 | "angular-animate": "~1.3.0" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // Gulp plugins 2 | var gulp = require('gulp'); 3 | var changed = require('gulp-changed'); 4 | var gutil = require('gulp-util'); 5 | var gulpNgConfig = require('gulp-ng-config'); 6 | 7 | // Misc 8 | var spawn = require('child_process').spawn; 9 | var argv = require('minimist')(process.argv.slice(2)); 10 | var rimraf = require('rimraf'); 11 | 12 | // Webpack 13 | var webpack = require('webpack'); 14 | var WebpackDevServer = require('webpack-dev-server'); 15 | var webpackConfig = require('./webpack.config'); 16 | var ngAnnotatePlugin = require('ng-annotate-webpack-plugin'); 17 | 18 | // Karma 19 | var karma = require('karma'); 20 | var karmaServer = karma.server; 21 | 22 | // Ports 23 | var ports = { 24 | livereload: 35730, 25 | dev: 5000 26 | }; 27 | 28 | // Paths 29 | var paths = { 30 | other: [ 31 | '!src/index.html', 32 | 'src/images/**', 33 | 'src/fonts/**', 34 | '!src/**/*.js', 35 | '!src/**/*.coffee', 36 | '!src/**/*.scss', 37 | '!src/**/*.tpl.html' 38 | ], 39 | distDir: './dist/' 40 | }; 41 | 42 | 43 | // Webpack config 44 | if (argv.production) { // --production option 45 | webpackConfig.plugins = webpackConfig 46 | .plugins 47 | .concat( 48 | new ngAnnotatePlugin(), 49 | new webpack.optimize.UglifyJsPlugin() 50 | ); 51 | 52 | webpackConfig.devtool = false; 53 | webpackConfig.debug = false; 54 | } 55 | 56 | 57 | var prodConfig = Object.create(webpackConfig); 58 | prodConfig.plugins = webpackConfig.plugins.concat( 59 | new webpack.DefinePlugin({ 60 | 'process-env': { 61 | 'NODE_ENV': JSON.stringify('production') 62 | } 63 | }), 64 | new webpack.optimize.DedupePlugin(), 65 | new webpack.optimize.UglifyJsPlugin() 66 | ); 67 | 68 | gulp.task('webpack:build', function (done) { 69 | webpack(prodConfig, function (err, stats) { 70 | if (err) { 71 | throw new gutil.PluginError('webpack:build', err); 72 | } 73 | 74 | gutil.log('[webpack:build]', stats.toString({ 75 | colors: true 76 | })); 77 | 78 | done(); 79 | }); 80 | }); 81 | 82 | var webpackDevConfig = Object.create(webpackConfig); 83 | webpackDevConfig.devtool = 'eval'; 84 | webpackDevConfig.debug = true; 85 | webpackDevCompiler = webpack(webpackDevConfig); 86 | 87 | gulp.task('webpack-dev-server', function (cb) { 88 | new WebpackDevServer(webpack(webpackDevConfig), { 89 | contentBase: './dist/', 90 | quiet: false, 91 | noInfo: false, 92 | lazy: false, 93 | watchDelay: 300, 94 | stats: { 95 | colors: true 96 | } 97 | }).listen(ports.dev, '0.0.0.0', function (err) { 98 | 99 | if (err) { 100 | throw new gutil.PluginError('webpack-dev-server', err); 101 | } 102 | 103 | gutil.log('[webpack-dev-server]', 'http://localhost:' + ports.dev); 104 | 105 | }); 106 | 107 | }); 108 | 109 | 110 | // gulp config, parses src/config/_default and outputs an ngConstant ready for 111 | // injection 112 | gulp.task('config', function () { 113 | gulp.src('./src/config/_default.json') 114 | .pipe(gulpNgConfig('tthew.config')) 115 | .pipe(gulp.dest('./config')); 116 | }); 117 | 118 | // gulp other, moves changed files from source to other 119 | gulp.task('other', function () { 120 | gulp.src(paths.other) 121 | .pipe(changed(paths.distDir)) 122 | .pipe(gulp.dest(paths.distDir)); 123 | }); 124 | 125 | // gulp test:unit, runs unit test suite 126 | gulp.task('test:unit', function () { 127 | karmaServer.start({ 128 | configFile: __dirname + '/karma.conf.js', 129 | singleRun: true 130 | }); 131 | }); 132 | 133 | // gulp test, runs full test suite 134 | gulp.task('test', ['test:unit']); 135 | 136 | // clears dist directory 137 | gulp.task('clearTarget', function () { 138 | rimraf.sync(paths.distDir, gutil.log); 139 | }); 140 | 141 | // gulp build, runs build 142 | gulp.task('build', [ 143 | 'clearTarget', 144 | 'webpack:build', 145 | 'other' 146 | ]); 147 | 148 | // gulp watch, watch for changes 149 | gulp.task('watch', ['clearTarget', 'other'], function () { 150 | webpack(webpackDevConfig) 151 | .watch(200, function (err, stats) { 152 | if (err) { 153 | throw new gutil.PluginError('webpack', err); 154 | } 155 | 156 | gutil.log('[webpack]', stats.toString({ 157 | colors: true 158 | })); 159 | }); 160 | 161 | gulp.watch(paths.other, ['other']); 162 | }); 163 | 164 | // gulp serve, launches webpack-dev-server and watches 165 | gulp.task('serve', ['config', 'webpack-dev-server', 'watch']); 166 | 167 | // gulp, default gulp behavior (build) 168 | gulp.task('default', ['build']); 169 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | var fullWebpackConfig, webpack, webpackConfig; 2 | 3 | webpack = require('webpack'); 4 | 5 | fullWebpackConfig = require('./webpack.config.js'); 6 | 7 | webpackConfig = { 8 | module: fullWebpackConfig.module, 9 | resolve: fullWebpackConfig.resolve, 10 | plugins: [ 11 | new webpack.ResolverPlugin([ 12 | new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])], ["normal", "loader"]), new webpack.ContextReplacementPlugin(/.*$/, /a^/), new webpack.ProvidePlugin({ 13 | // 'angular': 'exports?window.angular!bower/angular' 14 | }), new webpack.ProvidePlugin({ 15 | "contentful": "contentful" 16 | }) 17 | ], 18 | devtool: 'eval', 19 | cache: true 20 | }; 21 | 22 | module.exports = function(config) { 23 | return config.set({ 24 | basePath: __dirname, 25 | frameworks: ['mocha', 'chai'], 26 | files: [ 27 | 'bower_components/angular/angular.js', 28 | 'bower_components/angular-mocks/angular-mocks.js', 29 | 'tests/**/*.spec.js' 30 | ], 31 | exclude: [], 32 | plugins: [ 33 | require('karma-webpack'), 34 | require('karma-mocha'), 35 | require('karma-chai'), 36 | require('karma-firefox-launcher') 37 | ], 38 | preprocessors: { 39 | 'tests/**/*.spec.js': ['webpack'] 40 | }, 41 | webpack: webpackConfig, 42 | reporters: ['progress'], 43 | port: 9876, 44 | colors: true, 45 | logLevel: config.LOG_INFO, 46 | autoWatch: true, 47 | browsers: ['Firefox'], 48 | singleRun: false 49 | }); 50 | }; 51 | 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tthew-gulp-angular-webpack-seed", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "", 6 | "scripts": { 7 | "install": "bower install", 8 | "start": "gulp serve", 9 | "test": "gulp test" 10 | }, 11 | "keywords": [], 12 | "engines": { 13 | "node": "0.10.x" 14 | }, 15 | "repository": "", 16 | "author": "Matt Richards ", 17 | "homepage": "", 18 | "devDependencies": { 19 | "chai": "^1.9.2", 20 | "coffee-loader": "*", 21 | "coffee-script": "~1.8.0", 22 | "contentful": "^0.1.2", 23 | "css-loader": "*", 24 | "exports-loader": "*", 25 | "extract-text-webpack-plugin": "^0.3.1", 26 | "file-loader": "^0.7.2", 27 | "gulp": "~3.8.8", 28 | "gulp-angular-templatecache": "^1.4.1", 29 | "gulp-cached": "1.0.1", 30 | "gulp-changed": "^1.0.0", 31 | "gulp-connect": "^2.0.6", 32 | "gulp-git": "^0.5.1", 33 | "gulp-karma": "0.0.4", 34 | "gulp-ng-config": "^0.1.7", 35 | "gulp-ng-constant": "^0.2.0", 36 | "gulp-util": "~3.0.1", 37 | "html-webpack-plugin": "^1.1.0", 38 | "imports-loader": "*", 39 | "jquery": "^2.1.1", 40 | "json-loader": "^0.5.1", 41 | "karma": "^0.12.24", 42 | "karma-chai": "^0.1.0", 43 | "karma-coffee-preprocessor": "^0.2.1", 44 | "karma-firefox-launcher": "^0.1.3", 45 | "karma-mocha": "^0.1.9", 46 | "karma-webpack": "^1.3.1", 47 | "local-tld-lib": "^1.0.2", 48 | "minimist": "1.1.0", 49 | "mocha": "^2.0.1", 50 | "moment": "^2.8.3", 51 | "ng-annotate": "^0.10.2", 52 | "ng-annotate-webpack-plugin": "^0.1.2", 53 | "ng-cache-loader": "0.0.1", 54 | "ngmin-webpack-plugin": "^0.1.3", 55 | "ngtemplate-loader": "^0.1.2", 56 | "pouchdb": "^3.0.6", 57 | "pouchdb-validation": "^1.2.0", 58 | "raw-loader": "*", 59 | "rimraf": "^2.2.8", 60 | "sass-loader": "^0.2.0", 61 | "showdown": "^0.3.1", 62 | "style-loader": "*", 63 | "url-loader": "^0.5.5", 64 | "webpack": "^1.4.9", 65 | "webpack-core": "^0.4.8", 66 | "webpack-dev-server": "~1.7.0" 67 | }, 68 | "browser": { 69 | "fs": false 70 | }, 71 | "dependencies": { 72 | "extract-text-webpack-plugin": "^0.3.5" 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/app.js: -------------------------------------------------------------------------------- 1 | /*** VENDOR ***/ 2 | require('bower/bootstrap-sass-official/assets/stylesheets/_bootstrap.scss'); 3 | require('bower/lodash/dist/lodash.js'); 4 | require('bower/foundation/css/normalize.css'); 5 | require('bower/angular-ui-router/release/angular-ui-router'); 6 | require('bower/angular-sanitize/angular-sanitize.js'); 7 | require('bower/angular-animate/angular-animate.js'); 8 | require('bower/restangular/src/restangular.js'); 9 | require('bower/angular-translate/angular-translate.js'); 10 | 11 | $ = require('jquery'); 12 | 13 | /*** APPLICATION ***/ 14 | 15 | // Config 16 | require('../config/_default.js'); 17 | 18 | // Locale 19 | require('./locale/en-gb.js'); 20 | 21 | // Modules 22 | require('./modules/orders'); 23 | 24 | // SCSS 25 | require('./scss/app.scss'); 26 | 27 | // Init App 28 | var app = angular.module('tthew.app', [ 29 | // Vendor 30 | 'ngSanitize', 31 | 'ui.router', 32 | 'restangular', 33 | 'pascalprecht.translate', 34 | // Modules 35 | 'tthew.orders', 36 | // App 37 | 'tthew.config' 38 | ]); 39 | 40 | // App config 41 | app.config(function ($urlRouterProvider, $translateProvider) { 42 | $urlRouterProvider.otherwise('/orders'); 43 | $translateProvider.preferredLanguage('en'); 44 | }); 45 | 46 | module.exports = app 47 | -------------------------------------------------------------------------------- /src/config/_default.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": { 3 | } 4 | } 5 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tthew-gulp-angular-webpack-seed 6 | 7 | 8 |
9 | 10 |
11 | 12 | {% for (var chunk in o.htmlWebpackPlugin.assets) { %} 13 | 14 | {% } %} 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/locale/de-de.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module("ngLocale", [], ["$provide", function($provide) { 3 | var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"}; 4 | function getDecimals(n) { 5 | n = n + ''; 6 | var i = n.indexOf('.'); 7 | return (i == -1) ? 0 : n.length - i - 1; 8 | } 9 | 10 | function getVF(n, opt_precision) { 11 | var v = opt_precision; 12 | 13 | if (undefined === v) { 14 | v = Math.min(getDecimals(n), 3); 15 | } 16 | 17 | var base = Math.pow(10, v); 18 | var f = ((n * base) | 0) % base; 19 | return {v: v, f: f}; 20 | } 21 | 22 | $provide.value("$locale", { 23 | "DATETIME_FORMATS": { 24 | "AMPMS": [ 25 | "vorm.", 26 | "nachm." 27 | ], 28 | "DAY": [ 29 | "Sonntag", 30 | "Montag", 31 | "Dienstag", 32 | "Mittwoch", 33 | "Donnerstag", 34 | "Freitag", 35 | "Samstag" 36 | ], 37 | "MONTH": [ 38 | "Januar", 39 | "Februar", 40 | "M\u00e4rz", 41 | "April", 42 | "Mai", 43 | "Juni", 44 | "Juli", 45 | "August", 46 | "September", 47 | "Oktober", 48 | "November", 49 | "Dezember" 50 | ], 51 | "SHORTDAY": [ 52 | "So.", 53 | "Mo.", 54 | "Di.", 55 | "Mi.", 56 | "Do.", 57 | "Fr.", 58 | "Sa." 59 | ], 60 | "SHORTMONTH": [ 61 | "Jan.", 62 | "Feb.", 63 | "M\u00e4rz", 64 | "Apr.", 65 | "Mai", 66 | "Juni", 67 | "Juli", 68 | "Aug.", 69 | "Sep.", 70 | "Okt.", 71 | "Nov.", 72 | "Dez." 73 | ], 74 | "fullDate": "EEEE, d. MMMM y", 75 | "longDate": "d. MMMM y", 76 | "medium": "dd.MM.y HH:mm:ss", 77 | "mediumDate": "dd.MM.y", 78 | "mediumTime": "HH:mm:ss", 79 | "short": "dd.MM.yy HH:mm", 80 | "shortDate": "dd.MM.yy", 81 | "shortTime": "HH:mm" 82 | }, 83 | "NUMBER_FORMATS": { 84 | "CURRENCY_SYM": "\u20ac", 85 | "DECIMAL_SEP": ",", 86 | "GROUP_SEP": ".", 87 | "PATTERNS": [ 88 | { 89 | "gSize": 3, 90 | "lgSize": 3, 91 | "maxFrac": 3, 92 | "minFrac": 0, 93 | "minInt": 1, 94 | "negPre": "-", 95 | "negSuf": "", 96 | "posPre": "", 97 | "posSuf": "" 98 | }, 99 | { 100 | "gSize": 3, 101 | "lgSize": 3, 102 | "maxFrac": 2, 103 | "minFrac": 2, 104 | "minInt": 1, 105 | "negPre": "-", 106 | "negSuf": "\u00a0\u00a4", 107 | "posPre": "", 108 | "posSuf": "\u00a0\u00a4" 109 | } 110 | ] 111 | }, 112 | "id": "de-de", 113 | "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;} 114 | }); 115 | }]); 116 | -------------------------------------------------------------------------------- /src/locale/en-gb.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | angular.module("ngLocale", [], ["$provide", function($provide) { 3 | var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"}; 4 | function getDecimals(n) { 5 | n = n + ''; 6 | var i = n.indexOf('.'); 7 | return (i == -1) ? 0 : n.length - i - 1; 8 | } 9 | 10 | function getVF(n, opt_precision) { 11 | var v = opt_precision; 12 | 13 | if (undefined === v) { 14 | v = Math.min(getDecimals(n), 3); 15 | } 16 | 17 | var base = Math.pow(10, v); 18 | var f = ((n * base) | 0) % base; 19 | return {v: v, f: f}; 20 | } 21 | 22 | $provide.value("$locale", { 23 | "DATETIME_FORMATS": { 24 | "AMPMS": [ 25 | "am", 26 | "pm" 27 | ], 28 | "DAY": [ 29 | "Sunday", 30 | "Monday", 31 | "Tuesday", 32 | "Wednesday", 33 | "Thursday", 34 | "Friday", 35 | "Saturday" 36 | ], 37 | "MONTH": [ 38 | "January", 39 | "February", 40 | "March", 41 | "April", 42 | "May", 43 | "June", 44 | "July", 45 | "August", 46 | "September", 47 | "October", 48 | "November", 49 | "December" 50 | ], 51 | "SHORTDAY": [ 52 | "Sun", 53 | "Mon", 54 | "Tue", 55 | "Wed", 56 | "Thu", 57 | "Fri", 58 | "Sat" 59 | ], 60 | "SHORTMONTH": [ 61 | "Jan", 62 | "Feb", 63 | "Mar", 64 | "Apr", 65 | "May", 66 | "Jun", 67 | "Jul", 68 | "Aug", 69 | "Sep", 70 | "Oct", 71 | "Nov", 72 | "Dec" 73 | ], 74 | "fullDate": "EEEE, d MMMM y", 75 | "longDate": "d MMMM y", 76 | "medium": "d MMM y HH:mm:ss", 77 | "mediumDate": "d MMM y", 78 | "mediumTime": "HH:mm:ss", 79 | "short": "dd/MM/y HH:mm", 80 | "shortDate": "dd/MM/y", 81 | "shortTime": "HH:mm" 82 | }, 83 | "NUMBER_FORMATS": { 84 | "CURRENCY_SYM": "\u00a3", 85 | "DECIMAL_SEP": ".", 86 | "GROUP_SEP": ",", 87 | "PATTERNS": [ 88 | { 89 | "gSize": 3, 90 | "lgSize": 3, 91 | "maxFrac": 3, 92 | "minFrac": 0, 93 | "minInt": 1, 94 | "negPre": "-", 95 | "negSuf": "", 96 | "posPre": "", 97 | "posSuf": "" 98 | }, 99 | { 100 | "gSize": 3, 101 | "lgSize": 3, 102 | "maxFrac": 2, 103 | "minFrac": 2, 104 | "minInt": 1, 105 | "negPre": "\u00a4-", 106 | "negSuf": "", 107 | "posPre": "\u00a4", 108 | "posSuf": "" 109 | } 110 | ] 111 | }, 112 | "id": "en-gb", 113 | "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;} 114 | }); 115 | }]); 116 | -------------------------------------------------------------------------------- /src/modules/orders/index.js: -------------------------------------------------------------------------------- 1 | // Templates 2 | require('./orders.tpl.html'); 3 | 4 | // Styles 5 | require('./orders.scss'); 6 | require('./orders/index.js'); 7 | require('./order'); 8 | 9 | var service = require('./orders.service.js'); 10 | var controller = require('./orders.controller.js'); 11 | 12 | angular.module('tthew.orders.controller', []) 13 | .controller('OrdersCtrl', controller); 14 | 15 | angular.module('tthew.orders.service', []) 16 | .factory('ordersService', service); 17 | 18 | var app = angular.module('tthew.orders', [ 19 | 'tthew.orders.service', 20 | 'tthew.orders.controller', 21 | 'tthew.orders.order', 22 | 'tthew.orders.orders.directive' 23 | ]); 24 | 25 | // States 26 | app.config(function ($stateProvider, $translateProvider) { 27 | $stateProvider 28 | .state('orders', { 29 | url: '/orders', 30 | templateUrl: 'orders.tpl.html', 31 | controller: 'OrdersCtrl as vm', 32 | resolve: { 33 | orders: function (ordersService) { 34 | return ordersService.getOrders(); 35 | } 36 | } 37 | }); 38 | 39 | $translateProvider.translations('en', require('./lang/en.json')); 40 | }); 41 | 42 | module.exports = app; 43 | -------------------------------------------------------------------------------- /src/modules/orders/lang/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "tthew": { 3 | "orders": { 4 | "TITLE": "Orders", 5 | "common": { 6 | "ORDER": "Order" 7 | }, 8 | "orders": { 9 | "ORDER_DATE": "Order Date", 10 | "ORDER_REF": "Order Ref.", 11 | "QUANTITY": "QTY", 12 | "ORDER": "Order", 13 | "LOOK_UP": "Look up" 14 | }, 15 | "order": { 16 | "NOT_FOUND": "Order Ref not found", 17 | "PRODUCT_TITLE": "Product", 18 | "AMOUNT": "Amount", 19 | "CARD_NUMBER": "Card #", 20 | "MESSAGE": "Message", 21 | "LINK": "Link", 22 | "TOTAL_ITEMS": "Total Items", 23 | "COMPLETE_ORDER": "Complete Order" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/modules/orders/order/index.js: -------------------------------------------------------------------------------- 1 | require('./orders.order.tpl.html'); 2 | var controller = require('./order.controller.js'); 3 | 4 | angular.module('tthew.orders.order.controller', []) 5 | .controller('OrderCtrl', controller); 6 | 7 | var app = angular.module('tthew.orders.order', [ 8 | 'tthew.orders.order.controller' 9 | ]); 10 | 11 | app.config(function ($stateProvider) { 12 | $stateProvider 13 | .state('orders.order', { 14 | url: '/:orderRef', 15 | templateUrl: 'orders.order.tpl.html', 16 | controller: 'OrderCtrl as vm', 17 | }); 18 | }); 19 | 20 | module.exports = app; 21 | -------------------------------------------------------------------------------- /src/modules/orders/order/order.controller.js: -------------------------------------------------------------------------------- 1 | module.exports = function (orders, $stateParams, $state) { 2 | var vm = this; 3 | 4 | vm.order = _.find(orders, function (order) { 5 | return order.orderRef === $stateParams.orderRef; 6 | }); 7 | 8 | vm.viewOrders = function () { 9 | $state.go('orders'); 10 | } 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/modules/orders/order/orders.order.tpl.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{::'tthew.orders.common.ORDER' | translate}} #{{::vm.order.orderRef}}

4 |
5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
{{::'tthew.orders.order.PRODUCT_TITLE' | translate}}{{::'tthew.orders.order.AMOUNT' | translate}}{{::'tthew.orders.order.MESSAGE' | translate}}
{{::item.productName}}{{::item.amount | currency}} 19 |

{{::item.giftMessage.line1}}

20 |

{{::item.giftMessage.line2}}

21 |

{{::item.giftMessage.line3}}

22 |

{{::item.giftMessage.line4}}

23 |
{{::'tthew.orders.order.TOTAL_ITEMS' | translate}}: {{::vm.order.items.length}}
32 |
33 |
34 | 35 |
36 |
37 | {{::'tthew.orders.order.NOT_FOUND' | translate}} 38 |
39 |
40 | -------------------------------------------------------------------------------- /src/modules/orders/orders.controller.js: -------------------------------------------------------------------------------- 1 | module.exports = function (orders) { 2 | var vm = this; 3 | vm.orders = orders; 4 | } 5 | -------------------------------------------------------------------------------- /src/modules/orders/orders.scss: -------------------------------------------------------------------------------- 1 | .tthew { 2 | &.app { 3 | .orders { 4 | 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/modules/orders/orders.service.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config, $q, Restangular) { 2 | // Public API 3 | var service = { 4 | getOrders: getOrders 5 | }; 6 | 7 | return service; 8 | 9 | function getOrders () { 10 | var mockResponse = [ 11 | { 12 | orderDate: new Date('12-06-2014 13:20:31'), 13 | orderRef: '12345', 14 | quantity: 3, 15 | items: [ 16 | { 17 | productName: 'Baseball Cap', 18 | amount: 20.00, 19 | giftMessage: { 20 | line1: 'Message Line 1', 21 | line2: 'Message Line 2', 22 | line3: 'Message Line 3', 23 | line4: 'Message Line 4' 24 | } 25 | }, 26 | { 27 | productName: 'Baseball Cap', 28 | amount: 20.00, 29 | giftMessage: { 30 | line1: 'Message Line 1', 31 | line2: 'Message Line 2', 32 | line3: 'Message Line 3', 33 | line4: 'Message Line 4' 34 | } 35 | }, 36 | { 37 | productName: 'Baseball', 38 | amount: 10.00, 39 | giftMessage: { 40 | line1: 'Message Line 1', 41 | line2: 'Message Line 2', 42 | line3: 'Message Line 3', 43 | line4: 'Message Line 4' 44 | } 45 | } 46 | ] 47 | }, 48 | { 49 | orderDate: new Date('12-06-2014 13:23:45'), 50 | orderRef: '1232457', 51 | quantity: 3, 52 | items: [ 53 | { 54 | productName: 'Baseball Cap', 55 | amount: 20.00, 56 | cardId: null, 57 | giftMessage: { 58 | line1: 'Message Line 1', 59 | line2: 'Message Line 2', 60 | line3: 'Message Line 3', 61 | line4: 'Message Line 4' 62 | } 63 | }, 64 | { 65 | productName: 'Baseball Cap', 66 | amount: 20.00, 67 | cardId: null, 68 | giftMessage: { 69 | line1: 'Message Line 1', 70 | line2: 'Message Line 2', 71 | line3: 'Message Line 3', 72 | line4: 'Message Line 4' 73 | } 74 | }, 75 | { 76 | productName: 'Women\'s Card', 77 | amount: 50.00, 78 | cardId: null, 79 | giftMessage: { 80 | line1: 'Message Line 1', 81 | line2: 'Message Line 2', 82 | line3: 'Message Line 3', 83 | line4: 'Message Line 4' 84 | } 85 | } 86 | ] 87 | }, 88 | { 89 | orderDate: new Date('12-06-2014 13:28:40'), 90 | orderRef: '3216547', 91 | quantity: 4, 92 | items: [ 93 | { 94 | productName: 'Baseball Cap', 95 | amount: 20.00, 96 | cardId: null, 97 | giftMessage: { 98 | line1: 'Message Line 1', 99 | line2: 'Message Line 2', 100 | line3: 'Message Line 3', 101 | line4: 'Message Line 4' 102 | } 103 | }, 104 | { 105 | productName: 'Baseball Cap', 106 | amount: 20.00, 107 | cardId: null, 108 | giftMessage: { 109 | line1: 'Message Line 1', 110 | line2: 'Message Line 2', 111 | line3: 'Message Line 3', 112 | line4: 'Message Line 4' 113 | } 114 | }, 115 | { 116 | productName: 'Women\'s Card', 117 | amount: 50.00, 118 | cardId: null, 119 | giftMessage: { 120 | line1: 'Message Line 1', 121 | line2: 'Message Line 2', 122 | line3: 'Message Line 3', 123 | line4: 'Message Line 4' 124 | } 125 | } 126 | ] 127 | } 128 | ]; 129 | 130 | return $q.when(mockResponse); 131 | } 132 | 133 | }; 134 | -------------------------------------------------------------------------------- /src/modules/orders/orders.tpl.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{::'tthew.orders.TITLE' | translate}}

4 | 5 |
6 |
7 | -------------------------------------------------------------------------------- /src/modules/orders/orders/index.js: -------------------------------------------------------------------------------- 1 | var directive = require('./orders.directive.js'); 2 | 3 | var app = angular.module('tthew.orders.orders.directive', []) 4 | .directive('orders', directive); 5 | 6 | module.exports = app; 7 | -------------------------------------------------------------------------------- /src/modules/orders/orders/orders.directive.js: -------------------------------------------------------------------------------- 1 | require('./orders.orders.tpl.html'); 2 | 3 | module.exports = function ($state) { 4 | return { 5 | restrict: 'E', 6 | templateUrl: 'orders.orders.tpl.html', 7 | scope: { 8 | orders: '=orders' 9 | }, 10 | link: function (scope) { 11 | scope.vm = scope.vm || {}; 12 | scope.vm.selectedOrders = []; 13 | 14 | scope.vm.checkAll = function (selected) { 15 | angular.forEach(scope.orders, function (order) { 16 | order.$selected = selected; 17 | }); 18 | }; 19 | 20 | scope.vm.viewOrder = function (orderRef) { 21 | $state.go('orders.order', {orderRef: orderRef}); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/modules/orders/orders/orders.orders.tpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | 25 | 26 | 27 |
5 | 9 | {{::'tthew.orders.orders.ORDER_REF' | translate}}{{::'tthew.orders.orders.ORDER_DATE' | translate}}{{::'tthew.orders.orders.QUANTITY' | translate}}
18 | 21 | {{::order.orderRef}}{{::order.orderDate | date}}{{::order.quantity}}
28 | 29 |
30 |
31 |
32 | 37 | 40 |
41 | 42 |
43 |
44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /src/scss/app.scss: -------------------------------------------------------------------------------- 1 | .tthew { 2 | &.app { 3 | 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/modules/orders/orders.service.spec.js: -------------------------------------------------------------------------------- 1 | var service = require('modules/orders/orders.service.js'); 2 | var expect = chai.expect; 3 | 4 | describe('orders.service', function () { 5 | beforeEach(function () { 6 | angular.module('test', []) 7 | .factory('service', service); 8 | }); 9 | 10 | describe('Public API', function () { 11 | describe('.getOrders()', function () { 12 | var service, $timeout; 13 | 14 | beforeEach(angular.mock.module('test', function ($provide) { 15 | $provide.value('config', {}); 16 | $provide.value('Restangular', {}); 17 | })); 18 | 19 | beforeEach(inject(function (_service_, _$timeout_) { 20 | service = _service_; 21 | $timeout = _$timeout_; 22 | })); 23 | 24 | it('should exist', function () { 25 | expect(service.getOrders).to.exist(); 26 | }); 27 | 28 | it('should return orders', function (done) { 29 | service.getOrders() 30 | .then(function (orders) { 31 | expect(orders.length).to.eq(3); 32 | }) 33 | .finally(done); 34 | $timeout.flush(); 35 | }); 36 | }); 37 | }); 38 | 39 | }); 40 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | var ngminPlugin = require('ngmin-webpack-plugin'); 4 | var HtmlWebpackPlugin = require('html-webpack-plugin'); 5 | var ExtractTextPlugin = require('extract-text-webpack-plugin'); 6 | var appRoot = path.join(__dirname, '/src'); 7 | var bowerRoot = path.join(__dirname, '/bower_components'); 8 | 9 | module.exports = { 10 | cache: true, 11 | debug: true, 12 | 13 | // The entry point 14 | entry: [ 15 | path.join(appRoot, '/app.js') 16 | ], 17 | 18 | output: { 19 | path: path.join(__dirname, './dist'), 20 | publicPath: './', 21 | libraryTarget: 'var', 22 | filename: '[hash].bundle.js', 23 | chunkFilename: '[chunkhash].js' 24 | }, 25 | 26 | module: { 27 | loaders: [ 28 | { 29 | // required to write 'require('./style.css')' 30 | test: /\.css$/, 31 | loaders: ['style','css'] 32 | }, 33 | { 34 | test: /\.scss$/, 35 | loader: 'style!css!sass?outputStyle=expanded&includePaths[]=' + bowerRoot + '/' 36 | }, 37 | { 38 | test: /\.coffee$/, 39 | loader: 'coffee' 40 | }, 41 | { 42 | // require raw html for partials 43 | test: /\.tpl\.html$/, 44 | loader: 'ng-cache' 45 | }, 46 | { 47 | // required for bootstrap icons 48 | test: /\.(woff|woff2)(\?(.*))?$/, 49 | loader: 'url?prefix=factorynts/&limit=5000&mimetype=application/font-woff' 50 | }, 51 | { 52 | test: /\.ttf(\?(.*))?$/, 53 | loader: 'file?prefix=fonts/' 54 | }, 55 | { 56 | test: /\.eot(\?(.*))?$/, 57 | loader: 'file?prefix=fonts/' 58 | }, 59 | { 60 | test: /\.svg(\?(.*))?$/, 61 | loader: 'file?prefix=fonts/' 62 | }, 63 | { 64 | test: /\.json$/, 65 | loader: 'json' 66 | } 67 | ], 68 | 69 | // don't parse some dependencies to speed up build. 70 | // can probably do this non-AMD/CommonJS deps 71 | noParse: [ 72 | path.join(bowerRoot, '/lodash/dist/lodash.js'), 73 | path.join(bowerRoot, '/jquery/dist/jquery.js'), 74 | path.join(bowerRoot, '/angular-route'), 75 | path.join(bowerRoot, '/angular-ui-router'), 76 | path.join(bowerRoot, '/angular-sanitize'), 77 | path.join(bowerRoot, '/angular-ui-select'), 78 | path.join(bowerRoot, '/angular-mocks'), 79 | path.join(bowerRoot, '/angular') 80 | ], 81 | }, 82 | 83 | resolve: { 84 | alias: { 85 | bower: bowerRoot, 86 | 'lodash': bowerRoot + '/lodash/dist/lodash.js' 87 | }, 88 | 89 | extensions: [ 90 | '', 91 | '.js', 92 | '.coffee', 93 | '.scss', 94 | '.css' 95 | ], 96 | 97 | root: [appRoot], 98 | }, 99 | 100 | singleRun: true, 101 | 102 | plugins: [ 103 | // bower.json resolving 104 | new webpack.ResolverPlugin([ 105 | new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin('bower.json', ['main']) 106 | ], ['normal', 'loader']), 107 | 108 | // disable dynamic requires 109 | new webpack.ContextReplacementPlugin(/.*$/, /a^/), 110 | 111 | new webpack.ProvidePlugin({ 112 | 'angular': 'exports?window.angular!bower/angular', 113 | '_': 'lodash' 114 | }), 115 | 116 | new HtmlWebpackPlugin({ 117 | template: __dirname + '/src/index.html' 118 | }), 119 | 120 | new ExtractTextPlugin("[name].css") 121 | 122 | 123 | ], 124 | 125 | devtool: 'eval' 126 | } 127 | --------------------------------------------------------------------------------