diff --git a/.gitignore b/.gitignore
index 123ae94..cf09d83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,9 +19,14 @@ coverage
# node-waf configuration
.lock-wscript
-# Compiled binary addons (http://nodejs.org/api/addons.html)
-build/Release
+# Compiled files
+public/js/main.js
+public/css/main.css
+public/views
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
+
+# Angular config
+src/app/token.js
diff --git a/assignment-rules.md b/assignment-rules.md
deleted file mode 100644
index a5d1eb0..0000000
--- a/assignment-rules.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Rules for this assignment
-
-Please be sure to read carefully as this will be crucial for achieving the goals for this week's assignment project.
-
-1. I expect everyone to show up or be with their pair partner as soon as possible in the morning to review and start on the project, there is A LOT to do
-1. Fork the assignment repo directly, no cross forking this time
-1. Once someone has forked the repo, create the PR right away
- 1. Give the PR a proper title and place `(wip)` in the title
- 1. You can keep making updates to the `master` branch and push that to your repo, the PR will keep updating
- 1. DO NOT submit a feature branch to the PR, only submit `master`
- 1. Work in your feature branches and commit often until you are ready to merge in the code from the feature branch to `master` and the owner of the
-1. In the PR, please be sure to write up a description of the project you are completing and submit the PR
- 1. This will be the only PR that is used throughout this week's assignment
-1. For the final assignment submission, update the PR title to remove `(wip)` and use `(final)`. Pushes to `master` after the title has been updated will be ignored, make sure you are done before you say you are done.
-1. Make sure to submit the URL of the PR to Canvas for final grading
diff --git a/comps/intelly-blog-III@2x.psd b/comps/intelly-blog-III@2x.psd
deleted file mode 100644
index ebfdb1a..0000000
Binary files a/comps/intelly-blog-III@2x.psd and /dev/null differ
diff --git a/comps/intelly-blog-post@2x.psd b/comps/intelly-blog-post@2x.psd
deleted file mode 100644
index 0964434..0000000
Binary files a/comps/intelly-blog-post@2x.psd and /dev/null differ
diff --git a/gulp/config.js b/gulp/config.js
new file mode 100644
index 0000000..47bb6f9
--- /dev/null
+++ b/gulp/config.js
@@ -0,0 +1,18 @@
+var dest = "./public";
+var src = "./src";
+
+module.exports = {
+ javascript: {
+ entryPoint: src + "/app/entry.js",
+ src: src + "/app/**/*.js",
+ dest: dest + "/js/"
+ },
+ sass: {
+ src: src + "/sass/**/*.scss",
+ dest: dest + "/css/"
+ },
+ html: {
+ src: src + "/app/**/*.html",
+ dest: dest + "/views/",
+ },
+};
diff --git a/gulp/tasks/csslint.js b/gulp/tasks/csslint.js
new file mode 100644
index 0000000..4c02e9d
--- /dev/null
+++ b/gulp/tasks/csslint.js
@@ -0,0 +1,9 @@
+var gulp = require("gulp");
+var config = require("../config").sass;
+var csslint = require("gulp-csslint");
+
+gulp.task("csslint", function () {
+ return gulp.src(config.dest + "**/*.css")
+ .pipe(csslint())
+ .pipe(csslint.reporter());
+});
diff --git a/gulp/tasks/default.js b/gulp/tasks/default.js
new file mode 100644
index 0000000..36c9f55
--- /dev/null
+++ b/gulp/tasks/default.js
@@ -0,0 +1,3 @@
+var gulp = require("gulp");
+
+gulp.task("default", ["sass", "html", "webpack", "watch", "serve", "jshint"]);
diff --git a/gulp/tasks/html.js b/gulp/tasks/html.js
new file mode 100644
index 0000000..0b29aff
--- /dev/null
+++ b/gulp/tasks/html.js
@@ -0,0 +1,7 @@
+var gulp = require("gulp");
+var config = require('../config').html;
+
+gulp.task('html', function() {
+ return gulp.src(config.src)
+ .pipe(gulp.dest(config.dest));
+});
diff --git a/gulp/tasks/jshint.js b/gulp/tasks/jshint.js
new file mode 100644
index 0000000..8958d19
--- /dev/null
+++ b/gulp/tasks/jshint.js
@@ -0,0 +1,10 @@
+var gulp = require("gulp");
+var config = require("../config").javascript;
+var jshint = require("gulp-jshint");
+var stylish = require("jshint-stylish");
+
+gulp.task("jshint", function () {
+ return gulp.src(config.src)
+ .pipe(jshint())
+ .pipe(jshint.reporter(stylish));
+});
diff --git a/gulp/tasks/sass.js b/gulp/tasks/sass.js
new file mode 100644
index 0000000..6263a79
--- /dev/null
+++ b/gulp/tasks/sass.js
@@ -0,0 +1,14 @@
+var gulp = require('gulp');
+var sass = require('gulp-sass');
+var sourcemaps = require('gulp-sourcemaps');
+var handleErrors = require('../util/handleErrors');
+var config = require('../config').sass;
+
+gulp.task('sass', function () {
+ return gulp.src(config.src)
+ .pipe(sourcemaps.init())
+ .pipe(sass())
+ .on('error', handleErrors)
+ .pipe(sourcemaps.write())
+ .pipe(gulp.dest(config.dest));
+});
diff --git a/gulp/tasks/serve.js b/gulp/tasks/serve.js
new file mode 100644
index 0000000..0943cdb
--- /dev/null
+++ b/gulp/tasks/serve.js
@@ -0,0 +1,7 @@
+var gulp = require("gulp");
+var server = require("gulp-express");
+
+gulp.task("serve", function () {
+ server.run(["server.js"]);
+ gulp.watch(["server.js"], [server.run]);
+});
diff --git a/gulp/tasks/watch.js b/gulp/tasks/watch.js
new file mode 100644
index 0000000..47706af
--- /dev/null
+++ b/gulp/tasks/watch.js
@@ -0,0 +1,9 @@
+var gulp = require("gulp");
+var config = require("../config");
+
+gulp.task('watch', function() {
+ gulp.watch(config.html.src, ["html"]);
+ gulp.watch(config.javascript.src, ["webpack", "jshint"]);
+ gulp.watch(config.sass.src, ["sass"]);
+ // gulp.watch(config.sass.dest + "**/*.css", ["csslint"]);
+});
diff --git a/gulp/tasks/webpack.js b/gulp/tasks/webpack.js
new file mode 100644
index 0000000..12fa06a
--- /dev/null
+++ b/gulp/tasks/webpack.js
@@ -0,0 +1,13 @@
+var gulp = require("gulp");
+var config = require("../config").javascript;
+var webpack = require("webpack-stream");
+
+gulp.task("webpack", function (callback) {
+ return gulp.src(config.entryPoint)
+ .pipe(webpack({
+ output: {
+ filename: "main.js",
+ }
+ }))
+ .pipe(gulp.dest(config.dest));
+});
diff --git a/gulp/util/handleErrors.js b/gulp/util/handleErrors.js
new file mode 100644
index 0000000..3c65f4f
--- /dev/null
+++ b/gulp/util/handleErrors.js
@@ -0,0 +1,15 @@
+var notify = require("gulp-notify");
+
+module.exports = function () {
+
+ var args = Array.prototype.slice.call(arguments);
+
+ // Send error to notification center with gulp-notify
+ notify.onError({
+ title: "Compile Error",
+ message: "<%= error %>"
+ }).apply(this, args);
+
+ // Keep gulp from hanging on this task
+ this.emit("end");
+};
diff --git a/gulpfile.js b/gulpfile.js
new file mode 100644
index 0000000..1dbda05
--- /dev/null
+++ b/gulpfile.js
@@ -0,0 +1,19 @@
+/*
+ gulpfile.js
+ ===========
+ Rather than manage one giant configuration file responsible
+ for creating multiple tasks, each task has been broken out into
+ its own file in gulp/tasks. Any files in that directory get
+ automatically required below.
+
+ To add a new task, simply add a new task file that directory.
+ gulp/tasks/default.js specifies the default set of tasks to run
+ when you run `gulp`.
+*/
+
+var requireDir = require('require-dir');
+
+// Require all tasks in gulp/tasks, including subfolders
+requireDir('./gulp/tasks', { recurse: true });
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7feb6e1
--- /dev/null
+++ b/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "mean-stack-1",
+ "version": "1.0.0",
+ "description": "Intelly - a simple MEAN stack blog app",
+ "main": "server.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/hollislau/mean-stack-1.git"
+ },
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/hollislau/mean-stack-1/issues"
+ },
+ "homepage": "https://github.com/hollislau/mean-stack-1",
+ "dependencies": {
+ "angular": "^1.4.7",
+ "angular-route": "^1.4.7",
+ "body-parser": "^1.14.1",
+ "express": "^4.13.3",
+ "gulp": "^3.9.0",
+ "gulp-csslint": "^0.2.0",
+ "gulp-express": "^0.3.5",
+ "gulp-jshint": "^1.11.2",
+ "gulp-notify": "^2.2.0",
+ "gulp-sass": "^2.0.4",
+ "gulp-sourcemaps": "^1.6.0",
+ "jshint-stylish": "^2.0.1",
+ "require-dir": "^0.3.0",
+ "webpack-stream": "^2.1.1"
+ }
+}
diff --git a/public/css/fonts/icomoon.eot b/public/css/fonts/icomoon.eot
new file mode 100644
index 0000000..2761603
Binary files /dev/null and b/public/css/fonts/icomoon.eot differ
diff --git a/public/css/fonts/icomoon.svg b/public/css/fonts/icomoon.svg
new file mode 100644
index 0000000..ec48438
--- /dev/null
+++ b/public/css/fonts/icomoon.svg
@@ -0,0 +1,16 @@
+
+
+
\ No newline at end of file
diff --git a/public/css/fonts/icomoon.ttf b/public/css/fonts/icomoon.ttf
new file mode 100644
index 0000000..e8bc90d
Binary files /dev/null and b/public/css/fonts/icomoon.ttf differ
diff --git a/public/css/fonts/icomoon.woff b/public/css/fonts/icomoon.woff
new file mode 100644
index 0000000..efeebf8
Binary files /dev/null and b/public/css/fonts/icomoon.woff differ
diff --git a/readme.md b/readme.md
index dde636c..68eed07 100644
--- a/readme.md
+++ b/readme.md
@@ -1,3 +1,9 @@
+# NOTE TO INSTRUCTORS FOR GRADING:
+
+You will need Hollis' Github API token to make this app work properly. Please refer to the comments attached to his Canvas assignment to find it. Create a "token.js" file in the "src" directory and create a module.exports object with a property and value of "oauthToken" and the API key, respectively. Just run "npm install" followed by "gulp", and you should now be able to CRUD some sample blogs. Enjoy!
+
+
+
# Week Six - MEAN Stack I
Last week we focussed on JavaScript basics. This week we're going to build up a function web application. You'll be expected to build on knowledge you've already learned (semantic HTML, Sass instead of CSS, in addition to all of the Angular work we'll be doing) and produce a "production-ready" blog for your assignment this week.
@@ -11,15 +17,15 @@ You can download the view that you are to complete [here](https://github.com/SEA
## The spec
-As a product owner, I need a functional prototype of the application. The final prototype must be visible in a desktop browser.
+As a product owner, I need a functional prototype of the application. The final prototype must be visible in a desktop browser.
-Prototype is to use best practices in coding HTML/CSS as there is the potential that some or all of the code will made it into production.
+Prototype is to use best practices in coding HTML/CSS as there is the potential that some or all of the code will made it into production.
-Aside from images as content, all visual assets should be produced via CSS or typography. The only background image asset allowed will be the main page header.
+Aside from images as content, all visual assets should be produced via CSS or typography. The only background image asset allowed will be the main page header.
The user should be able to create, read, update, and delete blog posts from the application. There should be at least two different views - one a list of available blog posts, the other a detail view of the blog post. You do not need to worry about authentication or user management for this assignment.
-### Requirements
+### Requirements
1. Semantically correct HTML is required as this will be the model for prod app integration
1. Think in terms of '*components*'; if all parts of the UI were lego blocks, who would you code that?
@@ -30,15 +36,15 @@ The user should be able to create, read, update, and delete blog posts from the
1. Write up a description for every plugin used (no limit, but you must justify them)
1. jQuery is not allowed. Angular's built-in DOM manipulation can manage most of what you'd need jQuery for.
-### Constraints
+### Constraints
1. Must work in all major browsers of latest versions;
* Desktop (IE Edge, Safari, Chrome and Firefox)
1. All interactions must be clearly functional
1. All code must pass HTML Tidy, CSS Lint, and JSHint.
-__DO NOT__ fence yourself in with invisible constraints. Unless it is specifically listed and/or we discussed it in lecture, there is not an expectation to meet an objective that has not been set.
+__DO NOT__ fence yourself in with invisible constraints. Unless it is specifically listed and/or we discussed it in lecture, there is not an expectation to meet an objective that has not been set.
-## The expectation
+## The expectation
In this assignment, you should be able to demonstrate mastery over the basics of Angular as well as creating a simple CRUD application. We're looking for you to build on best practices that you've already learned (proper HTML and CSS) as well as incorporate the new practices discussed during the course of the week.
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..867dc97
--- /dev/null
+++ b/server.js
@@ -0,0 +1,18 @@
+"use strict";
+
+var express = require("express");
+var app = express();
+var router = express.Router();
+
+app.set("port", (process.env.PORT || 5000));
+
+router.get("/", function (req, res) {
+ res.sendFile(__dirname + "/public/views/index.html");
+});
+
+app.use(express.static(__dirname + "/public"));
+app.use("/", router);
+
+app.listen(app.get("port"), function() {
+ console.log("Express server is running on port", app.get("port"));
+});
diff --git a/src/app/app.js b/src/app/app.js
new file mode 100644
index 0000000..4f95155
--- /dev/null
+++ b/src/app/app.js
@@ -0,0 +1,32 @@
+"use strict";
+
+require("angular");
+require("angular-route");
+
+(function () {
+
+ var app = angular.module("intellyBlog", ["ngRoute"]);
+
+ app.config(["$routeProvider", function ($routeProvider) {
+ $routeProvider.when("/blogs", {
+ templateUrl: "views/blogs/blogs_list.html",
+ controller: "BlogsCtrl as vm",
+ })
+ .when("/blogs/new", {
+ templateUrl: "views/blog/blog_form.html",
+ controller: "BlogFormCtrl as vm",
+ })
+ .when("/blogs/:blog_id/edit", {
+ templateUrl: "views/blog/blog_form.html",
+ controller: "BlogFormCtrl as vm",
+ })
+ .when("/blogs/:blog_id", {
+ templateUrl: "views/blog/blog_detail.html",
+ controller: "BlogCtrl as vm",
+ })
+ .otherwise({
+ redirectTo: "/blogs",
+ });
+ }]);
+
+})();
diff --git a/src/app/blog/blog.ctrl.js b/src/app/blog/blog.ctrl.js
new file mode 100644
index 0000000..6a2836a
--- /dev/null
+++ b/src/app/blog/blog.ctrl.js
@@ -0,0 +1,62 @@
+"use strict";
+
+require("../app.js");
+
+(function () {
+
+ angular.module("intellyBlog").controller("BlogCtrl", ["BlogsService", "$routeParams", "$http", "$filter", "$log", "$q", "$location", function (BlogsService, $routeParams, $http, $filter, $log, $q, $location) {
+ var vm = this;
+
+ vm.delete = deleteBlog;
+
+ initialize();
+
+ function initialize() {
+ BlogsService
+ .get($routeParams.blog_id)
+ .then(successHandler, errorHandler);
+ }
+
+ function successHandler (resp) {
+ var data = resp.data;
+ var blogObj = data.files.blog;
+
+ $http
+ .get(blogObj.raw_url)
+ .then(function (resp) {
+ setBlogInfo(data, resp.data);
+ }, function (resp) {
+ $log.error("Could not request " + blogObj.raw_url, resp);
+ });
+
+ $log.info("read", resp);
+ }
+
+ function errorHandler (resp) {
+ vm.error = resp.data;
+ $log.error("Could not read " + resp);
+ }
+
+ function setBlogInfo (gist, content) {
+ vm.blog = {
+ title: gist.description,
+ content: content,
+ author: gist.owner.login,
+ date: $filter("date")(gist.updated_at),
+ comments: gist.comments + " comments",
+ id: gist.id
+ };
+ }
+
+ function deleteBlog (blog) {
+ BlogsService.delete(blog).then(function (resp) {
+ $location.path("/blogs");
+ $log.info("deleted", resp);
+ }, function (resp) {
+ $log.error("Could not delete " + resp);
+ });
+ }
+
+ }]);
+
+})();
diff --git a/src/app/blog/blog_detail.html b/src/app/blog/blog_detail.html
new file mode 100644
index 0000000..56701e6
--- /dev/null
+++ b/src/app/blog/blog_detail.html
@@ -0,0 +1,15 @@
+