├── .bowerrc ├── .buildignore ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .travis.yml ├── .yo-rc.json ├── README.md ├── bower.json ├── build ├── config.js ├── errors.js └── injector │ ├── body.html │ └── head.html ├── client ├── .htaccess ├── .jshintrc ├── app │ ├── app.js │ ├── factories │ │ └── logger │ │ │ └── logger.factory.js │ ├── movies │ │ ├── movies-theatres │ │ │ ├── movies-theatres.controller.js │ │ │ ├── movies-theatres.controller.spec.js │ │ │ └── movies-theatres.html │ │ ├── movies.controller.js │ │ ├── movies.controller.spec.js │ │ ├── movies.html │ │ └── movies.js │ └── theatres │ │ ├── theatres-movies │ │ ├── theatres-movies.controller.js │ │ ├── theatres-movies.controller.spec.js │ │ └── theatres-movies.html │ │ ├── theatres.controller.js │ │ ├── theatres.controller.spec.js │ │ ├── theatres.html │ │ └── theatres.js ├── assets │ └── images │ │ └── yeoman.png ├── favicon.ico ├── index.html ├── robots.txt ├── styles │ ├── app.scss │ └── reset.scss └── vendor │ └── socket.io.js ├── e2e └── main │ ├── main.po.js │ └── main.spec.js ├── gulpfile.js ├── karma.conf.js ├── package.json ├── protractor.conf.js ├── requirements ├── diagrams │ ├── v1.png │ └── v2.png ├── list-of-resources.txt ├── mockups │ ├── 1-movies.png │ ├── 2-theatres.png │ ├── 3-showtimes.png │ ├── annotated-1-movies.png │ ├── annotated-2-theatres.png │ └── annotated-3-showtimes.png └── queries │ ├── 1-setup.sql │ └── 2-queries.sql ├── servers.runner.js └── servers └── server ├── .jshintrc ├── .jshintrc-spec ├── api ├── movie │ └── index.js └── theatre │ └── index.js ├── app.js ├── components └── errors │ └── index.js ├── config ├── environment │ ├── development.js │ ├── index.js │ ├── production.js │ └── test.js ├── express.js ├── local.env.js └── local.env.sample.js ├── data ├── db.js ├── mocks.js └── sql2sql-stb.sqlite ├── routes.js └── views └── 404.html /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "client/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | public 3 | .tmp 4 | .sass-cache 5 | .idea 6 | client/bower_components 7 | dist 8 | /server/config/local.env.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | - '0.11' 5 | before_script: 6 | - npm install -g bower grunt-cli 7 | - gem install sass 8 | - bower install 9 | services: mongodb -------------------------------------------------------------------------------- /.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-soa": { 3 | "insertRoutes": true, 4 | "registerRoutesFile": "servers/server/routes.js", 5 | "routesNeedle": "// Insert routes below", 6 | "routesBase": "/api/", 7 | "pluralizeRoutes": true, 8 | "insertSockets": true, 9 | "registerSocketsFile": "servers/server/config/socketio.js", 10 | "socketsNeedle": "// Insert sockets below", 11 | "filters": { 12 | "js": true, 13 | "html": true, 14 | "sass": true, 15 | "uirouter": true, 16 | "bootstrap": true, 17 | "uibootstrap": false 18 | } 19 | }, 20 | "generator-ng-component": { 21 | "routeDirectory": "client/app/", 22 | "directiveDirectory": "client/app/", 23 | "filterDirectory": "client/app/", 24 | "serviceDirectory": "client/app/", 25 | "basePath": "client", 26 | "moduleName": "", 27 | "filters": [ 28 | "uirouter" 29 | ], 30 | "extensions": [ 31 | "js", 32 | "html", 33 | "scss" 34 | ], 35 | "directiveSimpleTemplates": "", 36 | "directiveComplexTemplates": "", 37 | "filterTemplates": "", 38 | "serviceTemplates": "", 39 | "factoryTemplates": "", 40 | "controllerTemplates": "", 41 | "decoratorTemplates": "", 42 | "providerTemplates": "", 43 | "routeTemplates": "" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sequel to SQL - Watch Us Build 2 | 3 | *We changed the name of this series to "Watch Us Build," so you may see an occasional reference to its former name ("Soup to Bits") in this repository.* 4 | 5 | ## Pre-reqs 6 | 1. node and npm (https://nodejs.org) 7 | 2. gulp and bower (`npm install bower gulp -g`) 8 | 3. compass: `gem install compass` 9 | 10 | ## Installation 11 | 1. Clone repo: `git clone https://github.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL.git` 12 | 2. Install dependencies: `npm install && bower install` 13 | 14 | ## Run locally 15 | 1. `gulp serve` 16 | 17 | _This will get your local server running on port 3000 (http://localhost:3000)_ 18 | 19 | 20 | ### About this project 21 | This project was initially scaffolded with the [generator-soa](https://www.npmjs.com/package/generator-soa). Feel free to visit their page in case you run into any problems, chances are somebody else has ran into similar problems :) 22 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movies", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "angular": ">=1.2.*", 6 | "json3": "~3.3.1", 7 | "es5-shim": "~3.0.1", 8 | "jquery": "~1.11.0", 9 | "angular-sanitize": ">=1.2.*", 10 | "lodash": "~2.4.1", 11 | "angular-ui-router": "~0.2.10", 12 | "restangular": "~1.4.0" 13 | }, 14 | "devDependencies": { 15 | "angular-mocks": ">=1.2.*", 16 | "angular-scenario": ">=1.2.*" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build/config.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | app_file_name: 'app.js', 3 | modules_file_name: 'bundle.js', 4 | vendor_file_name: 'vendor.js', 5 | bower_file_name: 'bower_components.min.js', 6 | css_file_name: 'app.css', 7 | jade_file_name: 'cached_jade_templates.js', 8 | html_file_name: 'cached_html_templates.js', 9 | templates_file_name: 'cached_templates.js', 10 | module_name: 'moviesApp', 11 | client: { 12 | path: './client/', 13 | 14 | 15 | specs: ['./client/app/app.js', './client/{app,components}/**/*.js', './client/{app,components}/**/*.spec.js', './client/{app,components}/**/*.html'], 16 | scripts: { 17 | root: './client/app/app.js', 18 | modules: ['./client/{app,components}/**/*.js','!./client/{app,components}/**/*.spec.js', '!./client/app/app.js'], 19 | all: ['./client/{app,components}/**/*.js', '!./client/{app,components}/**/*.spec.js'], 20 | 21 | 22 | }, 23 | styles: { 24 | stylus: './client/styles/app.styl', 25 | less: './client/styles/app.less', 26 | sass: './client/styles/app.scss', 27 | css: ['./client/styles/**/*.css'], 28 | }, 29 | index: './client/index.html', 30 | templates:{ 31 | jade: './client/{app,components}/**/*.jade', 32 | html: ['./client/{app,components}/**/*.html'] 33 | }, 34 | bower: './client/bower_components/', 35 | vendor: ['./client/vendor/**/*.js'], 36 | images: ['./client/assets/images/**/*'] 37 | }, 38 | build: { 39 | path: './.tmp/', 40 | stylePath: './.tmp/styles/', 41 | styles: './.tmp/styles/*.css', 42 | scriptPath: './.tmp/scripts/', 43 | scripts: ['./tmp/app/app.js', './.tmp/{app,components}/**/*.js'], 44 | templatePath: './.tmp/templates/', 45 | templates: './.tmp/templates/*.js', 46 | dist: './dist/public', 47 | images: './.tmp/images/**/*', 48 | fonts: './.tmp/fonts' 49 | }, 50 | dist: { 51 | path: './dist/', 52 | images: './dist/assets/images/', 53 | scriptPath: './dist/scripts/', 54 | scripts: './dist/scritps/*.js', 55 | stylePath: './dist/styles/', 56 | styles: './dist/styles/*.css', 57 | bower: './dist/bower_components/', 58 | templatePath: './dist/templates/', 59 | templates: './dist/templates/*.js', 60 | index: './dist/index.html' 61 | }, 62 | server: { 63 | path: './servers/', 64 | base: './servers/server/app.js', 65 | all: ['./servers/**/app.js', '!./servers/server/app.js'] 66 | } 67 | } 68 | module.exports = config; -------------------------------------------------------------------------------- /build/errors.js: -------------------------------------------------------------------------------- 1 | // # Error handling setup 2 | // # See: http://www.artandlogic.com/blog/2014/05/error-handling-in-gulp/ 3 | 4 | var fatalLevel = require('yargs').argv.fatal; 5 | 6 | var ERROR_LEVELS = ['error', 'warning']; 7 | 8 | module.exports = { 9 | 10 | // # Convenience handlers to be used in gulp .on('error', callback) 11 | onError: function(error){ return handleError.call(this, 'error', error); }, 12 | onWarning: function(error){ return handleError.call(this, 'warning', error); } 13 | }; 14 | 15 | // # Log error and kill process 16 | var ndleError = function(level, error){ 17 | 18 | console.log( error.message ); 19 | console.log( "Level: " + level + " isFatal" + isFatal(level) ); 20 | if ( isFatal(level) ){ 21 | process.exit(1); 22 | } 23 | }; 24 | 25 | // # Determine if the error is fatal. Returns true if the given level is 26 | // # equal to or more severe than the given fatality level (via cli arg). 27 | // # Defaults to fatality level to error if not set by user or some task default 28 | var isFatal = function(level) { 29 | return ERROR_LEVELS.indexOf(level) <= ERROR_LEVELS.indexOf(fatalLevel || 'error'); 30 | }; -------------------------------------------------------------------------------- /build/injector/body.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /build/injector/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /client/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /client/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": true, 3 | "camelcase": true, 4 | "curly": true, 5 | "eqeqeq": true, 6 | "es3": false, 7 | "forin": true, 8 | "freeze": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": "nofunc", 12 | "newcap": true, 13 | "noarg": true, 14 | "noempty": true, 15 | "nonbsp": true, 16 | "nonew": true, 17 | "plusplus": false, 18 | "quotmark": "single", 19 | "undef": true, 20 | "unused": false, 21 | "strict": false, 22 | "maxparams": 10, 23 | "maxdepth": 5, 24 | "maxstatements": 40, 25 | "maxcomplexity": 8, 26 | "maxlen": 250, 27 | 28 | "asi": false, 29 | "boss": false, 30 | "debug": false, 31 | "eqnull": true, 32 | "esnext": false, 33 | "evil": false, 34 | "expr": false, 35 | "funcscope": false, 36 | "globalstrict": false, 37 | "iterator": false, 38 | "lastsemic": false, 39 | "laxbreak": false, 40 | "laxcomma": false, 41 | "loopfunc": true, 42 | "maxerr": false, 43 | "moz": false, 44 | "multistr": false, 45 | "notypeof": false, 46 | "proto": false, 47 | "scripturl": false, 48 | "shadow": false, 49 | "sub": true, 50 | "supernew": false, 51 | "validthis": false, 52 | "noyield": false, 53 | 54 | "browser": true, 55 | "node": true, 56 | 57 | "globals": { 58 | "jQuery": true, 59 | "angular": false, 60 | "console": true, 61 | "$": false, 62 | "_": true, 63 | "moment": true, 64 | "describe": true, 65 | "beforeEach": true, 66 | "module": true, 67 | "inject": true, 68 | "it": true, 69 | "expect": true, 70 | "browser": true, 71 | "element": true, 72 | "by": true 73 | } 74 | } -------------------------------------------------------------------------------- /client/app/app.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | 'use strict'; 3 | angular 4 | .module('moviesApp', [ 5 | 6 | 'ngSanitize', 7 | 'restangular', 8 | 'ui.router' 9 | 10 | ]) 11 | .constant('serverBaseUrl', 'http://localhost:9000') 12 | .constant('serverUrl', 'http://localhost:9000/api/') 13 | .config( appConfig ); 14 | 15 | appConfig.$inject = [ 16 | 'RestangularProvider', 17 | '$stateProvider', 18 | '$urlRouterProvider', 19 | '$locationProvider']; 20 | function appConfig(RestangularProvider, $stateProvider, $urlRouterProvider, $locationProvider) { 21 | $urlRouterProvider 22 | .when('', '/movies') 23 | .otherwise('/'); 24 | 25 | RestangularProvider.setBaseUrl('http://localhost:9000/api/'); 26 | RestangularProvider.setRestangularFields({ 27 | id: '_id', 28 | route: 'restangularRoute', 29 | selfLink: 'self.href' 30 | }); 31 | 32 | RestangularProvider.setResponseExtractor(function extractResponse(serverResponse) { 33 | return serverResponse.data; 34 | }); 35 | 36 | // $locationProvider.html5Mode(true); 37 | } 38 | 39 | 40 | }).call(this); 41 | -------------------------------------------------------------------------------- /client/app/factories/logger/logger.factory.js: -------------------------------------------------------------------------------- 1 | // ;(function() { 2 | // 'use strict'; 3 | // angular 4 | // .module('moviesApp') 5 | // .factory('logger', logger); 6 | // function logger() { 7 | // var logIt; 8 | // toastr.options = { 9 | // "closeButton": true, 10 | // "positionClass": "toast-bottom-right", 11 | // "timeOut": "3000" 12 | // }; 13 | // logIt = function(message, type) { 14 | // return toastr[type](message); 15 | // }; 16 | // return { 17 | // log: function(message) { 18 | // logIt(message, 'info'); 19 | // }, 20 | // logWarning: function(message) { 21 | // logIt(message, 'warning'); 22 | // }, 23 | // logSuccess: function(message) { 24 | // logIt(message, 'success'); 25 | // }, 26 | // logError: function(message) { 27 | // logIt(message, 'error'); 28 | // } 29 | // }; 30 | // } 31 | // }).call(this); 32 | 33 | -------------------------------------------------------------------------------- /client/app/movies/movies-theatres/movies-theatres.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular 4 | .module('moviesApp') 5 | .controller('MoviesTheatresCtrl', function (movie, theatres) { 6 | 7 | var vm = this; 8 | vm.movie = movie; 9 | vm.theatres = theatres; 10 | 11 | }); 12 | -------------------------------------------------------------------------------- /client/app/movies/movies-theatres/movies-theatres.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MoviesTheatresCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('moviesApp')); 7 | 8 | var MoviesTheatresCtrl, scope; 9 | 10 | // Initialize the controller and a mock scope 11 | beforeEach(inject(function ($controller, $rootScope) { 12 | scope = $rootScope.$new(); 13 | MoviesTheatresCtrl = $controller('MoviesTheatresCtrl', { 14 | $scope: scope 15 | }); 16 | })); 17 | 18 | it('should ...', function () { 19 | expect(1).toEqual(1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /client/app/movies/movies-theatres/movies-theatres.html: -------------------------------------------------------------------------------- 1 |

2 |

List of Theatres Playing Movie

3 | 4 |
6 | 7 |
8 |
9 |
10 |
11 | 12 |
13 |

14 | 15 |

 

16 | 17 |
18 | 21 |
22 | 23 |
24 |
25 |
26 | -------------------------------------------------------------------------------- /client/app/movies/movies.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular 4 | .module('moviesApp') 5 | .controller('MoviesCtrl', function MoviesCtrl(movies) { 6 | 7 | var vm = this; 8 | vm.movies = movies; 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /client/app/movies/movies.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MoviesCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('moviesApp')); 7 | 8 | var MoviesCtrl, scope; 9 | 10 | // Initialize the controller and a mock scope 11 | beforeEach(inject(function ($controller, $rootScope) { 12 | scope = $rootScope.$new(); 13 | MoviesCtrl = $controller('MoviesCtrl', { 14 | $scope: scope 15 | }); 16 | })); 17 | 18 | it('should ...', function () { 19 | expect(1).toEqual(1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /client/app/movies/movies.html: -------------------------------------------------------------------------------- 1 |
2 |
4 | 5 |
6 | 7 | 8 |

9 | 12 |

13 | 14 |

20 |

21 | 22 | 24 | See Theatres 25 | 26 |
27 |
28 |
29 | -------------------------------------------------------------------------------- /client/app/movies/movies.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('moviesApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('movies', { 7 | url: '/movies', 8 | templateUrl: 'app/movies/movies.html', 9 | controller: 'MoviesCtrl as vm', 10 | resolve: { 11 | movies: function(Restangular) { 12 | return Restangular 13 | .all('movies') 14 | .getList(); 15 | } 16 | } 17 | }) 18 | .state('movies_theatres', { 19 | url: '/movies/:movie/theatres', 20 | controller: 'MoviesTheatresCtrl', 21 | controllerAs: 'vm', 22 | templateUrl: 'app/movies/movies-theatres/movies-theatres.html', 23 | resolve: { 24 | movie: function($stateParams, Restangular) { 25 | return Restangular 26 | .one('movies', $stateParams.movie) 27 | .get(); 28 | }, 29 | 30 | theatres: function($stateParams, Restangular) { 31 | return Restangular.one('movies', $stateParams.movie) 32 | .all('theatres') 33 | .getList(); 34 | } 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /client/app/theatres/theatres-movies/theatres-movies.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('moviesApp') 4 | .controller('TheatresMoviesCtrl', function TheatresMoviesCtrl(theatre, movies) { 5 | 6 | var vm = this; 7 | vm.theatre = theatre; 8 | vm.movies = movies; 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /client/app/theatres/theatres-movies/theatres-movies.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TheatresMoviesCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('moviesApp')); 7 | 8 | var TheatresMoviesCtrl, scope; 9 | 10 | // Initialize the controller and a mock scope 11 | beforeEach(inject(function ($controller, $rootScope) { 12 | scope = $rootScope.$new(); 13 | TheatresMoviesCtrl = $controller('TheatresMoviesCtrl', { 14 | $scope: scope 15 | }); 16 | })); 17 | 18 | it('should ...', function () { 19 | expect(1).toEqual(1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /client/app/theatres/theatres-movies/theatres-movies.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
5 | 6 |
7 |
8 | 9 |
10 | 11 |
12 |

13 |
14 | 17 |
18 |
19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /client/app/theatres/theatres.controller.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular 4 | .module('moviesApp') 5 | .controller('TheatresCtrl', function TheatresCtrl(theatres) { 6 | 7 | var vm = this; 8 | vm.theatres = theatres; 9 | 10 | }); 11 | -------------------------------------------------------------------------------- /client/app/theatres/theatres.controller.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: TheatresCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('moviesApp')); 7 | 8 | var TheatresCtrl, scope; 9 | 10 | // Initialize the controller and a mock scope 11 | beforeEach(inject(function ($controller, $rootScope) { 12 | scope = $rootScope.$new(); 13 | TheatresCtrl = $controller('TheatresCtrl', { 14 | $scope: scope 15 | }); 16 | })); 17 | 18 | it('should ...', function () { 19 | expect(1).toEqual(1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /client/app/theatres/theatres.html: -------------------------------------------------------------------------------- 1 |
2 |
4 | 5 |
6 | 7 |

8 | 10 |

11 | 12 | 14 | See Movies 15 | 16 | 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /client/app/theatres/theatres.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | angular.module('moviesApp') 4 | .config(function ($stateProvider) { 5 | $stateProvider 6 | .state('theatres', { 7 | url: '/theatres', 8 | templateUrl: 'app/theatres/theatres.html', 9 | controller: 'TheatresCtrl as vm', 10 | resolve: { 11 | theatres: function(Restangular) { 12 | return Restangular 13 | .all('theatres') 14 | .getList(); 15 | } 16 | } 17 | }) 18 | .state('theatres_movies', { 19 | url: '/theatres/:theatre/movies', 20 | templateUrl: 'app/theatres/theatres-movies/theatres-movies.html', 21 | controller: 'TheatresMoviesCtrl as vm', 22 | resolve: { 23 | theatre: function($stateParams, Restangular) { 24 | return Restangular 25 | .one('theatres', $stateParams.theatre) 26 | .get(); 27 | }, 28 | 29 | movies: function($stateParams, Restangular) { 30 | return Restangular 31 | .one('theatres', $stateParams.theatre) 32 | .all('movies') 33 | .getList(); 34 | } 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /client/assets/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/client/assets/images/yeoman.png -------------------------------------------------------------------------------- /client/favicon.ico: -------------------------------------------------------------------------------- 1 |   �( @   -2Op"=p�Jt��Jt��b���������������������������������������������������b���Jt��Jt��"=p�Op-2O`O�O�O�O�O�O�O� $\�Jt��������������v���v���������������Jt�� $\�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� ;n�s���>���>���>���>���s��� ;n�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O� $\�]���^n��^n��]��� $\�O�O�O�O�O�O�O�O�O�O�O`O�O�O�O�O�O�O�O�O�O�O�n�*��*��n�O�O�O�O�O�O�O�O�O�O�O�  O�O�O�O�O�O�O�O�O�O�O�5>Y�5>Y�O�O�O�O�O�O�O�O�O�O�O�  -2O�O�O�O�O�O�O�O�O�O�&6e�&6e�O�O�O�O�O�O�O�O�O�O�-25r�4���E��� $\�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O�O� $\�E���4���5r�5r�E���M���M���v���0\��O�O�O�O�O�O�O� $\� $\�O�O�O�O�O�O�O�0\��v���M���M���E���5r�)��p&��p��&��������������b���Jt��Jt��Jt��0\��#i��.r��.r��#i��0\��Jt��Jt��Jt��b���������������&��p��&��)��p4���&��-���_������������������]���]�������7���p�����������p���7�������]���]�������������������_��-���-���4���qֈp��p��p����������������������p���7���#i��p�����������p���#i��7���p�����������������������p��&��-���qֈ8��(p��p��I���v���v���]���7���n���v���p���#i��]���v���v���]���#i��p���v���n���7���]���v���v���I���-���-���8��(;��`-���M���7���7���7���.r��R��E��R��E��7���7���7���7���E��R��E��R��.r��7���7���7���M���M���;��`���������������������������z��������������������������� 2 | �  ��� 3 | � 9� 9� 9� 9� 9� 9� 9� 9� 4 |  �n�n� 5 |  � 9� 9� 9� 9� 9� 9� 9� 9� 6 | ����*�x*��*��*��*��*��*��*��n�&��#��&��&��n�*��*��*��*��*��*��*��*�x*ݟ*��*��*��*��*��*��!��#��&��#��&��*��!��!��*��*��*��*��*��*��*ݟ*ݿ*��*��*��*��*��*��n�*��*�� 9� 9�*��*���*��*��*��*��*��*��*ݿ*��*��*��*��*��*��*��!��#��&��&��&��*��#��!��*��*��*��*��*��*��*��  ��������I�&��&��&��&��I���������  U��������� 7 |  �n�n� 8 |  ����������-2z����������������������z������������������������ 9 | ����������������������� 10 | ������������������������� 11 | ������������������������-2����������������������U�������������������z5r������������������-25r�U�����������z  ������������������������������?��� -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | moviesApp 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 |
32 | 39 |
40 | 41 |
42 |
43 |
44 |
45 |
46 | 47 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /client/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /client/styles/app.scss: -------------------------------------------------------------------------------- 1 | @import 'reset'; 2 | 3 | $base-space: 1.25em; 4 | $base-space-extra-small: $base-space * 0.25; 5 | $base-space-small: $base-space * 0.5; 6 | $base-space-large: $base-space * 2; 7 | $base-space-extra-large: $base-space * 4; 8 | 9 | $border-radius: 6px; 10 | $box-shadow: 0 1px 1px rgba(#000, 0.25); 11 | 12 | $color-background: #e3e4e4; 13 | $color-brand: #ccc; 14 | $color-primary: #978347; 15 | $color-invert: #fff; 16 | $color-subdue: #999; 17 | $color-text: #111; 18 | 19 | $font-family: Helvetica, Arial, sans-serif; 20 | $font-size: 16px; 21 | $font-size-small: 12px; 22 | $font-size-large: 22px; 23 | $line-height: 1.5; 24 | 25 | $site-header-height: 50px; 26 | $site-width: 960px; 27 | 28 | $item--movie-minHeight: 275px; 29 | 30 | body { 31 | background-color: $color-background; 32 | color: $color-text; 33 | font-family: $font-family; 34 | font-size: $font-size; 35 | line-height: $line-height; 36 | } 37 | 38 | h1, h2, h3, h4 { 39 | margin: 0 0 $base-space-extra-small; 40 | } 41 | 42 | h1, h2 { 43 | margin-bottom: $base-space-small; 44 | } 45 | 46 | ul, ol { 47 | margin: 0; 48 | padding: 0; 49 | list-style-type: none; 50 | } 51 | 52 | p { 53 | margin: 0 0 $base-space; 54 | } 55 | 56 | a { 57 | color: $color-primary; 58 | text-decoration: none; 59 | transition: 0.2s ease-in-out; 60 | 61 | &:hover, 62 | &:focus, 63 | &:active { 64 | color: $color-subdue; 65 | } 66 | } 67 | 68 | .btn { 69 | background: $color-primary; 70 | border: 1px solid transparent; 71 | border-radius: 20px; 72 | color: $color-invert; 73 | cursor: pointer; 74 | display: inline-block; 75 | font-size: $font-size-small; 76 | font-weight: bold; 77 | line-height: 3; 78 | padding: 0 $base-space-large; 79 | text-align: center; 80 | text-decoration: none; 81 | text-transform: uppercase; 82 | white-space: nowrap; 83 | 84 | &:hover, 85 | &:focus, 86 | &:active { 87 | background: darken($color-primary, 5%); 88 | color: $color-invert; 89 | } 90 | } 91 | 92 | .bucket-content { 93 | display: table-cell; 94 | width: 10000px; 95 | } 96 | 97 | .bucket-media { 98 | float: left; 99 | margin-right: $base-space; 100 | 101 | > img { 102 | display: block; 103 | max-width: none; 104 | } 105 | } 106 | 107 | .card { 108 | background: $color-invert; 109 | border-radius: $border-radius; 110 | box-shadow: $box-shadow; 111 | padding: $base-space; 112 | position: relative; 113 | } 114 | 115 | .card-btn { 116 | display: block; 117 | } 118 | 119 | .cell { 120 | margin-left: auto; 121 | margin-right: auto; 122 | max-width: $site-width; 123 | } 124 | 125 | .grid { 126 | display: block; 127 | margin-left: -$base-space; 128 | } 129 | 130 | .grid-box { 131 | box-sizing: border-box; 132 | float: left; 133 | margin: 0; 134 | margin-bottom: $base-space; 135 | padding-left: $base-space; 136 | width: 100%; 137 | } 138 | 139 | .grid-box--1of4 { 140 | width: 50%; 141 | 142 | @media screen and (min-width: 700px) { 143 | width: 33.333333333%; 144 | } 145 | 146 | @media screen and (min-width: 960px) { 147 | width: 25%; 148 | } 149 | } 150 | 151 | .row { 152 | overflow: hidden; 153 | padding: 0 $base-space; 154 | } 155 | 156 | .well { 157 | margin-bottom: $base-space; 158 | margin-top: $base-space; 159 | } 160 | 161 | .group::after { 162 | clear: both; 163 | content: ''; 164 | display: table; 165 | } 166 | 167 | .footer { 168 | color: $color-subdue; 169 | font-size: $font-size-small; 170 | margin-top: $base-space-large; 171 | padding-bottom: $base-space-extra-large; 172 | text-align: center; 173 | } 174 | 175 | .header { 176 | background: $color-text; 177 | height: $site-header-height; 178 | line-height: $site-header-height; 179 | } 180 | 181 | .header-brand { 182 | color: $color-brand; 183 | float: left; 184 | font-size: $font-size-large; 185 | margin-right: $base-space; 186 | } 187 | 188 | .header-nav { 189 | float: left; 190 | } 191 | 192 | .header-nav li { 193 | display: inline; 194 | font-size: $font-size-small; 195 | font-weight: bold; 196 | margin-right: $base-space; 197 | text-transform: uppercase; 198 | } 199 | 200 | .header-nav li a:active, 201 | .header-nav li a:focus, 202 | .header-nav li a:hover { 203 | color: $color-invert; 204 | } 205 | 206 | .labels .label { 207 | margin-right: $base-space-small; 208 | 209 | &:last-child { 210 | margin-right: 0; 211 | } 212 | } 213 | 214 | .label { 215 | background: $color-subdue; 216 | border-radius: 20px; 217 | color: $color-invert; 218 | font-size: $font-size-small; 219 | font-weight: bold; 220 | padding: $base-space-extra-small $base-space; 221 | } 222 | 223 | .item--movie { 224 | min-height: $item--movie-minHeight; 225 | text-align: center; 226 | 227 | .item-cover { 228 | height: 150px; 229 | margin-bottom: $base-space-small; 230 | } 231 | } 232 | 233 | .item--theatre .item-cover { 234 | height: 120px; 235 | width: 150px; 236 | } 237 | 238 | .item--theatre { 239 | margin-bottom: $base-space-small; 240 | } 241 | 242 | .item--theatre--min { 243 | margin-bottom: 0; 244 | text-align: center; 245 | 246 | .item-title { 247 | margin-bottom: $base-space; 248 | min-height: 70px; 249 | } 250 | } 251 | 252 | .item-cover { 253 | background-color: $color-background; 254 | display: block; 255 | } 256 | 257 | .item-subtitle { 258 | color: $color-subdue; 259 | } 260 | 261 | .item-title { 262 | font-size: $font-size-large; 263 | margin-bottom: 0; 264 | 265 | a { 266 | color: $color-text; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /client/styles/reset.scss: -------------------------------------------------------------------------------- 1 | /*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ 2 | 3 | /** 4 | * 1. Set default font family to sans-serif. 5 | * 2. Prevent iOS and IE text size adjust after device orientation change, 6 | * without disabling user zoom. 7 | */ 8 | 9 | html { 10 | font-family: sans-serif; /* 1 */ 11 | -ms-text-size-adjust: 100%; /* 2 */ 12 | -webkit-text-size-adjust: 100%; /* 2 */ 13 | } 14 | 15 | /** 16 | * Remove default margin. 17 | */ 18 | 19 | body { 20 | margin: 0; 21 | } 22 | 23 | /* HTML5 display definitions 24 | ========================================================================== */ 25 | 26 | /** 27 | * Correct `block` display not defined for any HTML5 element in IE 8/9. 28 | * Correct `block` display not defined for `details` or `summary` in IE 10/11 29 | * and Firefox. 30 | * Correct `block` display not defined for `main` in IE 11. 31 | */ 32 | 33 | article, 34 | aside, 35 | details, 36 | figcaption, 37 | figure, 38 | footer, 39 | header, 40 | hgroup, 41 | main, 42 | menu, 43 | nav, 44 | section, 45 | summary { 46 | display: block; 47 | } 48 | 49 | /** 50 | * 1. Correct `inline-block` display not defined in IE 8/9. 51 | * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. 52 | */ 53 | 54 | audio, 55 | canvas, 56 | progress, 57 | video { 58 | display: inline-block; /* 1 */ 59 | vertical-align: baseline; /* 2 */ 60 | } 61 | 62 | /** 63 | * Prevent modern browsers from displaying `audio` without controls. 64 | * Remove excess height in iOS 5 devices. 65 | */ 66 | 67 | audio:not([controls]) { 68 | display: none; 69 | height: 0; 70 | } 71 | 72 | /** 73 | * Address `[hidden]` styling not present in IE 8/9/10. 74 | * Hide the `template` element in IE 8/9/10/11, Safari, and Firefox < 22. 75 | */ 76 | 77 | [hidden], 78 | template { 79 | display: none; 80 | } 81 | 82 | /* Links 83 | ========================================================================== */ 84 | 85 | /** 86 | * Remove the gray background color from active links in IE 10. 87 | */ 88 | 89 | a { 90 | background-color: transparent; 91 | } 92 | 93 | /** 94 | * Improve readability of focused elements when they are also in an 95 | * active/hover state. 96 | */ 97 | 98 | a:active, 99 | a:hover { 100 | outline: 0; 101 | } 102 | 103 | /* Text-level semantics 104 | ========================================================================== */ 105 | 106 | /** 107 | * Address styling not present in IE 8/9/10/11, Safari, and Chrome. 108 | */ 109 | 110 | abbr[title] { 111 | border-bottom: 1px dotted; 112 | } 113 | 114 | /** 115 | * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. 116 | */ 117 | 118 | b, 119 | strong { 120 | font-weight: bold; 121 | } 122 | 123 | /** 124 | * Address styling not present in Safari and Chrome. 125 | */ 126 | 127 | dfn { 128 | font-style: italic; 129 | } 130 | 131 | /** 132 | * Address variable `h1` font-size and margin within `section` and `article` 133 | * contexts in Firefox 4+, Safari, and Chrome. 134 | */ 135 | 136 | h1 { 137 | font-size: 2em; 138 | margin: 0.67em 0; 139 | } 140 | 141 | /** 142 | * Address styling not present in IE 8/9. 143 | */ 144 | 145 | mark { 146 | background: #ff0; 147 | color: #000; 148 | } 149 | 150 | /** 151 | * Address inconsistent and variable font size in all browsers. 152 | */ 153 | 154 | small { 155 | font-size: 80%; 156 | } 157 | 158 | /** 159 | * Prevent `sub` and `sup` affecting `line-height` in all browsers. 160 | */ 161 | 162 | sub, 163 | sup { 164 | font-size: 75%; 165 | line-height: 0; 166 | position: relative; 167 | vertical-align: baseline; 168 | } 169 | 170 | sup { 171 | top: -0.5em; 172 | } 173 | 174 | sub { 175 | bottom: -0.25em; 176 | } 177 | 178 | /* Embedded content 179 | ========================================================================== */ 180 | 181 | /** 182 | * Remove border when inside `a` element in IE 8/9/10. 183 | */ 184 | 185 | img { 186 | border: 0; 187 | } 188 | 189 | /** 190 | * Correct overflow not hidden in IE 9/10/11. 191 | */ 192 | 193 | svg:not(:root) { 194 | overflow: hidden; 195 | } 196 | 197 | /* Grouping content 198 | ========================================================================== */ 199 | 200 | /** 201 | * Address margin not present in IE 8/9 and Safari. 202 | */ 203 | 204 | figure { 205 | margin: 1em 40px; 206 | } 207 | 208 | /** 209 | * Address differences between Firefox and other browsers. 210 | */ 211 | 212 | hr { 213 | box-sizing: content-box; 214 | height: 0; 215 | } 216 | 217 | /** 218 | * Contain overflow in all browsers. 219 | */ 220 | 221 | pre { 222 | overflow: auto; 223 | } 224 | 225 | /** 226 | * Address odd `em`-unit font size rendering in all browsers. 227 | */ 228 | 229 | code, 230 | kbd, 231 | pre, 232 | samp { 233 | font-family: monospace, monospace; 234 | font-size: 1em; 235 | } 236 | 237 | /* Forms 238 | ========================================================================== */ 239 | 240 | /** 241 | * Known limitation: by default, Chrome and Safari on OS X allow very limited 242 | * styling of `select`, unless a `border` property is set. 243 | */ 244 | 245 | /** 246 | * 1. Correct color not being inherited. 247 | * Known issue: affects color of disabled elements. 248 | * 2. Correct font properties not being inherited. 249 | * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. 250 | */ 251 | 252 | button, 253 | input, 254 | optgroup, 255 | select, 256 | textarea { 257 | color: inherit; /* 1 */ 258 | font: inherit; /* 2 */ 259 | margin: 0; /* 3 */ 260 | } 261 | 262 | /** 263 | * Address `overflow` set to `hidden` in IE 8/9/10/11. 264 | */ 265 | 266 | button { 267 | overflow: visible; 268 | } 269 | 270 | /** 271 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 272 | * All other form control elements do not inherit `text-transform` values. 273 | * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. 274 | * Correct `select` style inheritance in Firefox. 275 | */ 276 | 277 | button, 278 | select { 279 | text-transform: none; 280 | } 281 | 282 | /** 283 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 284 | * and `video` controls. 285 | * 2. Correct inability to style clickable `input` types in iOS. 286 | * 3. Improve usability and consistency of cursor style between image-type 287 | * `input` and others. 288 | */ 289 | 290 | button, 291 | html input[type="button"], /* 1 */ 292 | input[type="reset"], 293 | input[type="submit"] { 294 | -webkit-appearance: button; /* 2 */ 295 | cursor: pointer; /* 3 */ 296 | } 297 | 298 | /** 299 | * Re-set default cursor for disabled elements. 300 | */ 301 | 302 | button[disabled], 303 | html input[disabled] { 304 | cursor: default; 305 | } 306 | 307 | /** 308 | * Remove inner padding and border in Firefox 4+. 309 | */ 310 | 311 | button::-moz-focus-inner, 312 | input::-moz-focus-inner { 313 | border: 0; 314 | padding: 0; 315 | } 316 | 317 | /** 318 | * Address Firefox 4+ setting `line-height` on `input` using `!important` in 319 | * the UA stylesheet. 320 | */ 321 | 322 | input { 323 | line-height: normal; 324 | } 325 | 326 | /** 327 | * It's recommended that you don't attempt to style these elements. 328 | * Firefox's implementation doesn't respect box-sizing, padding, or width. 329 | * 330 | * 1. Address box sizing set to `content-box` in IE 8/9/10. 331 | * 2. Remove excess padding in IE 8/9/10. 332 | */ 333 | 334 | input[type="checkbox"], 335 | input[type="radio"] { 336 | box-sizing: border-box; /* 1 */ 337 | padding: 0; /* 2 */ 338 | } 339 | 340 | /** 341 | * Fix the cursor style for Chrome's increment/decrement buttons. For certain 342 | * `font-size` values of the `input`, it causes the cursor style of the 343 | * decrement button to change from `default` to `text`. 344 | */ 345 | 346 | input[type="number"]::-webkit-inner-spin-button, 347 | input[type="number"]::-webkit-outer-spin-button { 348 | height: auto; 349 | } 350 | 351 | /** 352 | * 1. Address `appearance` set to `searchfield` in Safari and Chrome. 353 | * 2. Address `box-sizing` set to `border-box` in Safari and Chrome. 354 | */ 355 | 356 | input[type="search"] { 357 | -webkit-appearance: textfield; /* 1 */ 358 | box-sizing: content-box; /* 2 */ 359 | } 360 | 361 | /** 362 | * Remove inner padding and search cancel button in Safari and Chrome on OS X. 363 | * Safari (but not Chrome) clips the cancel button when the search input has 364 | * padding (and `textfield` appearance). 365 | */ 366 | 367 | input[type="search"]::-webkit-search-cancel-button, 368 | input[type="search"]::-webkit-search-decoration { 369 | -webkit-appearance: none; 370 | } 371 | 372 | /** 373 | * Define consistent border, margin, and padding. 374 | */ 375 | 376 | fieldset { 377 | border: 1px solid #c0c0c0; 378 | margin: 0 2px; 379 | padding: 0.35em 0.625em 0.75em; 380 | } 381 | 382 | /** 383 | * 1. Correct `color` not being inherited in IE 8/9/10/11. 384 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 385 | */ 386 | 387 | legend { 388 | border: 0; /* 1 */ 389 | padding: 0; /* 2 */ 390 | } 391 | 392 | /** 393 | * Remove default vertical scrollbar in IE 8/9/10/11. 394 | */ 395 | 396 | textarea { 397 | overflow: auto; 398 | } 399 | 400 | /** 401 | * Don't inherit the `font-weight` (applied by a rule above). 402 | * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. 403 | */ 404 | 405 | optgroup { 406 | font-weight: bold; 407 | } 408 | 409 | /* Tables 410 | ========================================================================== */ 411 | 412 | /** 413 | * Remove most spacing between table cells. 414 | */ 415 | 416 | table { 417 | border-collapse: collapse; 418 | border-spacing: 0; 419 | } 420 | 421 | td, 422 | th { 423 | padding: 0; 424 | } 425 | -------------------------------------------------------------------------------- /e2e/main/main.po.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file uses the Page Object pattern to define the main page for tests 3 | * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ 4 | */ 5 | 6 | 'use strict'; 7 | 8 | var MainPage = function() { 9 | this.heroEl = element(by.css('.hero-unit')); 10 | this.h1El = this.heroEl.element(by.css('h1')); 11 | this.imgEl = this.heroEl.element(by.css('img')); 12 | }; 13 | 14 | module.exports = new MainPage(); 15 | 16 | -------------------------------------------------------------------------------- /e2e/main/main.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Main View', function() { 4 | var page; 5 | 6 | beforeEach(function() { 7 | browser.get('/'); 8 | page = require('./main.po'); 9 | }); 10 | 11 | it('should include jumbotron with correct data', function() { 12 | expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); 13 | expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); 14 | expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* jshint unused: false, node: true */ 2 | 'use strict'; 3 | 4 | var gulp = require('gulp'); 5 | var g = require('gulp-load-plugins')({lazy: false}); 6 | var noop = g.util.noop; 7 | var es = require('event-stream'); 8 | var bowerFiles = require('main-bower-files'); 9 | var rimraf = require('rimraf'); 10 | var queue = require('streamqueue'); 11 | var lazypipe = require('lazypipe'); 12 | var stylish = require('jshint-stylish'); 13 | var bower = require('./bower'); 14 | var util = require('util'); 15 | var browserSync = require('browser-sync'); 16 | var del = require('del'); 17 | var karma = require('karma').server; 18 | var mainBowerFiles = require('main-bower-files'); 19 | var reload = browserSync.reload; 20 | // GULP PATHS 21 | var errorHandler= require('./build/errors'); 22 | var config = require('./build/config'); 23 | var client = config.client; 24 | var tmp = config.build; 25 | var dist = config.dist; 26 | var runServers = require('./servers.runner.js'); 27 | var AUTOPREFIXER_BROWSERS = [ 28 | 'ie >= 10', 29 | 'ie_mob >= 10', 30 | 'ff >= 30', 31 | 'chrome >= 34', 32 | 'safari >= 7', 33 | 'opera >= 23', 34 | 'ios >= 7', 35 | 'android >= 4.4', 36 | 'bb >= 10' 37 | ]; 38 | 39 | /* 40 | | ################################### 41 | | 42 | | GULP - Every task is listed in the order it is called; 43 | | 44 | | NOTE: Tasks that are long are call are wrapped in functions that are listed near the bottom 45 | | The purpose of this is to help beiginners approach overwhelming gulp files 46 | | 47 | | ################################### 48 | */ 49 | 50 | /* 51 | | Default 52 | */ 53 | gulp.task('default', ['clean'], function (cb) { 54 | 55 | g.runSequence('copy', 'images', ['build'],'inject', cb); 56 | }); 57 | 58 | /* 59 | | SERVER 60 | */ 61 | gulp.task('server', ['serve']); 62 | gulp.task('serve',['clean'], function (done) { 63 | g.runSequence(['build', 'run:servers'], 'browserSync', done); 64 | }); 65 | 66 | /* 67 | | CLEAN and remove the .tmp directory 68 | */ 69 | gulp.task('clean',['remove:inject'], del.bind(null, ['./.tmp', './dist'])); 70 | 71 | 72 | /* 73 | | Browser Sync Server for Dev Environment 74 | */ 75 | gulp.task('browserSync', function(){ 76 | browserSync({ 77 | notify: false, 78 | // https: true, 79 | server: ['.tmp', 'client'], 80 | socket: { 81 | path: '/socket.io-client', 82 | clientPath: '/socket.io-client', 83 | namespace: '/socket.io-client' 84 | } 85 | }); 86 | gulp.watch(client.templates.jade, reload); 87 | gulp.watch(client.styles.sass, ['styles', reload]); 88 | 89 | 90 | gulp.watch(client.scripts.all, ['scripts']); 91 | gulp.watch(client.images, reload); 92 | }); 93 | 94 | /* 95 | | Run all servers in the servers folder 96 | */ 97 | gulp.task('run:servers', function(){ 98 | 99 | runServers.base(); 100 | }); 101 | 102 | /* 103 | | Browser Sync Server For Production Environment 104 | */ 105 | gulp.task('dist', ['default','run:servers'], function () { 106 | browserSync({ 107 | notify: false, 108 | // https: true, 109 | server: './dist', 110 | socket: { 111 | path: '/socket.io-client', 112 | clientPath: '/socket.io-client', 113 | namespace: '/socket.io-client' 114 | } 115 | }); 116 | }); 117 | 118 | /* 119 | | Gulp Karma Test 120 | */ 121 | gulp.task('test', ['karma:inject:bower', 'serve'], function ( done ){ 122 | 123 | karma.start({ 124 | configFile: __dirname + '/karma.conf.js' 125 | }, done); 126 | }); 127 | 128 | /* 129 | | JSHINT on client side scripts, but leaving them in there place; 130 | */ 131 | gulp.task('scripts', function(){ 132 | return gulp.src(client.scripts.all) 133 | .pipe( reload({stream: true, once: true}) ) 134 | .pipe( g.jshint() ) 135 | .pipe( g.jshint.reporter('jshint-stylish')) 136 | .pipe( gulp.dest( tmp.path ) ) 137 | .pipe( g.concat(config.app_file_name) ) 138 | .pipe( g.ngAnnotate({ 139 | // true helps add where @ngInject is not used. It infers. 140 | // Doesn't work with resolve, so we must be explicit there 141 | add: true 142 | })) 143 | .pipe( g.uglify() ) 144 | .pipe( gulp.dest( dist.scriptPath ) ) 145 | .pipe( g.if(!browserSync.active, g.jshint.reporter('fail'))); 146 | }); 147 | 148 | /* 149 | | Compile all styles 150 | */ 151 | gulp.task('styles', function () { 152 | // For best performance, don't add Sass partials to `gulp.src` 153 | return gulp.src( client.styles.sass ) 154 | .pipe( g.changed('styles', {extension: '.scss'} ) ) 155 | .pipe( g.rubySass({ 156 | style: 'expanded', 157 | // // // precision: 10, 158 | compass: true 159 | })) 160 | .on( 'error', console.error.bind( console ) ) 161 | .pipe( g.autoprefixer( {browsers: AUTOPREFIXER_BROWSERS} ) ) 162 | .pipe( g.concat( config.css_file_name ) ) 163 | .pipe( gulp.dest( tmp.stylePath ) ) 164 | .pipe( gulp.dest( dist.stylePath ) ); 165 | }); 166 | 167 | /* 168 | | COMPILE JADE and place them into the .tmp directory 169 | */ 170 | gulp.task('compile:jade', function(){ 171 | return gulp.src( client.templates.jade ) 172 | .pipe( g.jade() ) 173 | .pipe( g.angularTemplatecache( config.jade_file_name, { module: config.module_name })) 174 | .pipe( gulp.dest( tmp.templatePath )) 175 | .pipe( g.uglify() ) 176 | .pipe( gulp.dest( dist.templatePath ) ); 177 | }); 178 | 179 | /* 180 | | COMPILE JADE and place them into the .tmp directory 181 | */ 182 | gulp.task('compile:html', function(){ 183 | return gulp.src( client.templates.html ) 184 | .pipe( g.angularTemplatecache( config.html_file_name, { module: config.module_name } ) ) 185 | .pipe( gulp.dest( tmp.templatePath ) ) 186 | .pipe( g.uglify() ) 187 | .pipe( gulp.dest( dist.templatePath ) ); 188 | }); 189 | 190 | /* 191 | | COMPILE Both jade and html templates into the .tmp/templates 192 | */ 193 | gulp.task('compile:templates', function (done){ 194 | g.runSequence([ 195 | 'compile:jade', 196 | 'compile:html' 197 | ], done); 198 | }); 199 | 200 | /* 201 | | Run Scripts before injecting into client; 202 | */ 203 | gulp.task('build',[ 204 | 'styles', 205 | 'scripts', 206 | // 'compile:templates', 207 | 'inject:bower' 208 | ], function(){ 209 | 210 | return buildServerInjector(); 211 | }); 212 | 213 | 214 | /* 215 | | INJECT BOWER DEPENDENCIES with wiredep into client/index.html 216 | */ 217 | gulp.task('inject:bower', function () { 218 | 219 | var wiredep = require('wiredep').stream; 220 | return gulp.src(client.index) 221 | .pipe(wiredep({ 222 | directory: client.bower, 223 | exclude: ['bootstrap-sass-official'] 224 | })) 225 | .pipe( gulp.dest( client.path ) ); 226 | }); 227 | 228 | gulp.task('remove:inject', function(){ 229 | var head = gulp.src( './build/injector/head.html' ); 230 | var body = gulp.src( './build/injector/body.html' ); 231 | return gulp.src( client.index ) 232 | .pipe( g.inject(head,{ 233 | starttag: '', 234 | endtag: '', 235 | transform: function (filePath, file) { 236 | // return file contents as string 237 | return file.contents.toString('utf8'); 238 | } 239 | })) 240 | .pipe( g.inject(body,{ 241 | starttag: '', 242 | endtag: '', 243 | transform: function (filePath, file) { 244 | // return file contents as string 245 | return file.contents.toString('utf8'); 246 | } 247 | })) 248 | .pipe( gulp.dest( client.path ) ); 249 | }); 250 | 251 | /* DIST 252 | | MINIFY IMAGES for dist 253 | */ 254 | gulp.task('images', function () { 255 | return gulp.src(client.images) 256 | .pipe( g.cache( g.imagemin({ 257 | progressive: true, 258 | interlaced: true 259 | }))) 260 | .pipe( gulp.dest( dist.images ) ) 261 | .pipe( g.size( {title: 'images'} )); 262 | }); 263 | 264 | /* DIST 265 | | Copy All files for dist 266 | */ 267 | gulp.task('copy', function () { 268 | return gulp.src([ 269 | client.path + '*.*', 270 | 'node_modules/apache-server-configs/dist/.htaccess' 271 | ], { 272 | dot: true 273 | }).pipe( gulp.dest( dist.path ) ) 274 | .pipe( g.size( {title: 'copy'} ) ); 275 | }); 276 | 277 | /* DIST 278 | | BUILD BOWER copy all bower files to the dist/bower_components directory 279 | | - inject all bower scripts and styles into index.html 280 | */ 281 | gulp.task('build:bower',[ 282 | 'dist:bower:files'], 283 | function ( callback ){ 284 | g.runSequence('dist:inject:bower', callback); 285 | } 286 | ); 287 | 288 | /* 289 | | DIST BOWER FILES copy all bower files to the dist/bower_components directory 290 | */ 291 | gulp.task('dist:bower:files', function(){ 292 | 293 | return g.bower( client.bower ) 294 | .pipe( gulp.dest( dist.bower ) ); 295 | }); 296 | 297 | /* 298 | | DIST INJECT BOWER inject all bower scripts and styles into index.html 299 | */ 300 | gulp.task('dist:inject:bower',['dist:bower:files'], function(){ 301 | var wiredep = require('wiredep').stream; 302 | return gulp.src( dist.index ) 303 | .pipe(wiredep({ 304 | directory: dist.bower, 305 | exclude: ['bootstrap-sass-official'] 306 | })) 307 | .pipe( gulp.dest( dist.path ) ); 308 | }); 309 | 310 | gulp.task('inject',['dist:inject:bower'], function (){ 311 | 312 | return buildDistInjector(); 313 | }); 314 | 315 | 316 | gulp.task('karma:inject:bower', function(){ 317 | 318 | var bower = mainBowerFiles({ 319 | paths: { 320 | bowerrc: './.bowerrc', 321 | bowerJson: './bower.json', 322 | includeDev: true 323 | } 324 | }); 325 | 326 | return gulp.src('./karma.conf.js') 327 | .pipe( g.inject( gulp.src(bower), { 328 | starttag: 'bower= [', 329 | endtag: ']', 330 | addRootSlash: false, 331 | transform: function (filepath, file, i, length) { 332 | return ' "' + filepath + '"' + (i + 1 < length ? ',' : ''); 333 | } 334 | })) 335 | .pipe( g.inject( gulp.src(client.specs), { 336 | starttag: 'client= [', 337 | endtag: ']', 338 | addRootSlash: false, 339 | transform: function (filepath, file, i, length) { 340 | return ' "' + filepath + '"' + (i + 1 < length ? ',' : ''); 341 | } 342 | } 343 | )) 344 | .pipe( gulp.dest('./') ); 345 | }); 346 | 347 | /* 348 | | INJECT all scritps into index.html; 349 | */ 350 | function injector(options){ 351 | return options.target 352 | .pipe( g.inject(options.styles.src, options.styles.params) ) 353 | .pipe( g.inject(options.vendor.src, options.vendor.params) ) 354 | // .pipe( g.inject(options.root.src, options.root.params) ) 355 | .pipe( g.inject(options.bundle.src, options.bundle.params) ) 356 | .pipe( g.inject(options.templates.src, options.templates.params) ) 357 | .pipe( gulp.dest( options.dest ) ); 358 | } 359 | 360 | 361 | /* 362 | | BUILD INJECTOR PARAMS for gulp server then call injector() 363 | */ 364 | function buildServerInjector(){ 365 | var options = { 366 | target: gulp.src( client.index ), 367 | dest: client.path, 368 | styles: { 369 | src: gulp.src( tmp.styles, {read:false}), 370 | params: {addRootSlash:true, ignorePath: '.tmp', name:'styles'} 371 | }, 372 | vendor: { 373 | src: gulp.src( client.vendor, {read:false}), 374 | params: {addRootSlash:false, relative:true, name:'vendor'} 375 | }, 376 | bundle:{ 377 | src: gulp.src( tmp.scripts, {read:false}), 378 | params: {addRootSlash:true, ignorePath: '.tmp', name:'bundle'} 379 | }, 380 | templates: { 381 | src: gulp.src( tmp.templates, {read:false}), 382 | params: {addRootSlash:true, ignorePath:'.tmp', name:'templates'} 383 | } 384 | }; 385 | 386 | return injector(options); 387 | } 388 | 389 | 390 | function injectorDist(options){ 391 | return options.target 392 | .pipe( g.inject(options.styles.src, options.styles.params) ) 393 | .pipe( g.inject(options.vendor.src, options.vendor.params) ) 394 | .pipe( g.inject(options.root.src, options.root.params) ) 395 | .pipe( g.inject(options.bundle.src, options.bundle.params) ) 396 | .pipe( g.inject(options.templates.src, options.templates.params) ) 397 | .pipe( gulp.dest( dist.path ) ); 398 | } 399 | /* 400 | | BUILD INJECTOR PARAMS for gulp server then call injector() 401 | */ 402 | function buildDistInjector(){ 403 | var options = { 404 | target: gulp.src( dist.index ), 405 | dest: dist.path, 406 | styles: { 407 | src: gulp.src( dist.styles, {read:false}), 408 | params: {addRootSlash:false, relative:true, name:'styles'} 409 | }, 410 | vendor: { 411 | src: gulp.src( dist.scriptPath + config.vendor_file_name, {read:false}), 412 | params: {addRootSlash:false, relative:true, name:'vendor'} 413 | }, 414 | root: { 415 | src: gulp.src( dist.scriptPath + config.app_file_name, {read:false}), 416 | params: {addRootSlash:false, relative:true, name:'root'} 417 | }, 418 | bundle:{ 419 | src: gulp.src( dist.scriptPath + config.modules_file_name, {read:false}), 420 | params: {addRootSlash:false, relative:true, name:'bundle'} 421 | }, 422 | templates: { 423 | src: gulp.src( dist.templates, {read:false}), 424 | params: {addRootSlash:false, relative:true, name:'templates'} 425 | } 426 | }; 427 | 428 | return injectorDist(options); 429 | } 430 | 431 | 432 | 433 | /* 434 | | CLEAN and remove the dist directory 435 | */ 436 | gulp.task('build:scripts', ['scripts:root', 'scripts:bundle', 'scripts:vendor']); 437 | 438 | gulp.task('scripts:root', function(){ 439 | return gulp.src(client.scripts.root) 440 | .pipe( g.jshint()) 441 | .on('error', errorHandler.onWarning ) 442 | .pipe( g.jshint.reporter('default') ) 443 | .pipe( g.ngAnnotate() ) 444 | .pipe( g.rename(config.app_file_name ) ) 445 | .pipe( gulp.dest( dist.scriptPath ) ); 446 | }); 447 | gulp.task('scripts:bundle', function(){ 448 | return gulp.src( client.scripts.modules ) 449 | .pipe( g.jshint() ) 450 | .on('error', errorHandler.onWarning ) 451 | .pipe( g.jshint.reporter('default') ) 452 | .pipe( g.ngAnnotate() ) 453 | .pipe( g.concat( config.modules_file_name ) ) 454 | .pipe( g.uglify() ) 455 | .pipe( gulp.dest( dist.scriptPath ) ); 456 | }); 457 | 458 | gulp.task('scripts:vendor', function(){ 459 | return gulp.src( client.vendor ) 460 | .pipe( g.concat( config.vendor_file_name ) ) 461 | .pipe( g.uglify() ) 462 | .pipe( gulp.dest( dist.scriptPath ) ); 463 | }); 464 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.10/config/configuration-file.html 3 | 4 | 5 | module.exports = function(config) { 6 | 7 | var bower= [ 8 | ]; 9 | var client= [ 10 | ]; 11 | config.set({ 12 | // base path, that will be used to resolve files and exclude 13 | basePath: '', 14 | 15 | // testing framework to use (jasmine/mocha/qunit/...) 16 | frameworks: ['jasmine'], 17 | 18 | // list of files / patterns to load in the browser 19 | files: bower.concat(client), 20 | 21 | preprocessors: { 22 | '**/*.jade': 'ng-jade2js', 23 | '**/*.html': 'html2js', 24 | '**/*.coffee': 'coffee' 25 | }, 26 | 27 | ngHtml2JsPreprocessor: { 28 | stripPrefix: 'client/' 29 | }, 30 | 31 | ngJade2JsPreprocessor: { 32 | stripPrefix: 'client/' 33 | }, 34 | 35 | // list of files / patterns to exclude 36 | exclude: [], 37 | 38 | // web server port 39 | port: 8080, 40 | 41 | // level of logging 42 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 43 | logLevel: config.LOG_ERROR, 44 | 45 | 46 | // enable / disable watching file and executing tests whenever any file changes 47 | autoWatch: true, 48 | 49 | 50 | // Start these browsers, currently available: 51 | // - Chrome 52 | // - ChromeCanary 53 | // - Firefox 54 | // - Opera 55 | // - Safari (only Mac) 56 | // - PhantomJS 57 | // - IE (only Windows) 58 | browsers: ['Chrome'], 59 | 60 | 61 | // Continuous Integration mode 62 | // if true, it capture browsers, run tests and exit 63 | singleRun: false 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "movies", 3 | "version": "0.0.1", 4 | "main": "server/app.js", 5 | "dependencies": { 6 | "body-parser": "~1.5.0", 7 | "composable-middleware": "^0.3.0", 8 | "compression": "~1.0.1", 9 | "connect-mongo": "^0.4.1", 10 | "cookie-parser": "~1.0.1", 11 | "cors": "^2.4.1", 12 | "ejs": "~0.8.4", 13 | "errorhandler": "~1.0.0", 14 | "express": "~4.0.0", 15 | "express-session": "~1.0.2", 16 | "lodash": "~2.4.1", 17 | "method-override": "~1.0.0", 18 | "morgan": "~1.0.0", 19 | "serve-favicon": "~2.0.1", 20 | "sqlite3": "^3.0.8" 21 | }, 22 | "devDependencies": { 23 | "apache-server-configs": "^2.8.0", 24 | "browser-sync": "^1.3.7", 25 | "connect-livereload": "~0.4.0", 26 | "del": "^0.1.3", 27 | "event-stream": "^3.1.7", 28 | "gulp": "*", 29 | "gulp-angular-filesort": "^1.0.3", 30 | "gulp-angular-templatecache": "*", 31 | "gulp-autoprefixer": "^1.0.1", 32 | "gulp-bower": "0.0.6", 33 | "gulp-bower-files": "^0.2.7", 34 | "gulp-cache": "^0.2.2", 35 | "gulp-cached": "^1.0.1", 36 | "gulp-changed": "^1.0.0", 37 | "gulp-clean": "^0.3.1", 38 | "gulp-concat": "^2.4.1", 39 | "gulp-csslint": "~0.1.4", 40 | "gulp-csso": "^0.2.9", 41 | "gulp-embedlr": "~0.5.2", 42 | "gulp-express": "^0.1.0", 43 | "gulp-filesize": "*", 44 | "gulp-flatten": "*", 45 | "gulp-git": "^0.5.0", 46 | "gulp-header": "^1.0.2", 47 | "gulp-htmlmin": "^0.2.0", 48 | "gulp-if": "^1.2.4", 49 | "gulp-imagemin": "*", 50 | "gulp-inject": "^1.0.1", 51 | "gulp-jade": "^0.8.0", 52 | "gulp-jshint": "^1.8.4", 53 | "gulp-karma": "0.0.4", 54 | "gulp-livereload": "^2.1.1", 55 | "gulp-load-plugins": "^0.6.0", 56 | "gulp-minify-css": "~0.3.4", 57 | "gulp-ng-annotate": "~0.2.0", 58 | "gulp-ng-html2js": "~0.1.7", 59 | "gulp-ngmin": "^0.3.0", 60 | "gulp-nodemon": "^1.0.4", 61 | "gulp-recess": "*", 62 | "gulp-remember": "*", 63 | "gulp-rename": "^1.2.0", 64 | "gulp-rimraf": "^0.1.0", 65 | "gulp-ruby-sass": "^0.7.1", 66 | "gulp-sass": "^2.0.0", 67 | "gulp-run-sequence": "^0.3.2", 68 | "gulp-serve": "~0.2.0", 69 | "gulp-shell": "^0.2.9", 70 | "gulp-size": "^1.1.0", 71 | "gulp-sourcemaps": "*", 72 | "gulp-uglify": "^1.0.1", 73 | "gulp-uncss": "^0.4.5", 74 | "gulp-useref": "^1.0.1", 75 | "gulp-util": "^3.0.1", 76 | "gulp-watch": "^1.0.5", 77 | "http": "0.0.0", 78 | "jasmine-given": "*", 79 | "jit-grunt": "^0.5.0", 80 | "jshint-stylish": "~0.1.5", 81 | "karma": "~0.12.9", 82 | "karma-chai": "~0.1.0", 83 | "karma-chrome-launcher": "~0.1.3", 84 | "karma-coffee-preprocessor": "~0.2.1", 85 | "karma-firefox-launcher": "~0.1.3", 86 | "karma-html2js-preprocessor": "~0.1.0", 87 | "karma-jade-preprocessor": "0.0.11", 88 | "karma-jasmine": "~0.1.5", 89 | "karma-mocha": "~0.1.3", 90 | "karma-ng-html2js-preprocessor": "~0.1.0", 91 | "karma-ng-jade2js-preprocessor": "^0.1.2", 92 | "karma-ng-scenario": "~0.1.0", 93 | "karma-phantomjs-launcher": "~0.1.4", 94 | "karma-requirejs": "~0.2.1", 95 | "karma-script-launcher": "~0.1.0", 96 | "lazypipe": "~0.2.1", 97 | "main-bower-files": "^2.0.0", 98 | "nodemon": "^1.2.1", 99 | "open": "~0.0.4", 100 | "protractor": "~1.0.0", 101 | "requirejs": "~2.1.11", 102 | "rimraf": "^2.2.8", 103 | "should": "~3.3.1", 104 | "streamqueue": "~0.1.1", 105 | "supertest": "~0.11.0", 106 | "testem": "*", 107 | "time-grunt": "~0.3.1", 108 | "tiny-lr": "^0.1.4", 109 | "util": "^0.10.3", 110 | "wiredep": "^1.8.5", 111 | "yargs": "*" 112 | }, 113 | "engines": { 114 | "node": ">=0.12.5" 115 | }, 116 | "scripts": { 117 | "start": "node servers/server/app.js", 118 | "test": "gulp test", 119 | "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" 120 | }, 121 | "private": true 122 | } 123 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration 2 | // https://github.com/angular/protractor/blob/master/referenceConf.js 3 | 4 | 'use strict'; 5 | 6 | exports.config = { 7 | // The timeout for each script run on the browser. This should be longer 8 | // than the maximum time your application needs to stabilize between tasks. 9 | allScriptsTimeout: 110000, 10 | 11 | // A base URL for your application under test. Calls to protractor.get() 12 | // with relative paths will be prepended with this. 13 | baseUrl: 'http://localhost:' + (process.env.PORT || '9000'), 14 | 15 | // If true, only chromedriver will be started, not a standalone selenium. 16 | // Tests for browsers other than chrome will not run. 17 | chromeOnly: true, 18 | 19 | // list of files / patterns to load in the browser 20 | specs: [ 21 | 'e2e/**/*.spec.js' 22 | ], 23 | 24 | // Patterns to exclude. 25 | exclude: [], 26 | 27 | // ----- Capabilities to be passed to the webdriver instance ---- 28 | // 29 | // For a full list of available capabilities, see 30 | // https://code.google.com/p/selenium/wiki/DesiredCapabilities 31 | // and 32 | // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js 33 | capabilities: { 34 | 'browserName': 'chrome' 35 | }, 36 | 37 | // ----- The test framework ----- 38 | // 39 | // Jasmine and Cucumber are fully supported as a test and assertion framework. 40 | // Mocha has limited beta support. You will need to include your own 41 | // assertion framework if working with mocha. 42 | framework: 'jasmine', 43 | 44 | // ----- Options to be passed to minijasminenode ----- 45 | // 46 | // See the full list at https://github.com/juliemr/minijasminenode 47 | jasmineNodeOpts: { 48 | defaultTimeoutInterval: 30000 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /requirements/diagrams/v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/diagrams/v1.png -------------------------------------------------------------------------------- /requirements/diagrams/v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/diagrams/v2.png -------------------------------------------------------------------------------- /requirements/list-of-resources.txt: -------------------------------------------------------------------------------- 1 | 2 | 2:15 3 | Vertabelo: Database Modeling app 4 | http://www.vertabelo.com 5 | 6 | 15:15 7 | Building Blocks of Express 8 | https://www.codeschool.com/courses/building-blocks-of-express-js 9 | 10 | 15:25 11 | Shaping up with AngularJS 12 | http://campus.codeschool.com/courses/shaping-up-with-angular-js 13 | 14 | 15:30 15 | Soup to Bits GitHub repo 16 | https://github.com/codeschool/SequelToSQLSoupToBits 17 | 18 | 39:30 19 | Stack Overflow 20 | http://stackoverflow.com 21 | 22 | 40:00 23 | Limit and offset in sqlite 24 | https://www.sqlite.org/lang_select.html#limitoffset 25 | 26 | Limit and offset in pgsql 27 | http://www.postgresql.org/docs/9.4/static/queries-limit.html 28 | 29 | 41:50 30 | Datetime in MySQL 31 | https://dev.mysql.com/doc/refman/5.0/en/datetime.html 32 | 33 | 42:50 34 | Rails Active Record 35 | http://guides.rubyonrails.org/active_record_basics.html 36 | 37 | Rails for Zombies Redux 38 | https://www.codeschool.com/courses/rails-for-zombies-redux 39 | 40 | 43:20 41 | PostGIS: Plugin that allows geospatial queries in pgsql 42 | http://postgis.net 43 | 44 | 44:00 45 | "Groupon: Geolocation" Feature Focus 46 | https://www.codeschool.com/screencasts/groupon-geolocation 47 | 48 | 45:00 49 | NodeJS ORMs and Query Builders: 50 | http://knexjs.org 51 | http://bookshelfjs.org 52 | 53 | 45:45 54 | SailsJS: NodeJS Framework 55 | http://sailsjs.org 56 | 57 | 46:25 58 | Waterline: ORM built into SailJS 59 | https://github.com/balderdashy/waterline 60 | -------------------------------------------------------------------------------- /requirements/mockups/1-movies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/mockups/1-movies.png -------------------------------------------------------------------------------- /requirements/mockups/2-theatres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/mockups/2-theatres.png -------------------------------------------------------------------------------- /requirements/mockups/3-showtimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/mockups/3-showtimes.png -------------------------------------------------------------------------------- /requirements/mockups/annotated-1-movies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/mockups/annotated-1-movies.png -------------------------------------------------------------------------------- /requirements/mockups/annotated-2-theatres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/mockups/annotated-2-theatres.png -------------------------------------------------------------------------------- /requirements/mockups/annotated-3-showtimes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/requirements/mockups/annotated-3-showtimes.png -------------------------------------------------------------------------------- /requirements/queries/1-setup.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Source Server : sql2sql-stb 3 | Source Server Type : SQLite 4 | Source Server Version : 3007006 5 | Source Database : main 6 | 7 | Target Server Type : SQLite 8 | Target Server Version : 3007006 9 | File Encoding : utf-8 10 | 11 | */ 12 | 13 | PRAGMA foreign_keys = false; 14 | 15 | -- ---------------------------- 16 | -- Table structure for "movies" 17 | -- ---------------------------- 18 | DROP TABLE IF EXISTS "movies"; 19 | CREATE TABLE "movies" ( 20 | "id" integer PRIMARY KEY AUTOINCREMENT, 21 | "title" text 22 | ); 23 | 24 | -- ---------------------------- 25 | -- Records of "movies" 26 | -- ---------------------------- 27 | BEGIN; 28 | INSERT INTO "movies" VALUES (1, 'Don Juan'); 29 | INSERT INTO "movies" VALUES (2, 'The Lost World'); 30 | INSERT INTO "movies" VALUES (3, 'Peter Pan'); 31 | INSERT INTO "movies" VALUES (4, 'Robin Hood'); 32 | INSERT INTO "movies" VALUES (5, 'Wolfman'); 33 | COMMIT; 34 | 35 | -- ---------------------------- 36 | -- Table structure for "showtimes" 37 | -- ---------------------------- 38 | DROP TABLE IF EXISTS "showtimes"; 39 | CREATE TABLE "showtimes" ( 40 | "id" integer PRIMARY KEY AUTOINCREMENT, 41 | "time" text, 42 | "movie_id" integer, 43 | "theatre_id" integer, 44 | CONSTRAINT "fk_movies" FOREIGN KEY ("movie_id") REFERENCES "movies" ("id"), 45 | CONSTRAINT "fk_theatres" FOREIGN KEY ("theatre_id") REFERENCES "theatres" ("id") 46 | ); 47 | 48 | -- ---------------------------- 49 | -- Records of "showtimes" 50 | -- ---------------------------- 51 | BEGIN; 52 | INSERT INTO "showtimes" VALUES (1, '3:00 PM', 1, 1); 53 | INSERT INTO "showtimes" VALUES (2, '4:00 PM', 1, 2); 54 | INSERT INTO "showtimes" VALUES (3, '6:00 PM', 2, 3); 55 | INSERT INTO "showtimes" VALUES (4, '5:00 PM', 4, 3); 56 | INSERT INTO "showtimes" VALUES (5, '10:00 AM', 1, 4); 57 | INSERT INTO "showtimes" VALUES (6, '12:00 PM', 1, 1); 58 | INSERT INTO "showtimes" VALUES (7, '11:00 AM', 3, 1); 59 | INSERT INTO "showtimes" VALUES (8, '2:00 PM', 3, 1); 60 | INSERT INTO "showtimes" VALUES (9, '4:00 PM', 1, 2); 61 | INSERT INTO "showtimes" VALUES (10, '10:00 AM', 3, 2); 62 | INSERT INTO "showtimes" VALUES (11, '2:00 PM', 2, 1); 63 | INSERT INTO "showtimes" VALUES (12, '10:00 AM', 5, 1); 64 | INSERT INTO "showtimes" VALUES (13, '1:00 PM', 5, 1); 65 | INSERT INTO "showtimes" VALUES (14, '3:00 PM', 5, 1); 66 | COMMIT; 67 | 68 | -- ---------------------------- 69 | -- Table structure for "theatres" 70 | -- ---------------------------- 71 | DROP TABLE IF EXISTS "theatres"; 72 | CREATE TABLE "theatres" ( 73 | "id" integer PRIMARY KEY AUTOINCREMENT, 74 | "name" text 75 | ); 76 | 77 | -- ---------------------------- 78 | -- Records of "theatres" 79 | -- ---------------------------- 80 | BEGIN; 81 | INSERT INTO "theatres" VALUES (1, 'Plaza Cinema Café'); 82 | INSERT INTO "theatres" VALUES (2, 'AMC Universal Cineplex'); 83 | INSERT INTO "theatres" VALUES (3, 'Regal Winter Park'); 84 | INSERT INTO "theatres" VALUES (4, 'AMC Altamonte'); 85 | INSERT INTO "theatres" VALUES (5, 'Regal The Loop'); 86 | COMMIT; 87 | 88 | PRAGMA foreign_keys = true; 89 | -------------------------------------------------------------------------------- /requirements/queries/2-queries.sql: -------------------------------------------------------------------------------- 1 | -- THEATRES SECTION 2 | -- 3 | -- 4 | -- ALL THEATRES: 5 | -- 6 | -- 1) all theatres 7 | SELECT id, name 8 | FROM theatres; 9 | 10 | -- THEATRE PAGE: 11 | -- 12 | -- 2) one theatre 13 | SELECT id, name 14 | FROM theatres 15 | WHERE id = 1; 16 | 17 | -- 3) all movies playing in a theatre 18 | SELECT s.time, m.title 19 | FROM showtimes s 20 | INNER JOIN movies m ON m.id = s.movie_id 21 | WHERE s.theatre_id = 1 22 | ORDER BY m.title 23 | 24 | 25 | 26 | -- MOVIES SECTION 27 | -- 28 | -- 29 | -- MOVIE PAGE: 30 | -- 31 | -- 4) movie title 32 | SELECT id, title 33 | FROM movies 34 | WHERE id = 1; 35 | 36 | 37 | -- 5) list of theatres and showtimes 38 | SELECT t.name, s.time 39 | FROM showtimes s 40 | INNER JOIN theatres t ON t.id = s.theatre_id 41 | WHERE s.movie_id = 1 42 | ORDER BY t.name; 43 | 44 | -- 45 | -- ALL MOVIES PAGE: 46 | -- 47 | -- 6) all movies 48 | SELECT id, title 49 | FROM movies; 50 | 51 | -- 7a) counting in how many theatres a movie is being played 52 | -- point out count is incorrect (look back at data) 53 | SELECT movie_id, COUNT(theatre_id) 54 | FROM showtimes 55 | GROUP BY movie_id; 56 | 57 | -- 7b) add distinct to previous query 58 | SELECT movie_id, COUNT(DISTINCT theatre_id) 59 | FROM showtimes 60 | GROUP BY movie_id 61 | 62 | 63 | -- 8a) merge queries #6 and #7 into one 64 | -- all movie with their titles and count of theatres 65 | SELECT m.id, m.title, COUNT(DISTINCT s.theatre_id) as theatresCount 66 | FROM showtimes s 67 | INNER JOIN movies m ON m.id = s.movie_id 68 | GROUP BY s.movie_id 69 | 70 | -- 8b) show all movies with their count 71 | -- point that we were missing one movie that didn't have a count 72 | SELECT m.id, m.title, COUNT(DISTINCT s.theatre_id) as theatresCount 73 | FROM movies m 74 | LEFT OUTER JOIN showtimes s ON m.id = s.movie_id 75 | GROUP BY s.movie_id 76 | ORDER BY theatresCount DESC 77 | -------------------------------------------------------------------------------- /servers.runner.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /* 5 | * By requiring the the vile, node runs the server; 6 | */ 7 | module.exports.all = function(){ 8 | // Run all servers located in the servers dir 9 | require('./servers/server/app.js'); 10 | } 11 | module.exports.base = function(){ 12 | // Run Only the base server to serve up the app 13 | require('./servers/server/app.js'); 14 | } 15 | -------------------------------------------------------------------------------- /servers/server/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "esnext": true, 4 | "bitwise": true, 5 | "eqeqeq": true, 6 | "immed": true, 7 | "latedef": "nofunc", 8 | "newcap": true, 9 | "noarg": true, 10 | "regexp": true, 11 | "undef": true, 12 | "smarttabs": true, 13 | "asi": true, 14 | "debug": true 15 | } 16 | -------------------------------------------------------------------------------- /servers/server/.jshintrc-spec: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ".jshintrc", 3 | "globals": { 4 | "describe": true, 5 | "it": true, 6 | "before": true, 7 | "beforeEach": true, 8 | "after": true, 9 | "afterEach": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /servers/server/api/movie/index.js: -------------------------------------------------------------------------------- 1 | /* jshint unused: false */ 2 | 'use strict'; 3 | 4 | var express = require('express'); 5 | var router = express.Router(); 6 | 7 | /** 8 | * Mock Data 9 | */ 10 | var mockData = require('../../data/mocks'); 11 | 12 | /** 13 | * Dynamic data 14 | */ 15 | var db = require('../../data/db'); 16 | var queryAllMovies = 'SELECT m.id, m.title, COUNT(DISTINCT s.theatre_id) as theatresCount FROM movies m LEFT OUTER JOIN showtimes s ON m.id = s.movie_id GROUP BY s.movie_id ORDER BY theatresCount DESC'; 17 | var queryOneMovie = 'SELECT title FROM movies WHERE id = ?;'; 18 | var queryAllTheatres = 'SELECT s.time, t.name FROM showtimes s INNER JOIN theatres t ON t.id = s.theatre_id WHERE s.movie_id = ? ORDER BY t.name'; 19 | 20 | 21 | /** 22 | * Retuns list of all movies 23 | */ 24 | router.get('/', function(req, res) { 25 | 26 | var promise; 27 | 28 | if (queryAllMovies) { 29 | promise = db.query(queryAllMovies); 30 | } else { 31 | promise = mockData.movies.all; 32 | } 33 | 34 | promise 35 | .then(function(movies) { 36 | res.json({ 37 | data: movies 38 | }); 39 | }) 40 | .catch(function(err) { 41 | res.status(500).send({error: err}); 42 | }); 43 | 44 | }); 45 | 46 | /** 47 | * Returns single movie 48 | */ 49 | router.get('/:movie', function(req, res) { 50 | 51 | var promise; 52 | 53 | if (queryOneMovie) { 54 | promise = db.query(queryOneMovie, req.params.movie, 'one'); 55 | } else { 56 | promise = mockData.movies.one; 57 | } 58 | 59 | promise 60 | .then(function(movie) { 61 | res.json({ 62 | data: movie 63 | }); 64 | }) 65 | .catch(function(err) { 66 | res.status(500).send({ error: err }); 67 | }); 68 | 69 | }); 70 | 71 | /** 72 | * Returns list of theatres associated with a movie 73 | */ 74 | router.get('/:movie/theatres', function(req, res) { 75 | 76 | var promise; 77 | 78 | if (queryAllTheatres) { 79 | promise = db.query(queryAllTheatres, req.params.movie); 80 | } else { 81 | promise = mockData.movies.allTheatres; 82 | } 83 | 84 | var allTheatres = []; 85 | 86 | promise 87 | .then(function(theatres) { 88 | 89 | var prevTheatre; 90 | var currentTheatreIndex; 91 | 92 | theatres.forEach(function(theatre) { 93 | 94 | if (!(prevTheatre && prevTheatre.name === theatre.name)) { 95 | 96 | currentTheatreIndex = allTheatres.length 97 | 98 | // different 99 | allTheatres.push({ 100 | id: theatre.id, 101 | name: theatre.name, 102 | showtimes: [] 103 | }); 104 | 105 | } 106 | 107 | allTheatres[currentTheatreIndex].showtimes.push({ 108 | time: theatre.time 109 | }); 110 | 111 | prevTheatre = theatre; 112 | 113 | }); 114 | 115 | res.json({ 116 | data: allTheatres 117 | }); 118 | }) 119 | .catch(function(err) { 120 | res.status(500).send({ error: err }); 121 | }); 122 | 123 | }); 124 | 125 | module.exports = router; 126 | -------------------------------------------------------------------------------- /servers/server/api/theatre/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var express = require('express'); 4 | 5 | var router = express.Router(); 6 | 7 | /** 8 | * Mock Data 9 | */ 10 | var mockData = require('../../data/mocks'); 11 | 12 | /** 13 | * Dynamic data 14 | */ 15 | var db = require('../../data/db'); 16 | var queryAllTheatres = 'SELECT id, name FROM theatres'; 17 | var queryOneTheatre = 'SELECT id, name FROM theatres WHERE id = ?;'; 18 | var queryAllTheatresMovies = 'SELECT s.time, m.title FROM showtimes s INNER JOIN movies m ON m.id = s.movie_id WHERE s.theatre_id = ? ORDER BY m.title;'; 19 | 20 | router.get('/', function(req, res) { 21 | 22 | var promise; 23 | 24 | if (queryAllTheatres) { 25 | promise = db.query(queryAllTheatres); 26 | } else { 27 | promise = mockData.theatres.all; 28 | } 29 | 30 | promise 31 | .then(function(theatres) { 32 | res.json({ 33 | data: theatres 34 | }) 35 | .catch(function(err) { 36 | res.status(500).send({ error: err }); 37 | }); 38 | }); 39 | 40 | }); 41 | 42 | router.get('/:theatre', function(req, res) { 43 | 44 | var promise; 45 | 46 | if (queryOneTheatre) { 47 | promise = db.query(queryOneTheatre, req.params.theatre, 'one'); 48 | } else { 49 | promise = mockData.theatres.one; 50 | } 51 | 52 | promise 53 | .then(function(theatre) { 54 | res.json({ 55 | data: theatre 56 | }); 57 | }) 58 | .catch(function(err) { 59 | res.status(500).send({ error: err }); 60 | }); 61 | }); 62 | 63 | router.get('/:theatre/movies', function(req, res) { 64 | 65 | var allMovies = []; 66 | var promise; 67 | 68 | if (queryAllTheatresMovies) { 69 | promise = db.query(queryAllTheatresMovies, req.params.theatre) 70 | } else { 71 | promise = mockData.theatres.allMovies; 72 | } 73 | 74 | promise 75 | .then(function(movies) { 76 | 77 | var prevMovie; 78 | var currentMovieIndex; 79 | 80 | movies.forEach(function(movie) { 81 | 82 | if (!(prevMovie && prevMovie.title === movie.title)) { 83 | 84 | currentMovieIndex = allMovies.length; 85 | 86 | // different 87 | allMovies.push({ 88 | title: movie.title, 89 | showtimes: [] 90 | }); 91 | 92 | } 93 | 94 | allMovies[currentMovieIndex].showtimes.push({ 95 | time: movie.time 96 | }); 97 | 98 | prevMovie = movie; 99 | 100 | }); 101 | 102 | }) 103 | 104 | .then(function() { 105 | res.json({ 106 | data: allMovies 107 | }); 108 | }) 109 | .catch(function(err) { 110 | res.status(500).send({ error: err }); 111 | }); 112 | 113 | }); 114 | 115 | 116 | module.exports = router; 117 | -------------------------------------------------------------------------------- /servers/server/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main application file 3 | */ 4 | 5 | 'use strict'; 6 | 7 | // Set default node environment to development 8 | process.env.NODE_ENV = process.env.NODE_ENV || 'development'; 9 | 10 | var express = require('express'); 11 | var config = require('./config/environment'); 12 | // Setup server 13 | var app = express(); 14 | var server = require('http').createServer(app); 15 | require('./config/express')(app); 16 | require('./routes')(app); 17 | 18 | // Start server 19 | server.listen(config.port, config.ip, function () { 20 | console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); 21 | }); 22 | 23 | // Expose app 24 | exports = module.exports = app; -------------------------------------------------------------------------------- /servers/server/components/errors/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Error responses 3 | */ 4 | 5 | 'use strict'; 6 | 7 | module.exports[404] = function pageNotFound(req, res) { 8 | var viewFilePath = '404'; 9 | var statusCode = 404; 10 | var result = { 11 | status: statusCode 12 | }; 13 | 14 | res.status(result.status); 15 | res.json(result); 16 | }; 17 | -------------------------------------------------------------------------------- /servers/server/config/environment/development.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Development specific configuration 4 | // ================================== 5 | module.exports = { 6 | // MongoDB connection options 7 | mongo: { 8 | uri: 'mongodb://localhost/movies-dev' 9 | }, 10 | 11 | seedDB: true 12 | }; 13 | -------------------------------------------------------------------------------- /servers/server/config/environment/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var path = require('path'); 4 | var _ = require('lodash'); 5 | 6 | function requiredProcessEnv(name) { 7 | if(!process.env[name]) { 8 | throw new Error('You must set the ' + name + ' environment variable'); 9 | } 10 | return process.env[name]; 11 | } 12 | 13 | // All configurations will extend these options 14 | // ============================================ 15 | var all = { 16 | env: process.env.NODE_ENV, 17 | 18 | // Root path of server 19 | root: path.normalize(__dirname + '/../../../..'), 20 | 21 | // Server port 22 | port: process.env.PORT || 9000, 23 | 24 | // Should we populate the DB with sample data? 25 | seedDB: false, 26 | 27 | // Secret for session, you will want to change this and make it an environment variable 28 | secrets: { 29 | session: 'movies-secret' 30 | }, 31 | 32 | // List of user roles 33 | userRoles: ['guest', 'user', 'admin'], 34 | 35 | // MongoDB connection options 36 | mongo: { 37 | options: { 38 | db: { 39 | safe: true 40 | } 41 | } 42 | }, 43 | 44 | }; 45 | 46 | // Export the config object based on the NODE_ENV 47 | // ============================================== 48 | module.exports = _.merge( 49 | all, 50 | require('./' + process.env.NODE_ENV + '.js') || {}); -------------------------------------------------------------------------------- /servers/server/config/environment/production.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Production specific configuration 4 | // ================================= 5 | module.exports = { 6 | // Server IP 7 | ip: process.env.OPENSHIFT_NODEJS_IP || 8 | process.env.IP || 9 | undefined, 10 | 11 | // Server port 12 | port: process.env.OPENSHIFT_NODEJS_PORT || 13 | process.env.PORT || 14 | 8080, 15 | 16 | // MongoDB connection options 17 | mongo: { 18 | uri: process.env.MONGOLAB_URI || 19 | process.env.MONGOHQ_URL || 20 | process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME || 21 | 'mongodb://localhost/movies' 22 | } 23 | }; -------------------------------------------------------------------------------- /servers/server/config/environment/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Test specific configuration 4 | // =========================== 5 | module.exports = { 6 | // MongoDB connection options 7 | mongo: { 8 | uri: 'mongodb://localhost/movies-test' 9 | } 10 | }; -------------------------------------------------------------------------------- /servers/server/config/express.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Express configuration 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var express = require('express'); 8 | var favicon = require('serve-favicon'); 9 | var morgan = require('morgan'); 10 | var compression = require('compression'); 11 | var bodyParser = require('body-parser'); 12 | var methodOverride = require('method-override'); 13 | var cookieParser = require('cookie-parser'); 14 | var errorHandler = require('errorhandler'); 15 | var path = require('path'); 16 | var config = require('./environment'); 17 | 18 | module.exports = function(app) { 19 | var env = app.get('env'); 20 | 21 | app.set('views', config.root + '/servers/server/views'); 22 | app.engine('html', require('ejs').renderFile); 23 | app.set('view engine', 'html'); 24 | app.use(compression()); 25 | app.use(bodyParser.urlencoded({ extended: false })); 26 | app.use(bodyParser.json()); 27 | app.use(methodOverride()); 28 | app.use(cookieParser()); 29 | 30 | if ('production' === env) { 31 | app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); 32 | app.use(express.static(path.join(config.root, 'public'))); 33 | app.set('appPath', config.root + '/public'); 34 | app.use(morgan('dev')); 35 | } 36 | 37 | if ('development' === env || 'test' === env) { 38 | // app.use(require('connect-livereload')()); 39 | // app.use(express.static(path.join(config.root, '.tmp'))); 40 | // app.use(express.static(path.join(config.root, 'client'))); 41 | // app.set('appPath', 'client'); 42 | app.use(morgan('dev')); 43 | app.use(errorHandler()); // Error handler - has to be last 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /servers/server/config/local.env.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Use local.env.js for environment variables that grunt will set when the server starts locally. 4 | // Use for your api keys, secrets, etc. This file should not be tracked by git. 5 | // 6 | // You will need to set these on the server you deploy to. 7 | 8 | module.exports = { 9 | DOMAIN: 'http://localhost:9000', 10 | SESSION_SECRET: "movies-secret", 11 | // Control debug level for modules using visionmedia/debug 12 | DEBUG: '' 13 | }; 14 | -------------------------------------------------------------------------------- /servers/server/config/local.env.sample.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Use local.env.js for environment variables that grunt will set when the server starts locally. 4 | // Use for your api keys, secrets, etc. This file should not be tracked by git. 5 | // 6 | // You will need to set these on the server you deploy to. 7 | 8 | module.exports = { 9 | DOMAIN: 'http://localhost:9000', 10 | SESSION_SECRET: 'movies-secret', 11 | 12 | // Control debug level for modules using visionmedia/debug 13 | DEBUG: '' 14 | }; 15 | -------------------------------------------------------------------------------- /servers/server/data/db.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var sqlite3 = require('sqlite3').verbose(); 4 | var db = new sqlite3.Database(__dirname + '/sql2sql-stb.sqlite'); 5 | 6 | 7 | exports.query = function query(sqlQuery, params, action) { 8 | 9 | // DB Promise... 10 | var dbPromise = new Promise(function(resolve, reject) { 11 | 12 | // Stop right here if query is empty (db driver breaks otherwise) 13 | if (!sqlQuery) { 14 | return reject('We need a SQL query before proceeding'); 15 | } 16 | 17 | // Once sqlite file is read, callback gets executed 18 | db.serialize(function() { 19 | 20 | // For demo purposes, discards params if sqlQuery has no dynamic clauses 21 | if (params && !/\?/.test(sqlQuery)) { 22 | params = undefined; 23 | } 24 | 25 | // Executes query, calling either `each` or `all` function 26 | db[ (action === 'one') ? 'each' : 'all' ](sqlQuery, params, function(err, res) { 27 | 28 | // Rejects promise if error happened 29 | if (err) { 30 | return reject(err); 31 | } 32 | 33 | // Resolves list of movies 34 | resolve(res); 35 | 36 | }); 37 | 38 | }); 39 | 40 | }); 41 | 42 | return dbPromise; 43 | 44 | }; 45 | -------------------------------------------------------------------------------- /servers/server/data/mocks.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * All movies 5 | */ 6 | var allMovies = new Promise(function(resolve) { 7 | 8 | resolve([ 9 | { 10 | id: 1, 11 | title: 'Sample Movie', 12 | theatresCount: 2 13 | }, 14 | { 15 | id: 1, 16 | title: 'Sample Movie', 17 | theatresCount: 2 18 | }, 19 | { 20 | id: 1, 21 | title: 'Sample Movie', 22 | theatresCount: 2 23 | }, 24 | { 25 | id: 1, 26 | title: 'Sample Movie', 27 | theatresCount: 2 28 | }, 29 | { 30 | id: 1, 31 | title: 'Sample Movie', 32 | theatresCount: 2 33 | }, 34 | { 35 | id: 1, 36 | title: 'Sample Movie', 37 | theatresCount: 2 38 | }, 39 | { 40 | id: 1, 41 | title: 'Sample Movie', 42 | theatresCount: 2 43 | } 44 | ]); 45 | 46 | }); 47 | 48 | /** 49 | * One movie 50 | */ 51 | var oneMovie = new Promise(function (resolve) { 52 | 53 | resolve({ 54 | id: 1, 55 | title: 'Sample Movie' 56 | }); 57 | 58 | }); 59 | 60 | /** 61 | * All theatres 62 | */ 63 | var allTheatres = new Promise(function(resolve) { 64 | 65 | resolve([ 66 | { 67 | id: 1, 68 | name: 'Sample Theatre' 69 | }, 70 | { 71 | id: 1, 72 | name: 'Sample Theatre' 73 | }, 74 | { 75 | id: 1, 76 | name: 'Sample Theatre' 77 | }, 78 | { 79 | id: 1, 80 | name: 'Sample Theatre' 81 | }, 82 | { 83 | id: 1, 84 | name: 'Sample Theatre' 85 | }, 86 | { 87 | id: 1, 88 | name: 'Sample Theatre' 89 | }, 90 | { 91 | id: 1, 92 | name: 'Sample Theatre' 93 | } 94 | ]); 95 | 96 | }); 97 | 98 | /** 99 | * All movie theatres 100 | */ 101 | var allMovieTheatres = new Promise(function(resolve) { 102 | 103 | resolve([ 104 | { 105 | id: 1, 106 | name: 'Sample Theatre', 107 | time: '3:00 PM' 108 | }, 109 | { 110 | id: 1, 111 | name: 'Sample Theatre', 112 | time: '4:00 PM' 113 | }, 114 | { 115 | id: 1, 116 | name: 'Sample Theatre', 117 | time: '5:00 PM' 118 | }, 119 | { 120 | id: 1, 121 | name: 'Sample Theatre 2', 122 | time: '3:00 PM' 123 | }, 124 | { 125 | id: 1, 126 | name: 'Sample Theatre 2', 127 | time: '4:00 PM' 128 | }, 129 | { 130 | id: 1, 131 | name: 'Sample Theatre 2', 132 | time: '5:00 PM' 133 | } 134 | ]); 135 | 136 | }); 137 | 138 | var allShowtimes = new Promise(function(resolve) { 139 | 140 | resolve([ 141 | { 142 | time: '3:00 PM' 143 | }, 144 | { 145 | time: '3:00 PM' 146 | }, 147 | { 148 | time: '3:00 PM' 149 | }, 150 | { 151 | time: '3:00 PM' 152 | } 153 | ]); 154 | 155 | }); 156 | 157 | /** 158 | * All theatre movies 159 | */ 160 | var allTheatreMovies = new Promise(function(resolve) { 161 | 162 | resolve([ 163 | { 164 | id: 1, 165 | title: 'Sample Movie', 166 | time: '3:00 PM' 167 | }, 168 | { 169 | id: 1, 170 | title: 'Sample Movie', 171 | time: '4:00 PM' 172 | }, 173 | { 174 | id: 1, 175 | title: 'Sample Movie', 176 | time: '5:00 PM' 177 | }, 178 | { 179 | id: 1, 180 | title: 'Sample Movie 2', 181 | time: '3:00 PM' 182 | }, 183 | { 184 | id: 1, 185 | title: 'Sample Movie 2', 186 | time: '4:00 PM' 187 | }, 188 | { 189 | id: 1, 190 | title: 'Sample Movie 2', 191 | time: '5:00 PM' 192 | } 193 | ]); 194 | 195 | }); 196 | 197 | var oneTheatre = new Promise(function (resolve) { 198 | resolve({ 199 | id: 1, 200 | name: 'Sample Theatre' 201 | }); 202 | }); 203 | 204 | var allTheatresMoviesShowtimes = new Promise(function (resolve) { 205 | resolve([ 206 | { 207 | time: '3:00 PM' 208 | }, 209 | { 210 | time: '3:00 PM' 211 | }, 212 | { 213 | time: '3:00 PM' 214 | }, 215 | { 216 | time: '3:00 PM' 217 | } 218 | ]); 219 | }); 220 | 221 | 222 | 223 | module.exports = { 224 | movies: { 225 | all: allMovies, 226 | one: oneMovie, 227 | allTheatres: allMovieTheatres 228 | }, 229 | 230 | theatres: { 231 | all: allTheatres, 232 | one: oneTheatre, 233 | allMovies: allTheatreMovies, 234 | allMoviesShowtimes: allTheatresMoviesShowtimes 235 | }, 236 | 237 | showtimes: { 238 | all: allShowtimes 239 | } 240 | }; 241 | -------------------------------------------------------------------------------- /servers/server/data/sql2sql-stb.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codeschool/WatchUsBuild-MovieListingsAppWithSQL/21f752a46f57fbdb3ab26a934deb72f8622b9beb/servers/server/data/sql2sql-stb.sqlite -------------------------------------------------------------------------------- /servers/server/routes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Main application routes 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var errors = require('./components/errors'); 8 | var cors = require('cors') 9 | module.exports = function(app) { 10 | app.use(cors()); 11 | // Insert routes below 12 | app.use('/api/theatres', require('./api/theatre')); 13 | app.use('/api/movies', require('./api/movie')); 14 | 15 | // All undefined asset or api routes should return a 404 16 | app.route('/:url(api|auth|components|app|bower_components|assets)/*') 17 | .get(errors[404]); 18 | 19 | // All other routes should redirect to the index.html 20 | app.route('/*') 21 | .get(function(req, res) { 22 | res.sendfile(app.get('appPath') + '/index.html'); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /servers/server/views/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |
144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | --------------------------------------------------------------------------------