diff --git a/lab-regan/.babelrc b/lab-regan/.babelrc
new file mode 100644
index 0000000..d018deb
--- /dev/null
+++ b/lab-regan/.babelrc
@@ -0,0 +1,4 @@
+{
+ "presets": ["es2015"]
+}
+
diff --git a/lab-regan/.eslintrc b/lab-regan/.eslintrc
new file mode 100644
index 0000000..f9befad
--- /dev/null
+++ b/lab-regan/.eslintrc
@@ -0,0 +1,20 @@
+{
+ "rules": {
+ "no-console": "off",
+ "indent": [ "error", 2 ],
+ "quotes": [ "error", "single" ],
+ "semi": ["error", "always"],
+ "linebreak-style": [ "error", "unix" ]
+ },
+ "env": {
+ "es6": true,
+ "node": true,
+ },
+ "ecmaFeatures": {
+ "modules": true,
+ "experimentalObjectRestSpread": true,
+ "impliedStrict": true
+ },
+ "extends": "eslint:recommended"
+}
+
diff --git a/lab-regan/.gitignore b/lab-regan/.gitignore
new file mode 100644
index 0000000..c756401
--- /dev/null
+++ b/lab-regan/.gitignore
@@ -0,0 +1,133 @@
+
+# Created by https://www.gitignore.io/api/osx,node,linux,windows
+
+### Linux ###
+*~
+
+# temporary files which can be created if a process still has a handle open of a deleted file
+.fuse_hidden*
+
+# KDE directory preferences
+.directory
+
+# Linux trash folder which might appear on any partition or disk
+.Trash-*
+
+# .nfs files are created when an open file is removed but is still being accessed
+.nfs*
+
+###Local Files###
+build
+
+### Node ###
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+
+
+
+### OSX ###
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+### Windows ###
+# Windows thumbnail cache files
+Thumbs.db
+ehthumbs.db
+ehthumbs_vista.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk
+
+# End of https://www.gitignore.io/api/osx,node,linux,windows
+
diff --git a/lab-regan/app/entry.js b/lab-regan/app/entry.js
new file mode 100644
index 0000000..cfbb13d
--- /dev/null
+++ b/lab-regan/app/entry.js
@@ -0,0 +1,62 @@
+'use strict';
+
+// require('./scss/reset.scss');
+// require('./scss/main.scss');
+
+require('./scss/core.scss');
+
+const angular = require('angular');
+const cowsay = require('cowsay-browser');
+
+const cowsayLab = angular.module('cowsayLab', []);
+
+cowsayLab.controller('CowsayController', ['$log', CowsayController]);
+
+function CowsayController($log){
+ $log.debug('CowsayController');
+
+ this.title = 'Creature Creeps! Make Em Speak!';
+ this.history = [];
+
+ cowsay.list((err, cowfiles) => {
+ this.cowfiles = cowfiles;
+ this.current = this.cowfiles[0];
+ });
+
+ this.update = function(input){
+ $log.debug('cowsayCtrl.update');
+ return cowsay.say({ text: input || 'Say wuuuut?!', f: this.current });
+ };
+
+ this.speak = function(input){
+ $log.debug('cowsayCtrl.speak');
+ this.spoken = this.update(input);
+ this.history.push(this.spoken);
+ };
+
+ this.undo = function(){
+ $log.debug('cowsayCtrl.undo');
+ this.history.pop();
+ this.spoken = this.history.pop() || '';
+ };
+}
+
+cowsayLab.controller('NavController', ['$log', NavController]);
+
+function NavController($log){
+ $log.debug('NavController');
+ this.routes = [
+ {
+ name: 'home',
+ url: '/home'
+ },
+ {
+ name: 'about',
+ url: '/about'
+ },
+ {
+ name: 'contact',
+ url: '/contact-us'
+ }
+ ];
+}
diff --git a/lab-regan/app/index.html b/lab-regan/app/index.html
new file mode 100644
index 0000000..9521f41
--- /dev/null
+++ b/lab-regan/app/index.html
@@ -0,0 +1,69 @@
+
+
+
+ You Have Arrived In Creature Land!
+
+
+
+
+
+
+
{{ cowsayCtrl.title }}
+
+ {{ cowsayCtrl.update(cowsayCtrl.text)}}
+
+
+
+ That is just wild!
+ Whoa no way!!!
+
+
+
+
+
diff --git a/lab-regan/app/scss/core.scss b/lab-regan/app/scss/core.scss
new file mode 100644
index 0000000..99cdf27
--- /dev/null
+++ b/lab-regan/app/scss/core.scss
@@ -0,0 +1,6 @@
+@import 'reset.scss';
+
+@import 'partials/base.scss';
+@import 'partials/layout.scss';
+@import 'partials/nav.scss';
+@import 'partials/modules.scss'
diff --git a/lab-regan/app/scss/main.scss b/lab-regan/app/scss/main.scss
new file mode 100644
index 0000000..9c6c4e5
--- /dev/null
+++ b/lab-regan/app/scss/main.scss
@@ -0,0 +1,130 @@
+// //base styles
+// * {
+// box-sizing: border-box;
+// }
+// body {
+// min-height: 100%;
+// // display: flex;
+// background: #31CF7F;
+// font-family: 'Nunito', sans-serif;
+// }
+// header {
+// display: flex;
+// align-items: center;
+// height: 4em;
+// background: #2FB4C5;
+// nav {
+// display: flex;
+// width: 100%;
+// justify-content: space-between;
+// .navLeft{
+// display: flex;
+// margin-left: 0.5em;
+// align-items: center;
+// width: 35%;
+// color: #eee;
+// font-size: 1.2em;
+// }
+// .navRight{
+// display: flex;
+// align-items: center;
+// width: 30%;
+// ul {
+// width: 90%;
+// display: flex;
+// justify-content: space-between;
+// a{
+// text-decoration: none;
+// color: #eee;
+// }
+// li:hover {
+// text-decoration: underline;
+// }
+// }
+// }
+// }
+// }
+//
+// footer {
+// border-top: 1px solid #491763;
+// display: flex;
+// align-items: center;
+// justify-content: center;
+// height: 3em;
+// background: #2FB4C5;
+// margin-top: 10em;
+// padding: 4em;
+// color: #eee;
+// flex-direction: column;
+// p {
+// font-size: 9px;
+// display: block;
+// }
+// }
+//
+//
+// //general element rules
+// h2 {
+// font-size: 8vh;
+// color: #eee;
+// margin: 2% 0 0 2.5%;
+// // width: 45%;
+// }
+//
+// button {
+// width: 100%;
+// }
+//
+// //layout and modules
+// .cow {
+// border: 0.5em solid #491763;
+// color: #eee;
+// margin: 2% 0 0 2.5%;
+// width: 45%;
+// }
+//
+// .upperForm {
+// color: white;
+// margin-top: -17em;
+// width: 15%;
+// margin-right: 30%;
+// float: right;
+// * {
+// display: block;
+// width: 15em;
+// height: 2.5em;
+// border-radius: 5px;
+// }
+// label {
+// margin-bottom: -1em;
+// margin-top: 1.25em;
+// }
+// button {
+// color: #832FA6;
+// font-style: bold;
+// font-size: 1.25vw;
+// text-transform: uppercase;
+// background: #eee;
+// }
+// }
+// .undo {
+// margin-right: 30%;
+// float: right;
+// width: 15%;
+// margin-top: -3em;
+// .undoButton {
+// background: #eee;
+// width: 15em;
+// height: 2.5em;
+// border-radius: 5px;
+//
+// }
+// }
+// .monster {
+// width: 3em;
+// height: 3em;
+// img {
+// width: 100%;
+// height: 100%;
+// }
+// }
diff --git a/lab-regan/app/scss/partials/_base.scss b/lab-regan/app/scss/partials/_base.scss
new file mode 100644
index 0000000..8fd160c
--- /dev/null
+++ b/lab-regan/app/scss/partials/_base.scss
@@ -0,0 +1,20 @@
+* {
+ box-sizing: border-box;
+}
+
+body {
+ min-height: 100%;
+ background: #31CF7F;
+ font-family: 'Nunito', sans-serif;
+}
+
+h2 {
+ font-size: 8vh;
+ color: #eee;
+ margin: 2% 0 0 2.5%;
+ // width: 45%;
+}
+
+button {
+ width: 100%;
+}
diff --git a/lab-regan/app/scss/partials/_layout.scss b/lab-regan/app/scss/partials/_layout.scss
new file mode 100644
index 0000000..bb11847
--- /dev/null
+++ b/lab-regan/app/scss/partials/_layout.scss
@@ -0,0 +1,73 @@
+
+
+.cow {
+ border: 0.5em solid #491763;
+ color: #eee;
+ margin: 2% 0 0 2.5%;
+ width: 45%;
+}
+
+.undo {
+ margin-right: 30%;
+ float: right;
+ width: 15%;
+ margin-top: -3em;
+ .undoButton {
+ background: #eee;
+ width: 15em;
+ height: 2.5em;
+ border-radius: 5px;
+
+ }
+}
+
+.upperForm {
+ color: white;
+ margin-top: -17em;
+ width: 15%;
+ margin-right: 30%;
+ float: right;
+ * {
+ display: block;
+ width: 15em;
+ height: 2.5em;
+ border-radius: 5px;
+ }
+ label {
+ margin-bottom: -1em;
+ margin-top: 1.25em;
+ }
+ button {
+ color: #832FA6;
+ font-style: bold;
+ font-size: 1.25vw;
+ text-transform: uppercase;
+ background: #eee;
+ }
+}
+
+.monster {
+ width: 3em;
+ height: 3em;
+ img {
+ width: 100%;
+ height: 100%;
+ }
+}
+
+footer {
+border-top: 1px solid #491763;
+display: flex;
+align-items: center;
+justify-content: center;
+height: 3em;
+background: #491763;
+margin-top: 10em;
+padding: 4em;
+color: #eee;
+flex-direction: column;
+p {
+ font-size: 9px;
+ display: block;
+}
+}
diff --git a/lab-regan/app/scss/partials/_modules.scss b/lab-regan/app/scss/partials/_modules.scss
new file mode 100644
index 0000000..e69de29
diff --git a/lab-regan/app/scss/partials/_nav.scss b/lab-regan/app/scss/partials/_nav.scss
new file mode 100644
index 0000000..c8df691
--- /dev/null
+++ b/lab-regan/app/scss/partials/_nav.scss
@@ -0,0 +1,37 @@
+header {
+ display: flex;
+ align-items: center;
+ height: 4em;
+ // background: #2FB4C5;
+ background: #491763;
+ nav {
+ display: flex;
+ width: 100%;
+ justify-content: space-between;
+ .navLeft{
+ display: flex;
+ margin-left: 0.5em;
+ align-items: center;
+ width: 35%;
+ color: #eee;
+ font-size: 1.2em;
+ }
+ .navRight{
+ display: flex;
+ align-items: center;
+ width: 30%;
+ ul {
+ width: 90%;
+ display: flex;
+ justify-content: space-between;
+ a{
+ text-decoration: none;
+ color: #eee;
+ }
+ li:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+ }
+}
diff --git a/lab-regan/app/scss/reset.scss b/lab-regan/app/scss/reset.scss
new file mode 100644
index 0000000..ed11813
--- /dev/null
+++ b/lab-regan/app/scss/reset.scss
@@ -0,0 +1,48 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
diff --git a/lab-regan/assets/cute-monster.svg b/lab-regan/assets/cute-monster.svg
new file mode 100644
index 0000000..0b6e7a1
--- /dev/null
+++ b/lab-regan/assets/cute-monster.svg
@@ -0,0 +1,40 @@
+
+
+
diff --git a/lab-regan/karma.conf.js b/lab-regan/karma.conf.js
new file mode 100644
index 0000000..e018930
--- /dev/null
+++ b/lab-regan/karma.conf.js
@@ -0,0 +1,41 @@
+// Karma configuration
+// Generated on Wed Mar 22 2017 10:37:21 GMT-0700 (PDT)
+
+const webpackConfig = require('./webpack.config.js');
+webpackConfig.entry = {};
+
+module.exports = function(config) {
+ config.set({
+ webpack: webpackConfig,
+ basePath: '',
+
+ frameworks: ['jasmine'],
+
+ files: [
+ 'test/**/*-test.js'
+ ],
+
+ exclude: [
+ ],
+
+ preprocessors: {
+ 'test/**/*-test.js':['webpack']
+ },
+
+ reporters: ['mocha'],
+
+ port: 9876,
+
+ colors: true,
+
+ logLevel: config.LOG_INFO,
+
+ autoWatch: true,
+
+ browsers: ['PhantomJS'],
+
+ singleRun: false,
+
+ concurrency: Infinity
+ })
+}
diff --git a/lab-regan/package.json b/lab-regan/package.json
new file mode 100644
index 0000000..04179c2
--- /dev/null
+++ b/lab-regan/package.json
@@ -0,0 +1,44 @@
+{
+ "name": "lab-regan",
+ "version": "1.0.0",
+ "description": "",
+ "main": "webpack.config.js",
+ "directories": {
+ "test": "test"
+ },
+ "scripts": {
+ "test": "./node_modules/karma/bin/karma start --single-run",
+ "test-watch": "./node_modules/karma/bin/karma start",
+ "watch": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot",
+ "build": "./node_modules/webpack/bin.webpack.js",
+ "lint": "jshint **.js"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "angular": "^1.6.3",
+ "babel-core": "^6.24.0",
+ "babel-loader": "^6.4.1",
+ "babel-preset-es2015": "^6.24.0",
+ "cowsay-browser": "^1.1.8",
+ "css-loader": "^0.27.3",
+ "extract-text-webpack-plugin": "^1.0.1",
+ "html-webpack-plugin": "^2.28.0",
+ "node-sass": "^4.5.1",
+ "sass-loader": "^6.0.3",
+ "style-loader": "^0.16.0",
+ "webpack": "^1.14.0"
+ },
+ "devDependencies": {
+ "angular-mocks": "^1.6.3",
+ "jasmine-core": "^2.5.2",
+ "jshint": "^2.9.4",
+ "karma": "^1.5.0",
+ "karma-jasmine": "^1.1.0",
+ "karma-mocha-reporter": "^2.2.3",
+ "karma-phantomjs-launcher": "^1.0.4",
+ "karma-webpack": "^2.0.3",
+ "webpack-dev-server": "^1.16.2"
+ }
+}
diff --git a/lab-regan/test/cowsay-ctrl-test.js b/lab-regan/test/cowsay-ctrl-test.js
new file mode 100644
index 0000000..9d681a5
--- /dev/null
+++ b/lab-regan/test/cowsay-ctrl-test.js
@@ -0,0 +1,54 @@
+'use strict';
+
+require('./lib/test-setup.js');
+
+const angular = require('angular');
+const cowsay = require('cowsay-browser');
+
+describe('Cowsay Controller', function(){
+ beforeEach( () => {
+ angular.mock.module('cowsayLab');
+ angular.mock.inject($controller => {
+ this.cowsayCtrl = new $controller('CowsayController');
+ });
+ });
+ describe('initial properties', () => {
+ it('title property should equal "Creature Creeps! Make Em Speak!"', () => {
+ expect(this.cowsayCtrl.title).toBe('Creature Creeps! Make Em Speak!');
+ });
+ it('history property should be an empty array', () => {
+ expect(Array.isArray(this.cowsayCtrl.history)).toBe(true);
+ });
+ it('list of cowfiles should show proper cowfiles', () => {
+ cowsay.list((err, list) => {
+ expect(this.cowsayCtrl.cowfiles).toEqual(list);
+ expect(this.cowsayCtrl.current).toEqual(list[0]);
+ });
+ });
+ });
+ describe('#update', () => {
+ it('should return a cow that says testing', () => {
+ let expected = cowsay.say({text: 'testing', f: this.cowsayCtrl.current});
+ let result = this.cowsayCtrl.update('testing');
+ expect(result).toEqual(expected);
+ });
+ });
+ describe('#speak', () => {
+ it('should return a cow that says', () => {
+ let expected = cowsay.say({text: 'testing', f: this.cowsayCtrl.current});
+ this.cowsayCtrl.speak('testing');
+ expect(this.cowsayCtrl.spoken).toEqual(expected);
+ expect(this.cowsayCtrl.history[0]).toEqual(expected);
+ });
+ });
+ describe('#undo', () => {
+ it('should return a cow that says testing', () => {
+ let expected = cowsay.say({text: 'testing', f: this.cowsayCtrl.current});
+ this.cowsayCtrl.speak('testing');
+ this.cowsayCtrl.speak('testing again');
+ this.cowsayCtrl.undo();
+ expect(this.cowsayCtrl.spoken).toEqual(expected);
+ expect(this.cowsayCtrl.history.length).toEqual(0);
+ });
+ });
+});
diff --git a/lab-regan/test/lib/test-setup.js b/lab-regan/test/lib/test-setup.js
new file mode 100644
index 0000000..1e69a80
--- /dev/null
+++ b/lab-regan/test/lib/test-setup.js
@@ -0,0 +1,3 @@
+'use strict';
+require('../../app/entry.js');
+require('angular-mocks');
diff --git a/lab-regan/webpack.config.js b/lab-regan/webpack.config.js
new file mode 100644
index 0000000..826e574
--- /dev/null
+++ b/lab-regan/webpack.config.js
@@ -0,0 +1,35 @@
+'use strict';
+
+const HTMLPlugin = require('html-webpack-plugin');
+const ExtractTextPlugin = require('extract-text-webpack-plugin');
+
+module.exports = {
+ entry: `${__dirname}/app/entry.js`,
+ output: {
+ filename: 'bundle.js',
+ path: 'build'
+ },
+ plugins: [
+ new HTMLPlugin({
+ template: `${__dirname}/app/index.html`
+ }),
+ new ExtractTextPlugin('bundle.css')
+ ],
+ module: {
+ loaders: [
+ {
+ test: /\.js$/,
+ exclude: /node_modules/,
+ loader: 'babel'
+ },
+ {
+ test: /\.scss$/,
+ loader: 'style!css!sass!'
+ },
+ {
+ test: /\.(eot|woff|tff|svg).*/,
+ loader: 'url?limit=10000&name=fonts/[hash].[ext]'
+ }
+ ]
+ }
+};