From be8ce38700576089044523cee1724c2e929200d0 Mon Sep 17 00:00:00 2001 From: Michael Prentice Date: Sun, 28 Aug 2016 06:33:52 -0400 Subject: [PATCH] Move to Firebase hosting. Remove server code. (#102) * add view for event lists * use /event as path prefix, allow /:tag as url * fix redirecting tags * only show 100 upcoming events * update tag data * update(eventList): wrap large event lists in performant `md-virtual-repeat` Fix some styling issues (more to do still) Refactor routing, components, and asset locations. Remove empty files. Added in some one time bindings for performance. Filter in controller as much as possible for performance. * fix(tests): fix issues caused by refactoring * fix(jshint): fix JSHint error that was breaking the server build Use minified version of ngGeolocation. Bump version of ngGeolocation. * update(karma): Remove references to old libraries. * update(tag): always keep tag data in $rootScope Sometimes this was being stored in the $scope which wasn't always visible and caused duplication of data. Update to Angular Material 1.0.0. * fix(main): clicking nearby events doesn't open event * refactor(app): remove all server code. configure Firebase hosting. * fix(main): put back 2-way binding to fix a number of syncing issues. update dependencies fix grunt copy task to copy image assets to `dist/` properly * update(tests): use NodeJS 6.x in Travis-CI. Remove old dependencies from server. Fixes #55. * fix(tests): remove useless tests! fix main test. fix JSCS failures remove excess server requests and more code that assumes that the URL will be prefixed * update(readme,config): update README add BrowserSync config for use with lite-server add `local.env.sample.js` with instructions for configuring keys locally remove UrlShortener docs update local dev and production deployment steps update License years * fix(map): using back button causes map to stay centered in the wrong spot * fix(routing): invalid event URLs leave the user on a blank page * fix(map): marker links don't work * update(map): update to latest angular-google-maps and Google Maps SDK (3.24) * fix(map): event list page isn't loading events for the tag properly also fixed repeated items in md-virtual-repeat caused by one-time bindings fix exception on event page if event doesn't have geo data * update(readme): add important build step to deployment guide * fix(tests): update text to work with the fix to tagged events page i.e. it should ask the Hub for upcoming events only --- .dockerignore | 3 - .firebaserc | 5 + .gitignore | 7 +- .jscsrc | 5 - .travis.yml | 3 +- .yo-rc.json | 2 +- Dockerfile | 9 - Gruntfile.js | 152 +-------------- README.md | 42 ++--- app.yaml | 42 ----- bower.json | 4 +- bs-config.json | 5 + client/{ => app}/assets/images/logo.svg | 0 .../event-map/event-map.directive.js | 92 +++++++++ .../{ => components}/event-map/event-map.html | 2 +- .../components/footer/footer.directive.js | 2 +- .../{ => app}/components/footer/footer.html | 0 .../components/topNav/topNav.directive.js | 2 +- .../{ => app}/components/topNav/topNav.html | 2 +- client/app/event-map/event-map.css | 0 client/app/event-map/event-map.directive.js | 89 --------- .../app/event-map/event-map.directive.spec.js | 27 --- client/app/firefly.config.js | 26 ++- client/app/firefly.module.js | 26 +-- client/app/main/eventList.html | 64 +++++++ client/app/main/main.controller.js | 56 ++++-- client/app/main/main.controller.spec.js | 3 +- client/app/main/main.css | 11 +- client/app/main/main.html | 40 ++-- client/app/main/main.js | 10 - client/app/moments/moments.directive.js | 160 ++++++++-------- .../app/shorturl/shorturl.controller.spec.js | 21 --- client/app/shorturl/shorturl.js | 16 -- .../shorturl/shorturlAnalytics.controller.js | 118 ------------ client/app/shorturl/shorturlAnalytics.html | 42 ----- .../app/shorturl/shorturlEvent.controller.js | 12 +- client/app/shorturl/shorturlEvent.html | 29 +-- client/index.html | 10 +- firebase.json | 11 ++ karma.conf.js | 17 +- local.env.sample.js | 13 ++ package.json | 121 +++++------- server/.jshintrc | 21 --- server/api/shorturl/index.js | 15 -- server/api/shorturl/shorturl.controller.js | 107 ----------- server/api/shorturl/shorturl.model.js | 35 ---- server/api/shorturl/shorturl.spec.js | 22 --- server/app.js | 28 --- server/components/errors/index.js | 20 -- server/config/environment/development.js | 23 --- server/config/environment/index.js | 48 ----- server/config/environment/production.js | 29 --- server/config/environment/test.js | 10 - server/config/express.js | 44 ----- server/config/local.env.sample.js | 22 --- server/routes.js | 177 ------------------ server/views/404.html | 157 ---------------- 57 files changed, 450 insertions(+), 1609 deletions(-) delete mode 100644 .dockerignore create mode 100644 .firebaserc delete mode 100644 Dockerfile delete mode 100644 app.yaml create mode 100644 bs-config.json rename client/{ => app}/assets/images/logo.svg (100%) create mode 100644 client/app/components/event-map/event-map.directive.js rename client/app/{ => components}/event-map/event-map.html (88%) rename client/{ => app}/components/footer/footer.directive.js (86%) rename client/{ => app}/components/footer/footer.html (100%) rename client/{ => app}/components/topNav/topNav.directive.js (75%) rename client/{ => app}/components/topNav/topNav.html (66%) delete mode 100644 client/app/event-map/event-map.css delete mode 100644 client/app/event-map/event-map.directive.js delete mode 100644 client/app/event-map/event-map.directive.spec.js create mode 100644 client/app/main/eventList.html delete mode 100644 client/app/main/main.js delete mode 100644 client/app/shorturl/shorturl.controller.spec.js delete mode 100644 client/app/shorturl/shorturl.js delete mode 100644 client/app/shorturl/shorturlAnalytics.controller.js delete mode 100644 client/app/shorturl/shorturlAnalytics.html create mode 100644 firebase.json create mode 100644 local.env.sample.js delete mode 100644 server/.jshintrc delete mode 100644 server/api/shorturl/index.js delete mode 100644 server/api/shorturl/shorturl.controller.js delete mode 100644 server/api/shorturl/shorturl.model.js delete mode 100644 server/api/shorturl/shorturl.spec.js delete mode 100644 server/app.js delete mode 100644 server/components/errors/index.js delete mode 100644 server/config/environment/development.js delete mode 100644 server/config/environment/index.js delete mode 100644 server/config/environment/production.js delete mode 100644 server/config/environment/test.js delete mode 100644 server/config/express.js delete mode 100644 server/config/local.env.sample.js delete mode 100644 server/routes.js delete mode 100644 server/views/404.html diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index e250b4f..0000000 --- a/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules -*.log -.git diff --git a/.firebaserc b/.firebaserc new file mode 100644 index 0000000..54a5cd5 --- /dev/null +++ b/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "firefly-1c312" + } +} diff --git a/.gitignore b/.gitignore index 53e5672..c2d1105 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ node_modules -public .tmp -.sass-cache .idea -client/bower_components +bower_components dist -/server/config/local.env.js \ No newline at end of file +local.env.js +*.log diff --git a/.jscsrc b/.jscsrc index 00d5473..abfae80 100644 --- a/.jscsrc +++ b/.jscsrc @@ -53,11 +53,6 @@ "disallowSpacesInsideArrayBrackets": null, "disallowSpacesInsideParentheses": true, - "validateJSDoc": { - "checkParamNames": true, - "requireParamTypes": null - }, - "disallowMultipleLineBreaks": true, "disallowCommaBeforeLineBreak": null, diff --git a/.travis.yml b/.travis.yml index 8f5fa2e..24f40cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: node_js node_js: - - '0.12' + - '6' before_script: - npm install -g bower grunt-cli - bower install -services: mongodb diff --git a/.yo-rc.json b/.yo-rc.json index 725a44c..d0fccbe 100644 --- a/.yo-rc.json +++ b/.yo-rc.json @@ -33,4 +33,4 @@ "css" ] } -} \ No newline at end of file +} diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 8129f3c..0000000 --- a/Dockerfile +++ /dev/null @@ -1,9 +0,0 @@ -FROM node:5.11.0 - -ENV NODE_ENV production -RUN mkdir -p /app/ -WORKDIR /app/ -COPY dist/ /app/ -RUN npm install -EXPOSE 80 -CMD node server/app.js diff --git a/Gruntfile.js b/Gruntfile.js index 7ca37b3..aa811e6 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,18 +1,15 @@ module.exports = function (grunt) { - var opn = require('opn'); // jshint ignore:line var localConfig; try { - localConfig = require('./server/config/local.env'); + localConfig = require('./local.env'); } catch (e) { localConfig = {}; } const DOMAIN = localConfig.DOMAIN || process.env.DOMAIN || 'localhost'; - const PORT = localConfig.PORT || process.env.PORT || 9000; const HUB_IP = localConfig.HUB_IP || 'https://hub.gdgx.io/'; // 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', @@ -35,22 +32,6 @@ module.exports = function (grunt) { client: require('./bower.json').appPath || 'client', dist: 'dist' }, - express: { - options: { - port: PORT - }, - dev: { - options: { - script: 'server/app.js', - debug: true - } - }, - prod: { - options: { - script: 'dist/server/app.js' - } - } - }, watch: { injectJS: { files: [ @@ -67,10 +48,6 @@ module.exports = function (grunt) { ], tasks: ['injector:css'] }, - mochaTest: { - files: ['server/**/*.spec.js'], - tasks: ['env:test', 'mochaTest'] - }, jsTest: { files: [ '<%= yeoman.client %>/{app,components}/**/*.spec.js', @@ -93,16 +70,6 @@ module.exports = function (grunt) { 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 - } } }, @@ -112,12 +79,6 @@ module.exports = function (grunt) { jshintrc: '.jshintrc', reporter: require('jshint-stylish') }, - server: { - options: { - jshintrc: 'server/.jshintrc' - }, - src: [ 'server/{,*/}*.js'] - }, all: [ '<%= yeoman.client %>/{app,components}/**/*.js', '!<%= yeoman.client %>/{app,components}/**/*.spec.js', @@ -154,12 +115,10 @@ module.exports = function (grunt) { '.tmp', '<%= yeoman.dist %>/*', '!<%= yeoman.dist %>/.git*', - '!<%= yeoman.dist %>/.openshift', '!<%= yeoman.dist %>/Procfile' ] }] - }, - server: '.tmp' + } }, // Add vendor prefixed styles @@ -186,31 +145,6 @@ module.exports = function (grunt) { } }, - // Use nodemon to run server in debug mode with an initial breakpoint - nodemon: { - debug: { - script: 'server/app.js', - options: { - nodeArgs: ['--debug-brk'], - env: { - PORT: PORT - }, - 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 () { - opn('http://localhost:' + PORT + '/debug?port=5858'); - }, 500); - }); - } - } - } - }, - // Automatically inject Bower components into the app wiredep: { target: { @@ -376,8 +310,8 @@ module.exports = function (grunt) { '*.{ico,png,txt}', '.htaccess', 'bower_components/**/*', - 'assets/images/{,*/}*.{webp}', - 'assets/fonts/**/*', + 'app/assets/images/**/*', + 'app/assets/fonts/**/*', 'index.html', '.well-known/assetlinks.json' ] @@ -386,13 +320,6 @@ module.exports = function (grunt) { cwd: '.tmp/images', dest: '<%= yeoman.dist %>/public/assets/images', src: ['generated/*'] - }, { - expand: true, - dest: '<%= yeoman.dist %>', - src: [ - 'package.json', - 'server/**/*' - ] }] }, styles: { @@ -416,21 +343,12 @@ module.exports = function (grunt) { remote: 'heroku', branch: 'master' } - }, - openshift: { - options: { - remote: 'openshift', - branch: 'master' - } } }, // Run some tasks in parallel to speed up the build process concurrent: { - server: [ - ], - test: [ - ], + test: [], debug: { tasks: [ 'nodemon', @@ -454,13 +372,6 @@ module.exports = function (grunt) { } }, - mochaTest: { - options: { - reporter: 'spec' - }, - src: ['server/**/*.spec.js'] - }, - protractor: { options: { configFile: 'protractor.conf.js' @@ -542,57 +453,9 @@ module.exports = function (grunt) { }, 1500); }); - grunt.registerTask('open', function () { - opn('http://localhost:' + PORT); - }); - - 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', '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('test', function(target) { - if (target === 'server') { - return grunt.task.run([ - 'env:all', - 'env:test', - 'mochaTest' - ]); - } - - else if (target === 'client') { + if (target === 'client') { return grunt.task.run([ - 'clean:server', 'env:all', 'concurrent:test', 'injector', @@ -603,21 +466,18 @@ module.exports = function (grunt) { 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' ]); } diff --git a/README.md b/README.md index 0a4810c..ff10fb0 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,10 @@ Firefly This project is to help organizers to better promote their events. -It relies on the data stored on the GDG[x] Hub. +It relies on the data stored in the GDG[x] Hub. **List of events** -Organizers can use short urls like http://devfest.gdgroups.org to promote devfests, or -http://brussels.gdgroups.org (to be implemented) to promote events for GDG Brussels. - -**Url shortener** -Furthermore, it can be used as url shortener like http://gdgroups.org/4363aa to direct to a specific event's page. -The statistics are shown at http://gdgroups.org/4363aa/analytics +Organizers can use URLs like [gdg.events/devfest/events](https://gdg.events/devfest/events) to promote DevFest events. There is no need for organizers to clone this repo or host this project on their servers. @@ -27,40 +22,34 @@ Discuss [issues](https://github.com/gdg-x/firefly/issues) and [pull requests](ht Local Development ================= 1. Clone the git repository. -1. Create and customize `server/config/local.env.js` based on `server/config/local.env.sample.js`. +1. Create and customize `local.env.js` based on `local.env.sample.js`. 1. Define the **GOOGLE_API_KEY**: The API key for your project, available from the [Cloud Console](https://cloud.google.com/console) - 1. Create a new project then go to APIs & Auth->APIs, activate Google+ API. + 1. Create a new project then go to APIs & Auth->APIs, activate Google+ and Google Maps JavaScript v3 APIs. 1. Go to APIs & Auth->Credentials. Add Credentials->API key->Browser key->Create (keep `Any referrer allowed` set). -1. Define the **SESSION_SECRET**: The Client secret for your project, available from the [Cloud Console](https://cloud.google.com/console) - 1. Go to APIs & Auth->Credentials. Add Credentials->API key->OAuth 2.0 client ID->Web Application->Create. -1. `bower install` +1. `cd firefly` 1. `npm install` -1. `mongod` or `mongostart` (configured [here](https://github.com/gdg-x/hub/wiki/MongoDB-Config)). -1. `grunt serve` - -* `client` contains the AngularJS app -* `server` contains the logic for the url shortener +1. `npm start` Prod Deployment =============== -1. `git fetch` -1. `git pull` -1. `grunt build` -1. `npm runScript configProd` -1. `nohup npm start &` -1. Check the server at http://gdgroups.org +1. `cd firefly` +1. `npm install -g firebase-tools` +1. `firebase login` +1. `grunt` +1. `firebase deploy` +1. Check the server at https://gdg.events ###Contributors See [list of contributors](https://github.com/gdg-x/firefly/graphs/contributors) Maintainer: [@splaktar](https://github.com/splaktar). -######GDG Apps, GDG[x] are not endorsed and/or supported by Google, the corporation. +######GDG Apps, GDG[x] is not endorsed and/or supported by Google, the corporation. License -------- - © 2013-2015 GDG[x] + © 2013-2016 GDG[x] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -73,6 +62,3 @@ License WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - - - diff --git a/app.yaml b/app.yaml deleted file mode 100644 index 51d7a19..0000000 --- a/app.yaml +++ /dev/null @@ -1,42 +0,0 @@ -# [START runtime] -runtime: nodejs -vm: true -api_version: 1 -module: default -# [END runtime] - -# [START resources] -resources: - cpu: .5 - memory_gb: 1.3 - disk_size_gb: 10 -# [END resources] - -# [START scaling] -automatic_scaling: - min_num_instances: 1 - max_num_instances: 5 - cool_down_period_sec: 60 - cpu_utilization: - target_utilization: 0.5 -# [END scaling] - -env_variables: - NODE_ENV: production - PORT: 9000 - MONGOHQ_URL: mongodb://mongo-db-8s6m/firefly - DOMAIN: gdgroups.org - -network: - forwarded_ports: - - 80:9000 - name: gdg-x - instance_tag: web - -health_check: - enable_health_check: True - check_interval_sec: 30 - timeout_sec: 10 - unhealthy_threshold: 2 - healthy_threshold: 2 - restart_threshold: 60 diff --git a/bower.json b/bower.json index 70e0fa4..449efd8 100644 --- a/bower.json +++ b/bower.json @@ -11,9 +11,9 @@ "angular-cookies": "1.4.8", "angular-sanitize": "1.4.8", "angular-route": "1.4.8", - "angular-material": "1.0.0-rc5", + "angular-material": "1.0.0", "angular-google-chart": "0.0.11", - "angular-google-maps": "2.3.3", + "angular-google-maps": "2.3.4", "font-awesome": "4.3.0", "ngGeolocation": "0.0.8", "lodash": "3.9.3", diff --git a/bs-config.json b/bs-config.json new file mode 100644 index 0000000..d54db65 --- /dev/null +++ b/bs-config.json @@ -0,0 +1,5 @@ +{ + "port": 5000, + "files": ["./client/**/*"], + "server": { "baseDir": "./client" } +} diff --git a/client/assets/images/logo.svg b/client/app/assets/images/logo.svg similarity index 100% rename from client/assets/images/logo.svg rename to client/app/assets/images/logo.svg diff --git a/client/app/components/event-map/event-map.directive.js b/client/app/components/event-map/event-map.directive.js new file mode 100644 index 0000000..0de9c96 --- /dev/null +++ b/client/app/components/event-map/event-map.directive.js @@ -0,0 +1,92 @@ +'use strict'; + +angular.module('fireflyApp').directive('eventMap', eventMap); + +eventMap.$inject = ['$http', 'uiGmapGoogleMapApi', 'config']; + +function eventMap($http, uiGmapGoogleMapApi, config) { + return { + templateUrl: 'app/components/event-map/event-map.html', + restrict: 'EA', + scope: { + position: '=', + tag: '=' + }, + controller: function($scope) { + $scope.map = { + center: { + latitude: 45, + longitude: -73 + }, + zoom: 5, + control: {}, + cluster: { + maxZoom: 7 + }, + options: { + scrollwheel: false, + draggable: true + } + }; + + $scope.markers = []; + + }, + link: function(scope) { + + uiGmapGoogleMapApi.then(function(maps) { + scope.maps = maps; + maps.event.addListener(scope.map.control.getGMap(), 'tilesloaded', function() { + maps.event.trigger(scope.map.control.getGMap(), 'resize'); + }); + }); + + scope.$watch('position', function(position) { + if (position) { + scope.map.center = position; + } + }); + + var processEvents = function(data) { + var i; + for (i = 0; i < data.items.length; i++) { + var event = data.items[i]; + + if (event.geo) { + var marker = { + id: event._id, + chapter: event.chapter, + eventUrl: event.eventUrl, + title: event.title, + start: event.start, + end: event.end, + timezone: event.timezone, + show: false, + coordinates: { + latitude: event.geo.lat, + longitude: event.geo.lng + } + }; + + marker.onClick = function(marker) { + marker.show = !marker.show; + }.bind(marker, marker); + + scope.markers.push(marker); + if (scope.maps) { + scope.maps.event.trigger(scope.map.control.getGMap(), 'resize'); + } + } + } + }; + + if (scope.tag) { + $http.jsonp(config.HUB_IP + 'api/v1/events/tag/' + scope.tag + + '/upcoming?perpage=1000&callback=JSON_CALLBACK').success(processEvents); + } else { + $http.jsonp(config.HUB_IP + 'api/v1/events/upcoming?perpage=1000&callback=JSON_CALLBACK') + .success(processEvents); + } + } + }; +} diff --git a/client/app/event-map/event-map.html b/client/app/components/event-map/event-map.html similarity index 88% rename from client/app/event-map/event-map.html rename to client/app/components/event-map/event-map.html index 1a75fc6..416ad8b 100644 --- a/client/app/event-map/event-map.html +++ b/client/app/components/event-map/event-map.html @@ -4,7 +4,7 @@ coords="'coordinates'" doCluster="true" click="onClick">
- {{marker.title}} + {{marker.title}}
diff --git a/client/components/footer/footer.directive.js b/client/app/components/footer/footer.directive.js similarity index 86% rename from client/components/footer/footer.directive.js rename to client/app/components/footer/footer.directive.js index 07f5b70..5deadbc 100644 --- a/client/components/footer/footer.directive.js +++ b/client/app/components/footer/footer.directive.js @@ -3,7 +3,7 @@ angular.module('fireflyApp') .directive('fireflyFooter', function (themeService) { return { - templateUrl: 'components/footer/footer.html', + templateUrl: 'app/components/footer/footer.html', restrict: 'EA', scope: { tag: '=' diff --git a/client/components/footer/footer.html b/client/app/components/footer/footer.html similarity index 100% rename from client/components/footer/footer.html rename to client/app/components/footer/footer.html diff --git a/client/components/topNav/topNav.directive.js b/client/app/components/topNav/topNav.directive.js similarity index 75% rename from client/components/topNav/topNav.directive.js rename to client/app/components/topNav/topNav.directive.js index 42393ec..0896b5e 100644 --- a/client/components/topNav/topNav.directive.js +++ b/client/app/components/topNav/topNav.directive.js @@ -3,7 +3,7 @@ angular.module('fireflyApp') .directive('fireflyTopNav', function () { return { - templateUrl: 'components/topNav/topNav.html', + templateUrl: 'app/components/topNav/topNav.html', restrict: 'EA', scope: { tag: '=' diff --git a/client/components/topNav/topNav.html b/client/app/components/topNav/topNav.html similarity index 66% rename from client/components/topNav/topNav.html rename to client/app/components/topNav/topNav.html index 324eb1b..7807fde 100644 --- a/client/components/topNav/topNav.html +++ b/client/app/components/topNav/topNav.html @@ -1,6 +1,6 @@
- + {{ tag.title }}
diff --git a/client/app/event-map/event-map.css b/client/app/event-map/event-map.css deleted file mode 100644 index e69de29..0000000 diff --git a/client/app/event-map/event-map.directive.js b/client/app/event-map/event-map.directive.js deleted file mode 100644 index ad91513..0000000 --- a/client/app/event-map/event-map.directive.js +++ /dev/null @@ -1,89 +0,0 @@ -'use strict'; - -angular.module('fireflyApp') - .directive('eventMap', function ($http, uiGmapGoogleMapApi, config) { - return { - templateUrl: 'app/event-map/event-map.html', - restrict: 'EA', - scope: { - position: '=', - tag: '=' - }, - controller: function($scope) { - $scope.map = { - center: { - latitude: 45, - longitude: -73 - }, - zoom: 5, - control: {}, - cluster: { - maxZoom: 7 - }, - options: { - scrollwheel: false, - draggable: true - } - }; - - $scope.markers = []; - - }, - link: function (scope) { - - uiGmapGoogleMapApi.then(function(maps) { - scope.maps = maps; - maps.event.addListener(scope.map.control.getGMap(), 'tilesloaded', function() { - maps.event.trigger(scope.map.control.getGMap(), 'resize'); - }); - }); - - scope.$watch('position', function(position) { - if (position) { - angular.copy(position, scope.map.center); - } - }); - - var processEvents = function(data) { - var i; - for (i = 0; i < data.items.length; i++) { - var event = data.items[i]; - - if (event.geo) { - var marker = { - id: event._id, - chapter: event.chapter, - eventUrl: event.eventUrl, - title: event.title, - start: event.start, - end: event.end, - timezone: event.timezone, - show: false, - coordinates: { - latitude: event.geo.lat, - longitude: event.geo.lng - } - }; - - marker.onClick = function(marker) { - marker.show = !marker.show; - }.bind(marker, marker); - - scope.markers.push(marker); - if (scope.maps) { - scope.maps.event.trigger(scope.map.control.getGMap(), 'resize'); - } - } - } - }; - - if (scope.tag) { - $http.jsonp(config.HUB_IP + 'api/v1/events/tag/' + scope.tag + - '/upcoming?perpage=1000&callback=JSON_CALLBACK').success(processEvents); - } else { - $http.jsonp(config.HUB_IP + 'api/v1/events/upcoming?perpage=1000&callback=JSON_CALLBACK') - .success(processEvents); - } - } - }; - }); diff --git a/client/app/event-map/event-map.directive.spec.js b/client/app/event-map/event-map.directive.spec.js deleted file mode 100644 index f568b71..0000000 --- a/client/app/event-map/event-map.directive.spec.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -describe('Directive: eventMap', function () { - var element, scope, $httpBackend; - - // load the directive's module and view - //noinspection JSValidateTypes - beforeEach(module('fireflyApp')); - //noinspection JSValidateTypes - beforeEach(module('app/event-map/event-map.html')); - beforeEach(inject(function ($rootScope, _$httpBackend_, config) { - $httpBackend = _$httpBackend_; - scope = $rootScope.$new(); - - $httpBackend.expectJSONP( - config.HUB_IP + 'api/v1/tags/' + scope.prefix + '?callback=JSON_CALLBACK').respond([]); - $httpBackend.expectJSONP( - config.HUB_IP + 'api/v1/events/upcoming?perpage=1000&callback=JSON_CALLBACK').respond([]); - })); - - it('should make hidden element visible', inject(function ($compile) { - element = angular.element(''); - element = $compile(element)(scope); - scope.$apply(); - expect(element.text()).toBeDefined(); - })); -}); diff --git a/client/app/firefly.config.js b/client/app/firefly.config.js index 98ba413..2f6d7df 100644 --- a/client/app/firefly.config.js +++ b/client/app/firefly.config.js @@ -1,20 +1,36 @@ 'use strict'; angular.module('fireflyApp') - .constant('GOOGLE_API_KEY', 'Set in server/config/local.env.js') + .constant('GOOGLE_API_KEY', 'Set in /local.env.js') .config(function ($routeProvider, $locationProvider, $mdThemingProvider) { - $routeProvider.otherwise({ redirectTo: '/' }); - $locationProvider.html5Mode(true); $mdThemingProvider.theme('default') .primaryPalette('grey') .accentPalette('indigo'); + + $routeProvider + .when('/', { + templateUrl: 'app/main/main.html', + controller: 'MainCtrl', + controllerAs: 'vm' + }) + .when('/:tag/events', { + templateUrl: 'app/main/eventList.html', + controller: 'MainCtrl', + controllerAs: 'vm' + }) + .when('/event/:hash', { + templateUrl: 'app/shorturl/shorturlEvent.html', + controller: 'ShorturlEventCtrl', + controllerAs: 'vm' + }) + .otherwise({ redirectTo: '/' }); }) .config(function (uiGmapGoogleMapApiProvider, GOOGLE_API_KEY) { uiGmapGoogleMapApiProvider.configure({ key: GOOGLE_API_KEY, - v: '3.23', + v: '3.24', libraries: 'weather,geometry,visualization' }); }) @@ -25,6 +41,6 @@ function config(GOOGLE_API_KEY) { GOOGLE_API_KEY: GOOGLE_API_KEY, HUB_IP: 'https://hub.gdgx.io/', DOMAIN: 'gdg.events', - DEFAULT_PREFIX: 'ioextended' + DEFAULT_PREFIX: 'devfest' }; } diff --git a/client/app/firefly.module.js b/client/app/firefly.module.js index ec3caf8..de40d0f 100644 --- a/client/app/firefly.module.js +++ b/client/app/firefly.module.js @@ -2,31 +2,7 @@ angular.module('fireflyApp', ['ngCookies', 'ngResource', 'ngSanitize', 'ngRoute', 'ngMaterial', 'ngAria', 'googlechart', 'uiGmapgoogle-maps', 'ngGeolocation', 'linkify', 'viewhead', 'ja.qr' -]).run(function($rootScope, $window, $geolocation, $http, config) { - - $rootScope.all = window.location.search.indexOf('all') >= 0; - $rootScope.prefix = window.location.host.replace('.' + config.DOMAIN, ''); - - if ($rootScope.prefix === window.location.host) { - $rootScope.prefix = config.DEFAULT_PREFIX; - } - - if ($rootScope.prefix) { - $http.jsonp(config.HUB_IP + 'api/v1/tags/' + $rootScope.prefix + '?callback=JSON_CALLBACK') - .success(function(data) { - $rootScope.tag = data; - $rootScope.tagColor = { - 'background-color': $rootScope.tag.color, - height: '4px' - }; - }) - .error(function() { - // Redirect invalid prefix to base domain. - $window.location.href = - window.location.hostname.substr($rootScope.prefix.length + 1, - window.location.hostname.length); - }); - } +]).run(function($rootScope, $window, $geolocation) { $geolocation.watchPosition({ timeout: 60000, maximumAge: 250, diff --git a/client/app/main/eventList.html b/client/app/main/eventList.html new file mode 100644 index 0000000..b32e4f1 --- /dev/null +++ b/client/app/main/eventList.html @@ -0,0 +1,64 @@ +
+ + + + {{ tag.title }} + + + +
+ + + +

About {{ tag.title }}

+

{{ tag.description }}

+
+
+ + + + +

+ {{ tag.title }} Events near you +

+ + + +
+

{{ event.title }}

+

{{ event.start | date : 'medium' : event.timezone }}

+
+
+
+
+ + Waiting for location... + +
+
+ + + +

+ Upcoming {{ tag.title }} Events +

+ + + +
+

{{ event.title }}

+

{{ event.start | date : 'medium' : event.timezone }}

+
+
+
+
+ + No upcoming events in this series. + +
+
+ +
+
diff --git a/client/app/main/main.controller.js b/client/app/main/main.controller.js index c64e820..da62d1a 100644 --- a/client/app/main/main.controller.js +++ b/client/app/main/main.controller.js @@ -1,25 +1,46 @@ 'use strict'; angular.module('fireflyApp') - .controller('MainCtrl', function ($rootScope, $scope, $http, $location, $window, config) { - $scope.domain = config.DOMAIN; - $scope.nearEvent = undefined; + .controller('MainCtrl', function ($rootScope, $filter, $routeParams, $http, $location, $window, $timeout, config) { + var vm = this; + vm.domain = config.DOMAIN; + + if ($routeParams.tag) { + $rootScope.prefix = $routeParams.tag; + vm.all = false; + } else { + vm.all = false; + $rootScope.prefix = config.DEFAULT_PREFIX; + } + + $rootScope.$on('$geolocation.position.changed', function() { + vm.nearEvents = $filter('orderBy')(vm.nearEvents, distanceFromHere); + }); + + $http.jsonp(config.HUB_IP + 'api/v1/tags/' + $rootScope.prefix + '?callback=JSON_CALLBACK') + .success(function (data) { + $rootScope.tag = data; + $rootScope.tagColor = { + 'background-color': $rootScope.tag.color, + height: '4px' + }; + }); $http.jsonp(config.HUB_IP + 'api/v1/events/stats?callback=JSON_CALLBACK') .success(function(data) { - $scope.tags = data.upcoming_top_tags; // jshint ignore:line + vm.tags = data.upcoming_top_tags; // jshint ignore:line } ); - $scope.openEvent = function (eventId) { - $location.path('/' + eventId); + vm.openEvent = function (eventId) { + $location.path('/event/' + eventId); }; - $scope.openTag = function (path) { - $window.location.href = 'http://' + path; + vm.openTag = function (path) { + $location.path(path + '/events/'); }; - $scope.distanceFromHere = function (_item, _startPoint) { + function distanceFromHere(_item, _startPoint) { var start = null; if (!_item.geo) { @@ -60,25 +81,26 @@ angular.module('fireflyApp') var num = radiansTo(start, end) * 3958.8; return Math.round(num * 100) / 100; - }; + } var processNextEvent = function(data) { if (data && data.items) { - $scope.nextEvent = data.items[0]; - $scope.allEvents = data.items; + vm.nextEvent = data.items[0]; + vm.nearEvents = $filter('orderBy')(data.items, distanceFromHere); + vm.nextEvents = $filter('orderBy')(data.items, 'start'); } }; - if ($scope.prefix) { - if ($scope.all) { - $http.jsonp(config.HUB_IP + 'api/v1/events/tag/' + $scope.prefix + + if ($rootScope.prefix) { + if (vm.all) { + $http.jsonp(config.HUB_IP + 'api/v1/events/tag/' + $rootScope.prefix + '?perpage=999&callback=JSON_CALLBACK').success(processNextEvent); } else { - $http.jsonp(config.HUB_IP + 'api/v1/events/tag/' + $scope.prefix + + $http.jsonp(config.HUB_IP + 'api/v1/events/tag/' + $rootScope.prefix + '/upcoming?perpage=999&callback=JSON_CALLBACK').success(processNextEvent); } } else { - if ($scope.all) { + if (vm.all) { $http.jsonp(config.HUB_IP + 'api/v1/events?perpage=100&callback=JSON_CALLBACK') .success(processNextEvent); } else { diff --git a/client/app/main/main.controller.spec.js b/client/app/main/main.controller.spec.js index 7d0f0f0..2fa13d2 100644 --- a/client/app/main/main.controller.spec.js +++ b/client/app/main/main.controller.spec.js @@ -6,7 +6,6 @@ describe('Controller: MainCtrl', function () { // load the controller's module //noinspection JSValidateTypes beforeEach(module('fireflyApp')); - // Initialize the controller and a mock scope beforeEach(inject(function (_$httpBackend_, $controller, $rootScope, _config_) { $httpBackend = _$httpBackend_; @@ -29,6 +28,6 @@ describe('Controller: MainCtrl', function () { .respond([]); $httpBackend.flush(); - expect(scope.tags.length).toBe(4); + expect(MainCtrl.tags.length).toBe(4); }); }); diff --git a/client/app/main/main.css b/client/app/main/main.css index 283a210..df57b09 100644 --- a/client/app/main/main.css +++ b/client/app/main/main.css @@ -1,18 +1,15 @@ .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; @@ -21,11 +18,9 @@ text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); background: #4393B9; } - #card-box { - margin-top: 15px; + margin-top: 16px; } - -md-card { - margin-top: 15px; +.eventListContainer { + height: 400px; } diff --git a/client/app/main/main.html b/client/app/main/main.html index 1030a88..5a4f318 100644 --- a/client/app/main/main.html +++ b/client/app/main/main.html @@ -1,8 +1,8 @@
- - - {{tag.title}} + + + {{ tag.title }} @@ -11,14 +11,14 @@

- {{tag.title}} Events near you + {{ tag.title }} Events near you

- +
-

{{ event.title }}

-

{{event.start | date : 'medium' : event.timezone}}

+

{{ ::event.title }}

+

{{ ::event.start | date : 'medium' : event.timezone }}

@@ -31,18 +31,18 @@

{{ event.title }}

- Upcoming {{tag.title}} Events + Upcoming {{ tag.title }} Events

- - + +
-

{{ event.title }}

-

{{event.start | date : 'medium' : event.timezone}}

+

{{ ::event.title }}

+

{{ ::event.start | date : 'medium' : event.timezone }}

- + No upcoming events in this series.
@@ -50,8 +50,8 @@

{{ event.title }}

-

About {{tag.title}}

-

{{tag.description}}

+

About {{ tag.title }}

+

{{ tag.description }}

@@ -59,11 +59,11 @@

About {{tag.title}}

Popular topics

-
-

{{ key }}

-

{{ value }} events

+

{{ ::key }}

+

{{ ::value }} events

diff --git a/client/app/main/main.js b/client/app/main/main.js deleted file mode 100644 index 862d3a9..0000000 --- a/client/app/main/main.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -angular.module('fireflyApp') - .config(function ($routeProvider) { - $routeProvider - .when('/', { - templateUrl: 'app/main/main.html', - controller: 'MainCtrl' - }); - }); diff --git a/client/app/moments/moments.directive.js b/client/app/moments/moments.directive.js index e4c1934..e48e66e 100644 --- a/client/app/moments/moments.directive.js +++ b/client/app/moments/moments.directive.js @@ -1,91 +1,87 @@ 'use strict'; -angular.module('fireflyApp') - .directive('timeTimezone', ['$http', function() { - return { - restrict: 'E', - template: '{{formatDate}}', - scope: { - date: '=', - timezone: '=' - }, - link: function(scope) { +angular.module('fireflyApp').directive('timeTimezone', ['$http', function() { + return { + restrict: 'E', + template: '{{formatDate}}', + scope: { + date: '=', + timezone: '=' + }, + link: function(scope) { - var update = function upd() { - if (scope.timezone && scope.date) { - scope.formatDate = moment(scope.date).tz(scope.timezone).format('llll'); - } - }; + var update = function upd() { + if (scope.timezone && scope.date) { + scope.formatDate = moment(scope.date).tz(scope.timezone).format('llll'); + } + }; - scope.$watch('date', function() { - update(); - }); + scope.$watch('date', function() { + update(); + }); - scope.$watch('timezone', function() { - update(); - }); - } - }; - }]) - .directive('timeAgo', ['$timeout', function($timeout) { - return { - restrict: 'E', - template: '{{formatDate}}', - scope: { - date: '=' - }, - link: function(scope) { - var promise = null; + scope.$watch('timezone', function() { + update(); + }); + } + }; +}]).directive('timeAgo', ['$timeout', function($timeout) { + return { + restrict: 'E', + template: '{{formatDate}}', + scope: { + date: '=' + }, + link: function(scope) { + var promise = null; - var update = function() { - if (scope.date) { - scope.formatDate = moment(scope.date).fromNow(); - promise = $timeout(update, 60000, false); - } - }; + var update = function() { + if (scope.date) { + scope.formatDate = moment(scope.date).fromNow(); + promise = $timeout(update, 60000, false); + } + }; - scope.$watch('date', function() { - if (promise) { - $timeout.cancel(promise); - } - update(); - }); - } - }; - }]) - .directive('localTime', [function() { - return { - restrict: 'E', - template: '{{formatDate}}', - scope: { - date: '=' - }, - link: function(scope) { - var update = function upd() { - scope.formatDate = moment(scope.date).format('llll'); - }; + scope.$watch('date', function() { + if (promise) { + $timeout.cancel(promise); + } + update(); + }); + } + }; +}]).directive('localTime', [function() { + return { + restrict: 'E', + template: '{{formatDate}}', + scope: { + date: '=' + }, + link: function(scope) { + var update = function upd() { + scope.formatDate = moment(scope.date).format('llll'); + }; - scope.$watch('date', function() { - update(); - }); - } - }; - }]) - .directive('time', [function() { - return { - restrict: 'E', - template: '{{formatDate}}', - scope: { - date: '=' - }, - link: function(scope) { - var update = function upd() { - scope.formatDate = moment(scope.date).utc().format('llll'); - }; + scope.$watch('date', function() { + update(); + }); + } + }; +}]).directive('time', [function() { + return { + restrict: 'E', + template: '{{formatDate}}', + scope: { + date: '=' + }, + link: function(scope) { + var update = function upd() { + scope.formatDate = moment(scope.date).utc().format('llll'); + }; - scope.$watch('date', function() { - update(); - }); - } - }; - }]); + scope.$watch('date', function() { + update(); + }); + } + }; +}]); diff --git a/client/app/shorturl/shorturl.controller.spec.js b/client/app/shorturl/shorturl.controller.spec.js deleted file mode 100644 index f853fa8..0000000 --- a/client/app/shorturl/shorturl.controller.spec.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; - -describe('Controller: ShorturlEventCtrl', function () { - var ShorturlEventCtrl, scope; - - // load the controller's module - //noinspection JSValidateTypes - beforeEach(module('fireflyApp')); - - // Initialize the controller and a mock scope - beforeEach(inject(function ($controller, $rootScope) { - scope = $rootScope.$new(); - ShorturlEventCtrl = $controller('ShorturlEventCtrl', { - $scope: scope - }); - })); - - it('should ...', function () { - expect(1).toEqual(1); - }); -}); diff --git a/client/app/shorturl/shorturl.js b/client/app/shorturl/shorturl.js deleted file mode 100644 index 6246a89..0000000 --- a/client/app/shorturl/shorturl.js +++ /dev/null @@ -1,16 +0,0 @@ -'use strict'; - -angular.module('fireflyApp') - .config(function ($routeProvider) { - $routeProvider - .when('/:hash/analytics', { - templateUrl: 'app/shorturl/shorturlAnalytics.html', - controller: 'ShorturlAnalyticsCtrl', - controllerAs: 'vm' - }) - .when('/:hash/', { - templateUrl: 'app/shorturl/shorturlEvent.html', - controller: 'ShorturlEventCtrl', - controllerAs: 'vm' - }); - }); diff --git a/client/app/shorturl/shorturlAnalytics.controller.js b/client/app/shorturl/shorturlAnalytics.controller.js deleted file mode 100644 index b50a1d0..0000000 --- a/client/app/shorturl/shorturlAnalytics.controller.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - -angular.module('fireflyApp') - .controller('ShorturlAnalyticsCtrl', function ($http, $routeParams, config) { - var vm = this; - - vm.referrers = { - type: 'PieChart', - options: { - pieHole: 0.4 - }, - data: { - 'cols': [ - { - id: 't', - label: 'Referrer', - type: 'string' - }, - { - id: 's', - label: 'Hits', - type: 'number' - } - ], - 'rows': [] - } - }; - - vm.browsers = { - type: 'BarChart', - options: { - animation: { - duration: 300 - }, - legend: { - numberFormat: '#' - } - }, - data: { - 'cols': [ - {id: 't', label: 'Browser', type: 'string'}, - {id: 's', label: 'Hits', type: 'number'} - ], - 'rows': [] - } - }; - - vm.countries = { - type: 'GeoChart', - options: { - domain: 'IN' - }, - data: { - 'cols': [ - {id: 't', label: 'Country', type: 'string'}, - {id: 's', label: 'Hits', type: 'number'} - ], - 'rows': [] - } - }; - - vm.platforms = { - type: 'BarChart', - data: { - 'cols': [ - {id: 't', label: 'Platform', type: 'string'}, - {id: 's', label: 'Hits', type: 'number'} - ], - 'rows': [] - } - }; - - $http.get('/api/shorturl/' + $routeParams.hash).success(function (data) { - var i, j, k, l; - vm.shorturl = data; - vm.shorturlBase = 'http://' + config.DOMAIN + '/'; - - for (i = 0; i < vm.shorturl.referrers.length; i++) { - var ref = vm.shorturl.referrers[i]; - vm.referrers.data.rows.push({ - c: [ - {v: ref.name}, - {v: ref.hits} - ] - }); - } - - for (j = 0; j < vm.shorturl.browsers.length; j++) { - var browser = vm.shorturl.browsers[j]; - vm.browsers.data.rows.push({ - c: [ - {v: browser.name}, - {v: browser.hits} - ] - }); - } - - for (k = 0; k < vm.shorturl.countries.length; k++) { - var country = vm.shorturl.countries[k]; - vm.countries.data.rows.push({ - c: [ - {v: country.name}, - {v: country.hits} - ] - }); - } - - for (l = 0; l < vm.shorturl.platforms.length; l++) { - var platform = vm.shorturl.platforms[l]; - vm.platforms.data.rows.push({ - c: [ - {v: platform.name}, - {v: platform.hits} - ] - }); - } - }); - }); diff --git a/client/app/shorturl/shorturlAnalytics.html b/client/app/shorturl/shorturlAnalytics.html deleted file mode 100644 index 026a2b8..0000000 --- a/client/app/shorturl/shorturlAnalytics.html +++ /dev/null @@ -1,42 +0,0 @@ -
-
- - Source Event Link - - - Alternate Event Link - - - Link Resolution - - - Link Resolution - - - -
- -
-

Total Hits: {{ vm.shorturl.hits }}

-
- -
-

Referrers

-
-
-
-

Countries

-
-
-
-

Browsers

-
-
-
-

Platforms

-
-
-
diff --git a/client/app/shorturl/shorturlEvent.controller.js b/client/app/shorturl/shorturlEvent.controller.js index c2596c0..5f7f0ae 100644 --- a/client/app/shorturl/shorturlEvent.controller.js +++ b/client/app/shorturl/shorturlEvent.controller.js @@ -1,15 +1,15 @@ 'use strict'; angular.module('fireflyApp') - .controller('ShorturlEventCtrl', function ($http, $routeParams, config, themeService) { + .controller('ShorturlEventCtrl', function ($http, $routeParams, $location, $log, config, themeService) { var vm = this; vm.convertHex = themeService.convertHex; - $http.get('/api/shorturl/' + $routeParams.hash).success(function (data) { - vm.shorturl = data; - vm.gdgeventsshorturl = 'http://' + config.DOMAIN + '/' + data.hash; - $http.jsonp(config.HUB_IP + 'api/v1/events/' + - data.event_id + '?callback=JSON_CALLBACK').success(processEventData); // jshint ignore:line + $http.jsonp(config.HUB_IP + 'api/v1/events/' + $routeParams.hash + '?callback=JSON_CALLBACK') + .success(processEventData) + .catch(function() { + $log.warn('Event "' + $routeParams.hash + '" not found.'); + $location.path('/'); }); function processEventData(eventData) { diff --git a/client/app/shorturl/shorturlEvent.html b/client/app/shorturl/shorturlEvent.html index a11173a..5f59367 100644 --- a/client/app/shorturl/shorturlEvent.html +++ b/client/app/shorturl/shorturlEvent.html @@ -10,43 +10,26 @@

{{ vm.event.title }}

-

- event - Hosted by -

+

event Hosted by

{{ vm.chapter.name }}

- + Visit Chapter Site
- - - -

- - web - Event site -

-
- Short URL:
- {{ vm.gdgeventsshorturl }} -
+

web Event site

- + Google+ event - + Visit Event Site
-
+
diff --git a/client/index.html b/client/index.html index 52312fb..2a24e37 100644 --- a/client/index.html +++ b/client/index.html @@ -23,7 +23,6 @@ - @@ -94,17 +93,14 @@ - + + + - - - - - diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..f8041e6 --- /dev/null +++ b/firebase.json @@ -0,0 +1,11 @@ +{ + "hosting": { + "public": "dist/public", + "rewrites": [ + { + "source": "**", + "destination": "/index.html" + } + ] + } +} diff --git a/karma.conf.js b/karma.conf.js index 0715e2a..c5c63a3 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -7,12 +7,11 @@ module.exports = function(config) { // base path, that will be used to resolve files and exclude basePath: '', - // testing framework to use (jasmine/mocha/qunit/...) + // testing framework to use (jasmine/mocha) 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', @@ -23,24 +22,20 @@ module.exports = function(config) { 'client/bower_components/angular-animate/angular-animate.js', 'client/bower_components/angular-material/angular-material.js', 'client/bower_components/angular-google-chart/ng-google-chart.js', + 'client/bower_components/lodash/lodash.js', + 'client/bower_components/angular-simple-logger/dist/angular-simple-logger.js', 'client/bower_components/angular-google-maps/dist/angular-google-maps.js', - 'client/bower_components/angular-bootstrap/ui-bootstrap-tpls.js', 'client/bower_components/ngGeolocation/ngGeolocation.js', 'client/bower_components/angular-linkify/angular-linkify.js', 'client/bower_components/angularjs-viewhead/angularjs-viewhead.js', 'client/bower_components/devintent-qr/src/angular-qr.js', - 'client/bower_components/angular-simple-logger/dist/angular-simple-logger.js', - 'client/bower_components/lodash/lodash.js', 'client/app/firefly.module.js', 'client/app/firefly.config.js', 'client/app/**/*.js', - 'client/components/**/*.js', - 'client/app/**/*.html', - 'client/components/**/*.html' + 'client/app/**/*.html' ], preprocessors: { - '**/*.jade': 'ng-jade2js', '**/*.html': 'html2js' }, @@ -48,10 +43,6 @@ module.exports = function(config) { stripPrefix: 'client/' }, - ngJade2JsPreprocessor: { - stripPrefix: 'client/' - }, - // list of files / patterns to exclude exclude: [], diff --git a/local.env.sample.js b/local.env.sample.js new file mode 100644 index 0000000..6fa9d27 --- /dev/null +++ b/local.env.sample.js @@ -0,0 +1,13 @@ +'use strict'; + +// Use local.env.js for environment variables that grunt will set when the server is built. +// Use for your api keys, secrets, etc. The local.env.js file with your settings should not be tracked by git. +// +// You will need to set these to the production values and build with grunt before deploying to production. + +module.exports = { + DOMAIN: 'localhost', + // Hub Server + HUB_IP: 'https://hub.gdgx.io/', + GOOGLE_API_KEY: 'YOUR-KEY-HERE' +}; diff --git a/package.json b/package.json index 2397b70..7f45bea 100644 --- a/package.json +++ b/package.json @@ -2,93 +2,66 @@ "name": "firefly", "version": "1.3.2", "license": "Apache-2.0", - "main": "server/app.js", - "dependencies": { - "body-parser": "1.0.0", - "cookie-parser": "1.0.1", - "composable-middleware": "0.3.0", - "compression": "1.0.1", - "connect-mongo": "0.8.2", - "devintent-geoip-native": "0.0.9", - "ejs": "0.8.4", - "errorhandler": "1.0.0", - "express": "4.0.0", - "express-session": "1.0.2", - "lodash": "3.8.0", - "method-override": "1.0.0", - "mongoose": "4.2.8", - "morgan": "1.0.0", - "short-id": "0.1.0-1", - "static-favicon": "1.0.1", - "superagent": "1.4.0", - "ua-parser": "0.3.5" - }, + "main": "client/index.html", + "dependencies": {}, "devDependencies": { + "bower": "1.7.9", "connect-livereload": "0.4.0", - "grunt": "0.4.4", - "grunt-angular-templates": "0.5.4", + "grunt": "1.0.1", + "grunt-angular-templates": "1.1.0", "grunt-asset-injector": "0.1.0", - "grunt-autoprefixer": "0.7.2", - "grunt-build-control": "0.1.3", + "grunt-autoprefixer": "3.0.4", + "grunt-build-control": "0.7.1", "grunt-cli": "1.2.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.9.4", - "grunt-contrib-jshint": "0.10.0", - "grunt-contrib-uglify": "0.4.0", - "grunt-contrib-watch": "0.6.1", + "grunt-concurrent": "2.3.1", + "grunt-contrib-clean": "1.0.0", + "grunt-contrib-concat": "1.0.1", + "grunt-contrib-copy": "1.0.0", + "grunt-contrib-cssmin": "1.0.1", + "grunt-contrib-htmlmin": "2.0.0", + "grunt-contrib-imagemin": "1.0.1", + "grunt-contrib-jshint": "1.0.0", + "grunt-contrib-uglify": "2.0.0", + "grunt-contrib-watch": "1.0.0", "grunt-dom-munger": "3.4.0", - "grunt-env": "0.4.1", - "grunt-express-server": "0.4.17", - "grunt-google-cdn": "0.4.0", - "grunt-jscs": "1.8.0", - "grunt-karma": "0.12.1", - "grunt-mocha-test": "0.10.2", - "grunt-newer": "0.7.0", - "grunt-ng-annotate": "0.2.3", - "grunt-node-inspector": "0.1.5", - "grunt-nodemon": "0.2.0", - "grunt-protractor-runner": "1.1.0", - "grunt-replace": "0.9.3", + "grunt-env": "0.4.4", + "grunt-google-cdn": "0.4.3", + "grunt-jscs": "3.0.1", + "grunt-karma": "2.0.0", + "grunt-newer": "1.2.0", + "grunt-ng-annotate": "2.0.2", + "grunt-protractor-runner": "3.2.0", + "grunt-replace": "1.0.1", "grunt-rev": "0.1.0", - "grunt-svgmin": "0.4.0", - "grunt-usemin": "2.1.1", - "grunt-wiredep": "1.8.0", - "jit-grunt": "0.5.0", - "jshint-stylish": "0.1.5", - "karma": "0.13.22", + "grunt-svgmin": "3.3.0", + "grunt-usemin": "3.1.1", + "grunt-wiredep": "3.0.1", + "jit-grunt": "0.10.0", + "jshint-stylish": "2.2.1", + "karma": "1.2.0", "karma-chrome-launcher": "1.0.1", - "karma-coffee-preprocessor": "0.2.1", - "karma-firefox-launcher": "0.1.7", - "karma-html2js-preprocessor": "0.1.0", - "karma-jade-preprocessor": "0.0.11", - "karma-jasmine": "0.3.8", - "karma-ng-html2js-preprocessor": "0.1.0", - "karma-ng-jade2js-preprocessor": "0.1.2", - "karma-ng-scenario": "0.1.0", - "karma-phantomjs-launcher": "1.0.0", - "karma-requirejs": "0.2.1", - "karma-script-launcher": "0.1.0", - "opn": "3.0.2", - "phantomjs-prebuilt": "2.1.7", - "requirejs": "2.1.11", - "should": "7.0.4", - "supertest": "1.0.1", - "time-grunt": "0.3.1" + "karma-firefox-launcher": "1.0.0", + "karma-html2js-preprocessor": "1.0.0", + "karma-jasmine": "1.0.2", + "karma-ng-html2js-preprocessor": "1.0.0", + "karma-ng-scenario": "1.0.0", + "karma-phantomjs-launcher": "1.0.1", + "karma-requirejs": "1.0.0", + "karma-script-launcher": "1.0.0", + "lite-server": "2.2.2", + "phantomjs-prebuilt": "2.1.12", + "requirejs": "2.2.0", + "should": "11.1.0", + "supertest": "2.0.0", + "time-grunt": "1.4.0" }, "engines": { "node": ">=0.10.0" }, "scripts": { - "start": "grunt serve", - "startProd": "export PORT=3000;export DOMAIN=gdgroups.org;grunt serve:dist", - "configProd": "sudo iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 3000;", + "postinstall": "bower install", + "start": "lite-server", "test": "grunt test", - "monitor": "nodemon server/app.js", "update-webdriver": "node node_modules/grunt-protractor-runner/node_modules/protractor/bin/webdriver-manager update" }, "private": true diff --git a/server/.jshintrc b/server/.jshintrc deleted file mode 100644 index 21c3d8d..0000000 --- a/server/.jshintrc +++ /dev/null @@ -1,21 +0,0 @@ -{ - "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, - "globals": { - "describe": true, - "beforeEach": true, - "it": true, - "expect": true - } -} diff --git a/server/api/shorturl/index.js b/server/api/shorturl/index.js deleted file mode 100644 index aae5725..0000000 --- a/server/api/shorturl/index.js +++ /dev/null @@ -1,15 +0,0 @@ -'use strict'; - -var express = require('express'); -var controller = require('./shorturl.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; diff --git a/server/api/shorturl/shorturl.controller.js b/server/api/shorturl/shorturl.controller.js deleted file mode 100644 index 907d76e..0000000 --- a/server/api/shorturl/shorturl.controller.js +++ /dev/null @@ -1,107 +0,0 @@ -'use strict'; - -var _ = require('lodash'); -var shortUrlModel = require('./shorturl.model'); -var hash = require('short-id'); -var request = require('superagent'); -var localConfig; -try { - localConfig = require('../../config/local.env'); -} catch (e) { - localConfig = {}; -} -var DOMAIN = localConfig.DOMAIN || 'gdg.events'; -var HUB_IP = localConfig.HUB_IP || 'https://hub.gdgx.io/'; - -hash.configure({ - length: 6, - algorithm: 'sha1', - salt: Math.random -}); - -// Get list of shortUrls -exports.index = function(req, res) { - shortUrlModel.find(function (err, shorturls) { - if (err) { - return handleError(res, err); - } - return res.json(200, shorturls); - }); -}; - -// Get a single shortUrl -exports.show = function(req, res) { - shortUrlModel.findOne({ $or:[ {'hash': req.params.id }, {'event_id': req.params.id } ]}, function (err, shortUrl) { - if (err) { - return handleError(res, err); - } - - if (!shortUrl) { - request.get(HUB_IP + 'api/v1/events/' + req.params.id, function(err, hubRes) { - if (err || !hubRes || !hubRes.body || !hubRes.body._id) { - // If there is an error looking up the shortUrl, just redirect to default prefix. - return res.redirect(301, 'http://' + DOMAIN); - } - - shortUrlModel.create({ - event_id: hubRes.body._id, - chapter_id: hubRes.body.chapter, - hash: hash.store(hubRes.body._id + hubRes.body.chapter) - }, function(err, shortUrl) { - if (err) { - console.error(err); - return res.send(500, 'Unable to create new shortUrl entry.'); - } - - res.jsonp(shortUrl); - }); - }); - - } else { - return res.jsonp(shortUrl); - } - }); -}; - -// Creates a new shortUrl in the DB. -exports.create = function(req, res) { - shortUrlModel.create(req.body, function(err, shortUrl) { - if (err) { return handleError(res, err); } - return res.json(201, shortUrl); - }); -}; - -// Updates an existing shortUrl in the DB. -exports.update = function(req, res) { - if (req.body._id) { delete req.body._id; } - shortUrlModel.findById(req.params.id, function (err, shortUrl) { - if (err) { return handleError(res, err); } - if (!shortUrl) { return res.send(404); } - var updated = _.merge(shortUrl, req.body); - updated.save(function (err) { - if (err) { return handleError(res, err); } - return res.json(200, shortUrl); - }); - }); -}; - -// Deletes a shortUrl from the DB. -exports.destroy = function(req, res) { - shortUrlModel.findById(req.params.id, function (err, shortUrl) { - if (err) { return handleError(res, err); } - if (!shortUrl) { return res.send(404); } - shortUrl.remove(function(err) { - if (err) { return handleError(res, err); } - return res.send(204); - }); - }); -}; - -/** - * @param res - * @param err - * @returns {*} - */ -function handleError(res, err) { - return res.send(500, err); -} diff --git a/server/api/shorturl/shorturl.model.js b/server/api/shorturl/shorturl.model.js deleted file mode 100644 index 4d626aa..0000000 --- a/server/api/shorturl/shorturl.model.js +++ /dev/null @@ -1,35 +0,0 @@ -'use strict'; - -var mongoose = require('mongoose'), - Schema = mongoose.Schema; - -var ShortUrlSchema = new Schema({ - hash : { type : String, unique: true }, - url : { type : String, unique: true, sparse: true }, - event_id : { type : String, unique: true, sparse: true }, - chapter_id : { type : String }, - hits : { type : Number, default: 0 }, - platforms : [{ - name : { type : String }, - hits : { type : Number, default: 0 } - }], - referrers : [{ - name : { type : String }, - hits : { type : Number, default: 0 } - }], - browsers : [{ - name : { type : String }, - hits : { type : Number, default: 0 } - }], - countries : [{ - name : { type : String }, - hits : { type : Number, default: 0 } - }] -}); - -ShortUrlSchema.index({_id: 1, 'platforms.name': 1}, {unique: true}); -ShortUrlSchema.index({_id: 1, 'referrers.name': 1}, {unique: true}); -ShortUrlSchema.index({_id: 1, 'browsers.name': 1}, {unique: true}); -ShortUrlSchema.index({_id: 1, 'countries.name': 1}, {unique: true}); - -module.exports = mongoose.model('ShortUrl', ShortUrlSchema); diff --git a/server/api/shorturl/shorturl.spec.js b/server/api/shorturl/shorturl.spec.js deleted file mode 100644 index e832174..0000000 --- a/server/api/shorturl/shorturl.spec.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -var should = require('should'); -var app = require('../../app'); -var request = require('supertest'); - -describe('GET /api/shorturl', function() { - - it('should respond with JSON array', function(done) { - request(app) - .get('/api/shorturl') - .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 deleted file mode 100644 index 5b38186..0000000 --- a/server/app.js +++ /dev/null @@ -1,28 +0,0 @@ -'use strict'; - -/** - * Main application file - */ -// 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); - -// 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; diff --git a/server/components/errors/index.js b/server/components/errors/index.js deleted file mode 100644 index 4c5a57c..0000000 --- a/server/components/errors/index.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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 deleted file mode 100644 index 8549bb8..0000000 --- a/server/config/environment/development.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -// Development specific configuration -// ================================== -var localConfig; -try { - localConfig = require('../local.env'); -} catch (e) { - localConfig = {}; -} - -module.exports = { - // Server IP - ip: localConfig.SERVER_IP || '127.0.0.1', - // Server port - port: localConfig.PORT || 9000, - // MongoDB connection options - mongo: { - uri: localConfig.MONGO_URI || - 'mongodb://localhost/firefly-dev' - }, - seedDB: true -}; diff --git a/server/config/environment/index.js b/server/config/environment/index.js deleted file mode 100644 index ac2c69e..0000000 --- a/server/config/environment/index.js +++ /dev/null @@ -1,48 +0,0 @@ -'use strict'; - -var path = require('path'); -var _ = require('lodash'); -var localConfig; -try { - localConfig = require('../local.env'); -} catch (e) { - localConfig = {}; -} - -// All configurations will extend these options -// ============================================ -var all = { - env: process.env.NODE_ENV, - - // Root path of server - root: path.normalize(__dirname + '/../../..'), - - // Server port - port: localConfig.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: localConfig.SESSION_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') || {}); diff --git a/server/config/environment/production.js b/server/config/environment/production.js deleted file mode 100644 index 42bd841..0000000 --- a/server/config/environment/production.js +++ /dev/null @@ -1,29 +0,0 @@ -'use strict'; - -// Production specific configuration -// ================================= -var localConfig; -try { - localConfig = require('../local.env'); -} catch (e) { - localConfig = {}; -} - -module.exports = { - // Server IP - ip: process.env.IP || - localConfig.SERVER_IP || - undefined, - - // Server port - port: localConfig.PORT || - process.env.PORT || - 9000, - - // MongoDB connection options - mongo: { - uri: process.env.MONGO_URI || - localConfig.MONGO_URI || - 'mongodb://localhost/firefly' - } -}; diff --git a/server/config/environment/test.js b/server/config/environment/test.js deleted file mode 100644 index 0050f16..0000000 --- a/server/config/environment/test.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -// Test specific configuration -// =========================== -module.exports = { - // MongoDB connection options - mongo: { - uri: 'mongodb://localhost/firefly-test' - } -}; diff --git a/server/config/express.js b/server/config/express.js deleted file mode 100644 index bb72a02..0000000 --- a/server/config/express.js +++ /dev/null @@ -1,44 +0,0 @@ -'use strict'; - -/** - * Express configuration - */ -var express = require('express'); -var favicon = require('static-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 - } -}; diff --git a/server/config/local.env.sample.js b/server/config/local.env.sample.js deleted file mode 100644 index 5d8c76c..0000000 --- a/server/config/local.env.sample.js +++ /dev/null @@ -1,22 +0,0 @@ -'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 = { - // Localhost loopback domain to enable testing of prefixed subdomains locally. - DOMAIN: 'localtest.me:9000', - // Server IP - SERVER_IP: '127.0.0.1', - // Server port - PORT: 9000, - // Hub Server - HUB_IP: 'https://hub.gdgx.io/', - SESSION_SECRET: 'INSERT_YOUR_SERVER_OAUTH_SECRET_HERE', - GOOGLE_API_KEY: 'INSET_YOUR_API_KEY_HERE', - MONGO_URI: 'mongodb://localhost/firefly-dev', - // Control debug level for modules using visionmedia/debug - DEBUG: '' -}; diff --git a/server/routes.js b/server/routes.js deleted file mode 100644 index 79a04ab..0000000 --- a/server/routes.js +++ /dev/null @@ -1,177 +0,0 @@ -'use strict'; - -/** - * Main application routes - */ -var errors = require('./components/errors'); -var request = require('superagent'); -var shortUrlModel = require('./api/shorturl/shorturl.model'); -var hash = require('short-id'); -var geoip = require('devintent-geoip-native'); -var localConfig; -try { - localConfig = require('./config/local.env'); -} catch (e) { - localConfig = {}; -} - -module.exports = function(app) { - var DOMAIN = localConfig.DOMAIN || 'gdg.events'; - var HUB_IP = localConfig.HUB_IP || 'https://hub.gdgx.io/'; - - hash.configure({ - length: 6, - algorithm: 'sha1', - salt: Math.random - }); - - //noinspection JSCheckFunctionSignatures - app.use('/api/shorturl', require('./api/shorturl')); - - app.route('/:hash/*') - .get(function(req, res) { - res.sendfile(app.get('appPath') + '/index.html', null, null); - }); - - // 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('/:hash') - .get(function(req, res) { - shortUrlModel.findOne({ $or:[ {'hash': req.params.hash }, {'event_id': req.params.hash } ]}, - function(err, shortUrl) { - var me = this; - - /** - * @param err - * @param eventsRes - * @returns {*} - */ - function handleEventsResponse(err, eventsRes) { - if (err || !eventsRes || !eventsRes.body || !eventsRes.body._id) { - console.error(err); - // If there is an error looking up the shortUrl, just redirect to default prefix. - return res.redirect(301, 'http://' + DOMAIN); - } - - // Create a new DB entry for this new event shortUrl - shortUrlModel.create({ - event_id: eventsRes.body._id, - chapter_id: eventsRes.body.chapter, - hash: hash.store(eventsRes.body._id + eventsRes.body.chapter) - }, function(err, newShortUrl) { - if (err) { - console.error(err); - return res.send(500, 'Unable to create new shortUrl entry.'); - } else { - redirect(me, req, res, newShortUrl); - } - }); - } - - if (!shortUrl) { - request.get(HUB_IP + 'api/v1/events/' + req.params.hash, handleEventsResponse); - } else { - redirect(me, req, res, shortUrl); - } - }); - }); - - /** - * @param me - * @param req - * @param res - * @param shortUrl - */ - function redirect(me, req, res, shortUrl) { - if (shortUrl.event_id) { - res.redirect(301, 'http://' + DOMAIN + '/' + shortUrl.event_id + '/'); - } else { - res.redirect(301, shortUrl.url); - } - - var func = recordHit.bind(me, req, shortUrl); - process.nextTick(func); - } - - /** - * @param req - * @param shortUrl - */ - function recordHit(req, shortUrl) { - var geoIp = geoip.lookup(req.headers['x-forwarded-for'] || req.connection.remoteAddress); - var userAgent = require('ua-parser').parse(req.headers['user-agent']); - var referrer = req.headers.referrer || 'Unknown'; - var i, j, k, l; - - shortUrl.hits++; - - var platFound = false; - for (i = 0; i < shortUrl.platforms.length; i++) { - var platform = shortUrl.platforms[i]; - - if (platform.name === userAgent.os.family) { - platform.hits++; - platFound = true; - } - } - if (!platFound) { - shortUrl.platforms.push({ - name: userAgent.os.family, - hits: 1 - }) - } - - var browserFound = false; - for (j = 0; j < shortUrl.browsers.length; j++) { - var browser = shortUrl.browsers[j]; - - if (browser.name === userAgent.ua.family) { - browser.hits++; - browserFound = true; - } - } - if (!browserFound) { - shortUrl.browsers.push({ - name: userAgent.ua.family, - hits: 1 - }) - } - - var countryFound = false; - for (k = 0; k < shortUrl.countries.length; k++) { - var country = shortUrl.countries[k]; - - if (country.name === geoIp.name) { - country.hits++; - countryFound = true; - } - } - if (!countryFound) { - shortUrl.countries.push({ - name: geoIp.name || 'Unknown', - hits: 1 - }) - } - - var referrerFound = false; - for (l = 0; l < shortUrl.referrers.length; l++) { - var shortReferrer = shortUrl.referrers[l]; - - if (shortReferrer.name === referrer) { - shortReferrer.hits++; - referrerFound = true; - } - } - if (!referrerFound) { - shortUrl.referrers.push({ - name: referrer, - hits: 1 - }) - } - - shortUrl.save(); - } -}; diff --git a/server/views/404.html b/server/views/404.html deleted file mode 100644 index ec98e3c..0000000 --- a/server/views/404.html +++ /dev/null @@ -1,157 +0,0 @@ - - - - - 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:

-
    -
  • a mistyped address
  • -
  • an out-of-date link
  • -
- - -
- -