diff --git a/common/locales/en/challenge.json b/common/locales/en/challenge.json index abce3c33a6..edffd599a4 100644 --- a/common/locales/en/challenge.json +++ b/common/locales/en/challenge.json @@ -43,8 +43,8 @@ "exportChallengeCSV": "Export to CSV", "selectGroup": "Please select group", "challengeCreated": "Challenge created", - "sureDelCha": "Delete challenge, are you sure?", - "sureDelChaTavern": "Delete challenge, are you sure? Your gems will not be refunded.", + "sureDelCha": "Are you sure you want to delete this challenge?", + "sureDelChaTavern": "Are you sure you want to delete this challenge? Your gems will not be refunded.", "removeTasks": "Remove Tasks", "keepTasks": "Keep Tasks", "closeCha": "Close challenge and...", diff --git a/karma.conf.js b/karma.conf.js index 3cc261a9eb..3c1d0e59ee 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -35,44 +35,18 @@ module.exports = function(config) { 'website/public/bower_components/js-emoji/emoji.js', 'common/dist/scripts/habitrpg-shared.js', - "test/spec/mocks/translations.js", - "test/spec/mocks/sandbox.js", - "website/public/js/env.js", "website/public/js/app.js", "common/script/public/config.js", - "website/public/js/services/sharedServices.js", - "website/public/js/services/notificationServices.js", "common/script/public/userServices.js", "common/script/public/directives.js", - "website/public/js/services/analyticsServices.js", - "website/public/js/services/groupServices.js", - "website/public/js/services/chatServices.js", - "website/public/js/services/memberServices.js", - "website/public/js/services/guideServices.js", - "website/public/js/services/challengeServices.js", - "website/public/js/services/taskServices.js", - "website/public/js/services/paymentServices.js", - "website/public/js/services/questServices.js", - "website/public/js/services/statServices.js", - "website/public/js/filters/money.js", - "website/public/js/filters/roundLargeNumbers.js", - "website/public/js/filters/taskOrdering.js", + "website/public/js/services/**/*.js", + "website/public/js/filters/**/*.js", + "website/public/js/directives/**/*.directive.js", + "website/public/js/controllers/**/*.js", - "website/public/js/directives/focus-me.directive.js", - "website/public/js/directives/from-now.directive.js", - "website/public/js/directives/habitrpg-tasks.directive.js", - "website/public/js/directives/hrpg-sort-checklist.directive.js", - "website/public/js/directives/hrpg-sort-tags.directive.js", - "website/public/js/directives/hrpg-sort-tasks.directive.js", - "website/public/js/directives/popover-html-popup.directive.js", - "website/public/js/directives/popover-html.directive.js", - "website/public/js/directives/task-focus.directive.js", - "website/public/js/directives/when-scrolled.directive.js", - - "website/public/js/controllers/*.js", 'test/spec/mocks/**/*.js', 'test/spec/specHelper.js', 'test/spec/**/*.js' diff --git a/test/api/get_user.coffee b/test/api/get_user.coffee new file mode 100644 index 0000000000..530fa398a9 --- /dev/null +++ b/test/api/get_user.coffee @@ -0,0 +1,25 @@ +'use strict' + +require('../../website/src/server') + +describe 'User', -> + + before (done) -> + registerNewUser done, true + + describe 'GET /user', -> + it 'removes password from user object', (done) -> + request.get(baseURL + '/user') + .end (err, res) -> + expectCode res, 200 + localAuth = res.body.auth.local + expect(localAuth.hashed_password).to.not.exist + expect(localAuth.salt).to.not.exist + done() + + it 'removes apiToken from user object', (done) -> + request.get(baseURL + '/user') + .end (err, res) -> + expectCode res, 200 + expect(res.body.apiToken).to.not.exist + done() diff --git a/test/spec/directives/close-menu.directive.js b/test/spec/directives/close-menu.directive.js new file mode 100644 index 0000000000..3c50b16cc6 --- /dev/null +++ b/test/spec/directives/close-menu.directive.js @@ -0,0 +1,25 @@ +'use strict'; + +describe('closeMenu Directive', function() { + var menuElement, scope; + + beforeEach(module('habitrpg')); + + beforeEach(inject(function($rootScope, $compile) { + scope = $rootScope.$new(); + + var element = ''; + + menuElement = $compile(element)(scope); + scope.$digest(); + })); + + it('closes a connected menu when element is clicked', function() { + scope._expandedMenu = 'mobile'; + menuElement.appendTo(document.body); + + menuElement.triggerHandler('click'); + + expect(scope._expandedMenu).to.eql(null) + }); +}); diff --git a/test/spec/directives/expand-menu.directive.js b/test/spec/directives/expand-menu.directive.js new file mode 100644 index 0000000000..f73de77c5b --- /dev/null +++ b/test/spec/directives/expand-menu.directive.js @@ -0,0 +1,34 @@ +'use strict'; + +describe('expandMenu Directive', function() { + var menuElement, scope; + + beforeEach(module('habitrpg')); + + beforeEach(inject(function($rootScope, $compile) { + scope = $rootScope.$new(); + + var element = ''; + + menuElement = $compile(element)(scope); + scope.$digest(); + })); + + it('expands a connected menu when element is clicked', function() { + expect(scope._expandedMenu).to.not.exist; + menuElement.appendTo(document.body); + + menuElement.triggerHandler('click'); + + expect(scope._expandedMenu).to.eql('mobile') + }); + + it('closes a connected menu when it is already open', function() { + scope._expandedMenu = 'mobile'; + menuElement.appendTo(document.body); + + menuElement.triggerHandler('click'); + + expect(scope._expandedMenu).to.eql(null) + }); +}); diff --git a/website/public/js/controllers/menuCtrl.js b/website/public/js/controllers/menuCtrl.js index f9f8f9e69c..5549c527dd 100644 --- a/website/public/js/controllers/menuCtrl.js +++ b/website/public/js/controllers/menuCtrl.js @@ -9,10 +9,6 @@ angular.module('habitrpg') window.location.href = '/logout'; }; - $scope.expandMenu = function(menu) { - $scope._expandedMenu = ($scope._expandedMenu == menu) ? null : menu; - }; - function selectNotificationValue(mysteryValue, invitationValue, cardValue, unallocatedValue, messageValue, noneValue) { var user = $scope.user; if (user.purchased && user.purchased.plan && user.purchased.plan.mysteryItems && user.purchased.plan.mysteryItems.length) { diff --git a/website/public/js/directives/close-menu.directive.js b/website/public/js/directives/close-menu.directive.js new file mode 100644 index 0000000000..c932cfcd41 --- /dev/null +++ b/website/public/js/directives/close-menu.directive.js @@ -0,0 +1,20 @@ +'use strict'; + +(function(){ + + angular + .module('habitrpg') + .directive('closeMenu', closeMenu); + + function closeMenu() { + return { + restrict: 'A', + link: function($scope, element, attrs) { + element.on('click', function(event) { + $scope._expandedMenu = null; + $scope.$apply() + }); + } + } + } +}()); diff --git a/website/public/js/directives/expand-menu.directive.js b/website/public/js/directives/expand-menu.directive.js new file mode 100644 index 0000000000..246c649a70 --- /dev/null +++ b/website/public/js/directives/expand-menu.directive.js @@ -0,0 +1,20 @@ +'use strict'; + +(function(){ + + angular + .module('habitrpg') + .directive('expandMenu', expandMenu); + + function expandMenu() { + return { + restrict: 'A', + link: function($scope, element, attrs) { + element.on('click', function(event) { + $scope._expandedMenu = ($scope._expandedMenu === attrs.menu) ? null : attrs.menu; + $scope.$apply() + }); + } + } + } +}()); diff --git a/website/public/js/directives/focus-me.directive.js b/website/public/js/directives/focus-me.directive.js index da0bd20681..0fdec0e390 100644 --- a/website/public/js/directives/focus-me.directive.js +++ b/website/public/js/directives/focus-me.directive.js @@ -12,9 +12,9 @@ function focusMe($timeout, $parse) { return { - link: function(scope, element, attrs) { + link: function($scope, element, attrs) { var model = $parse(attrs.focusMe); - scope.$watch(model, function(value) { + $scope.$watch(model, function(value) { $timeout(function() { element[0].focus(); }); diff --git a/website/public/js/directives/from-now.directive.js b/website/public/js/directives/from-now.directive.js index 83ddae3b61..54236c92ee 100644 --- a/website/public/js/directives/from-now.directive.js +++ b/website/public/js/directives/from-now.directive.js @@ -11,18 +11,18 @@ ]; function fromNow($interval, $timeout) { - return function(scope, element, attr){ + return function($scope, element, attr){ var interval, timeout; var updateText = function(){ - element.text(moment(scope.message.timestamp).fromNow()); + element.text(moment($scope.message.timestamp).fromNow()); }; var setupInterval = function() { if(interval) $interval.cancel(interval); if(timeout) $timeout.cancel(timeout); - var diff = moment().diff(scope.message.timestamp, 'minute'); + var diff = moment().diff($scope.message.timestamp, 'minute'); if(diff < 60) { // Update every minute @@ -37,7 +37,7 @@ updateText(); setupInterval(); - scope.$on('$destroy', function() { + $scope.$on('$destroy', function() { if(interval) $interval.cancel(interval); if(timeout) $timeout.cancel(timeout); }); diff --git a/website/public/js/directives/habitrpg-tasks.directive.js b/website/public/js/directives/habitrpg-tasks.directive.js index 9708c58f9e..a185cf4f6b 100644 --- a/website/public/js/directives/habitrpg-tasks.directive.js +++ b/website/public/js/directives/habitrpg-tasks.directive.js @@ -19,10 +19,10 @@ // main: '@', // true if it's the user's main list // obj: '=' //}, - link: function(scope, element, attrs) { + link: function($scope, element, attrs) { // $scope.obj needs to come from controllers, so we can pass by ref - scope.main = attrs.main; - scope.modal = attrs.modal; + $scope.main = attrs.main; + $scope.modal = attrs.modal; var dailiesView; if(User.user.preferences.dailyDueDefaultView) { dailiesView = "remaining"; diff --git a/website/public/js/directives/popover-html-popup.directive.js b/website/public/js/directives/popover-html-popup.directive.js index d4f76a1267..0c378a6872 100644 --- a/website/public/js/directives/popover-html-popup.directive.js +++ b/website/public/js/directives/popover-html-popup.directive.js @@ -15,9 +15,9 @@ restrict: 'EA', replace: true, scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' }, - link: function(scope, element, attrs) { - scope.$watch('content', function(value, oldValue) { - scope.unsafeContent = $sce.trustAsHtml(scope.content); + link: function($scope, element, attrs) { + $scope.$watch('content', function(value, oldValue) { + $scope.unsafeContent = $sce.trustAsHtml($scope.content); }); }, templateUrl: 'template/popover/popover-html.html' diff --git a/website/public/js/directives/task-focus.directive.js b/website/public/js/directives/task-focus.directive.js index ae6a30c84b..00faf89259 100644 --- a/website/public/js/directives/task-focus.directive.js +++ b/website/public/js/directives/task-focus.directive.js @@ -13,8 +13,8 @@ */ function taskFocus($timeout) { - return function(scope, elem, attrs) { - scope.$watch(attrs.taskFocus, function(newVal) { + return function($scope, elem, attrs) { + $scope.$watch(attrs.taskFocus, function(newVal) { if (newVal) { $timeout(function() { elem[0].focus(); diff --git a/website/public/js/directives/when-scrolled.directive.js b/website/public/js/directives/when-scrolled.directive.js index 5fa0cbb998..86bd5fa7fb 100644 --- a/website/public/js/directives/when-scrolled.directive.js +++ b/website/public/js/directives/when-scrolled.directive.js @@ -6,12 +6,12 @@ .directive('whenScrolled', whenScrolled); function whenScrolled() { - return function(scope, elm, attr) { + return function($scope, elm, attr) { var raw = elm[0]; elm.bind('scroll', function() { if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) { - scope.$apply(attr.whenScrolled); + $scope.$apply(attr.whenScrolled); } }); }; diff --git a/website/public/manifest.json b/website/public/manifest.json index aac57bbc00..73fe262ee3 100644 --- a/website/public/manifest.json +++ b/website/public/manifest.json @@ -58,6 +58,8 @@ "js/filters/roundLargeNumbers.js", "js/filters/taskOrdering.js", + "js/directives/close-menu.directive.js", + "js/directives/expand-menu.directive.js", "js/directives/focus-me.directive.js", "js/directives/from-now.directive.js", "js/directives/habitrpg-tasks.directive.js", diff --git a/website/src/controllers/user.js b/website/src/controllers/user.js index 89ea68ef7b..4e261ec3d8 100644 --- a/website/src/controllers/user.js +++ b/website/src/controllers/user.js @@ -204,9 +204,9 @@ api.getUser = function(req, res, next) { user.stats.maxHealth = shared.maxHealth; user.stats.maxMP = res.locals.user._statsComputed.maxMP; delete user.apiToken; - if (user.auth) { - delete user.auth.hashed_password; - delete user.auth.salt; + if (user.auth && user.auth.local) { + delete user.auth.local.hashed_password; + delete user.auth.local.salt; } return res.json(200, user); }; diff --git a/website/views/shared/header/menu.jade b/website/views/shared/header/menu.jade index 251ef447af..12a3ac5298 100644 --- a/website/views/shared/header/menu.jade +++ b/website/views/shared/header/menu.jade @@ -2,9 +2,9 @@ nav.toolbar(ng-controller='MenuCtrl') .toolbar-container ul.toolbar-mobile-nav li.toolbar-mobile - a(ng-click='expandMenu("mobile")', ng-class='{active: _expandedMenu=="mobile"}') + a(data-expand-menu, menu="mobile", ng-class='{active: _expandedMenu=="mobile"}') span.glyphicon.glyphicon-align-justify - div(ng-if='_expandedMenu=="mobile"', ng-click='expandMenu(null)') + div(ng-show='_expandedMenu=="mobile"', data-close-menu) h4=env.t('menu') div ul.toolbar-submenu @@ -71,18 +71,18 @@ nav.toolbar(ng-controller='MenuCtrl') li.toolbar-subscribe-button button(ng-if='!user.purchased.plan.customerId',ui-sref='options.settings.subscription',popover-trigger='mouseenter',popover-placement='bottom',popover-title=env.t('subscriptions'),popover=env.t('subDescription'),popover-append-to-body='true')=env.t('subscribe') li.toolbar-controls-button - a(ng-click='expandMenu(null)')=env.t('close') + a(data-close-menu)=env.t('close') ul.toolbar-nav li.toolbar-button - a(ui-sref='tasks') + a(ui-sref='tasks', data-close-menu) span=env.t('tasks') li.toolbar-button-dropdown - a(ui-sref='options.profile.avatar') + a(ui-sref='options.profile.avatar', data-close-menu) span=env.t('user') - a(ng-click='expandMenu("avatar")', ng-class='{active: _expandedMenu == "avatar"}') + a(ng-class='{active: _expandedMenu == "avatar"}', data-expand-menu, menu='avatar') span ☰ - div(ng-if='_expandedMenu == "avatar"') - ul.toolbar-submenu(ng-click='expandMenu(null)') + div(ng-show='_expandedMenu == "avatar"', data-close-menu) + ul.toolbar-submenu li a(ui-sref='options.profile.avatar')=env.t('avatar') li @@ -94,12 +94,12 @@ nav.toolbar(ng-controller='MenuCtrl') li.toolbar-button-dropdown a(ui-sref='options.social.inbox', ng-if='user.inbox.newMessages') span.badge.badge-danger {{user.inbox.newMessages}} - a(ui-sref='options.social.tavern') + a(ui-sref='options.social.tavern', data-close-menu) span=env.t('social') - a(ng-click='expandMenu("social")', ng-class='{active: _expandedMenu == "social"}') + a(ng-class='{active: _expandedMenu == "social"}', data-expand-menu, menu='social') span ☰ - div(ng-if='_expandedMenu == "social"') - ul.toolbar-submenu(ng-click='expandMenu(null)') + div(ng-show='_expandedMenu == "social"', data-close-menu) + ul.toolbar-submenu li a(ui-sref='options.social.inbox') span.badge.badge-danger(ng-if='user.inbox.newMessages') {{user.inbox.newMessages}}   @@ -115,12 +115,12 @@ nav.toolbar(ng-controller='MenuCtrl') li a(ui-sref='options.social.hall.heroes')=env.t('hall') li.toolbar-button-dropdown - a(ui-sref='options.inventory.drops') + a(ui-sref='options.inventory.drops', data-close-menu) span=env.t('inventory') - a(ng-click='expandMenu("inventory")', ng-class='{active: _expandedMenu == "inventory"}') + a(ng-class='{active: _expandedMenu == "inventory"}' data-expand-menu, menu='inventory') span ☰ - div(ng-if='_expandedMenu == "inventory"') - ul.toolbar-submenu(ng-click='expandMenu(null)') + div(ng-show='_expandedMenu == "inventory"', data-close-menu) + ul.toolbar-submenu li a(ui-sref='options.inventory.drops')=env.t('market') li @@ -136,12 +136,12 @@ nav.toolbar(ng-controller='MenuCtrl') li a(ui-sref='options.inventory.seasonalshop')=env.t('seasonalShop') li.toolbar-button-dropdown - a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}') + a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}', data-close-menu) span=env.t('data') - a(ng-click='expandMenu("data")', ng-class='{active: _expandedMenu == "data"}') + a(ng-class='{active: _expandedMenu == "data"}', data-expand-menu, menu='data') span ☰ - div(ng-if='_expandedMenu == "data"') - ul.toolbar-submenu(ng-click='expandMenu(null)') + div(ng-show='_expandedMenu == "data"', data-close-menu) + ul.toolbar-submenu li a(target="_blank" ng-href='http://data.habitrpg.com?uuid={{user._id}}')=env.t('dataTool') li @@ -150,10 +150,10 @@ nav.toolbar(ng-controller='MenuCtrl') a(target="_blank" href='http://habitica.wikia.com/wiki/') span.glyphicon.glyphicon-question-sign span=env.t('help') - a(ng-click='expandMenu("help")', ng-class='{active: _expandedMenu == "help"}') + a(ng-class='{active: _expandedMenu == "help"}', data-expand-menu, menu='help') span ☰ - div(ng-if='_expandedMenu == "help"') - ul.toolbar-submenu(ng-click='expandMenu(null)') + div(ng-show='_expandedMenu == "help"', data-close-menu) + ul.toolbar-submenu li a(target="_blank" href='http://habitica.wikia.com/wiki/')=env.t('overview') li @@ -171,9 +171,9 @@ nav.toolbar(ng-controller='MenuCtrl') button.highlight(ui-sref='options.settings.subscription',popover-trigger='mouseenter',popover-placement='bottom',popover-title=env.t('subscriptions'),popover=env.t('subDescription'),popover-append-to-body='true')=env.t('subscribe') ul.toolbar-options li.toolbar-notifs - a(ng-click='expandMenu("notifs")') + a(data-expand-menu, menu='notifs') span.glyphicon(ng-class='iconClasses()') - div(ng-if='_expandedMenu=="notifs"') + div(ng-show='_expandedMenu=="notifs"', data-close-menu) h4=env.t('notifications') div ul.toolbar-notifs-notifs @@ -183,7 +183,7 @@ nav.toolbar(ng-controller='MenuCtrl') span.glyphicon.glyphicon-gift span=env.t('newSubscriberItem') li(ng-if='user.invitations.party.id') - a(ui-sref='options.social.party', ng-click='expandMenu(null)') + a(ui-sref='options.social.party') span.glyphicon.glyphicon-user span=env.t('invitedTo', {name: '{{user.invitations.party.name}}'}) li(ng-if='user.flags.cardReceived') @@ -193,11 +193,11 @@ nav.toolbar(ng-controller='MenuCtrl') a(ng-click='clearCards()', popover=env.t('clear'),popover-placement='right',popover-trigger='mouseenter',popover-append-to-body='true') span.glyphicon.glyphicon-remove-circle li(ng-repeat='guild in user.invitations.guilds') - a(ui-sref='options.social.guilds.public', ng-click='expandMenu(null)') + a(ui-sref='options.social.guilds.public') span.glyphicon.glyphicon-user span=env.t('invitedTo', {name: '{{guild.name}}'}) li(ng-if='user.flags.classSelected && !user.preferences.disableClasses && user.stats.points') - a(ui-sref='options.profile.stats', ng-click='expandMenu(null)') + a(ui-sref='options.profile.stats') span.glyphicon.glyphicon-plus-sign span=env.t('haveUnallocated', {points: '{{user.stats.points}}'}) li(ng-repeat='(k,v) in user.newMessages', ng-if='v.value') @@ -209,35 +209,35 @@ nav.toolbar(ng-controller='MenuCtrl') ul.toolbar-controls li.toolbar-controls-button - a(ng-click='expandMenu(null)')=env.t('close') + a(data-close-menu)=env.t('close') li.toolbar-audio - a(ng-click='expandMenu("audio")') + a(data-expand-menu, menu="audio") span.glyphicon(ng-class="{'glyphicon-volume-off':user.preferences.sound=='off', 'glyphicon-volume-up':user.preferences.sound!='off'}") - div(ng-if='_expandedMenu=="audio"',style='min-width:150px') + div(ng-show='_expandedMenu=="audio"',style='min-width:150px', data-close-menu) h4=env.t('audioTheme') div - ul.toolbar-submenu(ng-click='expandMenu(null)') + ul.toolbar-submenu // Using [{k,v}] instead of {k:v,k:v} to maintain order ('off' at top) for theme in ['off','danielTheBard', 'wattsTheme', 'gokulTheme'] li a(ng-class="{'bg-primary':user.preferences.sound=='#{theme}'}", ng-click="set({'preferences.sound':'#{theme}'})")=env.t('audioTheme_'+theme) ul.toolbar-controls li.toolbar-controls-button - a(ng-click='expandMenu(null)')=env.t('close') + a(data-close-menu)=env.t('close') li.toolbar-sync a(ng-click='User.sync()', popover=env.t('sync'),popover-placement='bottom',popover-trigger='mouseenter') span.glyphicon.glyphicon-refresh li.toolbar-settings - a(ng-click='expandMenu("settings")') + a(data-expand-menu, menu="settings") span.glyphicon.glyphicon-cog - div(ng-if='_expandedMenu=="settings"') + div(ng-show='_expandedMenu=="settings"', data-close-menu) h4=env.t('settings') div - ul.toolbar-submenu(ng-click='expandMenu(null)') + ul.toolbar-submenu li a(ng-click='logout()')=env.t('logout') - ul.toolbar-submenu(ng-click='expandMenu(null)') + ul.toolbar-submenu li a(ui-sref='options.settings.settings')=env.t('site') li @@ -250,14 +250,14 @@ nav.toolbar(ng-controller='MenuCtrl') a(ui-sref='options.settings.subscription')=env.t('subscription') li a(ui-sref='options.settings.notifications')=env.t('notifications') - ul.toolbar-submenu(ng-click='expandMenu(null)') + ul.toolbar-submenu li a(href="http://habitica.wikia.com/wiki/FAQ", target='_blank')=env.t('FAQ') li a(href="https://vimeo.com/57654086", target='_blank')=env.t('tutorials') ul.toolbar-controls li.toolbar-controls-button - a(ng-click='expandMenu(null)')=env.t('close') + a(data-close-menu)=env.t('close') ul.toolbar-wallet li.toolbar-gems(popover-trigger='mouseenter', popover-title=env.t('gemsPopoverTitle'), popover=env.t('gemsWhatFor'), popover-placement='bottom',popover-append-to-body='true') a.gem-wallet(ng-click='openModal("buyGems",{track:"Gems > Toolbar"})')