diff --git a/.babelrc b/.babelrc new file mode 100644 index 00000000..c13c5f62 --- /dev/null +++ b/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["es2015"] +} diff --git a/.eslintrc b/.eslintrc new file mode 100755 index 00000000..a79e0c68 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,22 @@ +{ + "rules": { + "no-console": "off", + "indent": [ "error", 2 ], + "quotes": [ "error", "single" ], + "semi": ["error", "always"], + "linebreak-style": [ "error", "unix" ], + # "linebreak-style": [ "error", "windows"] + }, + "env": { + "es6": true, + "node": true, + "mocha": true, + "jasmine": true + }, + "ecmaFeatures": { + "modules": true, + "experimentalObjectRestSpread": true, + "impliedStrict": true + }, + "extends": "eslint:recommended" +} diff --git a/.gitignore b/.gitignore new file mode 100755 index 00000000..cbc44fb3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,123 @@ + +# Created by https://www.gitignore.io/api/monodevelop,vim,node,windows,archlinuxpackages + +### ArchLinuxPackages ### +*.tar +*.tar.* +*.jar +*.exe +*.msi +*.zip +*.tgz +*.log +*.log.* +*.sig + +pkg/ +src/ + +### MonoDevelop ### +#User Specific +*.userprefs +*.usertasks + +#Mono Project Files +*.pidb +*.resources +test-results/ + +### Node ### +# Logs +logs +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 +build + +# 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' + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + +### 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 +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/monodevelop,vim,node,windows,archlinuxpackages diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..22b0a2cf --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: node_js +node_js: + - 'stable' +sudo: required +before_script: npm i +script: + - npm run test diff --git a/wireframes/assets/cf-logo.png b/app/assets/img/cf-logo.png similarity index 100% rename from wireframes/assets/cf-logo.png rename to app/assets/img/cf-logo.png diff --git a/app/component/gallery/create-gallery/_create-gallery.scss b/app/component/gallery/create-gallery/_create-gallery.scss new file mode 100644 index 00000000..42e59cf8 --- /dev/null +++ b/app/component/gallery/create-gallery/_create-gallery.scss @@ -0,0 +1,12 @@ +@import "../../../scss/lib/theme/vars"; + +.create-gallery { + input[type="text"] { + margin-bottom: $gutter-sm; + } + + button { + float: right; + padding: 2% 6%; + } +} diff --git a/app/component/gallery/create-gallery/create-gallery.html b/app/component/gallery/create-gallery/create-gallery.html new file mode 100644 index 00000000..fad19069 --- /dev/null +++ b/app/component/gallery/create-gallery/create-gallery.html @@ -0,0 +1,21 @@ + diff --git a/app/component/gallery/create-gallery/create-gallery.js b/app/component/gallery/create-gallery/create-gallery.js new file mode 100644 index 00000000..cabb2be6 --- /dev/null +++ b/app/component/gallery/create-gallery/create-gallery.js @@ -0,0 +1,23 @@ +'use strict'; + +require('./_create-gallery.scss'); + +module.exports = { + template: require('./create-gallery.html'), + controller: ['$log', 'galleryService', CreateGalleryController], + controllerAs: 'createGalleryCtrl' +}; + +function CreateGalleryController($log, galleryService) { + $log.debug('CreateGalleryController'); + + this.gallery = {}; + + this.createGallery = function() { + galleryService.createGallery(this.gallery) + .then( () => { + this.gallery.name = null; + this.gallery.desc = null; + }); + }; +} diff --git a/app/component/gallery/edit-gallery/_edit-gallery.scss b/app/component/gallery/edit-gallery/_edit-gallery.scss new file mode 100644 index 00000000..f8a2d9cd --- /dev/null +++ b/app/component/gallery/edit-gallery/_edit-gallery.scss @@ -0,0 +1,8 @@ +@import "../../../scss/lib/theme/vars"; + +.edit { + .item-lable { + margin-bottom: 1vw; + display: block; + } +} diff --git a/app/component/gallery/edit-gallery/edit-gallery.html b/app/component/gallery/edit-gallery/edit-gallery.html new file mode 100644 index 00000000..80a4a590 --- /dev/null +++ b/app/component/gallery/edit-gallery/edit-gallery.html @@ -0,0 +1,23 @@ +
+
+ + +
diff --git a/app/component/gallery/edit-gallery/edit-gallery.js b/app/component/gallery/edit-gallery/edit-gallery.js new file mode 100644 index 00000000..ea68b01a --- /dev/null +++ b/app/component/gallery/edit-gallery/edit-gallery.js @@ -0,0 +1,21 @@ +'use strict'; + +require('./_edit-gallery.scss'); + +module.exports = { + template: require('./edit-gallery.html'), + controller: ['$log', 'galleryService', EditGalleryController], + controllerAs: 'EditGalleryCtrl', + bindings: { + gallery: '<' + } +}; + +function EditGalleryController($log, galleryService) { + $log.debug('EditGalleryController'); + + this.updateGallery = function() { + $log.debug('EditGalleryCtrl.updateGallery'); + galleryService.updateGallery(this.gallery._id, this.gallery); + }; +}; diff --git a/app/component/gallery/gallery-item/_gallery-item.scss b/app/component/gallery/gallery-item/_gallery-item.scss new file mode 100644 index 00000000..519d171b --- /dev/null +++ b/app/component/gallery/gallery-item/_gallery-item.scss @@ -0,0 +1,41 @@ +@import "../../../scss/lib/theme/vars"; + +.gallery-item-container { + clear: both; + padding-top: $gutter-std; + + .gallery-item { + margin: $gutter-std 0 1% 0; + padding: $gutter-sm; + background: $app-secondary; + font-size: 3vw; + border-radius: $btn-radius; + } + + .edit-btns { + text-align: right; + + span { + display: inline-block; + + .del-btn { + color: $black; + background: $app-secondary; + } + } + } + + .current-item { + div { + margin-bottom: $gutter-sm; + + &:last-child { + margin-bottom: 1vw; + } + + .item-label { + font-weight: bold; + } + } + } +} diff --git a/app/component/gallery/gallery-item/gallery-item.html b/app/component/gallery/gallery-item/gallery-item.html new file mode 100644 index 00000000..5511a411 --- /dev/null +++ b/app/component/gallery/gallery-item/gallery-item.html @@ -0,0 +1,23 @@ + + +
  • + + edit + + delete +
  • diff --git a/app/component/gallery/gallery-item/gallery-item.js b/app/component/gallery/gallery-item/gallery-item.js new file mode 100644 index 00000000..18453222 --- /dev/null +++ b/app/component/gallery/gallery-item/gallery-item.js @@ -0,0 +1,22 @@ +'use strict'; + +require('./_gallery-item.scss'); + +module.exports = { + template: require('./gallery-item.html'), + controller: ['$log', 'galleryService', GalleryItemController], + controllerAs: 'galleryItemCtrl', + bindings: { + gallery: '<' + } +}; + +function GalleryItemController($log, galleryService) { + $log.debug('GalleryItemController'); + + this.showEditGallery = false; + + this.deleteGallery = function() { + galleryService.deleteGallery(this.gallery._id); + }; +}; diff --git a/app/component/gallery/thumbnail-container/_thumbnail-container.scss b/app/component/gallery/thumbnail-container/_thumbnail-container.scss new file mode 100644 index 00000000..3d264c12 --- /dev/null +++ b/app/component/gallery/thumbnail-container/_thumbnail-container.scss @@ -0,0 +1,10 @@ +@import "../../../scss/lib/theme/vars"; + +.thumbnail-container { + margin-top: 7.5%; + + h3 { + height: 5vw; + text-transform: capitalize; + } +} diff --git a/app/component/gallery/thumbnail-container/thumbnail-container.html b/app/component/gallery/thumbnail-container/thumbnail-container.html new file mode 100644 index 00000000..1a86d3c8 --- /dev/null +++ b/app/component/gallery/thumbnail-container/thumbnail-container.html @@ -0,0 +1,10 @@ +
    +

    {{ thumbnailContainerCtrl.gallery.name }}

    + + + +
    + + +
    +
    diff --git a/app/component/gallery/thumbnail-container/thumbnail-container.js b/app/component/gallery/thumbnail-container/thumbnail-container.js new file mode 100644 index 00000000..18bf16d4 --- /dev/null +++ b/app/component/gallery/thumbnail-container/thumbnail-container.js @@ -0,0 +1,11 @@ +'use strict'; + +require('./_thumbnail-container.scss'); + +module.exports = { + template: require('./thumbnail-container.html'), + controllerAs: 'thumbnailContainerCtrl', + bindings: { + gallery: '<' + } +}; diff --git a/app/component/gallery/thumbnail/_thumbnail.scss b/app/component/gallery/thumbnail/_thumbnail.scss new file mode 100644 index 00000000..223b0c26 --- /dev/null +++ b/app/component/gallery/thumbnail/_thumbnail.scss @@ -0,0 +1,17 @@ +@import "../../../scss/lib/theme/vars"; + +.thumbnail { + margin-top: $gutter-std * 2; + + img { + width: 100%; + margin-bottom: $gutter-sm; + } + + span { + display: block; + float: right; + padding-top: 1.5vw; + padding-bottom: 1.5vw; + } +} diff --git a/app/component/gallery/thumbnail/thumbnail.html b/app/component/gallery/thumbnail/thumbnail.html new file mode 100644 index 00000000..6490ebab --- /dev/null +++ b/app/component/gallery/thumbnail/thumbnail.html @@ -0,0 +1,5 @@ +
    + {{ thumbnailCtrl.pic.desc }} + delete + +
    diff --git a/app/component/gallery/thumbnail/thumbnail.js b/app/component/gallery/thumbnail/thumbnail.js new file mode 100644 index 00000000..fe96846a --- /dev/null +++ b/app/component/gallery/thumbnail/thumbnail.js @@ -0,0 +1,20 @@ +'use strict'; + +require('./_thumbnail.scss'); + +module.exports = { + template: require('./thumbnail.html'), + controller: ['$log', 'picService', ThumbnailController], + controllerAs: 'thumbnailCtrl', + bindings: { + pic: '<' + } +}; + +function ThumbnailController($log, picService) { + $log.debug('ThumbnailController'); + + this.deletePic = function() { + $log.debug('thumbnailCtrl.deletePic'); + }; +}; diff --git a/app/component/gallery/upload-pic/_upload-pic.scss b/app/component/gallery/upload-pic/_upload-pic.scss new file mode 100644 index 00000000..302dd9cf --- /dev/null +++ b/app/component/gallery/upload-pic/_upload-pic.scss @@ -0,0 +1,29 @@ +@import "../../../scss/lib/theme/vars"; + +.upload-pic { + h3 { + font-size: 2.5vw; + text-decoration: underline; + margin-bottom: 0; + } + + fieldset { + margin-top: 0; + + input { + margin-bottom: $gutter-sm; + } + } + + div { + text-align: right; + + .grab-img { + float: left; + margin-top: 1vw; + margin-right: $gutter-sm; + font-size: 2.5vw; + text-decoration: underline; + } + } +} diff --git a/app/component/gallery/upload-pic/upload-pic.html b/app/component/gallery/upload-pic/upload-pic.html new file mode 100644 index 00000000..a25c885e --- /dev/null +++ b/app/component/gallery/upload-pic/upload-pic.html @@ -0,0 +1,24 @@ +
    +
    +
    + +

    upload a new pic

    + +
    + + +
    + +
    +

    + select a pic to upload +

    + + +
    +
    +
    diff --git a/app/component/gallery/upload-pic/upload-pic.js b/app/component/gallery/upload-pic/upload-pic.js new file mode 100644 index 00000000..14ee542b --- /dev/null +++ b/app/component/gallery/upload-pic/upload-pic.js @@ -0,0 +1,29 @@ +'use strict'; + +require('./_upload-pic.scss'); + +module.exports ={ + template: require('./upload-pic.html'), + controller: ['$log', 'picService', UploadPicController], + controllerAs: 'uploadPicCtrl', + bindings: { + gallery: '<' + } +}; + +function UploadPicController($log, picService) { + $log.debug('UploadPicController'); + + this.pic = {}; + + this.uploadPic = function() { + $log.log('insider', this.pic) + picService.uploadGalleryPic(this.gallery, this.pic) + .then( () => { + + this.pic.name = null; + this.pic.desc = null; + this.pic.file = null; + }); + }; +}; diff --git a/app/component/landing/login/_login.scss b/app/component/landing/login/_login.scss new file mode 100644 index 00000000..fb9e6494 --- /dev/null +++ b/app/component/landing/login/_login.scss @@ -0,0 +1,13 @@ +@import "../../../scss/lib/theme/vars"; + +.login-form { + .input-std { + margin-bottom: $gutter-sm; + } + + button { + float: right; + padding-top: 1.5vw; + padding-bottom: 1.5vw; + } +} diff --git a/app/component/landing/login/login.html b/app/component/landing/login/login.html new file mode 100644 index 00000000..8addd906 --- /dev/null +++ b/app/component/landing/login/login.html @@ -0,0 +1,31 @@ +
    +
    +
    + +
    + +
    + +
    + + +
    +
    diff --git a/app/component/landing/login/login.js b/app/component/landing/login/login.js new file mode 100644 index 00000000..dec7c927 --- /dev/null +++ b/app/component/landing/login/login.js @@ -0,0 +1,27 @@ +'use strict'; + +require('./_login.scss'); + +module.exports = { + template: require('./login.html'), + controller: ['$log', '$location', 'authService', LoginController], + controllerAs: 'loginCtrl' +}; + +function LoginController($log, $location, authService) { + $log.debug('LoginController'); + + authService.getToken() + .then( () => { + $location.url('/home'); + }); + + this.login = function() { + $log.debug('loginCtrl.login'); + + authService.login(this.user) + .then( () => { + $location.url('/home'); + }); + }; +}; diff --git a/app/component/landing/signup/_signup.scss b/app/component/landing/signup/_signup.scss new file mode 100644 index 00000000..691be8f0 --- /dev/null +++ b/app/component/landing/signup/_signup.scss @@ -0,0 +1,13 @@ +@import "../../../scss/lib/theme/vars"; + +.signup { + .input-std { + margin-bottom: $gutter-sm; + } + + button { + float: right; + padding-top: 1.5vw; + padding-bottom: 1.5vw; + } +} diff --git a/app/component/landing/signup/signup.html b/app/component/landing/signup/signup.html new file mode 100644 index 00000000..be058573 --- /dev/null +++ b/app/component/landing/signup/signup.html @@ -0,0 +1,27 @@ +
    +

    sign in

    +
    + + + + + + + + +
    +
    +
    diff --git a/app/component/landing/signup/signup.js b/app/component/landing/signup/signup.js new file mode 100644 index 00000000..36bee61b --- /dev/null +++ b/app/component/landing/signup/signup.js @@ -0,0 +1,27 @@ +'use strict'; + +require('./_signup.scss'); + +module.exports = { + template: require('./signup.html'), + controller: ['$log', '$location', 'authService', SignupController], + controllerAs: 'signupCtrl' +}; + +function SignupController($log, $location, authService) { + $log.debug('SignupController'); + + authService.getToken() + .then( () => { + $location.url('/home'); + }); + + this.signup = function(user) { + $log.debug('signupCtrl.signup'); + + authService.signup(user) + .then( () => { + $location.url('/home') + }); + }; +}; diff --git a/app/component/navbar/_navbar.scss b/app/component/navbar/_navbar.scss new file mode 100644 index 00000000..3a4a2b76 --- /dev/null +++ b/app/component/navbar/_navbar.scss @@ -0,0 +1,30 @@ +@import "../../scss/lib/theme/vars"; + +.navbar { + img { + width: 5.5vw; + float: left; + margin-top: 1vw; + margin-left: 1%; + } + + h2 { + float: left; + font-size: 3vw; + color: $white; + margin-top: 2.25vw; + margin-left: 1vw; + } + + button { + float: right; + margin-top: 2vw; + margin-right: 1%; + font-size: 1.5vw; + } + + .dropdown { + position: absolute; + right: 0; + } +} diff --git a/app/component/navbar/navbar.html b/app/component/navbar/navbar.html new file mode 100644 index 00000000..4ad6e4d0 --- /dev/null +++ b/app/component/navbar/navbar.html @@ -0,0 +1,18 @@ + diff --git a/app/component/navbar/navbar.js b/app/component/navbar/navbar.js new file mode 100644 index 00000000..290210b4 --- /dev/null +++ b/app/component/navbar/navbar.js @@ -0,0 +1,43 @@ +'use strict'; + +require('./_navbar.scss'); + +module.exports = { + template: require('./navbar.html'), + controller: ['$log', '$location', '$rootScope', 'authService', NavbarController], + controllerAs: 'navbarCtrl' +}; + +function NavbarController($log, $location, $rootScope, authService) { + $log.debug('NavbarController'); + + this.checkPath = function() { + let path =$location.path(); + if (path === '/join') { + this.hideButtons = true; + } + + if (path !== '/join') { + this.hideButtons = false; + authService.getToken() + .catch( () => { + $location.url('/join#login'); + }); + } + }; + + this.checkPath(); + + $rootScope.$on('$locationChangeSuccess', () => { + this.checkPath(); + }); + + this.logout = function() { + $log.log('navbarCtrl.logout'); + this.hideButtons = true; + authService.logout() + .then( () => { + $location.url('.'); + }) + } +} diff --git a/app/config/log-config.js b/app/config/log-config.js new file mode 100644 index 00000000..01068985 --- /dev/null +++ b/app/config/log-config.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = ['$logProvider', logConfig]; + +function logConfig($logProvider) { + $logProvider.debugEnabled(__DEBUG__); +} diff --git a/app/config/router-config.js b/app/config/router-config.js new file mode 100644 index 00000000..5357c24b --- /dev/null +++ b/app/config/router-config.js @@ -0,0 +1,31 @@ +'use strict'; + +module.exports = ['$stateProvider', '$urlRouterProvider', routerConfig]; + +function routerConfig($stateProvider, $urlRouterProvider) { + $urlRouterProvider.when('', '/join#signup'); + $urlRouterProvider.when('/', '/join#signup'); + $urlRouterProvider.when('/signup', '/join#signup'); + $urlRouterProvider.when('/login', '/join#login'); + + let states = [ + { + name: 'home', + url: '/home', + template: require('../view/home/home.html'), + controller: 'HomeController', + controllerAs: 'homeCtrl' + }, + { + name: 'landing', + url: '/join', + template: require('../view/landing/landing.html'), + controller: 'LandingController', + controllerAs: 'landingCtrl' + } + ]; + + states.forEach( state => { + $stateProvider.state(state); + }); +}; diff --git a/app/entry.js b/app/entry.js new file mode 100644 index 00000000..7bd40491 --- /dev/null +++ b/app/entry.js @@ -0,0 +1,41 @@ +'use strict'; + +require('./scss/main.scss'); + +const path = require('path'); +const angular = require('angular'); +const camelcase = require('camelcase'); +const pascalcase = require('pascalcase'); +const uiRouter = require('angular-ui-router'); +const ngTouch = require('angular-touch'); +const ngAnimate = require('angular-animate'); +const uiBootstrap = require('angular-ui-bootstrap'); +const ngFileUpload = require('ng-file-upload'); + +const glgram = angular.module('glgram', [ngTouch, ngAnimate, uiRouter, uiBootstrap, ngFileUpload]); + +let context = require.context('./config/', true, /\.js$/); +context.keys().forEach( key => { + glgram.config(context(key)); +}); + +context = require.context('./view/', true, /\.js$/); +context.keys().forEach( key => { + let name = pascalcase(path.basename(key, '.js')); + let module = context(key); + glgram.controller(name, module); +}); + +context = require.context('./service/', true, /\.js$/); +context.keys().forEach( key => { + let name = camelcase(path.basename(key, '.js')); + let module = context(key); + glgram.service(name, module); +}); + +context = require.context('./component/', true, /\.js$/); +context.keys().forEach( key => { + let name = camelcase(path.basename(key, '.js')); + let module = context(key); + glgram.component(name, module); +}); diff --git a/app/filter/shout.js b/app/filter/shout.js new file mode 100644 index 00000000..3ebdc7f4 --- /dev/null +++ b/app/filter/shout.js @@ -0,0 +1,20 @@ +'use strict'; + + +// TODO MAKE A SHOUTING FILTER AND ANOTHER ONE AND PLUG THEM IN, ALSO ICONS + +module.exports = function() { + return function(galleries, searchTerm) { + let shoutedRegex = generateFuzzyRegex(searchTerm); + + return galleries.filter(gallery => { + return shoutedRegex.test(gallery.name.toUpperCase()); + }); + }; +}; + +function generateFuzzyRegex(input) { + if (!input) return /.*/; + let shoutedString = '.*' + input.toUpperCase().split('').join('.*') + '.*'; + return new RegExp(shoutedString); +}; diff --git a/app/index.html b/app/index.html new file mode 100644 index 00000000..c381ec13 --- /dev/null +++ b/app/index.html @@ -0,0 +1,19 @@ + + + + + + glgram. + + +
    + +

    glgram

    + +
    +
    + +
    + + + diff --git a/app/scss/lib/base/base.scss b/app/scss/lib/base/base.scss new file mode 100644 index 00000000..903df77b --- /dev/null +++ b/app/scss/lib/base/base.scss @@ -0,0 +1,74 @@ +html, body { + background: $app-primary; + font-family: $font-std; + width: 100%; + height: 100%; + color: $app-color; +} + +a { + text-decoration: none; + color: $black; + font-weight: 700; +} + +h1 { + font-size: 5vw; + margin-bottom: $gutter-sm; +} + +h2 { + font-size: 4vw; + margin: $gutter-sm/ 2.0; +} + +h3 { + font-size: 3vw; + margin-bottom: $gutter-sm; +} + +button { + padding: 1% 5%; + background: $btn-primary; + color: $white; + font-size: 2vw; + border: none; + border-radius: $btn-radius; + cursor: pointer; + transition: 350ms all; +} + +.btn-std { + @extend button; +} + +input[type="text"] { + width: 100%; + padding: 1.5vw 2vw; + font-size: 2vw; + border: solid 1px $app-primary; + border-radius: $btn-radius; + box-sizing: border-box; + + &:focus { + background: $app-primary; + color: $white; + } +} + +input[type="password"] { + @extend input[type="text"]; +} + +input[disabled="disabled"] { + background: $disabled; +} + +.input-std { + @extend input[type="text"]; + display: inline-block; +} + +fieldset { + margin: 2% 0; +} diff --git a/app/scss/lib/base/reset.scss b/app/scss/lib/base/reset.scss new file mode 100644 index 00000000..ed11813c --- /dev/null +++ b/app/scss/lib/base/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/app/scss/lib/bootstrap/_custom-bootstrap-vars.scss b/app/scss/lib/bootstrap/_custom-bootstrap-vars.scss new file mode 100644 index 00000000..e0496852 --- /dev/null +++ b/app/scss/lib/bootstrap/_custom-bootstrap-vars.scss @@ -0,0 +1,874 @@ +$bootstrap-sass-asset-helper: false !default; +// +// Variables +// -------------------------------------------------- + + +//== Colors +// +//## Gray and brand colors for use across Bootstrap. + +$gray-base: #000 !default; +$gray-darker: lighten($gray-base, 13.5%) !default; // #222 +$gray-dark: lighten($gray-base, 20%) !default; // #333 +$gray: lighten($gray-base, 33.5%) !default; // #555 +$gray-light: lighten($gray-base, 46.7%) !default; // #777 +$gray-lighter: lighten($gray-base, 93.5%) !default; // #eee + +$brand-primary: darken(#428bca, 6.5%) !default; // #337ab7 +$brand-success: #5cb85c !default; +$brand-info: #5bc0de !default; +$brand-warning: #f0ad4e !default; +$brand-danger: #d9534f !default; + + +//== Scaffolding +// +//## Settings for some of the most global styles. + +//** Background color for ``. +$body-bg: #fff !default; +//** Global text color on ``. +$text-color: $gray-dark !default; + +//** Global textual link color. +$link-color: $brand-primary !default; +//** Link hover color set via `darken()` function. +$link-hover-color: darken($link-color, 15%) !default; +//** Link hover decoration. +$link-hover-decoration: underline !default; + + +//== Typography +// +//## Font, line-height, and color for body text, headings, and more. + +$font-family-sans-serif: "Helvetica Neue", Helvetica, Arial, sans-serif !default; +$font-family-serif: Georgia, "Times New Roman", Times, serif !default; +//** Default monospace fonts for ``, ``, and `
    `.
    +$font-family-monospace:   Menlo, Monaco, Consolas, "Courier New", monospace !default;
    +$font-family-base:        $font-family-sans-serif !default;
    +
    +$font-size-base:          14px !default;
    +$font-size-large:         ceil(($font-size-base * 1.25)) !default; // ~18px
    +$font-size-small:         ceil(($font-size-base * 0.85)) !default; // ~12px
    +
    +$font-size-h1:            floor(($font-size-base * 2.6)) !default; // ~36px
    +$font-size-h2:            floor(($font-size-base * 2.15)) !default; // ~30px
    +$font-size-h3:            ceil(($font-size-base * 1.7)) !default; // ~24px
    +$font-size-h4:            ceil(($font-size-base * 1.25)) !default; // ~18px
    +$font-size-h5:            $font-size-base !default;
    +$font-size-h6:            ceil(($font-size-base * 0.85)) !default; // ~12px
    +
    +//** Unit-less `line-height` for use in components like buttons.
    +$line-height-base:        1.428571429 !default; // 20/14
    +//** Computed "line-height" (`font-size` * `line-height`) for use with `margin`, `padding`, etc.
    +$line-height-computed:    floor(($font-size-base * $line-height-base)) !default; // ~20px
    +
    +//** By default, this inherits from the ``.
    +$headings-font-family:    inherit !default;
    +$headings-font-weight:    500 !default;
    +$headings-line-height:    1.1 !default;
    +$headings-color:          inherit !default;
    +
    +
    +//== Iconography
    +//
    +//## Specify custom location and filename of the included Glyphicons icon font. Useful for those including Bootstrap via Bower.
    +
    +//** Load fonts from this directory.
    +
    +// [converter] If $bootstrap-sass-asset-helper if used, provide path relative to the assets load path.
    +// [converter] This is because some asset helpers, such as Sprockets, do not work with file-relative paths.
    +$icon-font-path: if($bootstrap-sass-asset-helper, "bootstrap/", "../fonts/bootstrap/") !default;
    +
    +//** File name for all font files.
    +$icon-font-name:          "glyphicons-halflings-regular" !default;
    +//** Element ID within SVG icon file.
    +$icon-font-svg-id:        "glyphicons_halflingsregular" !default;
    +
    +
    +//== Components
    +//
    +//## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
    +
    +$padding-base-vertical:     6px !default;
    +$padding-base-horizontal:   12px !default;
    +
    +$padding-large-vertical:    10px !default;
    +$padding-large-horizontal:  16px !default;
    +
    +$padding-small-vertical:    5px !default;
    +$padding-small-horizontal:  10px !default;
    +
    +$padding-xs-vertical:       1px !default;
    +$padding-xs-horizontal:     5px !default;
    +
    +$line-height-large:         1.3333333 !default; // extra decimals for Win 8.1 Chrome
    +$line-height-small:         1.5 !default;
    +
    +$border-radius-base:        4px !default;
    +$border-radius-large:       6px !default;
    +$border-radius-small:       3px !default;
    +
    +//** Global color for active items (e.g., navs or dropdowns).
    +$component-active-color:    #fff !default;
    +//** Global background color for active items (e.g., navs or dropdowns).
    +$component-active-bg:       $brand-primary !default;
    +
    +//** Width of the `border` for generating carets that indicate dropdowns.
    +$caret-width-base:          4px !default;
    +//** Carets increase slightly in size for larger components.
    +$caret-width-large:         5px !default;
    +
    +
    +//== Tables
    +//
    +//## Customizes the `.table` component with basic values, each used across all table variations.
    +
    +//** Padding for ``s and ``s.
    +$table-cell-padding:            8px !default;
    +//** Padding for cells in `.table-condensed`.
    +$table-condensed-cell-padding:  5px !default;
    +
    +//** Default background color used for all tables.
    +$table-bg:                      transparent !default;
    +//** Background color used for `.table-striped`.
    +$table-bg-accent:               #f9f9f9 !default;
    +//** Background color used for `.table-hover`.
    +$table-bg-hover:                #f5f5f5 !default;
    +$table-bg-active:               $table-bg-hover !default;
    +
    +//** Border color for table and cell borders.
    +$table-border-color:            #ddd !default;
    +
    +
    +//== Buttons
    +//
    +//## For each of Bootstrap's buttons, define text, background and border color.
    +
    +$btn-font-weight:                normal !default;
    +
    +$btn-default-color:              #333 !default;
    +$btn-default-bg:                 #fff !default;
    +$btn-default-border:             #ccc !default;
    +
    +$btn-primary-color:              #fff !default;
    +$btn-primary-bg:                 $brand-primary !default;
    +$btn-primary-border:             darken($btn-primary-bg, 5%) !default;
    +
    +$btn-success-color:              #fff !default;
    +$btn-success-bg:                 $brand-success !default;
    +$btn-success-border:             darken($btn-success-bg, 5%) !default;
    +
    +$btn-info-color:                 #fff !default;
    +$btn-info-bg:                    $brand-info !default;
    +$btn-info-border:                darken($btn-info-bg, 5%) !default;
    +
    +$btn-warning-color:              #fff !default;
    +$btn-warning-bg:                 $brand-warning !default;
    +$btn-warning-border:             darken($btn-warning-bg, 5%) !default;
    +
    +$btn-danger-color:               #fff !default;
    +$btn-danger-bg:                  $brand-danger !default;
    +$btn-danger-border:              darken($btn-danger-bg, 5%) !default;
    +
    +$btn-link-disabled-color:        $gray-light !default;
    +
    +// Allows for customizing button radius independently from global border radius
    +$btn-border-radius-base:         $border-radius-base !default;
    +$btn-border-radius-large:        $border-radius-large !default;
    +$btn-border-radius-small:        $border-radius-small !default;
    +
    +
    +//== Forms
    +//
    +//##
    +
    +//** `` background color
    +$input-bg:                       #fff !default;
    +//** `` background color
    +$input-bg-disabled:              $gray-lighter !default;
    +
    +//** Text color for ``s
    +$input-color:                    $gray !default;
    +//** `` border color
    +$input-border:                   #ccc !default;
    +
    +// TODO: Rename `$input-border-radius` to `$input-border-radius-base` in v4
    +//** Default `.form-control` border radius
    +// This has no effect on ``s in CSS.
    +$input-border-radius:            $border-radius-base !default;
    +//** Large `.form-control` border radius
    +$input-border-radius-large:      $border-radius-large !default;
    +//** Small `.form-control` border radius
    +$input-border-radius-small:      $border-radius-small !default;
    +
    +//** Border color for inputs on focus
    +$input-border-focus:             #66afe9 !default;
    +
    +//** Placeholder text color
    +$input-color-placeholder:        #999 !default;
    +
    +//** Default `.form-control` height
    +$input-height-base:              ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;
    +//** Large `.form-control` height
    +$input-height-large:             (ceil($font-size-large * $line-height-large) + ($padding-large-vertical * 2) + 2) !default;
    +//** Small `.form-control` height
    +$input-height-small:             (floor($font-size-small * $line-height-small) + ($padding-small-vertical * 2) + 2) !default;
    +
    +//** `.form-group` margin
    +$form-group-margin-bottom:       15px !default;
    +
    +$legend-color:                   $gray-dark !default;
    +$legend-border-color:            #e5e5e5 !default;
    +
    +//** Background color for textual input addons
    +$input-group-addon-bg:           $gray-lighter !default;
    +//** Border color for textual input addons
    +$input-group-addon-border-color: $input-border !default;
    +
    +//** Disabled cursor for form controls and buttons.
    +$cursor-disabled:                not-allowed !default;
    +
    +
    +//== Dropdowns
    +//
    +//## Dropdown menu container and contents.
    +
    +//** Background for the dropdown menu.
    +$dropdown-bg:                    #fff !default;
    +//** Dropdown menu `border-color`.
    +$dropdown-border:                rgba(0,0,0,.15) !default;
    +//** Dropdown menu `border-color` **for IE8**.
    +$dropdown-fallback-border:       #ccc !default;
    +//** Divider color for between dropdown items.
    +$dropdown-divider-bg:            #e5e5e5 !default;
    +
    +//** Dropdown link text color.
    +$dropdown-link-color:            $gray-dark !default;
    +//** Hover color for dropdown links.
    +$dropdown-link-hover-color:      darken($gray-dark, 5%) !default;
    +//** Hover background for dropdown links.
    +$dropdown-link-hover-bg:         #f5f5f5 !default;
    +
    +//** Active dropdown menu item text color.
    +$dropdown-link-active-color:     $component-active-color !default;
    +//** Active dropdown menu item background color.
    +$dropdown-link-active-bg:        $component-active-bg !default;
    +
    +//** Disabled dropdown menu item background color.
    +$dropdown-link-disabled-color:   $gray-light !default;
    +
    +//** Text color for headers within dropdown menus.
    +$dropdown-header-color:          $gray-light !default;
    +
    +//** Deprecated `$dropdown-caret-color` as of v3.1.0
    +$dropdown-caret-color:           #000 !default;
    +
    +
    +//-- Z-index master list
    +//
    +// Warning: Avoid customizing these values. They're used for a bird's eye view
    +// of components dependent on the z-axis and are designed to all work together.
    +//
    +// Note: These variables are not generated into the Customizer.
    +
    +$zindex-navbar:            1000 !default;
    +$zindex-dropdown:          1000 !default;
    +$zindex-popover:           1060 !default;
    +$zindex-tooltip:           1070 !default;
    +$zindex-navbar-fixed:      1030 !default;
    +$zindex-modal-background:  1040 !default;
    +$zindex-modal:             1050 !default;
    +
    +
    +//== Media queries breakpoints
    +//
    +//## Define the breakpoints at which your layout will change, adapting to different screen sizes.
    +
    +// Extra small screen / phone
    +//** Deprecated `$screen-xs` as of v3.0.1
    +$screen-xs:                  480px !default;
    +//** Deprecated `$screen-xs-min` as of v3.2.0
    +$screen-xs-min:              $screen-xs !default;
    +//** Deprecated `$screen-phone` as of v3.0.1
    +$screen-phone:               $screen-xs-min !default;
    +
    +// Small screen / tablet
    +//** Deprecated `$screen-sm` as of v3.0.1
    +$screen-sm:                  768px !default;
    +$screen-sm-min:              $screen-sm !default;
    +//** Deprecated `$screen-tablet` as of v3.0.1
    +$screen-tablet:              $screen-sm-min !default;
    +
    +// Medium screen / desktop
    +//** Deprecated `$screen-md` as of v3.0.1
    +$screen-md:                  992px !default;
    +$screen-md-min:              $screen-md !default;
    +//** Deprecated `$screen-desktop` as of v3.0.1
    +$screen-desktop:             $screen-md-min !default;
    +
    +// Large screen / wide desktop
    +//** Deprecated `$screen-lg` as of v3.0.1
    +$screen-lg:                  1200px !default;
    +$screen-lg-min:              $screen-lg !default;
    +//** Deprecated `$screen-lg-desktop` as of v3.0.1
    +$screen-lg-desktop:          $screen-lg-min !default;
    +
    +// So media queries don't overlap when required, provide a maximum
    +$screen-xs-max:              ($screen-sm-min - 1) !default;
    +$screen-sm-max:              ($screen-md-min - 1) !default;
    +$screen-md-max:              ($screen-lg-min - 1) !default;
    +
    +
    +//== Grid system
    +//
    +//## Define your custom responsive grid.
    +
    +//** Number of columns in the grid.
    +$grid-columns:              12 !default;
    +//** Padding between columns. Gets divided in half for the left and right.
    +$grid-gutter-width:         30px !default;
    +// Navbar collapse
    +//** Point at which the navbar becomes uncollapsed.
    +$grid-float-breakpoint:     $screen-sm-min !default;
    +//** Point at which the navbar begins collapsing.
    +$grid-float-breakpoint-max: ($grid-float-breakpoint - 1) !default;
    +
    +
    +//== Container sizes
    +//
    +//## Define the maximum width of `.container` for different screen sizes.
    +
    +// Small screen / tablet
    +$container-tablet:             (720px + $grid-gutter-width) !default;
    +//** For `$screen-sm-min` and up.
    +$container-sm:                 $container-tablet !default;
    +
    +// Medium screen / desktop
    +$container-desktop:            (940px + $grid-gutter-width) !default;
    +//** For `$screen-md-min` and up.
    +$container-md:                 $container-desktop !default;
    +
    +// Large screen / wide desktop
    +$container-large-desktop:      (1140px + $grid-gutter-width) !default;
    +//** For `$screen-lg-min` and up.
    +$container-lg:                 $container-large-desktop !default;
    +
    +
    +//== Navbar
    +//
    +//##
    +
    +// Basics of a navbar
    +$navbar-height:                    50px !default;
    +$navbar-margin-bottom:             $line-height-computed !default;
    +$navbar-border-radius:             $border-radius-base !default;
    +$navbar-padding-horizontal:        floor(($grid-gutter-width / 2)) !default;
    +$navbar-padding-vertical:          (($navbar-height - $line-height-computed) / 2) !default;
    +$navbar-collapse-max-height:       340px !default;
    +
    +$navbar-default-color:             #777 !default;
    +$navbar-default-bg:                #f8f8f8 !default;
    +$navbar-default-border:            darken($navbar-default-bg, 6.5%) !default;
    +
    +// Navbar links
    +$navbar-default-link-color:                #777 !default;
    +$navbar-default-link-hover-color:          #333 !default;
    +$navbar-default-link-hover-bg:             transparent !default;
    +$navbar-default-link-active-color:         #555 !default;
    +$navbar-default-link-active-bg:            darken($navbar-default-bg, 6.5%) !default;
    +$navbar-default-link-disabled-color:       #ccc !default;
    +$navbar-default-link-disabled-bg:          transparent !default;
    +
    +// Navbar brand label
    +$navbar-default-brand-color:               $navbar-default-link-color !default;
    +$navbar-default-brand-hover-color:         darken($navbar-default-brand-color, 10%) !default;
    +$navbar-default-brand-hover-bg:            transparent !default;
    +
    +// Navbar toggle
    +$navbar-default-toggle-hover-bg:           #ddd !default;
    +$navbar-default-toggle-icon-bar-bg:        #888 !default;
    +$navbar-default-toggle-border-color:       #ddd !default;
    +
    +
    +//=== Inverted navbar
    +// Reset inverted navbar basics
    +$navbar-inverse-color:                      lighten($gray-light, 15%) !default;
    +$navbar-inverse-bg:                         #222 !default;
    +$navbar-inverse-border:                     darken($navbar-inverse-bg, 10%) !default;
    +
    +// Inverted navbar links
    +$navbar-inverse-link-color:                 lighten($gray-light, 15%) !default;
    +$navbar-inverse-link-hover-color:           #fff !default;
    +$navbar-inverse-link-hover-bg:              transparent !default;
    +$navbar-inverse-link-active-color:          $navbar-inverse-link-hover-color !default;
    +$navbar-inverse-link-active-bg:             darken($navbar-inverse-bg, 10%) !default;
    +$navbar-inverse-link-disabled-color:        #444 !default;
    +$navbar-inverse-link-disabled-bg:           transparent !default;
    +
    +// Inverted navbar brand label
    +$navbar-inverse-brand-color:                $navbar-inverse-link-color !default;
    +$navbar-inverse-brand-hover-color:          #fff !default;
    +$navbar-inverse-brand-hover-bg:             transparent !default;
    +
    +// Inverted navbar toggle
    +$navbar-inverse-toggle-hover-bg:            #333 !default;
    +$navbar-inverse-toggle-icon-bar-bg:         #fff !default;
    +$navbar-inverse-toggle-border-color:        #333 !default;
    +
    +
    +//== Navs
    +//
    +//##
    +
    +//=== Shared nav styles
    +$nav-link-padding:                          10px 15px !default;
    +$nav-link-hover-bg:                         $gray-lighter !default;
    +
    +$nav-disabled-link-color:                   $gray-light !default;
    +$nav-disabled-link-hover-color:             $gray-light !default;
    +
    +//== Tabs
    +$nav-tabs-border-color:                     #ddd !default;
    +
    +$nav-tabs-link-hover-border-color:          $gray-lighter !default;
    +
    +$nav-tabs-active-link-hover-bg:             $body-bg !default;
    +$nav-tabs-active-link-hover-color:          $gray !default;
    +$nav-tabs-active-link-hover-border-color:   #ddd !default;
    +
    +$nav-tabs-justified-link-border-color:            #ddd !default;
    +$nav-tabs-justified-active-link-border-color:     $body-bg !default;
    +
    +//== Pills
    +$nav-pills-border-radius:                   $border-radius-base !default;
    +$nav-pills-active-link-hover-bg:            $component-active-bg !default;
    +$nav-pills-active-link-hover-color:         $component-active-color !default;
    +
    +
    +//== Pagination
    +//
    +//##
    +
    +$pagination-color:                     $link-color !default;
    +$pagination-bg:                        #fff !default;
    +$pagination-border:                    #ddd !default;
    +
    +$pagination-hover-color:               $link-hover-color !default;
    +$pagination-hover-bg:                  $gray-lighter !default;
    +$pagination-hover-border:              #ddd !default;
    +
    +$pagination-active-color:              #fff !default;
    +$pagination-active-bg:                 $brand-primary !default;
    +$pagination-active-border:             $brand-primary !default;
    +
    +$pagination-disabled-color:            $gray-light !default;
    +$pagination-disabled-bg:               #fff !default;
    +$pagination-disabled-border:           #ddd !default;
    +
    +
    +//== Pager
    +//
    +//##
    +
    +$pager-bg:                             $pagination-bg !default;
    +$pager-border:                         $pagination-border !default;
    +$pager-border-radius:                  15px !default;
    +
    +$pager-hover-bg:                       $pagination-hover-bg !default;
    +
    +$pager-active-bg:                      $pagination-active-bg !default;
    +$pager-active-color:                   $pagination-active-color !default;
    +
    +$pager-disabled-color:                 $pagination-disabled-color !default;
    +
    +
    +//== Jumbotron
    +//
    +//##
    +
    +$jumbotron-padding:              30px !default;
    +$jumbotron-color:                inherit !default;
    +$jumbotron-bg:                   $gray-lighter !default;
    +$jumbotron-heading-color:        inherit !default;
    +$jumbotron-font-size:            ceil(($font-size-base * 1.5)) !default;
    +$jumbotron-heading-font-size:    ceil(($font-size-base * 4.5)) !default;
    +
    +
    +//== Form states and alerts
    +//
    +//## Define colors for form feedback states and, by default, alerts.
    +
    +$state-success-text:             #3c763d !default;
    +$state-success-bg:               #dff0d8 !default;
    +$state-success-border:           darken(adjust-hue($state-success-bg, -10), 5%) !default;
    +
    +$state-info-text:                #31708f !default;
    +$state-info-bg:                  #d9edf7 !default;
    +$state-info-border:              darken(adjust-hue($state-info-bg, -10), 7%) !default;
    +
    +$state-warning-text:             #8a6d3b !default;
    +$state-warning-bg:               #fcf8e3 !default;
    +$state-warning-border:           darken(adjust-hue($state-warning-bg, -10), 5%) !default;
    +
    +$state-danger-text:              #a94442 !default;
    +$state-danger-bg:                #f2dede !default;
    +$state-danger-border:            darken(adjust-hue($state-danger-bg, -10), 5%) !default;
    +
    +
    +//== Tooltips
    +//
    +//##
    +
    +//** Tooltip max width
    +$tooltip-max-width:           200px !default;
    +//** Tooltip text color
    +$tooltip-color:               #fff !default;
    +//** Tooltip background color
    +$tooltip-bg:                  #000 !default;
    +$tooltip-opacity:             .9 !default;
    +
    +//** Tooltip arrow width
    +$tooltip-arrow-width:         5px !default;
    +//** Tooltip arrow color
    +$tooltip-arrow-color:         $tooltip-bg !default;
    +
    +
    +//== Popovers
    +//
    +//##
    +
    +//** Popover body background color
    +$popover-bg:                          #fff !default;
    +//** Popover maximum width
    +$popover-max-width:                   276px !default;
    +//** Popover border color
    +$popover-border-color:                rgba(0,0,0,.2) !default;
    +//** Popover fallback border color
    +$popover-fallback-border-color:       #ccc !default;
    +
    +//** Popover title background color
    +$popover-title-bg:                    darken($popover-bg, 3%) !default;
    +
    +//** Popover arrow width
    +$popover-arrow-width:                 10px !default;
    +//** Popover arrow color
    +$popover-arrow-color:                 $popover-bg !default;
    +
    +//** Popover outer arrow width
    +$popover-arrow-outer-width:           ($popover-arrow-width + 1) !default;
    +//** Popover outer arrow color
    +$popover-arrow-outer-color:           fade_in($popover-border-color, 0.05) !default;
    +//** Popover outer arrow fallback color
    +$popover-arrow-outer-fallback-color:  darken($popover-fallback-border-color, 20%) !default;
    +
    +
    +//== Labels
    +//
    +//##
    +
    +//** Default label background color
    +$label-default-bg:            $gray-light !default;
    +//** Primary label background color
    +$label-primary-bg:            $brand-primary !default;
    +//** Success label background color
    +$label-success-bg:            $brand-success !default;
    +//** Info label background color
    +$label-info-bg:               $brand-info !default;
    +//** Warning label background color
    +$label-warning-bg:            $brand-warning !default;
    +//** Danger label background color
    +$label-danger-bg:             $brand-danger !default;
    +
    +//** Default label text color
    +$label-color:                 #fff !default;
    +//** Default text color of a linked label
    +$label-link-hover-color:      #fff !default;
    +
    +
    +//== Modals
    +//
    +//##
    +
    +//** Padding applied to the modal body
    +$modal-inner-padding:         15px !default;
    +
    +//** Padding applied to the modal title
    +$modal-title-padding:         15px !default;
    +//** Modal title line-height
    +$modal-title-line-height:     $line-height-base !default;
    +
    +//** Background color of modal content area
    +$modal-content-bg:                             #fff !default;
    +//** Modal content border color
    +$modal-content-border-color:                   rgba(0,0,0,.2) !default;
    +//** Modal content border color **for IE8**
    +$modal-content-fallback-border-color:          #999 !default;
    +
    +//** Modal backdrop background color
    +$modal-backdrop-bg:           #000 !default;
    +//** Modal backdrop opacity
    +$modal-backdrop-opacity:      .5 !default;
    +//** Modal header border color
    +$modal-header-border-color:   #e5e5e5 !default;
    +//** Modal footer border color
    +$modal-footer-border-color:   $modal-header-border-color !default;
    +
    +$modal-lg:                    900px !default;
    +$modal-md:                    600px !default;
    +$modal-sm:                    300px !default;
    +
    +
    +//== Alerts
    +//
    +//## Define alert colors, border radius, and padding.
    +
    +$alert-padding:               15px !default;
    +$alert-border-radius:         $border-radius-base !default;
    +$alert-link-font-weight:      bold !default;
    +
    +$alert-success-bg:            $state-success-bg !default;
    +$alert-success-text:          $state-success-text !default;
    +$alert-success-border:        $state-success-border !default;
    +
    +$alert-info-bg:               $state-info-bg !default;
    +$alert-info-text:             $state-info-text !default;
    +$alert-info-border:           $state-info-border !default;
    +
    +$alert-warning-bg:            $state-warning-bg !default;
    +$alert-warning-text:          $state-warning-text !default;
    +$alert-warning-border:        $state-warning-border !default;
    +
    +$alert-danger-bg:             $state-danger-bg !default;
    +$alert-danger-text:           $state-danger-text !default;
    +$alert-danger-border:         $state-danger-border !default;
    +
    +
    +//== Progress bars
    +//
    +//##
    +
    +//** Background color of the whole progress component
    +$progress-bg:                 #f5f5f5 !default;
    +//** Progress bar text color
    +$progress-bar-color:          #fff !default;
    +//** Variable for setting rounded corners on progress bar.
    +$progress-border-radius:      $border-radius-base !default;
    +
    +//** Default progress bar color
    +$progress-bar-bg:             $brand-primary !default;
    +//** Success progress bar color
    +$progress-bar-success-bg:     $brand-success !default;
    +//** Warning progress bar color
    +$progress-bar-warning-bg:     $brand-warning !default;
    +//** Danger progress bar color
    +$progress-bar-danger-bg:      $brand-danger !default;
    +//** Info progress bar color
    +$progress-bar-info-bg:        $brand-info !default;
    +
    +
    +//== List group
    +//
    +//##
    +
    +//** Background color on `.list-group-item`
    +$list-group-bg:                 #fff !default;
    +//** `.list-group-item` border color
    +$list-group-border:             #ddd !default;
    +//** List group border radius
    +$list-group-border-radius:      $border-radius-base !default;
    +
    +//** Background color of single list items on hover
    +$list-group-hover-bg:           #f5f5f5 !default;
    +//** Text color of active list items
    +$list-group-active-color:       $component-active-color !default;
    +//** Background color of active list items
    +$list-group-active-bg:          $component-active-bg !default;
    +//** Border color of active list elements
    +$list-group-active-border:      $list-group-active-bg !default;
    +//** Text color for content within active list items
    +$list-group-active-text-color:  lighten($list-group-active-bg, 40%) !default;
    +
    +//** Text color of disabled list items
    +$list-group-disabled-color:      $gray-light !default;
    +//** Background color of disabled list items
    +$list-group-disabled-bg:         $gray-lighter !default;
    +//** Text color for content within disabled list items
    +$list-group-disabled-text-color: $list-group-disabled-color !default;
    +
    +$list-group-link-color:         #555 !default;
    +$list-group-link-hover-color:   $list-group-link-color !default;
    +$list-group-link-heading-color: #333 !default;
    +
    +
    +//== Panels
    +//
    +//##
    +
    +$panel-bg:                    #fff !default;
    +$panel-body-padding:          15px !default;
    +$panel-heading-padding:       10px 15px !default;
    +$panel-footer-padding:        $panel-heading-padding !default;
    +$panel-border-radius:         $border-radius-base !default;
    +
    +//** Border color for elements within panels
    +$panel-inner-border:          #ddd !default;
    +$panel-footer-bg:             #f5f5f5 !default;
    +
    +$panel-default-text:          $gray-dark !default;
    +$panel-default-border:        #ddd !default;
    +$panel-default-heading-bg:    #f5f5f5 !default;
    +
    +$panel-primary-text:          #fff !default;
    +$panel-primary-border:        $brand-primary !default;
    +$panel-primary-heading-bg:    $brand-primary !default;
    +
    +$panel-success-text:          $state-success-text !default;
    +$panel-success-border:        $state-success-border !default;
    +$panel-success-heading-bg:    $state-success-bg !default;
    +
    +$panel-info-text:             $state-info-text !default;
    +$panel-info-border:           $state-info-border !default;
    +$panel-info-heading-bg:       $state-info-bg !default;
    +
    +$panel-warning-text:          $state-warning-text !default;
    +$panel-warning-border:        $state-warning-border !default;
    +$panel-warning-heading-bg:    $state-warning-bg !default;
    +
    +$panel-danger-text:           $state-danger-text !default;
    +$panel-danger-border:         $state-danger-border !default;
    +$panel-danger-heading-bg:     $state-danger-bg !default;
    +
    +
    +//== Thumbnails
    +//
    +//##
    +
    +//** Padding around the thumbnail image
    +$thumbnail-padding:           4px !default;
    +//** Thumbnail background color
    +$thumbnail-bg:                $body-bg !default;
    +//** Thumbnail border color
    +$thumbnail-border:            #ddd !default;
    +//** Thumbnail border radius
    +$thumbnail-border-radius:     $border-radius-base !default;
    +
    +//** Custom text color for thumbnail captions
    +$thumbnail-caption-color:     $text-color !default;
    +//** Padding around the thumbnail caption
    +$thumbnail-caption-padding:   9px !default;
    +
    +
    +//== Wells
    +//
    +//##
    +
    +$well-bg:                     #f5f5f5 !default;
    +$well-border:                 darken($well-bg, 7%) !default;
    +
    +
    +//== Badges
    +//
    +//##
    +
    +$badge-color:                 #fff !default;
    +//** Linked badge text color on hover
    +$badge-link-hover-color:      #fff !default;
    +$badge-bg:                    $gray-light !default;
    +
    +//** Badge text color in active nav link
    +$badge-active-color:          $link-color !default;
    +//** Badge background color in active nav link
    +$badge-active-bg:             #fff !default;
    +
    +$badge-font-weight:           bold !default;
    +$badge-line-height:           1 !default;
    +$badge-border-radius:         10px !default;
    +
    +
    +//== Breadcrumbs
    +//
    +//##
    +
    +$breadcrumb-padding-vertical:   8px !default;
    +$breadcrumb-padding-horizontal: 15px !default;
    +//** Breadcrumb background color
    +$breadcrumb-bg:                 #f5f5f5 !default;
    +//** Breadcrumb text color
    +$breadcrumb-color:              #ccc !default;
    +//** Text color of current page in the breadcrumb
    +$breadcrumb-active-color:       $gray-light !default;
    +//** Textual separator for between breadcrumb elements
    +$breadcrumb-separator:          "/" !default;
    +
    +
    +//== Carousel
    +//
    +//##
    +
    +$carousel-text-shadow:                        0 1px 2px rgba(0,0,0,.6) !default;
    +
    +$carousel-control-color:                      #fff !default;
    +$carousel-control-width:                      15% !default;
    +$carousel-control-opacity:                    .5 !default;
    +$carousel-control-font-size:                  20px !default;
    +
    +$carousel-indicator-active-bg:                #fff !default;
    +$carousel-indicator-border-color:             #fff !default;
    +
    +$carousel-caption-color:                      #fff !default;
    +
    +
    +//== Close
    +//
    +//##
    +
    +$close-font-weight:           bold !default;
    +$close-color:                 #000 !default;
    +$close-text-shadow:           0 1px 0 #fff !default;
    +
    +
    +//== Code
    +//
    +//##
    +
    +$code-color:                  #c7254e !default;
    +$code-bg:                     #f9f2f4 !default;
    +
    +$kbd-color:                   #fff !default;
    +$kbd-bg:                      #333 !default;
    +
    +$pre-bg:                      #f5f5f5 !default;
    +$pre-color:                   $gray-dark !default;
    +$pre-border-color:            #ccc !default;
    +$pre-scrollable-max-height:   340px !default;
    +
    +
    +//== Type
    +//
    +//##
    +
    +//** Horizontal offset for forms and lists.
    +$component-offset-horizontal: 180px !default;
    +//** Text muted color
    +$text-muted:                  $gray-light !default;
    +//** Abbreviations and acronyms border color
    +$abbr-border-color:           $gray-light !default;
    +//** Headings small color
    +$headings-small-color:        $gray-light !default;
    +//** Blockquote small color
    +$blockquote-small-color:      $gray-light !default;
    +//** Blockquote font size
    +$blockquote-font-size:        ($font-size-base * 1.25) !default;
    +//** Blockquote border color
    +$blockquote-border-color:     $gray-lighter !default;
    +//** Page header border color
    +$page-header-border-color:    $gray-lighter !default;
    +//** Width of horizontal description list titles
    +$dl-horizontal-offset:        $component-offset-horizontal !default;
    +//** Point at which .dl-horizontal becomes horizontal
    +$dl-horizontal-breakpoint:    $grid-float-breakpoint !default;
    +//** Horizontal line color.
    +$hr-border:                   $gray-lighter !default;
    diff --git a/app/scss/lib/bootstrap/_custom-bootstrap.scss b/app/scss/lib/bootstrap/_custom-bootstrap.scss
    new file mode 100644
    index 00000000..b32e1cdb
    --- /dev/null
    +++ b/app/scss/lib/bootstrap/_custom-bootstrap.scss
    @@ -0,0 +1,50 @@
    +@import "custom-bootstrap-vars";
    +
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/mixins";
    +
    +// Reset and dependencies
    +// @import "~bootstrap-sass/assets/stylesheets/bootstrap/normalize";
    +// @import "~bootstrap-sass/assets/stylesheets/bootstrap/print";
    +// @import "~bootstrap-sass/assets/stylesheets/bootstrap/glyphicons";
    +
    +// Core CSS
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/scaffolding";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/type";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/code";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/grid";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/tables";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/forms";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/buttons";
    +
    +// Components
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/component-animations";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/dropdowns";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/button-groups";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/input-groups";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/navs";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/navbar";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/breadcrumbs";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/pagination";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/pager";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/labels";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/badges";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/jumbotron";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/thumbnails";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/alerts";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/progress-bars";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/media";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/list-group";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/panels";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-embed";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/wells";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/close";
    +
    +// Components w/ JavaScript
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/modals";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/tooltip";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/popovers";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/carousel";
    +
    +// Utility classes
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/utilities";
    +@import "~bootstrap-sass/assets/stylesheets/bootstrap/responsive-utilities";
    diff --git a/app/scss/lib/layout/_footer.scss b/app/scss/lib/layout/_footer.scss
    new file mode 100644
    index 00000000..88468ba3
    --- /dev/null
    +++ b/app/scss/lib/layout/_footer.scss
    @@ -0,0 +1,5 @@
    +footer {
    +  height: 10vw;
    +  background: $app-secondary;
    +  margin-top: $gutter-std * 2;
    +}
    diff --git a/app/scss/lib/layout/_header.scss b/app/scss/lib/layout/_header.scss
    new file mode 100644
    index 00000000..ad0e5164
    --- /dev/null
    +++ b/app/scss/lib/layout/_header.scss
    @@ -0,0 +1,4 @@
    +header {
    +  background: $app-primary;
    +  height: 8vw;  
    +}
    diff --git a/app/scss/lib/layout/_layout.scss b/app/scss/lib/layout/_layout.scss
    new file mode 100644
    index 00000000..cb94f019
    --- /dev/null
    +++ b/app/scss/lib/layout/_layout.scss
    @@ -0,0 +1,5 @@
    +main {
    +  width: 90%;
    +  margin: 5%;
    +  min-height: 600px;
    +}
    diff --git a/app/scss/lib/theme/_vars.scss b/app/scss/lib/theme/_vars.scss
    new file mode 100644
    index 00000000..9fbded1f
    --- /dev/null
    +++ b/app/scss/lib/theme/_vars.scss
    @@ -0,0 +1,21 @@
    +// Global variables //
    +
    +// Pallette  //
    +$app-primary: #444;
    +$app-color: $app-primary / 2;
    +$app-secondary: $app-primary * 2;
    +$btn-primary: $app-primary / 2;
    +$white: #fff;
    +$black: #000;
    +$disabled: #e3e3e3;
    +
    +// spacing //
    +$gutter-std: 5%;
    +$gutter-sm: $gutter-std / 2;
    +
    +// font //
    +$font-std: helvetica;
    +$font-secondary: monospace;
    +
    +// other //
    +$btn-radius: 5px;
    diff --git a/app/scss/main.scss b/app/scss/main.scss
    new file mode 100644
    index 00000000..69da6562
    --- /dev/null
    +++ b/app/scss/main.scss
    @@ -0,0 +1,24 @@
    +// ::: Reset :::: //
    +@import "./lib/base/reset";
    +
    +// bring in bootstrap componentes
    +@import "./lib/bootstrap/custom-bootstrap";
    +//  ::::: Base and Theme::::::::://
    +@import "lib/theme/vars";
    +@import "./lib/base/base";
    +// ::::: Layout :::::: //
    +@import "./lib/layout/header";
    +@import "./lib/layout/layout";
    +@import "./lib/layout/footer";
    +
    +@import "../view/landing/landing";
    +
    +// ::::: Components :::::://
    +@import "../component/landing/login/login";
    +@import "../component/landing/signup/signup";
    +@import "../component/gallery/create-gallery/create-gallery";
    +@import "../component/gallery/edit-gallery/edit-gallery";
    +@import "../component/gallery/gallery-item/gallery-item";
    +@import "../component/gallery/thumbnail/thumbnail";
    +@import "../component/gallery/thumbnail-container/thumbnail-container";
    +@import "../component/gallery/upload-pic/upload-pic";
    diff --git a/app/service/auth-service.js b/app/service/auth-service.js
    new file mode 100644
    index 00000000..41fe4c15
    --- /dev/null
    +++ b/app/service/auth-service.js
    @@ -0,0 +1,88 @@
    +'use strict';
    +
    +module.exports = ['$q', '$log', '$http', '$window', authService];
    +
    +function authService($q, $log, $http, $window) {
    +  $log.debug('authService');
    +
    +  let service = {};
    +  let token = null;
    +
    +  function setToken(_token) {
    +    $log.debug('authService.setToken');
    +
    +    if (! _token) {
    +      return $q.reject(new Error('no token'));
    +    };
    +
    +    $window.localStorage.setItem('token', _token);
    +    token = _token;
    +    return $q.resolve(token);
    +  }
    +
    +  service.getToken = function() {
    +    $log.debug('authService.getToken');
    +    if (token) {
    +      return $q.resolve(token);
    +    };
    +
    +    token = $window.localStorage.getItem('token');
    +    if (token) return $q.resolve(token);
    +    return $q.reject(new Error('token not found'));
    +  };
    +
    +  service.signup = function(user) {
    +    $log.debug('authService.signup');
    +
    +    let url = `${__API_URL__}/api/signup`;
    +    let config = {
    +      headers: {
    +        'Content-Type': 'application/json',
    +        'Accept': 'application/json'
    +      }
    +    };
    +
    +    return $http.post(url, user, config)
    +    .then( res => {
    +      $log.log('success:', res.data);
    +      return setToken(res.data);
    +    })
    +    .catch( err => {
    +      $log.error('failure:', err.message);
    +      return $q.reject(err);
    +    });
    +  };
    +
    +  service.logout = function() {
    +    $log.debug('authService.logout');
    +
    +    $window.localStorage.removeItem('token');
    +    token = null;
    +    return $q.resolve();
    +  };
    +
    +  service.login = function(user) {
    +    $log.debug('authService.login');
    +
    +    let url = `${__API_URL__}/api/login`;
    +    let base64 = $window.btoa(`${user.username}:${user.password}`);
    +    let config = {
    +      headers: {
    +        Accept: 'application/json',
    +        Authorization: `Basic ${base64}`
    +      }
    +    };
    +
    +    return $http.get(url, config)
    +    .then( res => {
    +      $log.log('success', res.data);
    +      return setToken(res.data);
    +    })
    +    .catch( err => {
    +      $log.error(err.message);
    +      return $q.reject(err);
    +    });
    +  };
    +
    +  return service;
    +};
    diff --git a/app/service/gallery-service.js b/app/service/gallery-service.js
    new file mode 100644
    index 00000000..355f1c1e
    --- /dev/null
    +++ b/app/service/gallery-service.js
    @@ -0,0 +1,142 @@
    +'use strict';
    +
    +module.exports = ['$q', '$log', '$http', 'authService', galleryService];
    +
    +function galleryService($q, $log, $http, authService) {
    +  $log.debug('galleryService');
    +
    +  let service = {};
    +  service.galleries = [];
    +
    +  service.createGallery = function(gallery) {
    +    $log.debug('createGallery');
    +
    +    return authService.getToken()
    +    .then( token => {
    +      let url = `${__API_URL__}/api/gallery`;
    +      let config = {
    +        headers: {
    +          Accept: 'application/json',
    +          'Content-Type': 'application/json',
    +          Authorization: `Bearer ${token}`
    +        }
    +      };
    +
    +      return $http.post(url, gallery, config);
    +    })
    +    .then( res => {
    +      $log.log('gallery created');
    +      let gallery = res.data;
    +      service.galleries.unshift(gallery);
    +      return gallery;
    +    })
    +    .catch( err => {
    +      $log.error(err.message);
    +      return $q.reject(err);
    +    });
    +  };
    +
    +  service.deleteGalleries = function(galleryID, galleryData) {
    +    $log.debug('galleryService.deleteGalleries');
    +    return authService.getToken()
    +    .then( token => {
    +      let url = `${__API_URL__}/api/gallery/${galleryID}`;
    +      let config = {
    +        headers: {
    +          Accept: 'application/json',
    +          Authorization: `Bearer ${token}`
    +        }
    +      };
    +      // TODO: create a delete request
    +    });
    +  };
    +
    +  service.fetchGalleries = function() {
    +    $log.debug('galleryService.fetchGalleries');
    +
    +    return authService.getToken()
    +    .then( token => {
    +      let url = `${__API_URL__}/api/gallery`;
    +      let config = {
    +        headers: {
    +          Accept: 'application/json',
    +          Authorization: `Bearer ${token}`
    +        }
    +      };
    +
    +      return $http.get(url, config);
    +    })
    +    .then( res => {
    +      $log.log('galleries retrieved');
    +      service.galleries = res.data;
    +      return service.galleries;
    +    })
    +    .catch( err => {
    +      $log.error(err.mesage);
    +      return $q.reject(err);
    +    });
    +  };
    +
    +  service.updateGallery = function(galleryID, galleryData) {
    +    $log.debug('galleryService.updateGallery');
    +
    +    return authService.getToken()
    +    .then( token => {
    +      let url = `${__API_URL__}/api/gallery/${galleryID}`;
    +      let config = {
    +        headers: {
    +          Accept: 'application/json',
    +          Authorization: `Bearer ${token}`,
    +          'Content-Type': 'application/json'
    +        }
    +      };
    +
    +      return $http.put(url, galleryData, config);
    +    })
    +    .then( res => {
    +      for (let i = 0; i < service.galleries.length; i++) {
    +        let current = service.galleries[i];
    +        if (current._id === galleryID) {
    +          service.galleries[i] = res.data;
    +          break;
    +        }
    +      }
    +      return res.data;
    +    })
    +    .catch( err => {
    +      $log.error(err.message);
    +      return $q.reject(err);
    +    });
    +  };
    +
    +  service.deleteGallery = function(galleryID) {
    +    $log.debug('service.deleteGallery');
    +
    +    return authService.getToken()
    +    .then( token => {
    +      let url = `${__API_URL__}/api/gallery/${galleryID}`;
    +      let config = {
    +        headers: {
    +          Authorization: `Bearer ${token}`
    +        }
    +      };
    +
    +      return $http.delete(url, config);
    +    })
    +    .then( res => {
    +      for (let i=0; i < service.galleries.length; i++) {
    +        let current = service.galleries[i];
    +        if (current._id === galleryID) {
    +          service.galleris.splice(i, 1);
    +          break;
    +        }
    +      }
    +    })
    +    .catch( err => {
    +      $log.error(err.message);
    +      return $q.reject(err);
    +    });
    +  };
    +
    +  return service;
    +};
    diff --git a/app/service/pic-service.js b/app/service/pic-service.js
    new file mode 100644
    index 00000000..18649794
    --- /dev/null
    +++ b/app/service/pic-service.js
    @@ -0,0 +1,45 @@
    +'use strict';
    +
    +module.exports = ['$q', '$log', '$http', 'Upload', 'authService', picService];
    +
    +function picService($q, $log, $http, Upload, authService) {
    +  $log.debug('picService');
    +
    +  let service = {};
    +
    +  service.uploadGalleryPic = function(galleryData, picData) {
    +    $log.debug('service.uploadGalleryPic');
    +    $log.log('ugp galleryData', galleryData);
    +    $log.log('ugp picData', picData);
    +    return authService.getToken()
    +    .then( token => {
    +      let url = `${__API_URL__}/api/gallery/${galleryData._id}/pic`;
    +      let headers = {
    +        Authorization: `Bearer ${token}`,
    +        Accept: 'application/json'
    +      };
    +
    +      return Upload.upload({
    +        url,
    +        headers,
    +        method: 'POST',
    +        data: {
    +          name: picData.name,
    +          desc: picData.desc,
    +          file: picData.file
    +        }
    +      });
    +    })
    +    .then( res => {
    +      galleryData.pics.unshift(res.data);
    +      $log.log('res.data', res.data);
    +      return res.data;
    +    })
    +    .catch( err => {
    +      $log.log(err.message);
    +      return $q.reject(err);
    +    })
    +  }
    +
    +  return service;
    +}
    diff --git a/app/view/home/_home.scss b/app/view/home/_home.scss
    new file mode 100644
    index 00000000..8b137891
    --- /dev/null
    +++ b/app/view/home/_home.scss
    @@ -0,0 +1 @@
    +
    diff --git a/app/view/home/home-controller.js b/app/view/home/home-controller.js
    new file mode 100644
    index 00000000..737d2bc5
    --- /dev/null
    +++ b/app/view/home/home-controller.js
    @@ -0,0 +1,29 @@
    +'use strict';
    +
    +require('./_home.scss');
    +
    +module.exports = ['$log', '$rootScope', 'galleryService', HomeController];
    +
    +function HomeController($log, $rootScope, galleryService) {
    +  $log.debug('HomeController');
    +  this.galleries = [];
    +  this.fetchGalleries = function() {
    +    galleryService.fetchGalleries()
    +    .then( galleries => {
    +      this.galleries = galleries;
    +      this.currentGallery = galleries[0];
    +    });
    +  };
    +
    +  this.galleryDeleteDone = function(gallery) {
    +    if (this.currentGallery._id === gallery._id) {
    +      this.currentGallery = null;
    +    }
    +  };
    +
    +  this.fetchGalleries();
    +
    +  $rootScope.$on('$locationChangeSuccess', () => {
    +    this.fetchGalleries();
    +  })
    +};
    diff --git a/app/view/home/home.html b/app/view/home/home.html
    new file mode 100644
    index 00000000..6ce3aff6
    --- /dev/null
    +++ b/app/view/home/home.html
    @@ -0,0 +1,60 @@
    +
    + + +
    +
      Galleries + +
    + + + + +
    diff --git a/app/view/landing/_landing.scss b/app/view/landing/_landing.scss new file mode 100644 index 00000000..53563e42 --- /dev/null +++ b/app/view/landing/_landing.scss @@ -0,0 +1,18 @@ +@import '../../scss/lib/theme/vars'; +.join-inner { + + div:last-child { + margin-top: 1.75vw; + text-align: right; + font-size: 2vw; + + p { + display: inline; + margin-right: 1vw; + } + + a { + margin-right: 1.5vw; + } + } +} diff --git a/app/view/landing/landing-controller.js b/app/view/landing/landing-controller.js new file mode 100644 index 00000000..783dfec3 --- /dev/null +++ b/app/view/landing/landing-controller.js @@ -0,0 +1,10 @@ +'use strict'; + +require('./_landing.scss'); + +module.exports = ['$log', '$location', '$rootScope', 'authService', LandingController]; + +function LandingController($log, $location, authService) { + let url = $location.url(); + this.showSignup = url === '/join#signup' || url === '/join'; +}; diff --git a/app/view/landing/landing.html b/app/view/landing/landing.html new file mode 100644 index 00000000..e72dd9ae --- /dev/null +++ b/app/view/landing/landing.html @@ -0,0 +1,47 @@ +
    +
    +
    + +

    already a member?

    + + sign in here. + +
    +
    + +
    +
    + +

    want to sign up?

    + + sign up here. + +
    +
    +
    + + +
    +
    + +

    + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

    +

    + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

    +

    + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

    +

    + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +

    +
    +
    + + +
    diff --git a/gulpfile.js b/gulpfile.js new file mode 100755 index 00000000..24190d6d --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,23 @@ +'use strict'; + +const gulp = require('gulp'); +const eslint = require('gulp-eslint'); +const mocha = require('gulp-mocha'); + +gulp.task('test', function () { + gulp.src('./test/*-test.js', { read: false}) + .pipe(mocha({reporter: 'spec'})); +}); + +gulp.task('lint', function() { + return gulp.src(['**/*.js', '!node_modules']) + .pipe(eslint()) + .pipe(eslint.format()) + .pipe(eslint.failAfterError()); +}); + +gulp.task('dev', function() { + gulp.watch(['**/*.js', '!node_modules'], ['lint', 'test']); +}); + +gulp.task('default', ['dev']); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 00000000..f6f6d029 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,29 @@ +const webpackConfig = require('./webpack.config.js'); +delete webpackConfig.entry; + +module.exports = function(config) { + config.set({ + webpack: webpackConfig, + basePath: '', + frameworks: ['jasmine'], + files: [ + 'app/entry.js', + 'test/**/*-test.js', + 'node_modules/angular-mocks/angular-mocks.js' + ], + exclude: [ + ], + preprocessors: { + 'test/**/*-test.js': ['webpack'], + 'app/entry.js': ['webpack'] + }, + reporters: ['mocha'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + autoWatch: true, + browsers: ['Chrome'], + singleRun: false, + concurrency: Infinity + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 00000000..d720e561 --- /dev/null +++ b/package.json @@ -0,0 +1,59 @@ +{ + "name": "glgram", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "./node_modules/webpack/bin/webpack.js", + "build-watch": "./node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot", + "lint": "eslint .", + "test": "./node_modules/karma/bin/karma start --single-run", + "test-watch": "./node_modules/karma/bin/karma start", + "start": "node server.js", + "heroku-postbuild": "webpack -p --progress" + }, + "keywords": [], + "author": "Gary Lundgren", + "license": "ISC", + "dependencies": { + "angular": "^1.6.3", + "angular-animate": "^1.6.3", + "angular-route": "^1.6.3", + "angular-touch": "^1.6.3", + "angular-ui-bootstrap": "^2.5.0", + "angular-ui-router": "^0.4.2", + "babel-core": "^6.24.0", + "babel-loader": "^6.4.1", + "babel-preset-es2015": "^6.24.0", + "bootstrap-sass": "^3.3.7", + "camelcase": "^4.0.0", + "clean-webpack-plugin": "^0.1.16", + "css-loader": "^0.27.3", + "dotenv": "^4.0.0", + "express": "^4.15.2", + "extract-text-webpack-plugin": "^2.1.0", + "file-loader": "^0.10.1", + "html-loader": "^0.4.5", + "html-webpack-plugin": "^2.28.0", + "ng-file-upload": "^12.2.13", + "node-sass": "^4.5.1", + "pascalcase": "^0.1.1", + "resolve-url-loader": "^2.0.2", + "sass-loader": "^6.0.3", + "style-loader": "^0.16.0", + "ui-router": "^1.0.0-alpha.3", + "url-loader": "^0.5.8", + "webpack": "^2.3.2" + }, + "devDependencies": { + "angular-mocks": "^1.6.3", + "jasmine-core": "^2.5.2", + "karma": "^1.5.0", + "karma-chrome-launcher": "^2.0.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": "^2.4.2" + } +} diff --git a/server.js b/server.js new file mode 100644 index 00000000..907833e4 --- /dev/null +++ b/server.js @@ -0,0 +1,11 @@ +'use strict'; + +const express = require('express'); +const app = express(); +const PORT = process.env.PORT || 8080; + +app.use(express.static(`${__dirname}/build`)); + +app.listen(PORT, function() { + console.log('server up: ' PORT); +}) diff --git a/test/auth-service-test.js b/test/auth-service-test.js new file mode 100644 index 00000000..754a2a7b --- /dev/null +++ b/test/auth-service-test.js @@ -0,0 +1,31 @@ +'use strict'; + +describe('Auth Service', function() { + + beforeEach( () => { + angular.mock.module('glgram'); + angular.mock.inject(($rootScope, authService, $window, $httpBackend) => { + this.$window = $window; + this.$rootScope = $rootScope; + this.authService = authService; + this.$httpBackend = $httpBackend; + }); + }); + + describe('authService.getToken', () => { + it('should return a token()', () => { + this.authService.token = null; + this.$window.localStorage.setItem('token', 'test token'); + + this.authService.getToken() + .then( token => { + expect(token).toEqual('test token'); + }) + .catch( err => { + expect(err).toEqual(null); + }); + + this.$rootScope.$apply(); + }); + }); +}); diff --git a/test/edit-gallery-component-test.js b/test/edit-gallery-component-test.js new file mode 100644 index 00000000..9f429638 --- /dev/null +++ b/test/edit-gallery-component-test.js @@ -0,0 +1,78 @@ +'use strict'; + +describe('Edit Gallery Component', function() { + + beforeEach(() => { + angular.mock.module('glgram'); + angular.mock.inject(($rootScope, $componentController, $httpBackend, authService) => { + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + this.authService = authService; + }); + }); + + + + it('should contain the proper component bindings', () => { + let mockBindings = { + gallery: { + name: 'test gallery name', + desc: 'test gallery description' + } + }; + + let editGalleryCtrl = this.$componentController('editGallery', null, mockBindings); + expect(editGalleryCtrl.gallery.name).toEqual(mockBindings.gallery.name); + expect(editGalleryCtrl.gallery.desc).toEqual(mockBindings.gallery.desc); + + this.$rootScope.$apply(); + }); + + + describe('editGalleryCtrl.updateGallery()', () => { + it('should make a valid PUT request', () => { + let url = 'http://localhost:5000/api/gallery/12345'; + let headers = { + Authorization: 'Bearer test token', + Accept: 'application/json', + 'Content-Type': 'application/json' + }; + + this.$httpBackend.expectPUT(url, { + _id: '12345', + name: 'updated name', + desc: 'updated description' + }, headers).respond(200); + + let mockBindings = { + gallery: { + _id: '12345', + name: 'updated name', + desc: 'updated description' + }, + }; + + let editGalleryCtrl = this.$componentController('editGallery', null, mockBindings); + editGalleryCtrl.gallery.name = 'updated name'; + editGalleryCtrl.gallery.desc = 'updated description'; + editGalleryCtrl.updateGallery(); + + expect(editGalleryCtrl.gallery.name).toEqual(mockBindings.gallery.name); + expect(editGalleryCtrl.gallery.desc).toEqual(mockBindings.gallery.desc); + + this.$httpBackend.flush(); + this.$rootScope.$apply(); + }); + }); +}); + + + + + + + + + + // it('should contain the proper component') diff --git a/test/example-test.js b/test/example-test.js new file mode 100644 index 00000000..23ae19f5 --- /dev/null +++ b/test/example-test.js @@ -0,0 +1,7 @@ +'use strict'; + +describe('Example Test', function() { + it('should pass this test', () => { + expect(true).toEqual(true); + }); +}); diff --git a/test/gallery-item-component-test.js b/test/gallery-item-component-test.js new file mode 100644 index 00000000..df4409e8 --- /dev/null +++ b/test/gallery-item-component-test.js @@ -0,0 +1,62 @@ +'use strict'; + +describe('Gallery Item Component', function() { + beforeEach(() => { + angular.mock.module('glgram'); + angular.mock.inject(($rootScope, $componentController, $httpBackend, authService) => { + this.$rootScope = $rootScope; + this.$componentController = $componentController; + this.$httpBackend = $httpBackend; + this.authService = authService; + }); + }); + + describe('galleryItemCtrl.deleteDone()', () => { + it('should call deleteDone', () => { + let mockBindings = { + gallery: { + _id: '12345', + name: 'test name', + desc: 'test description', + pics: [] + }, + deleteDone: function(data) { + expect(data.galleryData._id).toEqual('12345'); + } + }; + + let galleryItemCtrl = this.$componentController('galleryItem', null, mockBindings); + galleryItemCtrl.deleteDone({galleryData: galleryItemCtrl.gallery}); + + this.$rootScope.$apply(); + }); + }); + + it('should call deleteDone with gallery after galleryDelete', () => { + let url = 'http://localhost:5000/api/gallery/12345'; + let headers = { + Authorization: 'Bearer test token', + Accept: 'application/json, text/plain, */*' + }; + + let mockBindings = { + gallery: { + _id: '12345', + name: 'test name', + desc: 'test description', + pics: [] + }, + deleteDone: function(data) { + expect(data.galleryData._id).toEqual(mockBindings.gallery._id); + } + }; + + this.$httpBackend.expectDELETE(url, headers).respond(204); + + let galleryItemCtrl = this.$componentController('galleryItem', null, mockBindings); + galleryItemCtrl.deleteGallery(); + + this.$httpBackend.flush(); + this.$rootScope.$apply(); + }); +}); diff --git a/test/gallery-service-test.js b/test/gallery-service-test.js new file mode 100644 index 00000000..a5153017 --- /dev/null +++ b/test/gallery-service-test.js @@ -0,0 +1,41 @@ +'use strict'; + +describe('Gallery Service', function() { + + beforeEach(() => { + angular.mock.module('glgram'); + angular.mock.inject(($rootScope, authService, galleryService, $window, $httpBackend) => { + this.$window = $window; + this.$rootScope = $rootScope; + this.authService = authService; + this.galleryService = galleryService; + this.$httpBackend = $httpBackend; + }); + }); + + describe('galleryService.createGallery', () => { + it('should create a new gallery', () => { + let galleryData = { + name: 'example gallery', + desc: 'example description' + }; + let headers = { + 'Content-Type': 'application/json', + Accept: 'application/json', + Authorization: 'Bearer test token' + }; + + this.$httpBackend.expectPOST('http://localhost:5000/api/gallery', galleryData, headers) + .respond(200, { + username: 'testuser', + name: galleryData.name, + desc: galleryData.desc, + pics: [] + }); + + this.galleryService.createGallery(galleryData); + this.$httpBackend.flush(); + this.$rootScope.$apply(); + }); + }); +}); diff --git a/trial.html b/trial.html new file mode 100644 index 00000000..e69de29b diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..129cad88 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,50 @@ +'use strict'; + +const dotenv = require('dotenv'); +const webpack = require('webpack'); +const HTMLPlugin = require('html-webpack-plugin'); +const ExtractTextPlugin = require('extract-text-webpack-plugin'); + +const production = process.env.NODE_ENV === 'production'; + +dotenv.load(); + +module.exports = { + devtool: 'eval', + entry: `${__dirname}/app/entry.js`, + output: { + filename: 'bundle.js', + path: `${__dirname}/build` + }, + plugins: [ + new HTMLPlugin({ + template: `${__dirname}/app/index.html` + }), + new ExtractTextPlugin('bundle.css'), + new webpack.DefinePlugin({ + __API_URL__: JSON.stringify(process.env.API_URL), + __DEBUG__: JSON.stringify(!production) + }) + ], + module: { + rules: [ + { + test: /\.js$/, + exclude: /node_modules/, + loader: 'babel-loader' + }, + { + test: /\.html$/, + loader: 'html-loader' + }, + { + test: /\.scss$/, + loader: ExtractTextPlugin.extract(['css-loader', 'sass-loader']) + }, + { + test: /\.png$/, + loader: 'url-loader' + } + ] + } +};