diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..666f347 --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "client/bower_components" +} diff --git a/.buildignore b/.buildignore new file mode 100644 index 0000000..fc98b8e --- /dev/null +++ b/.buildignore @@ -0,0 +1 @@ +*.coffee \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c2cdfb8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 2 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c029da6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +node_modules +public +.tmp +.idea +client/bower_components +dist +/server/config/local.env.js \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..7989278 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,8 @@ +language: node_js +node_js: + - '0.10' + - '0.11' +before_script: + - npm install -g bower grunt-cli + - bower install +services: mongodb \ No newline at end of file diff --git a/.yo-rc.json b/.yo-rc.json new file mode 100644 index 0000000..d76348a --- /dev/null +++ b/.yo-rc.json @@ -0,0 +1,46 @@ +{ + "generator-angular-fullstack": { + "insertRoutes": true, + "registerRoutesFile": "server/routes.js", + "routesNeedle": "// Insert routes below", + "routesBase": "/api/", + "pluralizeRoutes": true, + "insertSockets": true, + "registerSocketsFile": "server/config/socketio.js", + "socketsNeedle": "// Insert sockets below", + "filters": { + "js": true, + "html": true, + "css": true, + "uirouter": true, + "bootstrap": true, + "uibootstrap": true, + "mongoose": true + } + }, + "generator-ng-component": { + "routeDirectory": "client/app/", + "directiveDirectory": "client/app/", + "filterDirectory": "client/app/", + "serviceDirectory": "client/app/", + "basePath": "client", + "moduleName": "", + "filters": [ + "uirouter" + ], + "extensions": [ + "js", + "html", + "css" + ], + "directiveSimpleTemplates": "", + "directiveComplexTemplates": "", + "filterTemplates": "", + "serviceTemplates": "", + "factoryTemplates": "", + "controllerTemplates": "", + "decoratorTemplates": "", + "providerTemplates": "", + "routeTemplates": "" + } +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..44342fc --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,613 @@ +// Generated on 2015-03-10 using generator-angular-fullstack 2.0.13 +'use strict'; + +module.exports = function (grunt) { + var localConfig; + try { + localConfig = require('./server/config/local.env'); + } catch(e) { + localConfig = {}; + } + + // Load grunt tasks automatically, when needed + require('jit-grunt')(grunt, { + express: 'grunt-express-server', + useminPrepare: 'grunt-usemin', + ngtemplates: 'grunt-angular-templates', + cdnify: 'grunt-google-cdn', + protractor: 'grunt-protractor-runner', + injector: 'grunt-asset-injector', + buildcontrol: 'grunt-build-control' + }); + + // Time how long tasks take. Can help when optimizing build times + require('time-grunt')(grunt); + + // Define the configuration for all the tasks + grunt.initConfig({ + + // Project settings + pkg: grunt.file.readJSON('package.json'), + yeoman: { + // configurable paths + client: require('./bower.json').appPath || 'client', + dist: 'dist' + }, + express: { + options: { + port: process.env.PORT || 9000 + }, + dev: { + options: { + script: 'server/app.js', + debug: true + } + }, + prod: { + options: { + script: 'dist/server/app.js' + } + } + }, + open: { + server: { + url: 'http://localhost:<%= express.options.port %>' + } + }, + watch: { + injectJS: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.js', + '!<%= yeoman.client %>/{app,components}/**/*.spec.js', + '!<%= yeoman.client %>/{app,components}/**/*.mock.js', + '!<%= yeoman.client %>/app/app.js'], + tasks: ['injector:scripts'] + }, + injectCss: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.css' + ], + tasks: ['injector:css'] + }, + mochaTest: { + files: ['server/**/*.spec.js'], + tasks: ['env:test', 'mochaTest'] + }, + jsTest: { + files: [ + '<%= yeoman.client %>/{app,components}/**/*.spec.js', + '<%= yeoman.client %>/{app,components}/**/*.mock.js' + ], + tasks: ['newer:jshint:all', 'karma'] + }, + gruntfile: { + files: ['Gruntfile.js'] + }, + livereload: { + files: [ + '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.css', + '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.html', + '{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js', + '!{.tmp,<%= yeoman.client %>}{app,components}/**/*.spec.js', + '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js', + '<%= yeoman.client %>/assets/images/{,*//*}*.{png,jpg,jpeg,gif,webp,svg}' + ], + options: { + livereload: true + } + }, + express: { + files: [ + 'server/**/*.{js,json}' + ], + tasks: ['express:dev', 'wait'], + options: { + livereload: true, + nospawn: true //Without this option specified express won't be reloaded + } + } + }, + + // Make sure code styles are up to par and there are no obvious mistakes + jshint: { + options: { + jshintrc: '<%= yeoman.client %>/.jshintrc', + reporter: require('jshint-stylish') + }, + server: { + options: { + jshintrc: 'server/.jshintrc' + }, + src: [ + 'server/**/*.js', + '!server/**/*.spec.js' + ] + }, + serverTest: { + options: { + jshintrc: 'server/.jshintrc-spec' + }, + src: ['server/**/*.spec.js'] + }, + all: [ + '<%= yeoman.client %>/{app,components}/**/*.js', + '!<%= yeoman.client %>/{app,components}/**/*.spec.js', + '!<%= yeoman.client %>/{app,components}/**/*.mock.js' + ], + test: { + src: [ + '<%= yeoman.client %>/{app,components}/**/*.spec.js', + '<%= yeoman.client %>/{app,components}/**/*.mock.js' + ] + } + }, + + // Empties folders to start fresh + clean: { + dist: { + files: [{ + dot: true, + src: [ + '.tmp', + '<%= yeoman.dist %>/*', + '!<%= yeoman.dist %>/.git*', + '!<%= yeoman.dist %>/.openshift', + '!<%= yeoman.dist %>/Procfile' + ] + }] + }, + server: '.tmp' + }, + + // Add vendor prefixed styles + autoprefixer: { + options: { + browsers: ['last 1 version'] + }, + dist: { + files: [{ + expand: true, + cwd: '.tmp/', + src: '{,*/}*.css', + dest: '.tmp/' + }] + } + }, + + // Debugging with node inspector + 'node-inspector': { + custom: { + options: { + 'web-host': 'localhost' + } + } + }, + + // Use nodemon to run server in debug mode with an initial breakpoint + nodemon: { + debug: { + script: 'server/app.js', + options: { + nodeArgs: ['--debug-brk'], + env: { + PORT: process.env.PORT || 9000 + }, + callback: function (nodemon) { + nodemon.on('log', function (event) { + console.log(event.colour); + }); + + // opens browser on initial server start + nodemon.on('config:update', function () { + setTimeout(function () { + require('open')('http://localhost:8080/debug?port=5858'); + }, 500); + }); + } + } + } + }, + + // Automatically inject Bower components into the app + wiredep: { + target: { + src: '<%= yeoman.client %>/index.html', + ignorePath: '<%= yeoman.client %>/', + exclude: [/bootstrap-sass-official/, /bootstrap.js/, '/json3/', '/es5-shim/'] + } + }, + + // Renames files for browser caching purposes + rev: { + dist: { + files: { + src: [ + '<%= yeoman.dist %>/public/{,*/}*.js', + '<%= yeoman.dist %>/public/{,*/}*.css', + '<%= yeoman.dist %>/public/assets/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', + '<%= yeoman.dist %>/public/assets/fonts/*' + ] + } + } + }, + + // Reads HTML for usemin blocks to enable smart builds that automatically + // concat, minify and revision files. Creates configurations in memory so + // additional tasks can operate on them + useminPrepare: { + html: ['<%= yeoman.client %>/index.html'], + options: { + dest: '<%= yeoman.dist %>/public' + } + }, + + // Performs rewrites based on rev and the useminPrepare configuration + usemin: { + html: ['<%= yeoman.dist %>/public/{,*/}*.html'], + css: ['<%= yeoman.dist %>/public/{,*/}*.css'], + js: ['<%= yeoman.dist %>/public/{,*/}*.js'], + options: { + assetsDirs: [ + '<%= yeoman.dist %>/public', + '<%= yeoman.dist %>/public/assets/images' + ], + // This is so we update image references in our ng-templates + patterns: { + js: [ + [/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images'] + ] + } + } + }, + + // The following *-min tasks produce minified files in the dist folder + imagemin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.client %>/assets/images', + src: '{,*/}*.{png,jpg,jpeg,gif}', + dest: '<%= yeoman.dist %>/public/assets/images' + }] + } + }, + + svgmin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.client %>/assets/images', + src: '{,*/}*.svg', + dest: '<%= yeoman.dist %>/public/assets/images' + }] + } + }, + + // Allow the use of non-minsafe AngularJS files. Automatically makes it + // minsafe compatible so Uglify does not destroy the ng references + ngAnnotate: { + dist: { + files: [{ + expand: true, + cwd: '.tmp/concat', + src: '*/**.js', + dest: '.tmp/concat' + }] + } + }, + + // Package all the html partials into a single javascript payload + ngtemplates: { + options: { + // This should be the name of your apps angular module + module: 'jRoomsApp', + htmlmin: { + collapseBooleanAttributes: true, + collapseWhitespace: true, + removeAttributeQuotes: true, + removeEmptyAttributes: true, + removeRedundantAttributes: true, + removeScriptTypeAttributes: true, + removeStyleLinkTypeAttributes: true + }, + usemin: 'app/app.js' + }, + main: { + cwd: '<%= yeoman.client %>', + src: ['{app,components}/**/*.html'], + dest: '.tmp/templates.js' + }, + tmp: { + cwd: '.tmp', + src: ['{app,components}/**/*.html'], + dest: '.tmp/tmp-templates.js' + } + }, + + // Replace Google CDN references + cdnify: { + dist: { + html: ['<%= yeoman.dist %>/public/*.html'] + } + }, + + // Copies remaining files to places other tasks can use + copy: { + dist: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.client %>', + dest: '<%= yeoman.dist %>/public', + src: [ + '*.{ico,png,txt}', + '.htaccess', + 'bower_components/**/*', + 'assets/images/{,*/}*.{webp}', + 'assets/fonts/**/*', + 'index.html' + ] + }, { + expand: true, + cwd: '.tmp/images', + dest: '<%= yeoman.dist %>/public/assets/images', + src: ['generated/*'] + }, { + expand: true, + dest: '<%= yeoman.dist %>', + src: [ + 'package.json', + 'server/**/*' + ] + }] + }, + styles: { + expand: true, + cwd: '<%= yeoman.client %>', + dest: '.tmp/', + src: ['{app,components}/**/*.css'] + } + }, + + buildcontrol: { + options: { + dir: 'dist', + commit: true, + push: true, + connectCommits: false, + message: 'Built %sourceName% from commit %sourceCommit% on branch %sourceBranch%' + }, + heroku: { + options: { + remote: 'heroku', + branch: 'master' + } + }, + openshift: { + options: { + remote: 'openshift', + branch: 'master' + } + } + }, + + // Run some tasks in parallel to speed up the build process + concurrent: { + server: [ + ], + test: [ + ], + debug: { + tasks: [ + 'nodemon', + 'node-inspector' + ], + options: { + logConcurrentOutput: true + } + }, + dist: [ + 'imagemin', + 'svgmin' + ] + }, + + // Test settings + karma: { + unit: { + configFile: 'karma.conf.js', + singleRun: true + } + }, + + mochaTest: { + options: { + reporter: 'spec' + }, + src: ['server/**/*.spec.js'] + }, + + protractor: { + options: { + configFile: 'protractor.conf.js' + }, + chrome: { + options: { + args: { + browser: 'chrome' + } + } + } + }, + + env: { + test: { + NODE_ENV: 'test' + }, + prod: { + NODE_ENV: 'production' + }, + all: localConfig + }, + + injector: { + options: { + + }, + // Inject application script files into index.html (doesn't include bower) + scripts: { + options: { + transform: function(filePath) { + filePath = filePath.replace('/client/', ''); + filePath = filePath.replace('/.tmp/', ''); + return ''; + }, + starttag: '', + endtag: '' + }, + files: { + '<%= yeoman.client %>/index.html': [ + ['{.tmp,<%= yeoman.client %>}/{app,components}/**/*.js', + '!{.tmp,<%= yeoman.client %>}/app/app.js', + '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.spec.js', + '!{.tmp,<%= yeoman.client %>}/{app,components}/**/*.mock.js'] + ] + } + }, + + // Inject component css into index.html + css: { + options: { + transform: function(filePath) { + filePath = filePath.replace('/client/', ''); + filePath = filePath.replace('/.tmp/', ''); + return ''; + }, + starttag: '', + endtag: '' + }, + files: { + '<%= yeoman.client %>/index.html': [ + '<%= yeoman.client %>/{app,components}/**/*.css' + ] + } + } + }, + }); + + // Used for delaying livereload until after server has restarted + grunt.registerTask('wait', function () { + grunt.log.ok('Waiting for server reload...'); + + var done = this.async(); + + setTimeout(function () { + grunt.log.writeln('Done waiting!'); + done(); + }, 1500); + }); + + grunt.registerTask('express-keepalive', 'Keep grunt running', function() { + this.async(); + }); + + grunt.registerTask('serve', function (target) { + if (target === 'dist') { + return grunt.task.run(['build', 'env:all', 'env:prod', 'express:prod', 'wait', 'open', 'express-keepalive']); + } + + if (target === 'debug') { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'concurrent:server', + 'injector', + 'wiredep', + 'autoprefixer', + 'concurrent:debug' + ]); + } + + grunt.task.run([ + 'clean:server', + 'env:all', + 'concurrent:server', + 'injector', + 'wiredep', + 'autoprefixer', + 'express:dev', + 'wait', + 'open', + 'watch' + ]); + }); + + grunt.registerTask('server', function () { + grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); + grunt.task.run(['serve']); + }); + + grunt.registerTask('test', function(target) { + if (target === 'server') { + return grunt.task.run([ + 'env:all', + 'env:test', + 'mochaTest' + ]); + } + + else if (target === 'client') { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'concurrent:test', + 'injector', + 'autoprefixer', + 'karma' + ]); + } + + else if (target === 'e2e') { + return grunt.task.run([ + 'clean:server', + 'env:all', + 'env:test', + 'concurrent:test', + 'injector', + 'wiredep', + 'autoprefixer', + 'express:dev', + 'protractor' + ]); + } + + else grunt.task.run([ + 'test:server', + 'test:client' + ]); + }); + + grunt.registerTask('build', [ + 'clean:dist', + 'concurrent:dist', + 'injector', + 'wiredep', + 'useminPrepare', + 'autoprefixer', + 'ngtemplates', + 'concat', + 'ngAnnotate', + 'copy:dist', + 'cdnify', + 'cssmin', + 'uglify', + 'rev', + 'usemin' + ]); + + grunt.registerTask('default', [ + 'newer:jshint', + 'test', + 'build' + ]); +}; diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d7f1051 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ +GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..aed6491 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +Write this. diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..6ee5238 --- /dev/null +++ b/bower.json @@ -0,0 +1,22 @@ +{ + "name": "jRooms", + "version": "0.0.0", + "dependencies": { + "angular": ">=1.2.*", + "json3": "~3.3.1", + "es5-shim": "~3.0.1", + "jquery": "~1.11.0", + "bootstrap": "~3.1.1", + "angular-resource": ">=1.2.*", + "angular-cookies": ">=1.2.*", + "angular-sanitize": ">=1.2.*", + "angular-bootstrap": "~0.11.0", + "font-awesome": ">=4.1.0", + "lodash": "~2.4.1", + "angular-ui-router": "~0.2.10" + }, + "devDependencies": { + "angular-mocks": ">=1.2.*", + "angular-scenario": ">=1.2.*" + } +} diff --git a/client/.htaccess b/client/.htaccess new file mode 100644 index 0000000..cb84cb9 --- /dev/null +++ b/client/.htaccess @@ -0,0 +1,543 @@ +# Apache Configuration File + +# (!) Using `.htaccess` files slows down Apache, therefore, if you have access +# to the main server config file (usually called `httpd.conf`), you should add +# this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. + +# ############################################################################## +# # CROSS-ORIGIN RESOURCE SHARING (CORS) # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Cross-domain AJAX requests | +# ------------------------------------------------------------------------------ + +# Enable cross-origin AJAX requests. +# http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity +# http://enable-cors.org/ + +# +# Header set Access-Control-Allow-Origin "*" +# + +# ------------------------------------------------------------------------------ +# | CORS-enabled images | +# ------------------------------------------------------------------------------ + +# Send the CORS header for images when browsers request it. +# https://developer.mozilla.org/en/CORS_Enabled_Image +# http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html +# http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ + + + + + SetEnvIf Origin ":" IS_CORS + Header set Access-Control-Allow-Origin "*" env=IS_CORS + + + + +# ------------------------------------------------------------------------------ +# | Web fonts access | +# ------------------------------------------------------------------------------ + +# Allow access from all domains for web fonts + + + + Header set Access-Control-Allow-Origin "*" + + + + +# ############################################################################## +# # ERRORS # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | 404 error prevention for non-existing redirected folders | +# ------------------------------------------------------------------------------ + +# Prevent Apache from returning a 404 error for a rewrite if a directory +# with the same name does not exist. +# http://httpd.apache.org/docs/current/content-negotiation.html#multiviews +# http://www.webmasterworld.com/apache/3808792.htm + +Options -MultiViews + +# ------------------------------------------------------------------------------ +# | Custom error messages / pages | +# ------------------------------------------------------------------------------ + +# You can customize what Apache returns to the client in case of an error (see +# http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: + +ErrorDocument 404 /404.html + + +# ############################################################################## +# # INTERNET EXPLORER # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Better website experience | +# ------------------------------------------------------------------------------ + +# Force IE to render pages in the highest available mode in the various +# cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. + + + Header set X-UA-Compatible "IE=edge" + # `mod_headers` can't match based on the content-type, however, we only + # want to send this header for HTML pages and not for the other resources + + Header unset X-UA-Compatible + + + +# ------------------------------------------------------------------------------ +# | Cookie setting from iframes | +# ------------------------------------------------------------------------------ + +# Allow cookies to be set from iframes in IE. + +# +# Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" +# + +# ------------------------------------------------------------------------------ +# | Screen flicker | +# ------------------------------------------------------------------------------ + +# Stop screen flicker in IE on CSS rollovers (this only works in +# combination with the `ExpiresByType` directives for images from below). + +# BrowserMatch "MSIE" brokenvary=1 +# BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 +# BrowserMatch "Opera" !brokenvary +# SetEnvIf brokenvary 1 force-no-vary + + +# ############################################################################## +# # MIME TYPES AND ENCODING # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Proper MIME types for all files | +# ------------------------------------------------------------------------------ + + + + # Audio + AddType audio/mp4 m4a f4a f4b + AddType audio/ogg oga ogg + + # JavaScript + # Normalize to standard type (it's sniffed in IE anyways): + # http://tools.ietf.org/html/rfc4329#section-7.2 + AddType application/javascript js jsonp + AddType application/json json + + # Video + AddType video/mp4 mp4 m4v f4v f4p + AddType video/ogg ogv + AddType video/webm webm + AddType video/x-flv flv + + # Web fonts + AddType application/font-woff woff + AddType application/vnd.ms-fontobject eot + + # Browsers usually ignore the font MIME types and sniff the content, + # however, Chrome shows a warning if other MIME types are used for the + # following fonts. + AddType application/x-font-ttf ttc ttf + AddType font/opentype otf + + # Make SVGZ fonts work on iPad: + # https://twitter.com/FontSquirrel/status/14855840545 + AddType image/svg+xml svg svgz + AddEncoding gzip svgz + + # Other + AddType application/octet-stream safariextz + AddType application/x-chrome-extension crx + AddType application/x-opera-extension oex + AddType application/x-shockwave-flash swf + AddType application/x-web-app-manifest+json webapp + AddType application/x-xpinstall xpi + AddType application/xml atom rdf rss xml + AddType image/webp webp + AddType image/x-icon ico + AddType text/cache-manifest appcache manifest + AddType text/vtt vtt + AddType text/x-component htc + AddType text/x-vcard vcf + + + +# ------------------------------------------------------------------------------ +# | UTF-8 encoding | +# ------------------------------------------------------------------------------ + +# Use UTF-8 encoding for anything served as `text/html` or `text/plain`. +AddDefaultCharset utf-8 + +# Force UTF-8 for certain file formats. + + AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml + + + +# ############################################################################## +# # URL REWRITES # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Rewrite engine | +# ------------------------------------------------------------------------------ + +# Turning on the rewrite engine and enabling the `FollowSymLinks` option is +# necessary for the following directives to work. + +# If your web host doesn't allow the `FollowSymlinks` option, you may need to +# comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the +# performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks + +# Also, some cloud hosting services require `RewriteBase` to be set: +# http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site + + + Options +FollowSymlinks + # Options +SymLinksIfOwnerMatch + RewriteEngine On + # RewriteBase / + + +# ------------------------------------------------------------------------------ +# | Suppressing / Forcing the "www." at the beginning of URLs | +# ------------------------------------------------------------------------------ + +# The same content should never be available under two different URLs especially +# not with and without "www." at the beginning. This can cause SEO problems +# (duplicate content), therefore, you should choose one of the alternatives and +# redirect the other one. + +# By default option 1 (no "www.") is activated: +# http://no-www.org/faq.php?q=class_b + +# If you'd prefer to use option 2, just comment out all the lines from option 1 +# and uncomment the ones from option 2. + +# IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Option 1: rewrite www.example.com → example.com + + + RewriteCond %{HTTPS} !=on + RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] + RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Option 2: rewrite example.com → www.example.com + +# Be aware that the following might not be a good idea if you use "real" +# subdomains for certain parts of your website. + +# +# RewriteCond %{HTTPS} !=on +# RewriteCond %{HTTP_HOST} !^www\..+$ [NC] +# RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] +# + + +# ############################################################################## +# # SECURITY # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Content Security Policy (CSP) | +# ------------------------------------------------------------------------------ + +# You can mitigate the risk of cross-site scripting and other content-injection +# attacks by setting a Content Security Policy which whitelists trusted sources +# of content for your site. + +# The example header below allows ONLY scripts that are loaded from the current +# site's origin (no inline scripts, no CDN, etc). This almost certainly won't +# work as-is for your site! + +# To get all the details you'll need to craft a reasonable policy for your site, +# read: http://html5rocks.com/en/tutorials/security/content-security-policy (or +# see the specification: http://w3.org/TR/CSP). + +# +# Header set Content-Security-Policy "script-src 'self'; object-src 'self'" +# +# Header unset Content-Security-Policy +# +# + +# ------------------------------------------------------------------------------ +# | File access | +# ------------------------------------------------------------------------------ + +# Block access to directories without a default document. +# Usually you should leave this uncommented because you shouldn't allow anyone +# to surf through every directory on your server (which may includes rather +# private places like the CMS's directories). + + + Options -Indexes + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Block access to hidden files and directories. +# This includes directories used by version control systems such as Git and SVN. + + + RewriteCond %{SCRIPT_FILENAME} -d [OR] + RewriteCond %{SCRIPT_FILENAME} -f + RewriteRule "(^|/)\." - [F] + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Block access to backup and source files. +# These files may be left by some text editors and can pose a great security +# danger when anyone has access to them. + + + Order allow,deny + Deny from all + Satisfy All + + +# ------------------------------------------------------------------------------ +# | Secure Sockets Layer (SSL) | +# ------------------------------------------------------------------------------ + +# Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: +# prevent `https://www.example.com` when your certificate only allows +# `https://secure.example.com`. + +# +# RewriteCond %{SERVER_PORT} !^443 +# RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] +# + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +# Force client-side SSL redirection. + +# If a user types "example.com" in his browser, the above rule will redirect him +# to the secure version of the site. That still leaves a window of opportunity +# (the initial HTTP connection) for an attacker to downgrade or redirect the +# request. The following header ensures that browser will ONLY connect to your +# server via HTTPS, regardless of what the users type in the address bar. +# http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ + +# +# Header set Strict-Transport-Security max-age=16070400; +# + +# ------------------------------------------------------------------------------ +# | Server software information | +# ------------------------------------------------------------------------------ + +# Avoid displaying the exact Apache version number, the description of the +# generic OS-type and the information about Apache's compiled-in modules. + +# ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! + +# ServerTokens Prod + + +# ############################################################################## +# # WEB PERFORMANCE # +# ############################################################################## + +# ------------------------------------------------------------------------------ +# | Compression | +# ------------------------------------------------------------------------------ + + + + # Force compression for mangled headers. + # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + + # Compress all output labeled with one of the following MIME-types + # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` + # and can remove the `` and `` lines + # as `AddOutputFilterByType` is still in the core directives). + + AddOutputFilterByType DEFLATE application/atom+xml \ + application/javascript \ + application/json \ + application/rss+xml \ + application/vnd.ms-fontobject \ + application/x-font-ttf \ + application/x-web-app-manifest+json \ + application/xhtml+xml \ + application/xml \ + font/opentype \ + image/svg+xml \ + image/x-icon \ + text/css \ + text/html \ + text/plain \ + text/x-component \ + text/xml + + + + +# ------------------------------------------------------------------------------ +# | Content transformations | +# ------------------------------------------------------------------------------ + +# Prevent some of the mobile network providers from modifying the content of +# your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. + +# +# Header set Cache-Control "no-transform" +# + +# ------------------------------------------------------------------------------ +# | ETag removal | +# ------------------------------------------------------------------------------ + +# Since we're sending far-future expires headers (see below), ETags can +# be removed: http://developer.yahoo.com/performance/rules.html#etags. + +# `FileETag None` is not enough for every server. + + Header unset ETag + + +FileETag None + +# ------------------------------------------------------------------------------ +# | Expires headers (for better cache control) | +# ------------------------------------------------------------------------------ + +# The following expires headers are set pretty far in the future. If you don't +# control versioning with filename-based cache busting, consider lowering the +# cache time for resources like CSS and JS to something like 1 week. + + + + ExpiresActive on + ExpiresDefault "access plus 1 month" + + # CSS + ExpiresByType text/css "access plus 1 year" + + # Data interchange + ExpiresByType application/json "access plus 0 seconds" + ExpiresByType application/xml "access plus 0 seconds" + ExpiresByType text/xml "access plus 0 seconds" + + # Favicon (cannot be renamed!) + ExpiresByType image/x-icon "access plus 1 week" + + # HTML components (HTCs) + ExpiresByType text/x-component "access plus 1 month" + + # HTML + ExpiresByType text/html "access plus 0 seconds" + + # JavaScript + ExpiresByType application/javascript "access plus 1 year" + + # Manifest files + ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" + ExpiresByType text/cache-manifest "access plus 0 seconds" + + # Media + ExpiresByType audio/ogg "access plus 1 month" + ExpiresByType image/gif "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType image/png "access plus 1 month" + ExpiresByType video/mp4 "access plus 1 month" + ExpiresByType video/ogg "access plus 1 month" + ExpiresByType video/webm "access plus 1 month" + + # Web feeds + ExpiresByType application/atom+xml "access plus 1 hour" + ExpiresByType application/rss+xml "access plus 1 hour" + + # Web fonts + ExpiresByType application/font-woff "access plus 1 month" + ExpiresByType application/vnd.ms-fontobject "access plus 1 month" + ExpiresByType application/x-font-ttf "access plus 1 month" + ExpiresByType font/opentype "access plus 1 month" + ExpiresByType image/svg+xml "access plus 1 month" + + + +# ------------------------------------------------------------------------------ +# | Filename-based cache busting | +# ------------------------------------------------------------------------------ + +# If you're not using a build process to manage your filename version revving, +# you might want to consider enabling the following directives to route all +# requests such as `/css/style.12345.css` to `/css/style.css`. + +# To understand why this is important and a better idea than `*.css?v231`, read: +# http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring + +# +# RewriteCond %{REQUEST_FILENAME} !-f +# RewriteCond %{REQUEST_FILENAME} !-d +# RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] +# + +# ------------------------------------------------------------------------------ +# | File concatenation | +# ------------------------------------------------------------------------------ + +# Allow concatenation from within specific CSS and JS files, e.g.: +# Inside of `script.combined.js` you could have +# +# +# and they would be included into this single file. + +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES application/javascript application/json +# SetOutputFilter INCLUDES +# +# +# Options +Includes +# AddOutputFilterByType INCLUDES text/css +# SetOutputFilter INCLUDES +# +# + +# ------------------------------------------------------------------------------ +# | Persistent connections | +# ------------------------------------------------------------------------------ + +# Allow multiple requests to be sent over the same TCP connection: +# http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. + +# Enable if you serve a lot of static content but, be aware of the +# possible disadvantages! + +# +# Header set Connection Keep-Alive +# diff --git a/client/.jshintrc b/client/.jshintrc new file mode 100644 index 0000000..52c6a6d --- /dev/null +++ b/client/.jshintrc @@ -0,0 +1,38 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "globals": { + "jQuery": true, + "angular": true, + "console": true, + "$": true, + "_": true, + "moment": true, + "describe": true, + "beforeEach": true, + "module": true, + "inject": true, + "it": true, + "expect": true, + "browser": true, + "element": true, + "by": true + } +} diff --git a/client/app/Communicator/Communicator.service.js b/client/app/Communicator/Communicator.service.js new file mode 100644 index 0000000..3e35c62 --- /dev/null +++ b/client/app/Communicator/Communicator.service.js @@ -0,0 +1,32 @@ +'use strict'; + +angular.module('jRoomsApp').service('Communicator', function ($http) { + + this.openJUB = 'https://api.jacobs-cs.club'; + this.server = 'http://localhost:3000'; + + this.sendGET = function(route, body, fn) { + $http.get(this.server + route, body) + .success(function(data, status, headers, config) { + fn(null, data); + }) + .error(function(data, status, headers, config) { + fn({ status: status, error: data }, null); + }); + } + + this.sendPOST = function(route, body, fn) { + $http.post(this.server + route, body) + .success(function(data, status, headers, config) { + fn(null, data); + }) + .error(function(data, status, headers, config) { + fn({ status: status, error: data }, null); + }); + } + + this.getCurrentUser = function(fn) { + this.sendGET('/user/me', {}, fn); + } + +}); diff --git a/client/app/Communicator/Communicator.service.spec.js b/client/app/Communicator/Communicator.service.spec.js new file mode 100644 index 0000000..50de56e --- /dev/null +++ b/client/app/Communicator/Communicator.service.spec.js @@ -0,0 +1,18 @@ +'use strict'; + +describe('Service: Communicator', function () { + + // load the service's module + beforeEach(module('jRoomsApp')); + + // instantiate service + var Communicator; + beforeEach(inject(function (_Communicator_) { + Communicator = _Communicator_; + })); + + it('should do something', function () { + expect(!!Communicator).toBe(true); + }); + +}); diff --git a/client/app/State/State.service.js b/client/app/State/State.service.js new file mode 100644 index 0000000..4e9a4e9 --- /dev/null +++ b/client/app/State/State.service.js @@ -0,0 +1,52 @@ +'use strict'; + +angular.module('jRoomsApp') + .service('State', function ($cookies, Communicator) { + this.openJUB = 'https://api.jacobs-cs.club'; + + this.loggedIn = false; + this.isAdmin = false; + this.user = null; + this.currentPhase = null; + + if ($cookies.token) { + Communicator.getCurrentUser(function(err, data) { + if (err == null && data != null) { + this.user = data; + } + }); + } + + this.login = function() { + console.log($cookies); + + window.addEventListener('message', function(e) { + if (e.origin !== this.openJUB) return; + var data = JSON.parse(e.data); + + if (data && data.token) { + this.loggedIn = true; + $cookies.token = data.token; + + Communicator.getCurrentUser(function(err, data) { + if (err == null && data != null) { + this.user = data; + } + }); + } + else { + this.logout(); + } + }); + + window.open('https://api.jacobs-cs.club/view/login', '_blank', 'width=500, height=500, resizeable=0, toolbar=0, scrollbar=0, location=0'); + } + + this.logout = function() { + this.loggedIn = false; + this.isAdmin = false; + this.user = null; + + $cookies.token = null; + } +}); diff --git a/client/app/State/State.service.spec.js b/client/app/State/State.service.spec.js new file mode 100644 index 0000000..7f068d5 --- /dev/null +++ b/client/app/State/State.service.spec.js @@ -0,0 +1,18 @@ +'use strict'; + +describe('Service: State', function () { + + // load the service's module + beforeEach(module('jRoomsApp')); + + // instantiate service + var State; + beforeEach(inject(function (_State_) { + State = _State_; + })); + + it('should do something', function () { + expect(!!State).toBe(true); + }); + +}); diff --git a/client/app/about/about.controller.js b/client/app/about/about.controller.js new file mode 100644 index 0000000..3c1419a --- /dev/null +++ b/client/app/about/about.controller.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('jRoomsApp') + .controller('AboutCtrl', function ($scope) { + $scope.message = 'Hello'; + }); diff --git a/client/app/about/about.controller.spec.js b/client/app/about/about.controller.spec.js new file mode 100644 index 0000000..d6890d8 --- /dev/null +++ b/client/app/about/about.controller.spec.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('Controller: AboutCtrl', function () { + + // load the controller's module + beforeEach(module('jRoomsApp')); + + var AboutCtrl, scope; + + // Initialize the controller and a mock scope + beforeEach(inject(function ($controller, $rootScope) { + scope = $rootScope.$new(); + AboutCtrl = $controller('AboutCtrl', { + $scope: scope + }); + })); + + it('should ...', function () { + expect(1).toEqual(1); + }); +}); diff --git a/client/app/about/about.css b/client/app/about/about.css new file mode 100644 index 0000000..e69de29 diff --git a/client/app/about/about.html b/client/app/about/about.html new file mode 100644 index 0000000..b001c2f --- /dev/null +++ b/client/app/about/about.html @@ -0,0 +1,3 @@ +
+This is the about view. +
diff --git a/client/app/about/about.js b/client/app/about/about.js new file mode 100644 index 0000000..6c1292e --- /dev/null +++ b/client/app/about/about.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('jRoomsApp') + .config(function ($stateProvider) { + $stateProvider + .state('about', { + url: '/about', + templateUrl: 'app/about/about.html', + controller: 'AboutCtrl' + }); + }); \ No newline at end of file diff --git a/client/app/admin/admin.controller.js b/client/app/admin/admin.controller.js new file mode 100644 index 0000000..3018eb6 --- /dev/null +++ b/client/app/admin/admin.controller.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('jRoomsApp') + .controller('AdminCtrl', function ($scope) { + $scope.message = 'Hello'; + }); diff --git a/client/app/admin/admin.controller.spec.js b/client/app/admin/admin.controller.spec.js new file mode 100644 index 0000000..1a9921e --- /dev/null +++ b/client/app/admin/admin.controller.spec.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('Controller: AdminCtrl', function () { + + // load the controller's module + beforeEach(module('jRoomsApp')); + + var AdminCtrl, scope; + + // Initialize the controller and a mock scope + beforeEach(inject(function ($controller, $rootScope) { + scope = $rootScope.$new(); + AdminCtrl = $controller('AdminCtrl', { + $scope: scope + }); + })); + + it('should ...', function () { + expect(1).toEqual(1); + }); +}); diff --git a/client/app/admin/admin.css b/client/app/admin/admin.css new file mode 100644 index 0000000..e69de29 diff --git a/client/app/admin/admin.html b/client/app/admin/admin.html new file mode 100644 index 0000000..50f1c16 --- /dev/null +++ b/client/app/admin/admin.html @@ -0,0 +1,3 @@ +
+This is the admin view. +
diff --git a/client/app/admin/admin.js b/client/app/admin/admin.js new file mode 100644 index 0000000..0de2336 --- /dev/null +++ b/client/app/admin/admin.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('jRoomsApp') + .config(function ($stateProvider) { + $stateProvider + .state('admin', { + url: '/admin', + templateUrl: 'app/admin/admin.html', + controller: 'AdminCtrl' + }); + }); \ No newline at end of file diff --git a/client/app/app.css b/client/app/app.css new file mode 100644 index 0000000..5a90f72 --- /dev/null +++ b/client/app/app.css @@ -0,0 +1,39 @@ + +/** + * Bootstrap Fonts + */ + +@font-face { + font-family: 'Glyphicons Halflings'; + src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot'); + src: url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), + url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.woff') format('woff'), + url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.ttf') format('truetype'), + url('../bower_components/bootstrap/fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); +} + +/** + *Font Awesome Fonts + */ + +@font-face { + font-family: 'FontAwesome'; + src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?v=4.1.0'); + src: url('../bower_components/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'), + url('../bower_components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'), + url('../bower_components/font-awesome/fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'), + url('../bower_components/font-awesome/fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg'); + font-weight: normal; + font-style: normal; +} + +/** + * App-wide Styles + */ + +.browsehappy { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} diff --git a/client/app/app.js b/client/app/app.js new file mode 100644 index 0000000..f39d536 --- /dev/null +++ b/client/app/app.js @@ -0,0 +1,15 @@ +'use strict'; + +angular.module('jRoomsApp', [ + 'ngCookies', + 'ngResource', + 'ngSanitize', + 'ui.router', + 'ui.bootstrap' +]) + .config(function ($stateProvider, $urlRouterProvider, $locationProvider) { + $urlRouterProvider + .otherwise('/'); + + $locationProvider.html5Mode(true); + }); \ No newline at end of file diff --git a/client/app/faq/faq.controller.js b/client/app/faq/faq.controller.js new file mode 100644 index 0000000..90e64e0 --- /dev/null +++ b/client/app/faq/faq.controller.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('jRoomsApp') + .controller('FaqCtrl', function ($scope) { + $scope.message = 'Hello'; + }); diff --git a/client/app/faq/faq.controller.spec.js b/client/app/faq/faq.controller.spec.js new file mode 100644 index 0000000..25ee833 --- /dev/null +++ b/client/app/faq/faq.controller.spec.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('Controller: FaqCtrl', function () { + + // load the controller's module + beforeEach(module('jRoomsApp')); + + var FaqCtrl, scope; + + // Initialize the controller and a mock scope + beforeEach(inject(function ($controller, $rootScope) { + scope = $rootScope.$new(); + FaqCtrl = $controller('FaqCtrl', { + $scope: scope + }); + })); + + it('should ...', function () { + expect(1).toEqual(1); + }); +}); diff --git a/client/app/faq/faq.css b/client/app/faq/faq.css new file mode 100644 index 0000000..e69de29 diff --git a/client/app/faq/faq.html b/client/app/faq/faq.html new file mode 100644 index 0000000..9fcbd87 --- /dev/null +++ b/client/app/faq/faq.html @@ -0,0 +1,3 @@ +
+This is the faq view. +
diff --git a/client/app/faq/faq.js b/client/app/faq/faq.js new file mode 100644 index 0000000..bd0d1ce --- /dev/null +++ b/client/app/faq/faq.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('jRoomsApp') + .config(function ($stateProvider) { + $stateProvider + .state('faq', { + url: '/faq', + templateUrl: 'app/faq/faq.html', + controller: 'FaqCtrl' + }); + }); \ No newline at end of file diff --git a/client/app/home/home.controller.js b/client/app/home/home.controller.js new file mode 100644 index 0000000..ef63b00 --- /dev/null +++ b/client/app/home/home.controller.js @@ -0,0 +1,6 @@ +'use strict'; + +angular.module('jRoomsApp') + .controller('HomeCtrl', function ($scope) { + $scope.message = 'Hello'; + }); diff --git a/client/app/home/home.controller.spec.js b/client/app/home/home.controller.spec.js new file mode 100644 index 0000000..e8acb61 --- /dev/null +++ b/client/app/home/home.controller.spec.js @@ -0,0 +1,21 @@ +'use strict'; + +describe('Controller: HomeCtrl', function () { + + // load the controller's module + beforeEach(module('jRoomsApp')); + + var HomeCtrl, scope; + + // Initialize the controller and a mock scope + beforeEach(inject(function ($controller, $rootScope) { + scope = $rootScope.$new(); + HomeCtrl = $controller('HomeCtrl', { + $scope: scope + }); + })); + + it('should ...', function () { + expect(1).toEqual(1); + }); +}); diff --git a/client/app/home/home.css b/client/app/home/home.css new file mode 100644 index 0000000..e69de29 diff --git a/client/app/home/home.html b/client/app/home/home.html new file mode 100644 index 0000000..84d2c43 --- /dev/null +++ b/client/app/home/home.html @@ -0,0 +1,3 @@ +
+This is the home view. +
diff --git a/client/app/home/home.js b/client/app/home/home.js new file mode 100644 index 0000000..60f1731 --- /dev/null +++ b/client/app/home/home.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('jRoomsApp') + .config(function ($stateProvider) { + $stateProvider + .state('home', { + url: '/home', + templateUrl: 'app/home/home.html', + controller: 'HomeCtrl' + }); + }); \ No newline at end of file diff --git a/client/app/main/main.controller.js b/client/app/main/main.controller.js new file mode 100644 index 0000000..da792d4 --- /dev/null +++ b/client/app/main/main.controller.js @@ -0,0 +1,22 @@ +'use strict'; + +angular.module('jRoomsApp') + .controller('MainCtrl', function ($scope, $http) { + $scope.awesomeThings = []; + + $http.get('/api/things').success(function(awesomeThings) { + $scope.awesomeThings = awesomeThings; + }); + + $scope.addThing = function() { + if($scope.newThing === '') { + return; + } + $http.post('/api/things', { name: $scope.newThing }); + $scope.newThing = ''; + }; + + $scope.deleteThing = function(thing) { + $http.delete('/api/things/' + thing._id); + }; + }); diff --git a/client/app/main/main.controller.spec.js b/client/app/main/main.controller.spec.js new file mode 100644 index 0000000..8f94d30 --- /dev/null +++ b/client/app/main/main.controller.spec.js @@ -0,0 +1,28 @@ +'use strict'; + +describe('Controller: MainCtrl', function () { + + // load the controller's module + beforeEach(module('jRoomsApp')); + + var MainCtrl, + scope, + $httpBackend; + + // Initialize the controller and a mock scope + beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) { + $httpBackend = _$httpBackend_; + $httpBackend.expectGET('/api/things') + .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']); + + scope = $rootScope.$new(); + MainCtrl = $controller('MainCtrl', { + $scope: scope + }); + })); + + it('should attach a list of things to the scope', function () { + $httpBackend.flush(); + expect(scope.awesomeThings.length).toBe(4); + }); +}); diff --git a/client/app/main/main.css b/client/app/main/main.css new file mode 100644 index 0000000..ba3408a --- /dev/null +++ b/client/app/main/main.css @@ -0,0 +1,30 @@ +.thing-form { + margin: 20px 0; +} + +#banner { + border-bottom: none; + margin-top: -20px; +} + +#banner h1 { + font-size: 60px; + line-height: 1; + letter-spacing: -1px; +} + +.hero-unit { + position: relative; + padding: 30px 15px; + color: #F5F5F5; + text-align: center; + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); + background: #4393B9; +} + +.footer { + text-align: center; + padding: 30px 0; + margin-top: 70px; + border-top: 1px solid #E5E5E5; +} \ No newline at end of file diff --git a/client/app/main/main.html b/client/app/main/main.html new file mode 100644 index 0000000..ae96313 --- /dev/null +++ b/client/app/main/main.html @@ -0,0 +1,22 @@ +
+ + + +
+
+
+

Features:

+ +
+
+
+ +
diff --git a/client/app/main/main.js b/client/app/main/main.js new file mode 100644 index 0000000..b53f672 --- /dev/null +++ b/client/app/main/main.js @@ -0,0 +1,11 @@ +'use strict'; + +angular.module('jRoomsApp') + .config(function ($stateProvider) { + $stateProvider + .state('main', { + url: '/', + templateUrl: 'app/main/main.html', + controller: 'MainCtrl' + }); + }); \ No newline at end of file diff --git a/client/assets/images/yeoman.png b/client/assets/images/yeoman.png new file mode 100644 index 0000000..7d0a1ac Binary files /dev/null and b/client/assets/images/yeoman.png differ diff --git a/client/components/footer/footer.html b/client/components/footer/footer.html new file mode 100644 index 0000000..4e38e60 --- /dev/null +++ b/client/components/footer/footer.html @@ -0,0 +1,11 @@ + diff --git a/client/components/modal/modal.css b/client/components/modal/modal.css new file mode 100644 index 0000000..f5cc0d9 --- /dev/null +++ b/client/components/modal/modal.css @@ -0,0 +1,23 @@ +.modal-primary .modal-header, +.modal-info .modal-header, +.modal-success .modal-header, +.modal-warning .modal-header, +.modal-danger .modal-header { + color: #fff; + border-radius: 5px 5px 0 0; +} +.modal-primary .modal-header { + background: #428bca; +} +.modal-info .modal-header { + background: #5bc0de; +} +.modal-success .modal-header { + background: #5cb85c; +} +.modal-warning .modal-header { + background: #f0ad4e; +} +.modal-danger .modal-header { + background: #d9534f; +} \ No newline at end of file diff --git a/client/components/modal/modal.html b/client/components/modal/modal.html new file mode 100644 index 0000000..4580254 --- /dev/null +++ b/client/components/modal/modal.html @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/client/components/modal/modal.service.js b/client/components/modal/modal.service.js new file mode 100644 index 0000000..0cc8d7a --- /dev/null +++ b/client/components/modal/modal.service.js @@ -0,0 +1,77 @@ +'use strict'; + +angular.module('jRoomsApp') + .factory('Modal', function ($rootScope, $modal) { + /** + * Opens a modal + * @param {Object} scope - an object to be merged with modal's scope + * @param {String} modalClass - (optional) class(es) to be applied to the modal + * @return {Object} - the instance $modal.open() returns + */ + function openModal(scope, modalClass) { + var modalScope = $rootScope.$new(); + scope = scope || {}; + modalClass = modalClass || 'modal-default'; + + angular.extend(modalScope, scope); + + return $modal.open({ + templateUrl: 'components/modal/modal.html', + windowClass: modalClass, + scope: modalScope + }); + } + + // Public API here + return { + + /* Confirmation modals */ + confirm: { + + /** + * Create a function to open a delete confirmation modal (ex. ng-click='myModalFn(name, arg1, arg2...)') + * @param {Function} del - callback, ran when delete is confirmed + * @return {Function} - the function to open the modal (ex. myModalFn) + */ + delete: function(del) { + del = del || angular.noop; + + /** + * Open a delete confirmation modal + * @param {String} name - name or info to show on modal + * @param {All} - any additional args are passed staight to del callback + */ + return function() { + var args = Array.prototype.slice.call(arguments), + name = args.shift(), + deleteModal; + + deleteModal = openModal({ + modal: { + dismissable: true, + title: 'Confirm Delete', + html: '

Are you sure you want to delete ' + name + ' ?

', + buttons: [{ + classes: 'btn-danger', + text: 'Delete', + click: function(e) { + deleteModal.close(e); + } + }, { + classes: 'btn-default', + text: 'Cancel', + click: function(e) { + deleteModal.dismiss(e); + } + }] + } + }, 'modal-danger'); + + deleteModal.result.then(function(event) { + del.apply(event, args); + }); + }; + } + } + }; + }); diff --git a/client/components/navbar/navbar.controller.js b/client/components/navbar/navbar.controller.js new file mode 100644 index 0000000..d9fc875 --- /dev/null +++ b/client/components/navbar/navbar.controller.js @@ -0,0 +1,12 @@ +'use strict'; + +angular.module('jRoomsApp') + .controller('NavbarCtrl', function ($scope, $location, State) { + + $scope.isCollapsed = true; + $scope.state = State; + + $scope.isActive = function(route) { + return route === $location.path(); + }; + }); \ No newline at end of file diff --git a/client/components/navbar/navbar.html b/client/components/navbar/navbar.html new file mode 100644 index 0000000..e7c2c5b --- /dev/null +++ b/client/components/navbar/navbar.html @@ -0,0 +1,26 @@ + diff --git a/client/favicon.ico b/client/favicon.ico new file mode 100644 index 0000000..8a163fb Binary files /dev/null and b/client/favicon.ico differ diff --git a/client/index.html b/client/index.html new file mode 100644 index 0000000..fffc6e5 --- /dev/null +++ b/client/index.html @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/robots.txt b/client/robots.txt new file mode 100644 index 0000000..9417495 --- /dev/null +++ b/client/robots.txt @@ -0,0 +1,3 @@ +# robotstxt.org + +User-agent: * diff --git a/e2e/main/main.po.js b/e2e/main/main.po.js new file mode 100644 index 0000000..6718608 --- /dev/null +++ b/e2e/main/main.po.js @@ -0,0 +1,15 @@ +/** + * This file uses the Page Object pattern to define the main page for tests + * https://docs.google.com/presentation/d/1B6manhG0zEXkC-H-tPo2vwU06JhL8w9-XCF9oehXzAQ + */ + +'use strict'; + +var MainPage = function() { + this.heroEl = element(by.css('.hero-unit')); + this.h1El = this.heroEl.element(by.css('h1')); + this.imgEl = this.heroEl.element(by.css('img')); +}; + +module.exports = new MainPage(); + diff --git a/e2e/main/main.spec.js b/e2e/main/main.spec.js new file mode 100644 index 0000000..61745a8 --- /dev/null +++ b/e2e/main/main.spec.js @@ -0,0 +1,16 @@ +'use strict'; + +describe('Main View', function() { + var page; + + beforeEach(function() { + browser.get('/'); + page = require('./main.po'); + }); + + it('should include jumbotron with correct data', function() { + expect(page.h1El.getText()).toBe('\'Allo, \'Allo!'); + expect(page.imgEl.getAttribute('src')).toMatch(/assets\/images\/yeoman.png$/); + expect(page.imgEl.getAttribute('alt')).toBe('I\'m Yeoman'); + }); +}); diff --git a/karma.conf.js b/karma.conf.js new file mode 100644 index 0000000..d698526 --- /dev/null +++ b/karma.conf.js @@ -0,0 +1,80 @@ +// Karma configuration +// http://karma-runner.github.io/0.10/config/configuration-file.html + +module.exports = function(config) { + config.set({ + // base path, that will be used to resolve files and exclude + basePath: '', + + // testing framework to use (jasmine/mocha/qunit/...) + frameworks: ['jasmine'], + + // list of files / patterns to load in the browser + files: [ + 'client/bower_components/jquery/dist/jquery.js', + 'client/bower_components/angular/angular.js', + 'client/bower_components/angular-mocks/angular-mocks.js', + 'client/bower_components/angular-resource/angular-resource.js', + 'client/bower_components/angular-cookies/angular-cookies.js', + 'client/bower_components/angular-sanitize/angular-sanitize.js', + 'client/bower_components/angular-route/angular-route.js', + 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js', + 'client/bower_components/lodash/dist/lodash.compat.js', + 'client/bower_components/angular-ui-router/release/angular-ui-router.js', + 'client/app/app.js', + 'client/app/app.coffee', + 'client/app/**/*.js', + 'client/app/**/*.coffee', + 'client/components/**/*.js', + 'client/components/**/*.coffee', + 'client/app/**/*.jade', + 'client/components/**/*.jade', + 'client/app/**/*.html', + 'client/components/**/*.html' + ], + + preprocessors: { + '**/*.jade': 'ng-jade2js', + '**/*.html': 'html2js', + '**/*.coffee': 'coffee', + }, + + ngHtml2JsPreprocessor: { + stripPrefix: 'client/' + }, + + ngJade2JsPreprocessor: { + stripPrefix: 'client/' + }, + + // list of files / patterns to exclude + exclude: [], + + // web server port + port: 8080, + + // level of logging + // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG + logLevel: config.LOG_INFO, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + browsers: ['PhantomJS'], + + + // Continuous Integration mode + // if true, it capture browsers, run tests and exit + singleRun: false + }); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..8cf1ea9 --- /dev/null +++ b/package.json @@ -0,0 +1,84 @@ +{ + "name": "jRooms", + "version": "0.0.0", + "main": "server/app.js", + "dependencies": { + "express": "~4.0.0", + "morgan": "~1.0.0", + "body-parser": "~1.5.0", + "method-override": "~1.0.0", + "serve-favicon": "~2.0.1", + "cookie-parser": "~1.0.1", + "express-session": "~1.0.2", + "errorhandler": "~1.0.0", + "compression": "~1.0.1", + "lodash": "~2.4.1", + "ejs": "~0.8.4", + "mongoose": "~3.8.8", + "composable-middleware": "^0.3.0", + "connect-mongo": "^0.4.1" + }, + "devDependencies": { + "grunt": "~0.4.4", + "grunt-autoprefixer": "~0.7.2", + "grunt-wiredep": "~1.8.0", + "grunt-concurrent": "~0.5.0", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-concat": "~0.4.0", + "grunt-contrib-copy": "~0.5.0", + "grunt-contrib-cssmin": "~0.9.0", + "grunt-contrib-htmlmin": "~0.2.0", + "grunt-contrib-imagemin": "~0.7.1", + "grunt-contrib-jshint": "~0.10.0", + "grunt-contrib-uglify": "~0.4.0", + "grunt-contrib-watch": "~0.6.1", + "grunt-google-cdn": "~0.4.0", + "grunt-newer": "~0.7.0", + "grunt-ng-annotate": "^0.2.3", + "grunt-rev": "~0.1.0", + "grunt-svgmin": "~0.4.0", + "grunt-usemin": "~2.1.1", + "grunt-env": "~0.4.1", + "grunt-node-inspector": "~0.1.5", + "grunt-nodemon": "~0.2.0", + "grunt-angular-templates": "^0.5.4", + "grunt-dom-munger": "^3.4.0", + "grunt-protractor-runner": "^1.1.0", + "grunt-asset-injector": "^0.1.0", + "grunt-karma": "~0.8.2", + "grunt-build-control": "DaftMonk/grunt-build-control", + "grunt-mocha-test": "~0.10.2", + "jit-grunt": "^0.5.0", + "time-grunt": "~0.3.1", + "grunt-express-server": "~0.4.17", + "grunt-open": "~0.2.3", + "open": "~0.0.4", + "jshint-stylish": "~0.1.5", + "connect-livereload": "~0.4.0", + "karma-ng-scenario": "~0.1.0", + "karma-firefox-launcher": "~0.1.3", + "karma-script-launcher": "~0.1.0", + "karma-html2js-preprocessor": "~0.1.0", + "karma-ng-jade2js-preprocessor": "^0.1.2", + "karma-jasmine": "~0.1.5", + "karma-chrome-launcher": "~0.1.3", + "requirejs": "~2.1.11", + "karma-requirejs": "~0.2.1", + "karma-coffee-preprocessor": "~0.2.1", + "karma-jade-preprocessor": "0.0.11", + "karma-phantomjs-launcher": "~0.1.4", + "karma": "~0.12.9", + "karma-ng-html2js-preprocessor": "~0.1.0", + "supertest": "~0.11.0", + "should": "~3.3.1" + }, + "engines": { + "node": ">=0.10.0" + }, + "scripts": { + "start": "node server/app.js", + "test": "grunt test", + "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" + }, + "private": true +} diff --git a/protractor.conf.js b/protractor.conf.js new file mode 100644 index 0000000..cb66c67 --- /dev/null +++ b/protractor.conf.js @@ -0,0 +1,50 @@ +// Protractor configuration +// https://github.com/angular/protractor/blob/master/referenceConf.js + +'use strict'; + +exports.config = { + // The timeout for each script run on the browser. This should be longer + // than the maximum time your application needs to stabilize between tasks. + allScriptsTimeout: 110000, + + // A base URL for your application under test. Calls to protractor.get() + // with relative paths will be prepended with this. + baseUrl: 'http://localhost:' + (process.env.PORT || '9000'), + + // If true, only chromedriver will be started, not a standalone selenium. + // Tests for browsers other than chrome will not run. + chromeOnly: true, + + // list of files / patterns to load in the browser + specs: [ + 'e2e/**/*.spec.js' + ], + + // Patterns to exclude. + exclude: [], + + // ----- Capabilities to be passed to the webdriver instance ---- + // + // For a full list of available capabilities, see + // https://code.google.com/p/selenium/wiki/DesiredCapabilities + // and + // https://code.google.com/p/selenium/source/browse/javascript/webdriver/capabilities.js + capabilities: { + 'browserName': 'chrome' + }, + + // ----- The test framework ----- + // + // Jasmine and Cucumber are fully supported as a test and assertion framework. + // Mocha has limited beta support. You will need to include your own + // assertion framework if working with mocha. + framework: 'jasmine', + + // ----- Options to be passed to minijasminenode ----- + // + // See the full list at https://github.com/juliemr/minijasminenode + jasmineNodeOpts: { + defaultTimeoutInterval: 30000 + } +}; diff --git a/server/.jshintrc b/server/.jshintrc new file mode 100644 index 0000000..d7b958e --- /dev/null +++ b/server/.jshintrc @@ -0,0 +1,15 @@ +{ + "node": true, + "esnext": true, + "bitwise": true, + "eqeqeq": true, + "immed": true, + "latedef": "nofunc", + "newcap": true, + "noarg": true, + "regexp": true, + "undef": true, + "smarttabs": true, + "asi": true, + "debug": true +} diff --git a/server/.jshintrc-spec b/server/.jshintrc-spec new file mode 100644 index 0000000..b6b55cb --- /dev/null +++ b/server/.jshintrc-spec @@ -0,0 +1,11 @@ +{ + "extends": ".jshintrc", + "globals": { + "describe": true, + "it": true, + "before": true, + "beforeEach": true, + "after": true, + "afterEach": true + } +} diff --git a/server/api/thing/index.js b/server/api/thing/index.js new file mode 100644 index 0000000..845c9f0 --- /dev/null +++ b/server/api/thing/index.js @@ -0,0 +1,15 @@ +'use strict'; + +var express = require('express'); +var controller = require('./thing.controller'); + +var router = express.Router(); + +router.get('/', controller.index); +router.get('/:id', controller.show); +router.post('/', controller.create); +router.put('/:id', controller.update); +router.patch('/:id', controller.update); +router.delete('/:id', controller.destroy); + +module.exports = router; \ No newline at end of file diff --git a/server/api/thing/thing.controller.js b/server/api/thing/thing.controller.js new file mode 100644 index 0000000..be6541e --- /dev/null +++ b/server/api/thing/thing.controller.js @@ -0,0 +1,68 @@ +/** + * Using Rails-like standard naming convention for endpoints. + * GET /things -> index + * POST /things -> create + * GET /things/:id -> show + * PUT /things/:id -> update + * DELETE /things/:id -> destroy + */ + +'use strict'; + +var _ = require('lodash'); +var Thing = require('./thing.model'); + +// Get list of things +exports.index = function(req, res) { + Thing.find(function (err, things) { + if(err) { return handleError(res, err); } + return res.json(200, things); + }); +}; + +// Get a single thing +exports.show = function(req, res) { + Thing.findById(req.params.id, function (err, thing) { + if(err) { return handleError(res, err); } + if(!thing) { return res.send(404); } + return res.json(thing); + }); +}; + +// Creates a new thing in the DB. +exports.create = function(req, res) { + Thing.create(req.body, function(err, thing) { + if(err) { return handleError(res, err); } + return res.json(201, thing); + }); +}; + +// Updates an existing thing in the DB. +exports.update = function(req, res) { + if(req.body._id) { delete req.body._id; } + Thing.findById(req.params.id, function (err, thing) { + if (err) { return handleError(res, err); } + if(!thing) { return res.send(404); } + var updated = _.merge(thing, req.body); + updated.save(function (err) { + if (err) { return handleError(res, err); } + return res.json(200, thing); + }); + }); +}; + +// Deletes a thing from the DB. +exports.destroy = function(req, res) { + Thing.findById(req.params.id, function (err, thing) { + if(err) { return handleError(res, err); } + if(!thing) { return res.send(404); } + thing.remove(function(err) { + if(err) { return handleError(res, err); } + return res.send(204); + }); + }); +}; + +function handleError(res, err) { + return res.send(500, err); +} \ No newline at end of file diff --git a/server/api/thing/thing.model.js b/server/api/thing/thing.model.js new file mode 100644 index 0000000..ed857cd --- /dev/null +++ b/server/api/thing/thing.model.js @@ -0,0 +1,12 @@ +'use strict'; + +var mongoose = require('mongoose'), + Schema = mongoose.Schema; + +var ThingSchema = new Schema({ + name: String, + info: String, + active: Boolean +}); + +module.exports = mongoose.model('Thing', ThingSchema); \ No newline at end of file diff --git a/server/api/thing/thing.spec.js b/server/api/thing/thing.spec.js new file mode 100644 index 0000000..17c8c6c --- /dev/null +++ b/server/api/thing/thing.spec.js @@ -0,0 +1,20 @@ +'use strict'; + +var should = require('should'); +var app = require('../../app'); +var request = require('supertest'); + +describe('GET /api/things', function() { + + it('should respond with JSON array', function(done) { + request(app) + .get('/api/things') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + res.body.should.be.instanceof(Array); + done(); + }); + }); +}); diff --git a/server/app.js b/server/app.js new file mode 100644 index 0000000..82acdba --- /dev/null +++ b/server/app.js @@ -0,0 +1,32 @@ +/** + * Main application file + */ + +'use strict'; + +// Set default node environment to development +process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + +var express = require('express'); +var mongoose = require('mongoose'); +var config = require('./config/environment'); + +// Connect to database +mongoose.connect(config.mongo.uri, config.mongo.options); + +// Populate DB with sample data +if(config.seedDB) { require('./config/seed'); } + +// Setup server +var app = express(); +var server = require('http').createServer(app); +require('./config/express')(app); +require('./routes')(app); + +// Start server +server.listen(config.port, config.ip, function () { + console.log('Express server listening on %d, in %s mode', config.port, app.get('env')); +}); + +// Expose app +exports = module.exports = app; \ No newline at end of file diff --git a/server/components/errors/index.js b/server/components/errors/index.js new file mode 100644 index 0000000..4c5a57c --- /dev/null +++ b/server/components/errors/index.js @@ -0,0 +1,20 @@ +/** + * Error responses + */ + +'use strict'; + +module.exports[404] = function pageNotFound(req, res) { + var viewFilePath = '404'; + var statusCode = 404; + var result = { + status: statusCode + }; + + res.status(result.status); + res.render(viewFilePath, function (err) { + if (err) { return res.json(result, result.status); } + + res.render(viewFilePath); + }); +}; diff --git a/server/config/environment/development.js b/server/config/environment/development.js new file mode 100644 index 0000000..cf52332 --- /dev/null +++ b/server/config/environment/development.js @@ -0,0 +1,12 @@ +'use strict'; + +// Development specific configuration +// ================================== +module.exports = { + // MongoDB connection options + mongo: { + uri: 'mongodb://localhost/jrooms-dev' + }, + + seedDB: true +}; diff --git a/server/config/environment/index.js b/server/config/environment/index.js new file mode 100644 index 0000000..9a9c1bb --- /dev/null +++ b/server/config/environment/index.js @@ -0,0 +1,50 @@ +'use strict'; + +var path = require('path'); +var _ = require('lodash'); + +function requiredProcessEnv(name) { + if(!process.env[name]) { + throw new Error('You must set the ' + name + ' environment variable'); + } + return process.env[name]; +} + +// All configurations will extend these options +// ============================================ +var all = { + env: process.env.NODE_ENV, + + // Root path of server + root: path.normalize(__dirname + '/../../..'), + + // Server port + port: process.env.PORT || 9000, + + // Should we populate the DB with sample data? + seedDB: false, + + // Secret for session, you will want to change this and make it an environment variable + secrets: { + session: 'j-rooms-secret' + }, + + // List of user roles + userRoles: ['guest', 'user', 'admin'], + + // MongoDB connection options + mongo: { + options: { + db: { + safe: true + } + } + }, + +}; + +// Export the config object based on the NODE_ENV +// ============================================== +module.exports = _.merge( + all, + require('./' + process.env.NODE_ENV + '.js') || {}); \ No newline at end of file diff --git a/server/config/environment/production.js b/server/config/environment/production.js new file mode 100644 index 0000000..8973712 --- /dev/null +++ b/server/config/environment/production.js @@ -0,0 +1,23 @@ +'use strict'; + +// Production specific configuration +// ================================= +module.exports = { + // Server IP + ip: process.env.OPENSHIFT_NODEJS_IP || + process.env.IP || + undefined, + + // Server port + port: process.env.OPENSHIFT_NODEJS_PORT || + process.env.PORT || + 8080, + + // MongoDB connection options + mongo: { + uri: process.env.MONGOLAB_URI || + process.env.MONGOHQ_URL || + process.env.OPENSHIFT_MONGODB_DB_URL+process.env.OPENSHIFT_APP_NAME || + 'mongodb://localhost/jrooms' + } +}; \ No newline at end of file diff --git a/server/config/environment/test.js b/server/config/environment/test.js new file mode 100644 index 0000000..3482e30 --- /dev/null +++ b/server/config/environment/test.js @@ -0,0 +1,10 @@ +'use strict'; + +// Test specific configuration +// =========================== +module.exports = { + // MongoDB connection options + mongo: { + uri: 'mongodb://localhost/jrooms-test' + } +}; \ No newline at end of file diff --git a/server/config/express.js b/server/config/express.js new file mode 100644 index 0000000..80adef6 --- /dev/null +++ b/server/config/express.js @@ -0,0 +1,45 @@ +/** + * Express configuration + */ + +'use strict'; + +var express = require('express'); +var favicon = require('serve-favicon'); +var morgan = require('morgan'); +var compression = require('compression'); +var bodyParser = require('body-parser'); +var methodOverride = require('method-override'); +var cookieParser = require('cookie-parser'); +var errorHandler = require('errorhandler'); +var path = require('path'); +var config = require('./environment'); + +module.exports = function(app) { + var env = app.get('env'); + + app.set('views', config.root + '/server/views'); + app.engine('html', require('ejs').renderFile); + app.set('view engine', 'html'); + app.use(compression()); + app.use(bodyParser.urlencoded({ extended: false })); + app.use(bodyParser.json()); + app.use(methodOverride()); + app.use(cookieParser()); + + if ('production' === env) { + app.use(favicon(path.join(config.root, 'public', 'favicon.ico'))); + app.use(express.static(path.join(config.root, 'public'))); + app.set('appPath', config.root + '/public'); + app.use(morgan('dev')); + } + + if ('development' === env || 'test' === env) { + app.use(require('connect-livereload')()); + app.use(express.static(path.join(config.root, '.tmp'))); + app.use(express.static(path.join(config.root, 'client'))); + app.set('appPath', 'client'); + app.use(morgan('dev')); + app.use(errorHandler()); // Error handler - has to be last + } +}; \ No newline at end of file diff --git a/server/config/local.env.sample.js b/server/config/local.env.sample.js new file mode 100644 index 0000000..665b53d --- /dev/null +++ b/server/config/local.env.sample.js @@ -0,0 +1,14 @@ +'use strict'; + +// Use local.env.js for environment variables that grunt will set when the server starts locally. +// Use for your api keys, secrets, etc. This file should not be tracked by git. +// +// You will need to set these on the server you deploy to. + +module.exports = { + DOMAIN: 'http://localhost:9000', + SESSION_SECRET: 'jrooms-secret', + + // Control debug level for modules using visionmedia/debug + DEBUG: '' +}; diff --git a/server/config/seed.js b/server/config/seed.js new file mode 100644 index 0000000..2360e52 --- /dev/null +++ b/server/config/seed.js @@ -0,0 +1,31 @@ +/** + * Populate DB with sample data on server start + * to disable, edit config/environment/index.js, and set `seedDB: false` + */ + +'use strict'; + +var Thing = require('../api/thing/thing.model'); + + +Thing.find({}).remove(function() { + Thing.create({ + name : 'Development Tools', + info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' + }, { + name : 'Server and Client integration', + info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + }, { + name : 'Smart Build System', + info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' + }, { + name : 'Modular Structure', + info : 'Best practice client and server structures allow for more code reusability and maximum scalability' + }, { + name : 'Optimized Build', + info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' + },{ + name : 'Deployment Ready', + info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' + }); +}); \ No newline at end of file diff --git a/server/routes.js b/server/routes.js new file mode 100644 index 0000000..a67203b --- /dev/null +++ b/server/routes.js @@ -0,0 +1,23 @@ +/** + * Main application routes + */ + +'use strict'; + +var errors = require('./components/errors'); + +module.exports = function(app) { + + // Insert routes below + app.use('/api/things', require('./api/thing')); + + // All undefined asset or api routes should return a 404 + app.route('/:url(api|auth|components|app|bower_components|assets)/*') + .get(errors[404]); + + // All other routes should redirect to the index.html + app.route('/*') + .get(function(req, res) { + res.sendfile(app.get('appPath') + '/index.html'); + }); +}; diff --git a/server/views/404.html b/server/views/404.html new file mode 100644 index 0000000..ec98e3c --- /dev/null +++ b/server/views/404.html @@ -0,0 +1,157 @@ + + + + + Page Not Found :( + + + +
+

Not found :(

+

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

+

It looks like this was the result of either:

+ + + +
+ +