rename website/client to website/client-old

This commit is contained in:
Matteo Pagliazzi
2016-09-14 15:06:32 +02:00
parent 555ddbbe4c
commit 4b48b7a5f6
353 changed files with 57 additions and 57 deletions

View File

@@ -0,0 +1,140 @@
"use strict";
/*
The authentication controller (login & facebook)
*/
angular.module('habitrpg')
.controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','ApiUrl', '$modal', 'Analytics',
function($scope, $rootScope, User, $http, $location, $window, ApiUrl, $modal, Analytics) {
$scope.Analytics = Analytics;
$scope.logout = function() {
localStorage.clear();
$window.location.href = '/logout';
};
var runAuth = function(id, token) {
User.authenticate(id, token, function(err) {
if(!err) $scope.registrationInProgress = false;
Analytics.login();
Analytics.updateUser();
$window.location.href = ('/' + window.location.hash);
});
};
function errorAlert(data, status, headers, config) {
$scope.registrationInProgress = false;
if (status === 0) {
$window.alert(window.env.t('noReachServer'));
} else if (status === 400 && data.errors && _.isArray(data.errors)) { // bad requests
data.errors.forEach(function (err) {
$window.alert(err.message);
});
} else if (!!data && !!data.error) {
$window.alert(data.message);
} else {
$window.alert(window.env.t('errorUpCase') + ' ' + status);
}
};
$scope.registrationInProgress = false;
$scope.register = function() {
/*TODO highlight invalid inputs
we have this as a workaround for https://github.com/HabitRPG/habitrpg-mobile/issues/64
*/
var scope = angular.element(document.getElementById('registrationForm')).scope();
if (scope.registrationForm.$invalid) return;
$scope.registrationInProgress = true;
var url = ApiUrl.get() + "/api/v3/user/auth/local/register";
if (location.search && location.search.indexOf('Invite=') !== -1) { // matches groupInvite and partyInvite
url += location.search;
}
if($rootScope.selectedLanguage) {
var toAppend = url.indexOf('?') !== -1 ? '&' : '?';
url = url + toAppend + 'lang=' + $rootScope.selectedLanguage.code;
}
$http.post(url, scope.registerVals).success(function(res, status, headers, config) {
runAuth(res.data._id, res.data.apiToken);
Analytics.register();
}).error(errorAlert);
};
$scope.auth = function() {
var data = {
username: $scope.loginUsername || $('#loginForm input[name="username"]').val(),
password: $scope.loginPassword || $('#loginForm input[name="password"]').val()
};
//@TODO: Move all the $http methods to a service
$http.post(ApiUrl.get() + "/api/v3/user/auth/local/login", data)
.success(function(res, status, headers, config) {
runAuth(res.data.id, res.data.apiToken);
}).error(errorAlert);
};
$scope.playButtonClick = function() {
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Play'})
if (User.authenticated()) {
window.location.href = ('/' + window.location.hash);
} else {
$modal.open({
templateUrl: 'modals/login.html'
// Using controller: 'AuthCtrl' it causes problems
});
}
};
$scope.passwordReset = function(email){
if(email == null || email.length == 0) {
alert(window.env.t('invalidEmail'));
} else {
$http.post(ApiUrl.get() + '/api/v3/user/reset-password', {email:email})
.success(function(){
alert(window.env.t('newPassSent'));
})
.error(function(data){
alert(data.err);
});
}
};
// ------ Social ----------
hello.init({
facebook : window.env.FACEBOOK_KEY
});
$scope.socialLogin = function(network){
hello(network).login({scope:'email'}).then(function(auth){
$http.post(ApiUrl.get() + "/api/v3/user/auth/social", auth)
.success(function(res, status, headers, config) {
runAuth(res.data.id, res.data.apiToken);
}).error(errorAlert);
}, function( e ){
alert("Signin error: " + e.message );
});
};
$scope.clearLocalStorage = function () {
$scope.messageModal = {
title: window.env.t('localStorageClearing'),
body: window.env.t('localStorageClearingExplanation'),
noFooter: true,
};
$modal.open({
templateUrl: 'modals/message-modal.html',
scope: $scope
});
var threeSecondsForUsersToReadClearLocalStorageMessage = 3000;
setTimeout($scope.logout, threeSecondsForUsersToReadClearLocalStorageMessage);
};
}
]);

View File

@@ -0,0 +1,73 @@
'use strict';
habitrpg.controller('AutocompleteCtrl', ['$scope', '$timeout', 'Groups', 'User', 'InputCaret', function ($scope,$timeout,Groups,User,InputCaret) {
$scope.clearUserlist = function() {
$scope.response = [];
$scope.usernames = [];
}
$scope.filterUser = function(msg) {
if (!$scope.query || !msg.user) {
return false;
}
// Ignore casing when checking for username
var user = msg.user.toLowerCase();
var text = $scope.query.text.toLowerCase();
return user.indexOf(text) == 0;
}
$scope.performCompletion = function(msg) {
$scope.autoComplete(msg);
$scope.query = null;
}
$scope.addNewUser = function(user) {
if($.inArray(user.user,$scope.usernames) == -1) {
user.username = user.user;
$scope.usernames.push(user.user);
$scope.response.push(user);
}
}
$scope.clearUserlist();
$scope.chatChanged = function(newvalue,oldvalue){
if($scope.group && $scope.group.chat && $scope.group.chat.length > 0){
for(var i = 0; i < $scope.group.chat.length; i++) {
$scope.addNewUser($scope.group.chat[i]);
}
}
}
$scope.$watch('group.chat',$scope.chatChanged);
$scope.caretChanged = function(newCaretPos) {
var relativeelement = $('.chat-form div:first');
var textarea = $('.chat-form textarea');
var userlist = $('.list-at-user');
var offset = {
x: textarea.offset().left - relativeelement.offset().left,
y: textarea.offset().top - relativeelement.offset().top,
};
if(relativeelement) {
var caretOffset = InputCaret.getPosition(textarea);
userlist.css({
left: caretOffset.left + offset.x,
top: caretOffset.top + offset.y + 16
});
}
}
$scope.updateTimer = false;
$scope.$watch(function () { return $scope.caretPos; },function(newCaretPos) {
if($scope.updateTimer){
$timeout.cancel($scope.updateTimer)
}
$scope.updateTimer = $timeout(function(){
$scope.caretChanged(newCaretPos);
},$scope.watchDelay)
});
}]);

View File

@@ -0,0 +1,505 @@
habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', 'Tasks', 'Challenges', 'Notification', '$compile', 'Groups', '$state', '$stateParams', 'Members', 'Tasks', 'TAVERN_ID',
function($rootScope, $scope, Shared, User, Tasks, Challenges, Notification, $compile, Groups, $state, $stateParams, Members, Tasks, TAVERN_ID) {
// Use presence of cid to determine whether to show a list or a single
// challenge
$scope.cid = $state.params.cid;
$scope.groupIdFilter = $stateParams.groupIdFilter;
_getChallenges();
// FIXME $scope.challenges needs to be resolved first (see app.js)
$scope.groups = [];
Groups.Group.getGroups('party,guilds,tavern')
.then(function (response) {
$scope.groups = response.data.data;
});
// override score() for tasks listed in challenges-editing pages, so that nothing happens
$scope.score = function(){}
//------------------------------------------------------------
// Challenge
//------------------------------------------------------------
// Use this to force the top view to change, not just the nested view.
$scope.edit = function(challenge) {
$state.transitionTo('options.social.challenges.edit', {cid: challenge._id}, {
reload: true, inherit: false, notify: true
});
};
$scope.isUserMemberOf = function (challenge) {
return User.user.challenges.indexOf(challenge._id) !== -1;
}
$scope.editTask = Tasks.editTask;
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
$scope.canEdit = function(task) {
return true;
}
$scope.doubleClickTask = function (obj, task) {
if (obj._locked) {
return false;
}
if (task._editing) {
$scope.saveTask(task);
} else {
$scope.editTask(task);
}
}
/**
* Create
*/
$scope.create = function() {
//If the user has one filter selected, assume that the user wants to default to that group
var defaultGroup;
//Our filters contain all groups, but we only want groups that have atleast one challenge
var groupsWithChallenges = _.uniq(_.pluck($scope.groupsFilter, '_id'));
var len = groupsWithChallenges.length;
var filterCount = 0;
for ( var i = 0; i < len; i += 1 ) {
if ($scope.search.group[groupsWithChallenges[i]] === true) {
filterCount += 1;
defaultGroup = groupsWithChallenges[i];
}
if (filterCount >= 1 && defaultGroup) {
break;
}
}
if(!defaultGroup) defaultGroup = TAVERN_ID;
$scope.obj = $scope.newChallenge = {
name: '',
description: '',
habits: [],
dailys: [],
todos: [],
rewards: [],
leader: User.user._id,
group: defaultGroup,
timestamp: +(new Date),
members: [],
official: false
};
_calculateMaxPrize(defaultGroup);
};
/**
* Clone
*/
$scope.clone = function(challenge) {
var clonedTasks = {
habit: [],
daily: [],
todo: [],
reward: []
};
_(clonedTasks).each(function(val, type) {
if (challenge[type + 's']) {
challenge[type + 's'].forEach(_cloneTaskAndPush);
}
}).value();
$scope.obj = $scope.newChallenge = {
name: challenge.name,
shortName: challenge.shortName,
description: challenge.description,
habits: clonedTasks.habit,
dailys: clonedTasks.daily,
todos: clonedTasks.todo,
rewards: clonedTasks.reward,
leader: User.user._id,
group: challenge.group._id,
official: challenge.official,
prize: challenge.prize
};
function _cloneTaskAndPush(taskToClone) {
var task = Tasks.cloneTask(taskToClone);
clonedTasks[task.type].push(task);
}
};
/**
* Save
*/
$scope.save = function(challenge) {
if (!challenge.group) return alert(window.env.t('selectGroup'));
if (!challenge.shortName || challenge.shortName.length < 3) return alert(window.env.t('shortNameTooShort'));
var isNew = !challenge._id;
if(isNew && challenge.prize > $scope.maxPrize) {
return alert(window.env.t('challengeNotEnoughGems'));
}
if (isNew) {
var _challenge;
Challenges.createChallenge(challenge)
.then(function (response) {
_challenge = response.data.data;
Notification.text(window.env.t('challengeCreated'));
var challengeTasks = [];
challengeTasks = challengeTasks.concat(challenge.todos);
challengeTasks = challengeTasks.concat(challenge.habits);
challengeTasks = challengeTasks.concat(challenge.dailys);
challengeTasks = challengeTasks.concat(challenge.rewards);
return Tasks.createChallengeTasks(_challenge._id, challengeTasks);
})
.then(function (response) {
$state.transitionTo('options.social.challenges.detail', { cid: _challenge._id }, {
reload: true, inherit: false, notify: true
});
User.sync();
});
} else {
Challenges.updateChallenge(challenge._id, challenge)
.then(function (response) {
var _challenge = response.data.data;
$state.transitionTo('options.social.challenges.detail', { cid: _challenge._id }, {
reload: true, inherit: false, notify: true
});
User.sync();
});
}
};
/**
* Discard
*/
$scope.discard = function() {
$scope.newChallenge = null;
};
/**
* Close Challenge
* ------------------
*/
$scope.cancelClosing = function(challenge) {
$scope.popoverEl.popover('destroy');
$scope.popoverEl = undefined;
$scope.closingChal = undefined;
challenge.winner = undefined;
};
//@TODO: change to $scope.remove
$scope["delete"] = function(challenge) {
var warningMsg;
if(challenge.group._id == TAVERN_ID) {
warningMsg = window.env.t('sureDelChaTavern');
} else {
warningMsg = window.env.t('sureDelCha');
}
if (!confirm(warningMsg)) return;
Challenges.deleteChallenge(challenge._id)
.then(function (response) {
$scope.popoverEl.popover('destroy');
_backToChallenges();
});
};
$scope.selectWinner = function(challenge) {
if (!challenge.winner) return;
if (!confirm(window.env.t('youSure'))) return;
Challenges.selectChallengeWinner(challenge._id, challenge.winner)
.then(function (response) {
$scope.popoverEl.popover('destroy');
_backToChallenges();
});
}
$scope.close = function(challenge, $event) {
$scope.closingChal = challenge;
$scope.popoverEl = $($event.target);
var html = $compile('<div><div ng-include="\'partials/options.social.challenges.detail.close.html\'" /></div></div>')($scope);
$scope.popoverEl.popover('destroy').popover({
html: true,
placement: 'right',
trigger: 'manual',
title: window.env.t('closeCha'),
content: html
}).popover('show');
};
$scope.toggle = function(id){
if($state.includes('options.social.challenges.detail', {cid: id})){
$state.go('options.social.challenges')
}else{
$state.go('options.social.challenges.detail', {cid: id});
}
};
$scope.toggleMember = function(cid, uid){
if($state.includes('options.social.challenges.detail.member', {cid: cid, uid: uid})){
$state.go('options.social.challenges.detail')
}else{
$state.go('options.social.challenges.detail.member', {cid: cid, uid: uid});
}
};
//------------------------------------------------------------
// Tasks
//------------------------------------------------------------
function addChallengeTasks (listDef, challenge, tasks) {
var type = listDef.type;
// If the challenge has not been created, we bulk add tasks on save
tasks = tasks.map(function (task) {
return Shared.taskDefaults({
text: task,
type: type,
});
});
type = type + 's';
if (challenge._id) {
Tasks.createChallengeTasks(challenge._id, tasks).then(function (res) {
addToList(challenge, type, res.data.data);
});
} else {
addToList(challenge, type, tasks);
}
};
function addToList (challenge, type, tasks) {
if (!_.isArray(tasks)) {
tasks = [tasks];
}
if (!challenge[type]) {
challenge[type] = [];
}
challenge[type].unshift.apply(challenge[type], tasks);
}
$scope.addTask = function(listDef, challenge) {
Tasks.addTasks(listDef, function (listDef, tasks) {
addChallengeTasks(listDef, challenge, tasks);
});
}
$scope.removeTask = function(task, challenge) {
if (!confirm(window.env.t('sureDelete', {taskType: window.env.t(task.type), taskText: task.text}))) return;
//We only pass to the api if the challenge exists, otherwise, the tasks only exist on the client
if (challenge._id) Tasks.deleteTask(task._id);
var index = challenge[task.type + 's'].indexOf(task);
challenge[task.type + 's'].splice(index, 1);
};
$scope.saveTask = function(task){
angular.copy(task._edit, task);
task._edit = undefined;
task._editing = false;
Tasks.updateTask(task._id, task);
}
$scope.toggleBulk = Tasks.toggleBulk;
/*
--------------------------
Subscription
--------------------------
*/
$scope.join = function (challenge) {
Challenges.joinChallenge(challenge._id)
.then(function (response) {
User.user.challenges.push(challenge._id);
_getChallenges();
return Tasks.getUserTasks();
})
.then(function (response) {
var tasks = response.data.data;
User.syncUserTasks(tasks);
});
}
$scope.leave = function(keep, challenge) {
if (keep == 'cancel') {
$scope.selectedChal = undefined;
} else {
Challenges.leaveChallenge($scope.selectedChal._id, keep)
.then(function (response) {
var index = User.user.challenges.indexOf($scope.selectedChal._id);
delete User.user.challenges[index];
_getChallenges();
return Tasks.getUserTasks();
})
.then(function (response) {
var tasks = response.data.data;
User.syncUserTasks(tasks);
});
}
$scope.popoverEl.popover('destroy');
}
/**
* Named "clickLeave" to distinguish between "actual" leave above, since this triggers the
* "are you sure?" dialog.
*/
$scope.clickLeave = function(chal, $event) {
$scope.selectedChal = chal;
$scope.popoverEl = $($event.target);
var html = $compile(
'<a ng-controller="ChallengesCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
$scope.popoverEl.popover('destroy').popover({
html: true,
placement: 'top',
trigger: 'manual',
title: window.env.t('leaveCha'),
content: html
}).popover('show');
}
//------------------------------------------------------------
// Filtering
//------------------------------------------------------------
$scope.filterChallenges = function(chal){
if (!$scope.search) return true;
return _shouldShowChallenge(chal);
}
$scope.$watch('newChallenge.group', function(gid){
if (!gid) return;
_calculateMaxPrize(gid);
if (gid == TAVERN_ID && !($scope.newChallenge.prize > 0)) {
$scope.newChallenge.prize = 1;
}
})
$scope.selectAll = function(){
$scope.search.group = _.transform($scope.groups, function(searchPool, group){
searchPool[group._id] = true;
});
}
$scope.selectNone = function(){
$scope.search.group = _.transform($scope.groups, function(searchPool, group){
searchPool[group._id] = false;
});
}
$scope.shouldShow = function(task, list, prefs){
return true;
};
$scope.insufficientGemsForTavernChallenge = function() {
var balance = User.user.balance || 0;
var isForTavern = $scope.newChallenge.group == TAVERN_ID;
if (isForTavern) {
return balance <= 0;
} else {
return false;
}
}
$scope.sendMessageToChallengeParticipant = function(uid) {
Members.selectMember(uid)
.then(function () {
$rootScope.openModal('private-message', {controller:'MemberModalCtrl'});
});
};
$scope.sendGiftToChallengeParticipant = function(uid) {
Members.selectMember(uid)
.then(function () {
$rootScope.openModal('send-gift', {controller:'MemberModalCtrl'});
});
};
$scope.filterInitialChallenges = function() {
$scope.groupsFilter = _.uniq(_.compact(_.pluck($scope.challenges, 'group')), function(g) {return g._id});
$scope.search = {
group: _.transform($scope.groups, function(m,g) { m[g._id] = true;}),
_isMember: "either",
_isOwner: "either"
};
//If we game from a group, then override the filter to that group
if ($scope.groupIdFilter) {
$scope.search.group = {};
$scope.search.group[$scope.groupIdFilter] = true ;
}
}
function _calculateMaxPrize(gid) {
var userBalance = User.getBalanceInGems() || 0;
var availableGroupBalance = _calculateAvailableGroupBalance(gid);
$scope.maxPrize = userBalance + availableGroupBalance;
}
function _calculateAvailableGroupBalance(gid) {
var groupBalance = 0;
var group = _.find($scope.groups, { _id: gid });
if (group && group.balance && group.leader === User.user._id) {
groupBalance = group.balance * 4;
}
return groupBalance;
}
function _shouldShowChallenge (chal) {
// Have to check that the leader object exists first in the
// case where a challenge's leader deletes their account
var userIsOwner = (chal.leader && chal.leader._id) === User.user.id;
var groupSelected = $scope.search.group[chal.group ? chal.group._id : null];
var checkOwner = $scope.search._isOwner === 'either' || (userIsOwner === $scope.search._isOwner);
var checkMember = $scope.search._isMember === 'either' || ($scope.isUserMemberOf(chal) === $scope.search._isMember);
return groupSelected && checkOwner && checkMember;
}
function _backToChallenges(){
$scope.popoverEl.popover('destroy');
$scope.cid = null;
$state.go('options.social.challenges');
_getChallenges();
}
// Fetch single challenge if a cid is present; fetch multiple challenges
// otherwise
function _getChallenges() {
if ($scope.cid) {
Challenges.getChallenge($scope.cid)
.then(function (response) {
var challenge = response.data.data;
$scope.challenges = [challenge];
});
} else {
Challenges.getUserChallenges()
.then(function(response){
$scope.challenges = response.data.data;
$scope.filterInitialChallenges();
});
}
};
}]);

View File

@@ -0,0 +1,155 @@
'use strict';
habitrpg.controller('ChatCtrl', ['$scope', 'Groups', 'Chat', 'User', '$http', 'ApiUrl', 'Notification', 'Members', '$rootScope', 'Analytics',
function($scope, Groups, Chat, User, $http, ApiUrl, Notification, Members, $rootScope, Analytics){
$scope.message = {content:''};
$scope._sending = false;
$scope.isUserMentioned = function(user, message) {
if(message.hasOwnProperty("highlight"))
return message.highlight;
message.highlight = false;
var messagetext = message.text.toLowerCase();
var username = user.profile.name;
var mentioned = messagetext.indexOf(username.toLowerCase());
var pattern = username+"([^\w]|$){1}";
if(mentioned > -1) {
var preceedingchar = messagetext.substring(mentioned-1,mentioned);
if(mentioned == 0 || preceedingchar.trim() == '' || preceedingchar == '@'){
var regex = new RegExp(pattern,'i');
message.highlight = regex.test(messagetext);
}
}
return message.highlight;
}
$scope.postChat = function(group, message){
if (_.isEmpty(message) || $scope._sending) return;
$scope._sending = true;
// var previousMsg = (group.chat && group.chat[0]) ? group.chat[0].id : false;
Chat.postChat(group._id, message) //, previousMsg) not sending the previousMsg as we have real time updates
.then(function(response) {
var message = response.data.data.message;
if (message) {
group.chat.unshift(message);
group.chat.splice(200);
} else {
group.chat = response.data.data.chat;
}
$scope.message.content = '';
$scope._sending = false;
if (group.type == 'party') {
Analytics.updateUser({'partyID': group.id, 'partySize': group.memberCount});
}
if (group.privacy == 'public'){
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'group chat','groupType':group.type,'privacy':group.privacy,'groupName':group.name});
} else {
Analytics.track({'hitType':'event','eventCategory':'behavior','eventAction':'group chat','groupType':group.type,'privacy':group.privacy});
}
}, function(err){
$scope._sending = false;
});
}
$scope.deleteChatMessage = function(group, message){
if(message.uuid === User.user.id || (User.user.backer && User.user.contributor.admin)){
var previousMsg = (group.chat && group.chat[0]) ? group.chat[0].id : false;
if (confirm('Are you sure you want to delete this message?')) {
Chat.deleteChat(group._id, message.id, previousMsg)
.then(function (response) {
var i = _.findIndex(group.chat, {id: message.id});
if(i !== -1) group.chat.splice(i, 1);
});
}
}
}
$scope.likeChatMessage = function(group, message) {
if (message.uuid == User.user._id)
return Notification.text(window.env.t('foreverAlone'));
if (!message.likes) message.likes = {};
if (message.likes[User.user._id]) {
delete message.likes[User.user._id];
} else {
message.likes[User.user._id] = true;
}
Chat.like(group._id, message.id);
}
$scope.flagChatMessage = function(groupId,message) {
if(!message.flags) message.flags = {};
if (message.flags[User.user._id]) {
Notification.text(window.env.t('abuseAlreadyReported'));
} else {
$scope.abuseObject = message;
$scope.groupId = groupId;
$scope.isSystemMessage = message.uuid === 'system';
$rootScope.openModal('abuse-flag',{
controller:'MemberModalCtrl',
scope: $scope
});
}
};
$scope.copyToDo = function(message) {
var taskNotes = env.t("messageWroteIn", {
user: message.uuid == 'system'
? 'system'
: '[' + message.user + '](' + env.BASE_URL + '/static/front/#?memberId=' + message.uuid + ')',
group: '[' + $scope.group.name + '](' + window.location.href + ')'
});
var newScope = $scope.$new();
newScope.text = message.text;
newScope.notes = taskNotes;
$rootScope.openModal('copyChatToDo',{
controller:'CopyMessageModalCtrl',
scope: newScope
});
};
function handleGroupResponse (response) {
$scope.group = response;
if (!$scope.group._id) $scope.group = response.data.data;
};
$scope.sync = function(group) {
if (group.name === Groups.TAVERN_NAME) {
Groups.tavern(true).then(handleGroupResponse);
} else if (group._id === User.user.party._id) {
Groups.party(true).then(handleGroupResponse);
} else {
Groups.Group.get(group._id).then(handleGroupResponse);
}
Chat.markChatSeen(group._id);
}
// List of Ordering options for the party members list
$scope.partyOrderChoices = {
'level': window.env.t('sortLevel'),
'random': window.env.t('sortRandom'),
'pets': window.env.t('sortPets'),
'habitrpg_date_joined' : window.env.t('sortHabitrpgJoined'),
'party_date_joined': window.env.t('sortJoined'),
'habitrpg_last_logged_in': window.env.t('sortHabitrpgLastLoggedIn'),
'name': window.env.t('sortName'),
'backgrounds': window.env.t('sortBackgrounds'),
};
$scope.partyOrderAscendingChoices = {
'ascending': window.env.t('ascendingSort'),
'descending': window.env.t('descendingSort')
}
}]);

View File

@@ -0,0 +1,17 @@
'use strict';
habitrpg.controller("CopyMessageModalCtrl", ['$scope', 'User', 'Notification',
function($scope, User, Notification){
$scope.saveTodo = function() {
var newTask = {
text: $scope.text,
type: 'todo',
notes: $scope.notes
};
User.addTask({body:newTask});
Notification.text(window.env.t('messageAddedAsToDo'));
$scope.$close();
}
}]);

View File

@@ -0,0 +1,48 @@
"use strict";
habitrpg.controller("FiltersCtrl", ['$scope', '$rootScope', 'User', 'Shared',
function($scope, $rootScope, User, Shared) {
var user = User.user;
$scope._editing = false;
$scope._newTag = {name:''};
$scope.filterQuery = '';
var tagsSnap; // used to compare which tags need updating
$scope.saveOrEdit = function(){
if ($scope._editing) {
_.each(User.user.tags, function(tag){
// Send an update op for each changed tag (excluding new tags & deleted tags, this if() packs a punch)
if (tagsSnap[tag.id] && tagsSnap[tag.id].name != tag.name)
User.updateTag({params:{id:tag.id}, body:{name:tag.name}});
})
$scope._editing = false;
} else {
tagsSnap = angular.copy(user.tags);
tagsSnap = _.object(_.pluck(tagsSnap,'id'), tagsSnap);
$scope._editing = true;
}
};
$scope.toggleFilter = function(tag) {
if (!user.filters[tag.id]) {
user.filters[tag.id] = true;
} else {
delete user.filters[tag.id];
}
// no longer persisting this, it was causing a lot of confusion - users thought they'd permanently lost tasks
// Note: if we want to persist for just this computer, easy method is:
// User.save();
};
$scope.updateTaskFilter = function(){
user.filterQuery = $scope.filterQuery;
};
$scope.updateTaskFilter();
$scope.createTag = function() {
User.addTag({body:{name: $scope._newTag.name, id: Shared.uuid()}});
$scope._newTag.name = '';
};
}]);

View File

@@ -0,0 +1,180 @@
"use strict";
angular.module('habitrpg').controller("FooterCtrl",
['$scope', '$rootScope', 'User', '$http', 'Notification', 'ApiUrl', 'Social',
function($scope, $rootScope, User, $http, Notification, ApiUrl, Social) {
$scope.loadWidgets = Social.loadWidgets;
if(env.isStaticPage){
$scope.languages = env.availableLanguages;
$scope.selectedLanguage = _.find(env.availableLanguages, {code: env.language.code});
$rootScope.selectedLanguage = $scope.selectedLanguage;
$scope.changeLang = function(){
window.location = '?lang='+$scope.selectedLanguage.code;
}
}
/**
External Scripts
JS files not needed right away (google charts) or entirely optional (analytics)
Each file gets loaded async via $.getScript, so it doesn't bog page-load
*/
$scope.deferredScripts = function(){
// Amazon Payments
var amazonPaymentsUrl = 'https://static-na.payments-amazon.com/OffAmazonPayments/us/' +
(window.env.NODE_ENV === 'production' ? '' : 'sandbox/') + 'js/Widgets.js';
$.getScript(amazonPaymentsUrl);
// Stripe
$.getScript('//checkout.stripe.com/v2/checkout.js');
/* Google Content Experiments
if (window.env.NODE_ENV === 'production') {
$.getScript('//www.google-analytics.com/cx/api.js?experiment=boVO4eEyRfysNE5D53nCMQ', function(){
$rootScope.variant = cxApi.chooseVariation();
$rootScope.$apply();
})
} */
// Scripts only for desktop
if (!window.env.IS_MOBILE) {
// Add This
//$.getScript("//s7.addthis.com/js/300/addthis_widget.js#pubid=ra-5016f6cc44ad68a4"); //FIXME why isn't this working when here? instead it's now in <head>
var addthisServices = 'facebook,twitter,googleplus,tumblr,'+window.env.BASE_URL.replace('https://','').replace('http://','');
window.addthis_config = {
ui_click: true,
services_custom:{
name: "Download",
url: window.env.BASE_URL+"/export/avatar-"+User.user._id+".png",
icon: window.env.BASE_URL+"/favicon.ico"
},
services_expanded:addthisServices,
services_compact:addthisServices
};
// Google Charts
$.getScript("//www.google.com/jsapi", function() {
google.load("visualization", "1", {
packages: ["corechart"],
callback: function() {}
});
});
}
}
/**
* Debug functions. Note that the server route for gems is only available if process.env.DEBUG=true
*/
if (_.contains(['development','test'],window.env.NODE_ENV)) {
$scope.setHealthLow = function(){
User.set({
'stats.hp': 1
});
};
$scope.addMissedDay = function(numberOfDays){
if (!confirm("Are you sure you want to reset the day by " + numberOfDays + " day(s)?")) return;
User.setCron(numberOfDays);
};
$scope.addTenGems = function(){
User.addTenGems();
};
$scope.addHourglass = function(){
User.addHourglass();
};
$scope.addGold = function(){
User.set({
'stats.gp': User.user.stats.gp + 500,
});
};
$scope.addMana = function(){
User.set({
'stats.mp': User.user.stats.mp + 500,
});
};
$scope.addLevelsAndGold = function(){
User.set({
'stats.exp': User.user.stats.exp + 10000,
'stats.gp': User.user.stats.gp + 10000,
'stats.mp': User.user.stats.mp + 10000
});
};
$scope.addOneLevel = function(){
User.set({
'stats.exp': User.user.stats.exp + (Math.round(((Math.pow(User.user.stats.lvl, 2) * 0.25) + (10 * User.user.stats.lvl) + 139.75) / 10) * 10)
});
};
$scope.addQuestProgress = function(){
$http({
method: "POST",
url: 'api/v3/debug/quest-progress'
})
.then(function (response) {
Notification.text('Quest progress increased');
User.sync();
})
};
$scope.makeAdmin = function () {
User.makeAdmin();
};
$scope.openModifyInventoryModal = function () {
$rootScope.openModal('modify-inventory', {controller: 'FooterCtrl', scope: $scope });
$scope.showInv = { };
$scope.inv = {
gear: {},
special: {},
pets: {},
mounts: {},
eggs: {},
hatchingPotions: {},
food: {},
quests: {},
};
$scope.setAllItems = function (type, value) {
var set = $scope.inv[type];
for (var item in set) {
if (set.hasOwnProperty(item)) {
set[item] = value;
}
}
};
};
$scope.modifyInventory = function () {
$http({
method: "POST",
url: 'api/v3/debug/modify-inventory',
data: {
gear: $scope.showInv.gear ? $scope.inv.gear : null,
special: $scope.showInv.special ? $scope.inv.special : null,
pets: $scope.showInv.pets ? $scope.inv.pets : null,
mounts: $scope.showInv.mounts ? $scope.inv.mounts : null,
eggs: $scope.showInv.eggs ? $scope.inv.eggs : null,
hatchingPotions: $scope.showInv.hatchingPotions ? $scope.inv.hatchingPotions : null,
food: $scope.showInv.food ? $scope.inv.food : null,
quests: $scope.showInv.quests ? $scope.inv.quests : null,
}
})
.then(function (response) {
Notification.text('Inventory updated. Refresh or sync.');
})
};
}
}])

View File

@@ -0,0 +1,144 @@
"use strict";
habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', '$http', '$q', 'User', 'Members', '$state', 'Notification',
function($scope, $rootScope, Shared, Groups, $http, $q, User, Members, $state, Notification) {
$scope.isMemberOfPendingQuest = function (userid, group) {
if (!group.quest || !group.quest.members) return false;
if (group.quest.active) return false; // quest is started, not pending
return userid in group.quest.members && group.quest.members[userid] != false;
};
$scope.isMemberOfRunningQuest = function (userid, group) {
if (!group.quest || !group.quest.members) return false;
if (!group.quest.active) return false; // quest is pending, not started
return group.quest.members[userid];
};
$scope.isMemberOfGroup = function (userid, group) {
// If the group is a guild, just check for an intersection with the
// current user's guilds, rather than checking the members of the group.
if(group.type === 'guild') {
return _.detect(User.user.guilds, function(guildId) { return guildId === group._id });
}
// Similarly, if we're dealing with the user's current party, return true.
if(group.type === 'party') {
var currentParty = group;
if(currentParty._id && currentParty._id === group._id) return true;
}
if (!group.members) return false;
var memberIds = _.map(group.members, function(x){return x._id});
return ~(memberIds.indexOf(userid));
};
$scope.isMember = function (user, group) {
return ~(group.members.indexOf(user._id));
};
$scope.Members = Members;
$scope._editing = {group: false};
$scope.groupCopy = {};
$scope.editGroup = function (group) {
angular.copy(group, $scope.groupCopy);
group._editing = true;
};
$scope.saveEdit = function (group) {
var newLeader = $scope.groupCopy._newLeader && $scope.groupCopy._newLeader._id;
if (newLeader) {
$scope.groupCopy.leader = newLeader;
}
angular.copy($scope.groupCopy, group);
Groups.Group.update(group);
$scope.cancelEdit(group);
};
$scope.cancelEdit = function (group) {
group._editing = false;
$scope.groupCopy = {};
};
$scope.deleteAllMessages = function() {
if (confirm(window.env.t('confirmDeleteAllMessages'))) {
User.clearPMs();
}
};
// ------ Modals ------
$scope.clickMember = function (uid, forceShow) {
if (User.user._id == uid && !forceShow) {
if ($state.is('tasks')) {
$state.go('options.profile.avatar');
} else {
$state.go('tasks');
}
} else {
// We need the member information up top here, but then we pass it down to the modal controller
// down below. Better way of handling this?
Members.selectMember(uid)
.then(function () {
$rootScope.openModal('member', {controller: 'MemberModalCtrl', windowClass: 'profile-modal', size: 'lg'});
});
}
};
$scope.removeMember = function (group, member, isMember) {
// TODO find a better way to do this (share data with remove member modal)
$scope.removeMemberData = {
group: group,
member: member,
isMember: isMember
};
$rootScope.openModal('remove-member', {scope: $scope});
};
$scope.confirmRemoveMember = function (confirm) {
if (confirm) {
Groups.Group.removeMember(
$scope.removeMemberData.group._id,
$scope.removeMemberData.member._id,
$scope.removeMemberData.message
).then(function (response) {
if($scope.removeMemberData.isMember){
_.pull($scope.removeMemberData.group.members, $scope.removeMemberData.member);
}else{
_.pull($scope.removeMemberData.group.invites, $scope.removeMemberData.member);
}
$scope.removeMemberData = undefined;
});
} else {
$scope.removeMemberData = undefined;
}
};
$scope.openInviteModal = function (group) {
if (group.type !== 'party' && group.type !== 'guild') {
return console.log('Invalid group type.')
}
$rootScope.openModal('invite-' + group.type, {
controller:'InviteToGroupCtrl',
resolve: {
injectedGroup: function(){
return group;
}
}
});
};
$scope.quickReply = function (uid) {
Members.selectMember(uid)
.then(function (response) {
$rootScope.openModal('private-message', {controller: 'MemberModalCtrl'});
});
}
}]);

View File

@@ -0,0 +1,118 @@
'use strict';
habitrpg.controller("GuildsCtrl", ['$scope', 'Groups', 'User', 'Challenges', '$rootScope', '$state', '$location', '$compile', 'Analytics', 'Pusher',
function($scope, Groups, User, Challenges, $rootScope, $state, $location, $compile, Analytics, Pusher) {
$scope.groups = {
guilds: [],
public: [],
};
Groups.myGuilds()
.then(function (guilds) {
$scope.groups.guilds = guilds;
});
Groups.publicGuilds()
.then(function (guilds) {
$scope.groups.public = guilds;
});
$scope.type = 'guild';
$scope.text = window.env.t('guild');
var newGroup = function(){
return {type:'guild', privacy:'private'};
}
$scope.newGroup = newGroup()
$scope.create = function(group){
if (User.user.balance < 1) {
return $rootScope.openModal('buyGems', {track:"Gems > Create Group"});
}
if (confirm(window.env.t('confirmGuild'))) {
Groups.Group.create(group)
.then(function (response) {
var createdGroup = response.data.data;
$rootScope.hardRedirect('/#/options/groups/guilds/' + createdGroup._id);
});
}
}
$scope.join = function (group) {
var groupId = group._id;
// If we don't have the _id property, we are joining from an invitation
// which contains a id property of the group
if (group.id && !group._id) {
groupId = group.id;
}
Groups.Group.join(groupId)
.then(function (response) {
var joinedGroup = response.data.data;
User.user.guilds.push(joinedGroup._id);
_.pull(User.user.invitations.guilds, group);
$location.path('/options/groups/guilds/' + joinedGroup._id);
});
}
$scope.reject = function(invitationToReject) {
var index = _.findIndex(User.user.invitations.guilds, function(invite) { return invite.id === invitationToReject.id; });
User.user.invitations.guilds = User.user.invitations.guilds.splice(0, index);
Groups.Group.rejectInvite(invitationToReject.id);
}
$scope.leave = function(keep) {
if (keep == 'cancel') {
$scope.selectedGroup = undefined;
$scope.popoverEl.popover('destroy');
} else {
Groups.Group.leave($scope.selectedGroup._id, keep)
.success(function (data) {
var index = User.user.guilds.indexOf($scope.selectedGroup._id);
delete User.user.guilds[index];
$scope.selectedGroup = undefined;
$location.path('/options/groups/guilds');
});
}
}
$scope.clickLeave = function(group, $event){
$scope.selectedGroup = group;
$scope.popoverEl = $($event.target).closest('.btn');
var html, title;
Challenges.getGroupChallenges(group._id)
.then(function(response) {
var challenges = _.pluck(_.filter(response.data.data, function(c) {
return c.group._id == group._id;
}), '_id');
if (_.intersection(challenges, User.user.challenges).length > 0) {
html = $compile(
'<a ng-controller="GroupsCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
title = window.env.t('leaveGroupCha');
} else {
html = $compile(
'<a ng-controller="GroupsCtrl" ng-click="leave(\'keep-all\')">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
title = window.env.t('leaveGroup')
}
$scope.popoverEl.popover('destroy').popover({
html: true,
placement: 'top',
trigger: 'manual',
title: title,
content: html
}).popover('show');
});
}
}
]);

View File

@@ -0,0 +1,54 @@
"use strict";
habitrpg.controller("HallHeroesCtrl", ['$scope', '$rootScope', 'User', 'Notification', 'ApiUrl', 'Hall',
function($scope, $rootScope, User, Notification, ApiUrl, Hall) {
$scope.hero = undefined;
$scope.currentHeroIndex = undefined;
$scope.heroes = [];
Hall.getHeroes()
.then(function (response) {
$scope.heroes = response.data.data;
});
$scope.loadHero = function(uuid, heroIndex) {
$scope.currentHeroIndex = heroIndex;
Hall.getHero(uuid)
.then(function (response) {
$scope.hero = response.data.data;
});
}
$scope.saveHero = function(hero) {
$scope.hero.contributor.admin = ($scope.hero.contributor.level > 7) ? true : false;
Hall.updateHero($scope.hero)
.then(function (response) {
Notification.text("User updated");
$scope.hero = undefined;
$scope._heroID = undefined;
$scope.heroes[$scope.currentHeroIndex] = response.data.data;
$scope.currentHeroIndex = undefined;
});
}
$scope.populateContributorInput = function(id, index) {
$scope._heroID = id;
window.scrollTo(0, 200);
$scope.loadHero(id, index);
};
}]);
habitrpg.controller("HallPatronsCtrl", ['$scope', '$rootScope', 'User', 'Notification', 'ApiUrl', 'Hall',
function($scope, $rootScope, User, Notification, ApiUrl, Hall) {
var page = 0;
$scope.patrons = [];
$scope.loadMore = function() {
Hall.getPatrons(page++)
.then(function (response) {
$scope.patrons = $scope.patrons.concat(response.data.data);
});
}
$scope.loadMore();
}]);

View File

@@ -0,0 +1,67 @@
"use strict";
habitrpg.controller("HeaderCtrl", ['$scope', 'Groups', 'User',
function($scope, Groups, User) {
$scope.Math = window.Math;
$scope.user = User.user;
$scope.inviteOrStartParty = Groups.inviteOrStartParty;
function handlePartyResponse (party) {
$scope.party = party;
var triggerResort = function() {
$scope.partyMinusSelf = resortParty();
};
triggerResort();
$scope.$watch('user.party.order', triggerResort);
$scope.$watch('user.party.orderAscending', triggerResort);
}
Groups.party().then(handlePartyResponse, handlePartyResponse);
function resortParty() {
var result = _.sortBy(
_.filter($scope.party.members, function(member){
return member._id !== User.user._id;
}),
function (member) {
switch(User.user.party.order)
{
case 'level':
return member.stats.lvl;
break;
case 'random':
return Math.random();
break;
case 'pets':
return member.items.pets.length;
break;
case 'name':
return member.profile.name;
break;
case 'backgrounds':
return member.preferences.background;
break;
case 'habitrpg_date_joined':
return member.auth.timestamps.created;
break
case 'habitrpg_last_logged_in':
return member.auth.timestamps.loggedin;
break
default:
// party date joined
return true;
}
}
)
if (User.user.party.orderAscending == "descending") {
result = result.reverse()
}
return result;
}
}
]);

View File

@@ -0,0 +1,31 @@
'use strict';
habitrpg.controller('InboxCtrl', ['$scope', '$rootScope',
function ($scope, $rootScope) {
$scope.copyToDo = function (message) {
var messageUser;
if (message.uuid == 'system') {
messageUser = 'system';
} else {
messageUser = message.user;
}
var newScope = $scope.$new();
newScope.notes = env.t('taskFromInbox', {
from: messageUser,
message: message.text
});
newScope.text = env.t('taskTextFromInbox', {
from: messageUser
});
$rootScope.openModal('copyChatToDo',{
controller: 'CopyMessageModalCtrl',
scope: newScope
});
};
}
]);

View File

@@ -0,0 +1,404 @@
habitrpg.controller("InventoryCtrl",
['$rootScope', '$scope', 'Shared', '$window', 'User', 'Content', 'Analytics', 'Quests', 'Stats', 'Social', 'Achievement',
function($rootScope, $scope, Shared, $window, User, Content, Analytics, Quests, Stats, Social, Achievement) {
var user = User.user;
// convenience vars since these are accessed frequently
$scope.selectedEgg = null; // {index: 1, name: "Tiger", value: 5}
$scope.selectedPotion = null; // {index: 5, name: "Red", value: 3}
_updateDropAnimalCount(user.items);
// Social sharing buttons
$scope.loadWidgets = Social.loadWidgets;
// Functions from Quests service
$scope.lockQuest = Quests.lockQuest;
$scope.buyQuest = function(questScroll) {
Quests.buyQuest(questScroll)
.then(function(quest) {
$rootScope.selectedQuest = quest;
$rootScope.openModal('buyQuest', {controller:'InventoryCtrl'});
});
};
$scope.questPopover = Quests.questPopover;
$scope.showQuest = function(questScroll) {
Quests.showQuest(questScroll)
.then(function(quest) {
$rootScope.selectedQuest = quest;
$rootScope.openModal('showQuest', {controller:'InventoryCtrl'});
});
};
$scope.questInit = function() {
var key = $rootScope.selectedQuest.key;
Quests.initQuest(key).then(function() {
$rootScope.selectedQuest = undefined;
$scope.$close();
});
};
// count egg, food, hatchingPotion stack totals
var countStacks = function(items) { return _.reduce(items,function(m,v){return m+v;},0);}
$scope.$watch('user.items.eggs', function(eggs){ $scope.eggCount = countStacks(eggs); }, true);
$scope.$watch('user.items.hatchingPotions', function(pots){ $scope.potCount = countStacks(pots); }, true);
$scope.$watch('user.items.food', function(food){ $scope.foodCount = countStacks(food); }, true);
$scope.$watch('user.items.quests', function(quest){ $scope.questCount = countStacks(quest); }, true);
$scope.$watch('user.items.gear', function(gear){
$scope.gearByClass = {};
$scope.gearByType = {};
_.each(gear.owned, function(v,key){
if (v === false) {
return;
}
var item = Content.gear.flat[key];
var bonusMultiplier = 1;
if (_isClassItem(item)) {
bonusMultiplier = 1.5;
}
item._effectiveStr = item.str * bonusMultiplier;
item._effectiveCon = item.con * bonusMultiplier;
item._effectivePer = item.per * bonusMultiplier;
item._effectiveInt = item.int * bonusMultiplier;
if (!$scope.gearByClass[item.klass]) {
$scope.gearByClass[item.klass] = [];
}
$scope.gearByClass[item.klass].push(item);
if (!$scope.gearByType[item.type]) {
$scope.gearByType[item.type] = [];
}
$scope.gearByType[item.type].push(item);
})
}, true);
$scope.chooseEgg = function(egg){
if ($scope.selectedEgg && $scope.selectedEgg.key == egg) {
return $scope.selectedEgg = null; // clicked same egg, unselect
}
var eggData = _.findWhere(Content.eggs, {key:egg});
if (!$scope.selectedPotion) {
$scope.selectedEgg = eggData;
} else {
$scope.hatch(eggData, $scope.selectedPotion);
}
$scope.selectedFood = null;
}
$scope.choosePotion = function(potion){
if ($scope.selectedPotion && $scope.selectedPotion.key == potion) {
return $scope.selectedPotion = null; // clicked same egg, unselect
}
// we really didn't think through the way these things are stored and getting passed around...
var potionData = _.findWhere(Content.hatchingPotions, {key:potion});
if (!$scope.selectedEgg) {
$scope.selectedPotion = potionData;
} else {
$scope.hatch($scope.selectedEgg, potionData);
}
$scope.selectedFood = null;
}
$scope.chooseFood = function(food){
if ($scope.selectedFood && $scope.selectedFood.key == food) return $scope.selectedFood = null;
$scope.selectedFood = Content.food[food];
$scope.selectedEgg = $scope.selectedPotion = null;
}
$scope.sellInventory = function() {
var selected = $scope.selectedEgg ? 'selectedEgg' : $scope.selectedPotion ? 'selectedPotion' : $scope.selectedFood ? 'selectedFood' : undefined;
if (selected) {
var type = $scope.selectedEgg ? 'eggs' : $scope.selectedPotion ? 'hatchingPotions' : $scope.selectedFood ? 'food' : undefined;
User.sell({params:{type:type, key: $scope[selected].key}});
if (user.items[type][$scope[selected].key] < 1) {
$scope[selected] = null;
}
}
}
$scope.ownedItems = function(inventory){
return _.pick(inventory, function(v,k){return v>0;});
}
$scope.hatch = function(egg, potion){
var eggName = Content.eggs[egg.key].text();
var potName = Content.hatchingPotions[potion.key].text();
if (!$window.confirm(window.env.t('hatchAPot', {potion: potName, egg: eggName}))) return;
var userHasPet = user.items.pets[egg.key + '-' + potion.key] > 0;
var isPremiumPet = Content.hatchingPotions[potion.key].premium && !Content.dropEggs[egg.key];
User.hatch({params:{egg:egg.key, hatchingPotion:potion.key}});
if (!user.preferences.suppressModals.hatchPet && !userHasPet && !isPremiumPet) {
$scope.hatchedPet = {
egg: eggName,
potion: potName,
potionKey:potion.key,
eggKey: egg.key,
pet: 'Pet-' + egg.key + '-' + potion.key
};
$rootScope.openModal('hatchPet', {
scope: $scope,
size: 'sm'
});
}
$scope.selectedEgg = null;
$scope.selectedPotion = null;
_updateDropAnimalCount(user.items);
// Checks if beastmaster has been reached for the first time
if(!user.achievements.beastMaster
&& $scope.petCount >= 90) {
User.user.achievements.beastMaster = true;
Achievement.displayAchievement('beastMaster');
}
// Checks if Triad Bingo has been reached for the first time
if(!user.achievements.triadBingo
&& $scope.mountCount >= 90
&& Shared.count.dropPetsCurrentlyOwned(User.user.items.pets) >= 90) {
User.user.achievements.triadBingo = true;
Achievement.displayAchievement('triadBingo');
}
}
$scope.choosePet = function(egg, potion){
var pet = Content.petInfo[egg + '-' + potion];
var petDisplayName = pet.text();
// Feeding Pet
if ($scope.selectedFood) {
var food = $scope.selectedFood;
var startingMounts = $rootScope.countExists(user.items.mounts);
if (food.key === 'Saddle') {
if (!$window.confirm(window.env.t('useSaddle', {pet: petDisplayName}))) return;
} else if (!$window.confirm(window.env.t('feedPet', {name: petDisplayName, article: food.article, text: food.text()}))) {
return;
}
User.feed({params:{pet: pet.key, food: food.key}});
$scope.selectedFood = null;
_updateDropAnimalCount(user.items);
if ($rootScope.countExists(user.items.mounts) > startingMounts && !user.preferences.suppressModals.raisePet) {
$scope.raisedPet = {
displayName: petDisplayName,
spriteName: pet.key,
egg: egg,
potion: potion
}
$rootScope.openModal('raisePet', {
scope: $scope,
size:'sm'
});
}
// Checks if mountmaster has been reached for the first time
if(!user.achievements.mountMaster
&& $scope.mountCount >= 90) {
User.user.achievements.mountMaster = true;
Achievement.displayAchievement('mountMaster');
}
// Selecting Pet
} else {
User.equip({params:{type: 'pet', key: pet.key}});
}
}
$scope.chooseMount = function(egg, potion) {
User.equip({params:{type: 'mount', key: egg + '-' + potion}});
}
$scope.getSeasonalShopArray = function(set){
var flatGearArray = _.toArray(Content.gear.flat);
var filteredArray = _.where(flatGearArray, {index: set});
return filteredArray;
};
$scope.getSeasonalShopQuests = function(set){
var questArray = _.toArray(Content.quests);
var filteredArray = _.filter(questArray, function(q){
return q.key === ('egg');
});
return filteredArray;
};
$scope.dequip = function(itemSet){
switch (itemSet) {
case "battleGear":
for (item in user.items.gear.equipped){
var itemKey = user.items.gear.equipped[item];
if (user.items.gear.owned[itemKey]) {
User.equip({params: {type: 'equipped', key: itemKey}});
}
}
break;
case "costume":
for (item in user.items.gear.costume){
var itemKey = user.items.gear.costume[item];
if (user.items.gear.owned[itemKey]) {
User.equip({params: {type:"costume", key: itemKey}});
}
}
break;
case "petMountBackground":
var pet = user.items.currentPet;
if (pet) {
User.equip({params:{type: 'pet', key: pet}});
}
var mount = user.items.currentMount;
if (mount) {
User.equip({params:{type: 'mount', key: mount}});
}
var background = user.preferences.background;
if (background) {
User.unlock({query:{path:"background."+background}});
}
break;
}
};
var listenForEscape = function (event) {
if (event.keyCode === 27) {
$scope.deselectItem();
}
}
document.addEventListener('keydown', listenForEscape);
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams, options){
if (toState.name.indexOf('options.inventory') < 0) {
document.removeEventListener('keydown', listenForEscape);
}
});
$scope.deselectItem = function() {
$scope.selectedFood = null;
$scope.selectedPotion = null;
$scope.selectedEgg = null;
};
$scope.openCardsModal = function(type, numberOfVariations) {
var cardsModalScope = $rootScope.$new();
cardsModalScope.cardType = type;
cardsModalScope.cardMessage = _generateCard(type, numberOfVariations);
$rootScope.openModal('cards', {
scope: cardsModalScope
});
};
$scope.classBonusNotes = function (item) {
if (_isClassItem(item)) {
return window.env.t('classBonus');
}
};
$scope.hasAllTimeTravelerItems = function() {
return ($scope.hasAllTimeTravelerItemsOfType('mystery') &&
$scope.hasAllTimeTravelerItemsOfType('pets') &&
$scope.hasAllTimeTravelerItemsOfType('mounts'));
};
$scope.hasAllTimeTravelerItemsOfType = function(type) {
if (type === 'mystery') {
var itemsLeftInTimeTravelerStore = Content.timeTravelerStore(user.items.gear.owned);
var keys = Object.keys(itemsLeftInTimeTravelerStore);
return keys.length === 0;
}
if (type === 'pets' || type === 'mounts') {
for (var key in Content.timeTravelStable[type]) {
if (!user.items[type][key]) return false;
}
return true;
}
else return Console.log('Time Traveler item type must be in ["pets","mounts","mystery"]');
};
$scope.clickTimeTravelItem = function(type,key) {
if (user.purchased.plan.consecutive.trinkets < 1) return User.hourglassPurchase({params:{type:type,key:key}});
if (!window.confirm(window.env.t('hourglassBuyItemConfirm'))) return;
User.hourglassPurchase({params:{type:type,key:key}});
};
$scope.marketShopCategories = Shared.shops.getMarketCategories(user);
$scope.questShopCategories = Shared.shops.getQuestShopCategories(user);
$scope.timeTravelersCategories = Shared.shops.getTimeTravelersCategories(user);
$scope.seasonalShopCategories = Shared.shops.getSeasonalShopCategories(user);
$scope.shouldShowPremiumPetRow = function (potion) {
potion = Content.premiumHatchingPotions[potion];
if (!potion) {
return false;
}
if (user.items.hatchingPotions[potion.key] > 0) {
return true;
}
if (potion.canBuy()) {
return true;
}
var pets = Object.keys(user.items.pets);
var hasAPetOfPotion = pets.find(function (pet) {
return pet.indexOf(potion.key) !== -1;
});
return hasAPetOfPotion;
};
$scope.shouldShowPremiumPetSection = function () {
var potions = Content.premiumHatchingPotions;
return Object.keys(potions).find(function (potion) {
return $scope.shouldShowPremiumPetRow(potions[potion].key);
});
};
function _updateDropAnimalCount(items) {
$scope.petCount = Shared.count.beastMasterProgress(items.pets);
$scope.mountCount = Shared.count.mountMasterProgress(items.mounts);
$scope.beastMasterProgress = Stats.beastMasterProgress(items.pets);
$scope.mountMasterProgress = Stats.mountMasterProgress(items.mounts);
}
function _generateCard(kind, numberOfVariations) {
var random = Math.random() * numberOfVariations;
var selection = Math.floor(random);
return env.t(kind + selection);
}
function _isClassItem(item) {
var userClass = user.stats.class;
return item.klass === userClass || item.specialClass === userClass;
}
}
]);

View File

@@ -0,0 +1,85 @@
'use strict';
habitrpg.controller('InviteToGroupCtrl', ['$scope', '$rootScope', 'User', 'Groups', 'injectedGroup', '$http', 'Notification',
function($scope, $rootScope, User, Groups, injectedGroup, $http, Notification) {
$scope.group = injectedGroup;
$scope.inviter = User.user.profile.name;
_resetInvitees();
$scope.addUuid = function() {
$scope.invitees.push({uuid: ''});
};
$scope.addEmail = function() {
$scope.emails.push({name: '', email: ''});
};
$scope.inviteNewUsers = function(inviteMethod) {
if (!$scope.group._id) {
$scope.group.name = $scope.group.name || env.t('possessiveParty', {name: User.user.profile.name});
return Groups.Group.create($scope.group)
.then(function(response) {
$scope.group = response.data.data;
_inviteByMethod(inviteMethod);
});
}
_inviteByMethod(inviteMethod);
};
function _inviteByMethod(inviteMethod) {
var invitationDetails;
if (inviteMethod === 'email') {
var emails = _getEmails();
invitationDetails = {inviter: $scope.inviter, emails: emails};
} else if (inviteMethod === 'uuid') {
var uuids = _getOnlyUuids();
invitationDetails = {uuids: uuids};
} else {
return console.log('Invalid invite method.')
}
Groups.Group.invite($scope.group._id, invitationDetails)
.then(function () {
var invitesSent = invitationDetails.emails || invitationDetails.uuids;
var invitationString = invitesSent.length > 1 ? 'invitationsSent' : 'invitationSent';
Notification.text(window.env.t(invitationString));
_resetInvitees();
if ($scope.group.type === 'party') {
$rootScope.hardRedirect('/#/options/groups/party');
} else {
$rootScope.hardRedirect('/#/options/groups/guilds/' + $scope.group._id);
}
}, function(){
_resetInvitees();
});
}
function _getOnlyUuids() {
var uuids = _.pluck($scope.invitees, 'uuid');
var filteredUuids = _.filter(uuids, function(id) {
return id != '';
});
return filteredUuids;
}
function _getEmails() {
var emails = _.filter($scope.emails, function(obj) {
return obj.email != '';
});
return emails;
}
function _resetInvitees() {
var emptyEmails = [{name:"",email:""},{name:"",email:""}];
var emptyInvitees = [{uuid: ''}];
$scope.emails = emptyEmails;
$scope.invitees = emptyInvitees;
}
}]);

View File

@@ -0,0 +1,69 @@
"use strict";
habitrpg
.controller("MemberModalCtrl", ['$scope', '$rootScope', 'Members', 'Shared', '$http', 'Notification', 'Groups', 'Chat', '$controller', 'Stats',
function($scope, $rootScope, Members, Shared, $http, Notification, Groups, Chat, $controller, Stats) {
$controller('RootCtrl', {$scope: $scope});
$rootScope.appLoaded = true;
$scope.timestamp = function(timestamp){
return moment(timestamp).format($rootScope.User.user.preferences.dateFormat.toUpperCase());
}
$scope.statCalc = Stats;
// We watch Members.selectedMember because it's asynchronously set, so would be a hassle to handle updates here
$scope.$watch( function() { return Members.selectedMember; }, function (member) {
if(member) {
$scope.profile = member;
}
});
$scope.sendPrivateMessage = function(uuid, message){
if (!message) return;
Members.sendPrivateMessage(message, uuid)
.then(function (response) {
Notification.text(window.env.t('messageSentAlert'));
$rootScope.User.sync();
$scope.$close();
});
};
//@TODO: We don't send subscriptions so the structure has changed in the back. Update this when we update the views.
$scope.gift = {
type: 'gems',
gems: {amount: 0, fromBalance: true},
subscription: {key: ''},
message: ''
};
$scope.sendGift = function (uuid) {
Members.transferGems($scope.gift.message, uuid, $scope.gift.gems.amount)
.then(function (response) {
Notification.text(window.env.t('sentGems'));
$rootScope.User.sync();
$scope.$close();
});
};
$scope.reportAbuse = function(reporter, message, groupId) {
message.flags[reporter._id] = true;
Chat.flagChatMessage(groupId, message.id)
.then(function(data){
Notification.text(window.env.t('abuseReported'));
$scope.$close();
});
};
$scope.clearFlagCount = function(message, groupId) {
Chat.clearFlagCount(groupId, message.id)
.then(function(data){
message.flagCount = 0;
Notification.text("Flags cleared");
$scope.$close();
});
}
}
]);

View File

@@ -0,0 +1,108 @@
'use strict';
angular.module('habitrpg')
.controller('MenuCtrl', ['$scope', '$rootScope', '$http', 'Chat', 'Content',
function($scope, $rootScope, $http, Chat, Content) {
$scope.logout = function() {
localStorage.clear();
window.location.href = '/logout';
};
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) {
return mysteryValue;
} else if ((user.invitations.party && user.invitations.party.id) || (user.invitations.guilds && user.invitations.guilds.length > 0)) {
return invitationValue;
} else if (user.flags.cardReceived) {
return cardValue;
} else if (user.flags.classSelected && !(user.preferences && user.preferences.disableClasses) && user.stats.points) {
return unallocatedValue;
} else if (!(_.isEmpty(user.newMessages))) {
return messageValue;
} else {
return noneValue;
}
}
$scope.hasQuestProgress = function() {
var user = $scope.user;
if (user.party.quest) {
var userQuest = Content.quests[user.party.quest.key];
if (!userQuest) {
return false;
}
if (userQuest.boss && user.party.quest.progress.up > 0) {
return true;
}
if (userQuest.collect && user.party.quest.progress.collectedItems > 0) {
return true;
}
}
return false;
};
$scope.getQuestInfo = function() {
var user = $scope.user;
var questInfo = {};
if (user.party.quest) {
var userQuest = Content.quests[user.party.quest.key];
questInfo.title = userQuest.text();
if (userQuest.boss) {
questInfo.body = window.env.t('questTaskDamage', { damage: user.party.quest.progress.up.toFixed(1) });
} else if (userQuest.collect) {
questInfo.body = window.env.t('questTaskCollection', { items: user.party.quest.progress.collectedItems });
}
}
return questInfo;
};
$scope.clearMessages = Chat.markChatSeen;
$scope.clearCards = Chat.clearCards;
$scope.getNotificationsCount = function() {
var count = 0;
if($scope.user.invitations.party && $scope.user.invitations.party.id){
count++;
}
if($scope.user.purchased.plan && $scope.user.purchased.plan.mysteryItems.length){
count++;
}
if($scope.user.invitations.guilds){
count += $scope.user.invitations.guilds.length;
}
if($scope.user.flags.classSelected && !$scope.user.preferences.disableClasses && $scope.user.stats.points){
count += $scope.user.stats.points > 0 ? 1 : 0;
}
if($scope.user.newMessages) {
count += Object.keys($scope.user.newMessages).length;
}
return count;
};
$scope.iconClasses = function() {
return selectNotificationValue(
'glyphicon-gift',
'glyphicon-user',
'glyphicon-envelope',
'glyphicon-plus-sign',
'glyphicon-comment',
'glyphicon-comment inactive'
);
};
$scope.hasNoNotifications = function() {
return selectNotificationValue(false, false, false, false, false, true);
}
}
]);

View File

@@ -0,0 +1,186 @@
'use strict';
habitrpg.controller('NotificationCtrl',
['$scope', '$rootScope', 'Shared', 'Content', 'User', 'Guide', 'Notification', 'Analytics', 'Achievement',
function ($scope, $rootScope, Shared, Content, User, Guide, Notification, Analytics, Achievement) {
$rootScope.$watch('user.stats.hp', function (after, before) {
if (after <= 0){
$rootScope.playSound('Death');
$rootScope.openModal('death', {keyboard:false, backdrop:'static'});
} else if (after <= 30 && !User.user.flags.warnedLowHealth) {
$rootScope.openModal('lowHealth', {keyboard:false, backdrop:'static', controller:'UserCtrl', track:'Health Warning'});
}
if (after == before) return;
if (User.user.stats.lvl == 0) return;
Notification.hp(after - before, 'hp');
if (after < 0) $rootScope.playSound('Minus_Habit');
});
$rootScope.$watch('user.stats.exp', function(after, before) {
if (after == before) return;
if (User.user.stats.lvl == 0) return;
Notification.exp(after - before);
});
$rootScope.$watch('user.stats.gp', function(after, before) {
if (after == before) return;
if (User.user.stats.lvl == 0) return;
var money = after - before;
var bonus;
if (User.user._tmp) {
bonus = User.user._tmp.streakBonus || 0;
}
Notification.gp(money, bonus || 0);
//Append Bonus
if ((money > 0) && !!bonus) {
if (bonus < 0.01) bonus = 0.01;
Notification.text("+ " + Notification.coins(bonus) + ' ' + window.env.t('streakCoins'));
delete User.user._tmp.streakBonus;
}
});
$rootScope.$watch('user.stats.mp', function(after,before) {
if (after == before) return;
if (!User.user.flags.classSelected || User.user.preferences.disableClasses) return;
var mana = after - before;
Notification.mp(mana);
});
// Levels that already display modals and should not trigger generic Level Up
var unlockLevels = {
'3': 'drop system',
'10': 'class system',
'50': 'Orb of Rebirth'
}
$rootScope.$watch('user.stats.lvl', function(after, before) {
if (after <= before) return;
Notification.lvl();
$rootScope.playSound('Level_Up');
if (User.user._tmp && User.user._tmp.drop && (User.user._tmp.drop.type === 'Quest')) return;
if (unlockLevels['' + after]) return;
if (!User.user.preferences.suppressModals.levelUp) $rootScope.openModal('levelUp', {controller:'UserCtrl', size:'sm'});
});
$rootScope.$watch('!user.flags.classSelected && user.stats.lvl >= 10', function(after, before){
if(after){
$rootScope.openModal('chooseClass', {controller:'UserCtrl', keyboard:false, backdrop:'static'});
}
});
// Avoid showing the same notiication more than once
var lastShownNotifications = [];
function handleUserNotifications (after) {
if (!after || after.length === 0) return;
after.forEach(function (notification) {
if (lastShownNotifications.indexOf(notification.id) !== -1) {
return;
}
lastShownNotifications.push(notification.id);
if (lastShownNotifications.length > 10) {
lastShownNotifications.splice(0, 9);
}
var markAsRead = true;
switch (notification.type) {
case 'DROPS_ENABLED':
$rootScope.openModal('dropsEnabled');
break;
case 'REBIRTH_ENABLED':
$rootScope.openModal('rebirthEnabled');
break;
case 'WON_CHALLENGE':
User.sync().then( function() {
Achievement.displayAchievement('wonChallenge');
});
break;
case 'STREAK_ACHIEVEMENT':
Notification.streak(User.user.achievements.streak);
$rootScope.playSound('Achievement_Unlocked');
if (!User.user.preferences.suppressModals.streak) {
Achievement.displayAchievement('streak', {size: 'md'});
}
break;
case 'ULTIMATE_GEAR_ACHIEVEMENT':
Achievement.displayAchievement('ultimateGear', {size: 'md'});
break;
case 'REBIRTH_ACHIEVEMENT':
Achievement.displayAchievement('rebirth');
break;
case 'NEW_CONTRIBUTOR_LEVEL':
Achievement.displayAchievement('contributor', {size: 'md'});
break;
case 'CRON':
if (notification.data) {
if (notification.data.hp) Notification.hp(notification.data.hp, 'hp');
if (notification.data.mp) Notification.mp(notification.data.mp);
}
break;
default:
markAsRead = false; // If the notification is not implemented, skip it
break;
}
if (markAsRead) User.readNotification(notification.id);
});
User.user.notifications = []; // reset the notifications
}
// Since we don't use localStorage anymore, notifications for achievements and new contributor levels
// are now stored in user.notifications.
$rootScope.$watchCollection('userNotifications', function (after) {
if (!User.user._wrapped) return;
handleUserNotifications(after);
});
var handleUserNotificationsOnFirstSync = _.once(function () {
handleUserNotifications($rootScope.userNotifications);
});
$rootScope.$on('userUpdated', handleUserNotificationsOnFirstSync);
// TODO what about this?
$rootScope.$watch('user.achievements', function(){
$rootScope.playSound('Achievement_Unlocked');
}, true);
$rootScope.$watch('user.flags.armoireEmpty', function(after,before){
if (after == before || after == false) return;
$rootScope.openModal('armoireEmpty');
});
// Completed quest modal
$scope.$watch('user.party.quest.completed', function(after, before){
if (!after) return;
$rootScope.openModal('questCompleted', {controller:'InventoryCtrl'});
});
// Quest invitation modal
$scope.$watch('user.party.quest.RSVPNeeded && !user.party.quest.completed', function(after, before){
if (after != true) return;
$rootScope.openModal('questInvitation', {controller:'PartyCtrl'});
});
$rootScope.$on('responseError500', function(ev, error){
Notification.error(error);
});
$rootScope.$on('responseError', function(ev, error){
Notification.error(error, true);
});
$rootScope.$on('responseText', function(ev, error){
Notification.text(error);
});
// Show new-stuff modal on load
if (User.user.flags.newStuff)
$rootScope.openModal('newStuff', {size:'lg'});
}
]);

View File

@@ -0,0 +1,246 @@
'use strict';
habitrpg.controller("PartyCtrl", ['$rootScope','$scope','Groups','Chat','User','Challenges','$state','$compile','Analytics','Quests','Social', 'Achievement',
function($rootScope, $scope, Groups, Chat, User, Challenges, $state, $compile, Analytics, Quests, Social, Achievement) {
var PARTY_LOADING_MESSAGES = 4;
var user = User.user;
$scope.type = 'party';
$scope.text = window.env.t('party');
$scope.group = {loadingParty: true};
$scope.inviteOrStartParty = Groups.inviteOrStartParty;
$scope.loadWidgets = Social.loadWidgets;
// Random message between 1 and PARTY_LOADING_MESSAGES
var partyMessageNumber = Math.floor(Math.random() * PARTY_LOADING_MESSAGES) + 1;
$scope.partyLoadingMessage = window.env.t('partyLoading' + partyMessageNumber);
function handlePartyResponse (group) {
// Assign and not replace so that all the references get the modifications
_.assign($rootScope.party, group);
$scope.group = $rootScope.party;
$scope.group.loadingParty = false;
checkForNotifications();
if ($state.is('options.social.party')) {
if ('Notification' in window && window.Notification.permission === 'default') {
setTimeout(function () {
var notifsModal = $rootScope.openModal('enableDesktopNotifications', {
backdrop: true,
windowClass: 'vertically-centered-modals',
});
// Safari doesn't support promises
var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
function closeModal () { notifsModal.close(); }
if (isSafari) {
window.Notification.requestPermission(closeModal);
} else {
window.Notification.requestPermission().then(closeModal);
}
}, 100);
}
Chat.markChatSeen($scope.group._id);
}
}
function handlePartyError (response) {
$rootScope.party = $scope.group = $scope.newGroup = { type: 'party' };
}
if ($state.is('options.social.party') && $rootScope.party && $rootScope.party.id) {
Groups.party().then(handlePartyResponse, handlePartyError);
} else {
Groups.Group.syncParty().then(handlePartyResponse, handlePartyError);
}
function checkForNotifications () {
// Checks if user's party has reached 2 players for the first time.
if(!user.achievements.partyUp
&& $scope.group.memberCount >= 2) {
User.set({'achievements.partyUp':true});
Achievement.displayAchievement('partyUp');
}
// Checks if user's party has reached 4 players for the first time.
if(!user.achievements.partyOn
&& $scope.group.memberCount >= 4) {
User.set({'achievements.partyOn':true});
Achievement.displayAchievement('partyOn');
}
}
$scope.create = function(group) {
group.loadingParty = true;
if (!group.name) group.name = env.t('possessiveParty', {name: User.user.profile.name});
Groups.Group.create(group)
.then(function(response) {
Analytics.updateUser({'party.id': $scope.group ._id, 'partySize': 1});
$rootScope.hardRedirect('/#/options/groups/party');
});
};
$scope.join = function (party) {
Groups.Group.join(party.id)
.then(function (response) {
$rootScope.party = $scope.group = response.data.data;
User.sync();
Analytics.updateUser({'partyID': party.id});
$rootScope.hardRedirect('/#/options/groups/party');
});
};
// TODO: refactor guild and party leave into one function
$scope.leave = function (keep) {
if (keep == 'cancel') {
$scope.selectedGroup = undefined;
$scope.popoverEl.popover('destroy');
} else {
Groups.Group.leave($scope.selectedGroup._id, keep)
.then(function (response) {
Analytics.updateUser({'partySize':null,'partyID':null});
User.sync().then(function () {
$rootScope.hardRedirect('/#/options/groups/party');
});
});
}
};
// TODO: refactor guild and party clickLeave into one function
$scope.clickLeave = function(group, $event){
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Leave Party'});
$scope.selectedGroup = group;
$scope.popoverEl = $($event.target).closest('.btn');
var html, title;
html = $compile('<a ng-controller="GroupsCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>')($scope);
title = window.env.t('leavePartyCha');
//TODO: Move this to challenge service
Challenges.getGroupChallenges(group._id)
.then(function(response) {
var challenges = _.pluck(_.filter(response.data.data, function(c) {
return c.group._id == group._id;
}), '_id');
if (_.intersection(challenges, User.user.challenges).length > 0) {
html = $compile(
'<a ng-controller="GroupsCtrl" ng-click="leave(\'remove-all\')">' + window.env.t('removeTasks') + '</a><br/>\n<a ng-click="leave(\'keep-all\')">' + window.env.t('keepTasks') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
title = window.env.t('leavePartyCha');
} else {
html = $compile(
'<a ng-controller="GroupsCtrl" ng-click="leave(\'keep-all\')">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="leave(\'cancel\')">' + window.env.t('cancel') + '</a><br/>'
)($scope);
title = window.env.t('leaveParty');
}
$scope.popoverEl.popover('destroy').popover({
html: true,
placement: 'top',
trigger: 'manual',
title: title,
content: html
}).popover('show');
});
};
$scope.clickStartQuest = function () {
Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':'Start a Quest'});
var hasQuests = _.find(User.user.items.quests, function(quest) {
return quest > 0;
});
if (hasQuests){
$rootScope.openModal("ownedQuests", { controller:"InventoryCtrl" });
} else {
$rootScope.$state.go('options.inventory.quests');
}
};
$scope.leaveOldPartyAndJoinNewParty = function(newPartyId, newPartyName) {
if (confirm('Are you sure you want to delete your party and join ' + newPartyName + '?')) {
Groups.Group.leave(Groups.data.party._id, false)
.then(function() {
$rootScope.party = $scope.group = {
loadingParty: true
};
$scope.join({ id: newPartyId, name: newPartyName });
});
}
}
$scope.reject = function(party) {
Groups.Group.rejectInvite(party.id).then(function () {
User.sync();
});
}
$scope.questInit = function() {
var key = $rootScope.selectedQuest.key;
Quests.initQuest(key).then(function() {
$rootScope.selectedQuest = undefined;
$scope.$close();
});
};
$scope.questCancel = function(){
if (!confirm(window.env.t('sureCancel'))) return;
Quests.sendAction('quests/cancel')
.then(function(quest) {
$scope.group.quest = quest;
});
}
$scope.questAbort = function(){
if (!confirm(window.env.t('sureAbort'))) return;
if (!confirm(window.env.t('doubleSureAbort'))) return;
Quests.sendAction('quests/abort')
.then(function(quest) {
$scope.group.quest = quest;
});
}
$scope.questLeave = function(){
if (!confirm(window.env.t('sureLeave'))) return;
Quests.sendAction('quests/leave')
.then(function(quest) {
$scope.group.quest = quest;
});
}
$scope.questAccept = function(){
Quests.sendAction('quests/accept')
.then(function(quest) {
$scope.group.quest = quest;
});
};
$scope.questForceStart = function(){
Quests.sendAction('quests/force-start')
.then(function(quest) {
$scope.group.quest = quest;
});
};
$scope.questReject = function(){
Quests.sendAction('quests/reject')
.then(function(quest) {
$scope.group.quest = quest;
});
};
$scope.canEditQuest = function() {
var isQuestLeader = $scope.group.quest && $scope.group.quest.leader === User.user._id;
return isQuestLeader;
};
}
]);

View File

@@ -0,0 +1,386 @@
"use strict";
/* Make user and settings available for everyone through root scope.
*/
habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', 'Shared', 'Content', '$modal', '$timeout', 'ApiUrl', 'Payments','$sce','$window','Analytics','TAVERN_ID', 'Pusher',
function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups, Shared, Content, $modal, $timeout, ApiUrl, Payments, $sce, $window, Analytics, TAVERN_ID, Pusher) {
var user = User.user;
var IGNORE_SCROLL_PAGES = {
'options.social.challenges.detail': true,
'options.social.challenges': true
};
// Setup page once user is synced
var clearAppLoadedListener = $rootScope.$watch('appLoaded', function (after) {
if (after === true) {
// Initialize sticky header
$timeout(function () {
if (window.env.IS_MOBILE || User.user.preferences.stickyHeader === false) return;
$('.header-wrap').sticky({topSpacing:0});
});
// Remove listener
clearAppLoadedListener();
}
});
$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){
$rootScope.pageTitle = $state.current.title;
if (!($state.current.name in IGNORE_SCROLL_PAGES)) {
$window.scrollTo(0, 0);
}
if (!!fromState.name) Analytics.track({'hitType':'pageview','eventCategory':'navigation','eventAction':'navigate','page':'/#/'+toState.name});
if (toState.name=='options.social.inbox' && User.user.inbox && User.user.inbox.newMessages > 0) {
User.clearNewMessages();
}
});
$rootScope.appLoaded = false; // also used to indicate when the user is fully loaded
$rootScope.TAVERN_ID = TAVERN_ID;
$rootScope.User = User;
$rootScope.user = user;
$rootScope.moment = window.moment;
$rootScope._ = window._;
$rootScope.settings = User.settings;
$rootScope.Shared = Shared;
$rootScope.Content = Content;
$rootScope.Analytics = Analytics;
$rootScope.env = window.env;
$rootScope.Math = Math;
$rootScope.Groups = Groups;
$rootScope.toJson = angular.toJson;
$rootScope.Payments = Payments;
$rootScope.userNotifications = [];
$rootScope.party = {}; // to be extended later with real data
// Angular UI Router
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
// indexOf helper
$scope.indexOf = function(haystack, needle){
return haystack && ~haystack.indexOf(needle);
}
// styling helpers
$rootScope.userLevelStyle = function(user,style){
style = style || '';
var npc = (user && user.backer && user.backer.npc) ? user.backer.npc : '';
var level = (user && user.contributor && user.contributor.level) ? user.contributor.level : '';
style += $scope.userLevelStyleFromLevel(level,npc,style)
return style;
}
$scope.userAdminGlyphiconStyle = function(user,style){
style = style || '';
if(user && user.contributor && user.contributor.level)
style += $scope.userAdminGlyphiconStyleFromLevel(user.contributor.level,style)
return style;
}
$scope.userLevelStyleFromLevel = function(level,npc,style){
style = style || '';
if(npc)
style += ' label-npc';
if(level)
style += ' label-contributor-'+level;
return style;
}
$scope.userAdminGlyphiconStyleFromLevel = function(level,style){
style = style || '';
if(level)
if(level==8)
style += ' glyphicon glyphicon-star'; // moderator
if(level==9)
style += ' glyphicon icon-crown'; // staff
return style;
}
$rootScope.playSound = function(id){
if (!user.preferences.sound || user.preferences.sound == 'off') return;
var theme = user.preferences.sound;
var file = 'common/audio/' + theme + '/' + id;
document.getElementById('oggSource').src = file + '.ogg';
document.getElementById('mp3Source').src = file + '.mp3';
document.getElementById('sound').load();
}
// count pets, mounts collected totals, etc
$rootScope.countExists = function(items) {return _.reduce(items,function(m,v){return m+(v?1:0)},0)}
$scope.safeApply = function(fn) {
var phase = this.$root.$$phase;
if(phase == '$apply' || phase == '$digest') {
if(fn && (typeof(fn) === 'function')) {
fn();
}
} else {
this.$apply(fn);
}
};
$rootScope.set = User.set;
$rootScope.authenticated = User.authenticated;
var forceLoadBailey = function(template, options) {
$http.get('/new-stuff.html')
.success(function(data) {
$rootScope.latestBaileyMessage = $sce.trustAsHtml(data);
$modal.open({
templateUrl: 'modals/' + template + '.html',
controller: options.controller, // optional
scope: options.scope, // optional
resolve: options.resolve, // optional
keyboard: (options.keyboard === undefined ? true : options.keyboard), // optional
backdrop: (options.backdrop === undefined ? true : options.backdrop), // optional
size: options.size, // optional, 'sm' or 'lg'
windowClass: options.windowClass // optional
});
});
};
// Open a modal from a template expression (like ng-click,...)
// Otherwise use the proper $modal.open
$rootScope.openModal = function(template, options){//controller, scope, keyboard, backdrop){
if (!options) options = {};
if (options.track) Analytics.track({'hitType':'event','eventCategory':'button','eventAction':'click','eventLabel':options.track});
if(template === 'newStuff') return forceLoadBailey(template, options);
return $modal.open({
templateUrl: 'modals/' + template + '.html',
controller: options.controller, // optional
scope: options.scope, // optional
resolve: options.resolve, // optional
keyboard: (options.keyboard === undefined ? true : options.keyboard), // optional
backdrop: (options.backdrop === undefined ? true : options.backdrop), // optional
size: options.size, // optional, 'sm' or 'lg'
windowClass: options.windowClass // optional
});
}
$rootScope.dismissAlert = function() {
$rootScope.set({'flags.newStuff':false});
}
$rootScope.acceptCommunityGuidelines = function() {
$rootScope.set({'flags.communityGuidelinesAccepted':true});
}
$rootScope.notPorted = function(){
alert(window.env.t('notPorted'));
}
$rootScope.dismissErrorOrWarning = function(type, $index){
$rootScope.flash[type].splice($index, 1);
}
$scope.contribText = function(contrib, backer){
if (!contrib && !backer) return;
if (backer && backer.npc) return backer.npc;
var l = contrib && contrib.level;
if (l && l > 0) {
var level = (l < 3) ? window.env.t('friend') : (l < 5) ? window.env.t('elite') : (l < 7) ? window.env.t('champion') : (l < 8) ? window.env.t('legendary') : (l < 9) ? window.env.t('guardian') : window.env.t('heroic');
return level + ' ' + contrib.text;
}
}
$rootScope.charts = {};
$rootScope.toggleChart = function(id, task) {
var history = [], matrix, data, chart, options;
switch (id) {
case 'exp':
history = User.user.history.exp;
$rootScope.charts.exp = (history.length == 0) ? false : !$rootScope.charts.exp;
break;
case 'todos':
history = User.user.history.todos;
$rootScope.charts.todos = (history.length == 0) ? false : !$rootScope.charts.todos;
break;
default:
history = task.history;
$rootScope.charts[id] = (history.length == 0) ? false : !$rootScope.charts[id];
if (task && task._editing) task._editing = false;
}
matrix = [[env.t('date'), env.t('score')]];
_.each(history, function(obj) {
matrix.push([moment(obj.date).format(User.user.preferences.dateFormat.toUpperCase().replace('YYYY','YY') ), obj.value]);
});
data = google.visualization.arrayToDataTable(matrix);
options = {
title: window.env.t('history'),
backgroundColor: {
fill: 'transparent'
},
hAxis: {slantedText:true, slantedTextAngle: 90},
height:270,
width:300
};
chart = new google.visualization.LineChart($("." + id + "-chart")[0]);
chart.draw(data, options);
};
$rootScope.getGearArray = function(set){
var flatGearArray = _.toArray(Content.gear.flat);
var filteredArray = _.where(flatGearArray, {gearSet: set});
return filteredArray;
}
// @TODO: Extract equip and purchase into equipment service
$rootScope.equip = function(itemKey, equipType) {
equipType = equipType || (user.preferences.costume ? 'costume' : 'equipped');
var equipParams = {
type: equipType,
key: itemKey
};
User.equip({ params: equipParams });
}
$rootScope.purchase = function(type, item){
if (type == 'special') return User.buySpecialSpell({params:{key:item.key}});
var gems = user.balance * 4;
var price = item.value;
var message = "";
var itemName = window.env.t(Content.itemList[type].localeKey)
if (Content.itemList[type].isEquipment) {
var eligibleForPurchase = _canBuyEquipment(item.key);
if (!eligibleForPurchase) return false;
// @TODO: Attach gemValue to content so we don't have to do this
price = ((((item.specialClass == "wizard") && (item.type == "weapon")) || item.gearSet == "animal") + 1);
type = 'gear';
}
if (gems < price) return $rootScope.openModal('buyGems');
if (type === 'quests') {
if (item.previous) {message = window.env.t('alreadyEarnedQuestReward', {priorQuest: Content.quests[item.previous].text()})}
else if (item.lvl) {message = window.env.t('alreadyEarnedQuestLevel', {level: item.lvl})}
}
message += window.env.t('buyThis', {text: itemName, price: price, gems: gems});
if ($window.confirm(message))
User.purchase({params:{type:type,key:item.key}});
};
function _canBuyEquipment(itemKey) {
if (user.items.gear.owned[itemKey]) {
$window.alert(window.env.t('messageAlreadyOwnGear'));
} else if (user.items.gear.owned[itemKey] === false) {
$window.alert(window.env.t('messageAlreadyPurchasedGear'));
} else {
return true;
}
}
/*
------------------------
Spells
------------------------
*/
$scope.castStart = function(spell) {
if (User.user.stats.mp < spell.mana) return Notification.text(window.env.t('notEnoughMana'));
if (spell.immediateUse && User.user.stats.gp < spell.value)
return Notification.text('Not enough gold.');
$rootScope.applyingAction = true;
$scope.spell = spell;
if (spell.target == 'self') {
$scope.castEnd(null, 'self');
} else if (spell.target == 'party') {
Groups.party()
.then(function (party) {
party = (_.isArray(party) ? party : []).concat(User.user);
$scope.castEnd(party, 'party');
})
.catch(function (party) { // not in a party, act as a solo party
if (party && party.type === 'party') {
party = [User.user];
$scope.castEnd(party, 'party');
}
});
} else if (spell.target == 'tasks') {
var tasks = User.user.habits.concat(User.user.dailys).concat(User.user.rewards).concat(User.user.todos);
// exclude challenge tasks
tasks = tasks.filter(function (task) {
if (!task.challenge) return true;
return (!task.challenge.id || task.challenge.broken);
});
$scope.castEnd(tasks, 'tasks');
}
}
$scope.castEnd = function(target, type, $event){
if (!$rootScope.applyingAction) return 'No applying action';
$event && ($event.stopPropagation(),$event.preventDefault());
if ($scope.spell.target != type) return Notification.text(window.env.t('invalidTarget'));
$scope.spell.cast(User.user, target);
User.save();
var spell = $scope.spell;
var targetId = target ? target._id : null;
$scope.spell = null;
$rootScope.applyingAction = false;
var spellUrl = ApiUrl.get() + '/api/v3/user/class/cast/' + spell.key;
if (targetId) spellUrl += '?targetId=' + targetId;
$http.post(spellUrl)
.success(function(){ // TODO response will always include the modified data, no need to sync!
var msg = window.env.t('youCast', {spell: spell.text()});
switch (type) {
case 'task': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.text});break;
case 'user': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.profile.name});break;
case 'party': msg = window.env.t('youCastParty', {spell: spell.text()});break;
}
Notification.markdown(msg);
User.sync();
});
}
$rootScope.castCancel = function(){
$rootScope.applyingAction = false;
$scope.spell = null;
}
// Because our angular-ui-router uses anchors for urls (/#/options/groups/party), window.location.href=... won't
// reload the page. Perform manually.
$rootScope.hardRedirect = function(url){
window.location.href = url;
setTimeout(function() {
window.location.reload(false);
});
}
// Universal method for sending HTTP methods
$rootScope.http = function(method, route, data, alertMsg){
$http[method](ApiUrl.get() + route, data).success(function(){
if (alertMsg) Notification.text(window.env.t(alertMsg));
User.sync();
});
// error will be handled via $http interceptor
}
// Global Keyevents
var ctrlKeys = [17, 224, 91];
$scope.$on("habit:keydown", function (e, keyEvent) {
if (ctrlKeys.indexOf(keyEvent.keyCode) !== -1) {
$scope.ctrlPressed = true;
}
});
$scope.$on("habit:keyup", function (e, keyEvent) {
if (ctrlKeys.indexOf(keyEvent.keyCode) !== -1) {
$scope.ctrlPressed = false;
}
});
}
]);

View File

@@ -0,0 +1,303 @@
'use strict';
// Make user and settings available for everyone through root scope.
habitrpg.controller('SettingsCtrl',
['$scope', 'User', '$rootScope', '$http', 'ApiUrl', 'Guide', '$location', '$timeout', 'Content', 'Notification', 'Shared', '$compile',
function($scope, User, $rootScope, $http, ApiUrl, Guide, $location, $timeout, Content, Notification, Shared, $compile) {
var RELEASE_ANIMAL_TYPES = {
pets: 'releasePets',
mounts: 'releaseMounts',
both: 'releaseBoth',
};
// FIXME we have this re-declared everywhere, figure which is the canonical version and delete the rest
// $scope.auth = function (id, token) {
// User.authenticate(id, token, function (err) {
// if (!err) {
// alert('Login successful!');
// $location.path("/habit");
// }
// });
// }
// A simple object to map the key stored in the db (user.preferences.emailNotification[key])
// to its string but ONLY when the preferences' key and the string key don't match
var mapPrefToEmailString = {
'importantAnnouncements': 'inactivityEmails'
};
// If ?unsubFrom param is passed with valid email type,
// automatically unsubscribe users from that email and
// show an alert
$timeout(function(){
var unsubFrom = $location.search().unsubFrom;
if(unsubFrom){
var emailPrefKey = 'preferences.emailNotifications.' + unsubFrom;
var emailTypeString = env.t(mapPrefToEmailString[unsubFrom] || unsubFrom);
User.set({emailPrefKey: false});
User.user.preferences.emailNotifications[unsubFrom] = false;
Notification.text(env.t('correctlyUnsubscribedEmailType', {emailType: emailTypeString}));
$location.search({});
}
}, 1000);
$scope.hideHeader = function(){
User.set({"preferences.hideHeader":!User.user.preferences.hideHeader})
if (User.user.preferences.hideHeader && User.user.preferences.stickyHeader){
User.set({"preferences.stickyHeader":false});
$rootScope.$on('userSynced', function(){
window.location.reload();
});
}
}
$scope.toggleStickyHeader = function(){
$rootScope.$on('userSynced', function(){
window.location.reload();
});
User.set({"preferences.stickyHeader":!User.user.preferences.stickyHeader});
}
$scope.showTour = function(){
User.set({'flags.showTour':true});
Guide.goto('intro', 0, true);
}
$scope.showBailey = function(){
User.set({'flags.newStuff':true});
}
$scope.dayStart = User.user.preferences.dayStart;
$scope.openDayStartModal = function(dayStart) {
$scope.dayStart = +dayStart;
$scope.nextCron = _calculateNextCron();
$rootScope.openModal('change-day-start', { scope: $scope });
};
$scope.saveDayStart = function() {
User.setCustomDayStart(Math.floor($scope.dayStart));
};
$scope.language = window.env.language;
$scope.availableLanguages = window.env.availableLanguages;
$scope.changeLanguage = function(){
$rootScope.$on('userSynced', function(){
window.location.reload();
});
User.set({'preferences.language': $scope.language.code});
}
$scope.availableFormats = ['MM/dd/yyyy','dd/MM/yyyy', 'yyyy/MM/dd'];
$scope.reroll = function(confirm){
$scope.popoverEl.popover('destroy');
if (confirm) {
User.reroll({});
$rootScope.$state.go('tasks');
}
}
$scope.clickReroll = function($event){
$scope.popoverEl = $($event.target);
var html = $compile(
'<a ng-controller="SettingsCtrl" ng-click="$close(); reroll(true)">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="reroll(false)">' + window.env.t('cancel') + '</a><br/>'
)($scope);
$scope.popoverEl.popover('destroy').popover({
html: true,
placement: 'top',
trigger: 'manual',
title: window.env.t('confirmFortify'),
content: html
}).popover('show');
}
$scope.rebirth = function(confirm){
$scope.popoverEl.popover('destroy');
if (confirm) {
User.rebirth({});
$rootScope.$state.go('tasks');
}
}
$scope.clickRebirth = function($event){
$scope.popoverEl = $($event.target);
var html = $compile(
'<a ng-controller="SettingsCtrl" ng-click="$close(); rebirth(true)">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="rebirth(false)">' + window.env.t('cancel') + '</a><br/>'
)($scope);
$scope.popoverEl.popover('destroy').popover({
html: true,
placement: 'top',
trigger: 'manual',
title: window.env.t('confirmReborn'),
content: html
}).popover('show');
}
$scope.changeUser = function(attr, updates){
$http.put(ApiUrl.get() + '/api/v3/user/auth/update-'+attr, updates)
.success(function(){
alert(window.env.t(attr+'Success'));
_.each(updates, function(v,k){updates[k]=null;});
User.sync();
});
}
$scope.restoreValues = {};
$rootScope.openRestoreModal = function(){
$scope.restoreValues.stats = angular.copy(User.user.stats);
$scope.restoreValues.achievements = {streak: User.user.achievements.streak || 0};
$rootScope.openModal('restore', {scope:$scope});
};
$scope.restore = function(){
var stats = $scope.restoreValues.stats,
achievements = $scope.restoreValues.achievements;
User.set({
"stats.hp": stats.hp,
"stats.exp": stats.exp,
"stats.gp": stats.gp,
"stats.lvl": stats.lvl,
"stats.mp": stats.mp,
"achievements.streak": achievements.streak
});
}
$scope.reset = function(){
User.reset({});
User.sync();
$rootScope.$state.go('tasks');
}
$scope['delete'] = function(password) {
$http({
url: ApiUrl.get() + '/api/v3/user',
method: 'DELETE',
data: {password: password},
})
.then(function(res, code) {
localStorage.clear();
window.location.href = '/logout';
});
}
$scope.enterCoupon = function(code) {
$http.post(ApiUrl.get() + '/api/v3/coupons/enter/' + code).success(function(res,code){
if (code!==200) return;
User.sync();
Notification.text(env.t('promoCodeApplied'));
});
}
$scope.generateCodes = function(codes){
$http.post(ApiUrl.get() + '/api/v2/coupons/generate/'+codes.event+'?count='+(codes.count || 1))
.success(function(res,code){
$scope._codes = {};
if (code!==200) return;
window.location.href = '/api/v2/coupons?limit='+codes.count+'&_id='+User.user._id+'&apiToken='+User.settings.auth.apiToken;
})
}
$scope.clickRelease = function(type, $event){
// Close other popovers if they're open
$(".release_popover").not($event.target).popover('destroy');
// Handle clicking on the gem icon
if ($event.target.nodeName == "SPAN") {
$scope.releasePopoverEl = $($event.target.parentNode);
} else {
$scope.releasePopoverEl = $($event.target);
}
var html = $compile(
'<a ng-controller="SettingsCtrl" ng-click="$close(); releaseAnimals(\'' + type + '\')">' + window.env.t('confirm') + '</a><br/>\n<a ng-click="releaseAnimals()">' + window.env.t('cancel') + '</a><br/>'
)($scope);
$scope.releasePopoverEl.popover('destroy').popover({
html: true,
placement: 'top',
trigger: 'manual',
title: window.env.t('confirmPetKey'),
content: html
}).popover('show');
}
$scope.releaseAnimals = function (type) {
$scope.releasePopoverEl.popover('destroy');
var releaseFunction = RELEASE_ANIMAL_TYPES[type];
if (releaseFunction) {
User[releaseFunction]({});
$rootScope.$state.go('tasks');
}
}
// ---- Webhooks ------
$scope._newWebhook = {url:''};
$scope.$watch('user.preferences.webhooks',function(webhooks){
$scope.hasWebhooks = _.size(webhooks);
})
$scope.addWebhook = function(url) {
User.addWebhook({body:{url:url, enabled:true}});
$scope._newWebhook.url = '';
}
$scope.saveWebhook = function(id,webhook) {
delete webhook._editing;
User.updateWebhook({params:{id:id}, body:webhook});
}
$scope.deleteWebhook = function(id) {
User.deleteWebhook({params:{id:id}});
}
$scope.applyCoupon = function(coupon){
$http.post(ApiUrl.get() + '/api/v3/coupons/validate/'+coupon)
.success(function(){
Notification.text("Coupon applied!");
var subs = Content.subscriptionBlocks;
subs["basic_6mo"].discount = true;
subs["google_6mo"].discount = false;
});
}
$scope.gemGoldCap = function(subscription) {
var baseCap = 25;
var gemCapIncrement = 5;
var capIncrementThreshold = 3;
var gemCapExtra = User.user.purchased.plan.consecutive.gemCapExtra;
var blocks = Content.subscriptionBlocks[subscription.key].months / capIncrementThreshold;
var flooredBlocks = Math.floor(blocks);
var userTotalDropCap = baseCap + gemCapExtra + flooredBlocks * gemCapIncrement;
var maxDropCap = 50;
return [userTotalDropCap, maxDropCap];
};
$scope.numberOfMysticHourglasses = function(subscription) {
var numberOfHourglasses = Content.subscriptionBlocks[subscription.key].months / 3;
return Math.floor(numberOfHourglasses);
};
function _calculateNextCron() {
$scope.dayStart;
var nextCron = moment().hours($scope.dayStart).minutes(0).seconds(0).milliseconds(0);
var currentHour = moment().format('H');
if (currentHour >= $scope.dayStart) {
nextCron = nextCron.add(1, 'day');;
}
return +nextCron.format('x');
}
}
]);

View File

@@ -0,0 +1,24 @@
habitrpg.controller('SortableInventoryController', ['$scope',
function ($scope) {
var attributeSort = {
constitution: ['-(_effectiveCon)', '-(_effectiveCon+_effectiveInt+_effectivePer+_effectiveStr)'],
intelligence: ['-(_effectiveInt)', '-(_effectiveCon+_effectiveInt+_effectivePer+_effectiveStr)'],
perception: ['-(_effectivePer)', '-(_effectiveCon+_effectiveInt+_effectivePer+_effectiveStr)'],
strength: ['-(_effectiveStr)', '-(_effectiveCon+_effectiveInt+_effectivePer+_effectiveStr)'],
set: 'set'
}
$scope.setOrder = function (order) {
$scope.orderChoice = order;
if (order in attributeSort) {
$scope.order = attributeSort[order];
}
};
$scope.setGrouping = function (grouping) {
$scope.groupingChoice = grouping;
};
$scope.orderChoice = 'set';
$scope.setOrder($scope.orderChoice);
}]);

View File

@@ -0,0 +1,375 @@
"use strict";
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Content', 'Shared', 'Guide', 'Tasks', 'Analytics',
function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Content, Shared, Guide, Tasks, Analytics) {
$scope.obj = User.user; // used for task-lists
$scope.user = User.user;
var CTRL_KEYS = [17, 224, 91];
$scope.armoireCount = function(gear) {
return Shared.count.remainingGearInSet(gear, 'armoire');
};
$scope.score = function(task, direction) {
switch (task.type) {
case 'reward':
playRewardSound(task);
break;
case 'daily':
$rootScope.playSound('Daily');
break;
case 'todo':
$rootScope.playSound('ToDo');
break;
default:
if (direction === 'down') $rootScope.playSound('Minus_Habit');
else if (direction === 'up') $rootScope.playSound('Plus_Habit');
}
User.score({params:{task: task, direction:direction}});
Analytics.updateUser();
};
function addUserTasks(listDef, tasks) {
tasks = tasks.map(function (task) {
return {
text: task,
type: listDef.type,
tags: _.keys(User.user.filters),
};
});
User.addTask({
body: tasks,
});
}
$scope.addTask = function(listDef) {
Tasks.addTasks(listDef, addUserTasks);
if (listDef.type=='daily') Guide.goto('intro', 2);
};
$scope.toggleBulk = Tasks.toggleBulk;
$scope.editTask = Tasks.editTask;
$scope.canEdit = function(task) {
// can't edit challenge tasks
return !task.challenge.id;
}
$scope.doubleClickTask = function (obj, task) {
if (obj._locked) {
return false;
}
if (task._editing) {
$scope.saveTask(task);
} else {
$scope.editTask(task, User.user);
}
}
/**
* Add the new task to the actions log
*/
$scope.clearDoneTodos = function() {
if (!confirm(window.env.t('sureDeleteCompletedTodos'))) {
return;
}
Tasks.clearCompletedTodos();
User.user.todos = _.reject(User.user.todos, 'completed');
};
/**
* Pushes task to top or bottom of list
*/
$scope.pushTask = function(task, index, location) {
var to = (location === 'bottom' || $scope.ctrlPressed) ? -1 : 0;
User.sortTask({params:{id: task._id, taskType: task.type}, query:{from:index, to:to}})
};
/**
* This is calculated post-change, so task.completed=true if they just checked it
*/
$scope.changeCheck = function(task) {
if (task.completed) {
$scope.score(task, "up");
} else {
$scope.score(task, "down");
}
};
$scope.saveTask = function(task, stayOpen, isSaveAndClose) {
if (task._edit) {
angular.copy(task._edit, task);
}
task._edit = undefined;
if (task.checklist) {
task.checklist = _.filter(task.checklist, function (i) {
return !!i.text
});
}
User.updateTask(task, {body: task});
if (!stayOpen) task._editing = false;
if (isSaveAndClose) {
$("#task-" + task._id).parent().children('.popover').removeClass('in');
}
if (task.type == 'habit') Guide.goto('intro', 3);
};
$scope.completeChecklistItem = function completeChecklistItem(task) {
User.updateTask(task, {body: task});
};
/**
* Reset $scope.task to $scope.originalTask
*/
$scope.cancelTaskEdit = Tasks.cancelTaskEdit;
$scope.removeTask = function(task) {
if (!confirm(window.env.t('sureDelete', {taskType: window.env.t(task.type), taskText: task.text}))) return;
task._edit = undefined;
User.deleteTask({params:{id: task._id, taskType: task.type}})
};
$scope.unlink = function(task, keep) {
if (keep.search('-all') !== -1) { // unlink all tasks
Tasks.unlinkAllTasks(task.challenge.id, keep)
.success(function () {
User.sync({});
});
} else { // unlink a task
Tasks.unlinkOneTask(task._id, keep)
.success(function () {
User.sync({});
});
}
};
/*
------------------------
To-Dos
------------------------
*/
$scope._today = moment().add({days: 1});
$scope.loadedCompletedTodos = function () {
if (Tasks.loadedCompletedTodos === true) {
return;
}
User.user.todos = _.reject(User.user.todos, 'completed')
Tasks.getUserTasks(true)
.then(function (response) {
User.user.todos = User.user.todos.concat(response.data.data);
Tasks.loadedCompletedTodos = true;
});
}
/*
------------------------
Dailies
------------------------
*/
$scope.openDatePicker = function($event, task) {
$event.preventDefault();
$event.stopPropagation();
task._isDatePickerOpen = !task._isDatePickerOpen;
}
/*
------------------------
Checklists
------------------------
*/
function focusChecklist(task,index) {
window.setTimeout(function(){
$('#task-'+task._id+' .checklist-form input[type="text"]')[index].focus();
});
}
$scope.addChecklist = function(task) {
task._edit.checklist = [{completed:false, text:""}];
focusChecklist(task._edit,0);
}
$scope.addChecklistItem = function(task, $event, $index) {
if (task._edit.checklist[$index].text) {
if ($index === task._edit.checklist.length - 1) {
task._edit.checklist.push({ completed: false, text: '' });
}
focusChecklist(task._edit, $index + 1);
} else {
// TODO Provide UI feedback that this item is still blank
}
}
$scope.removeChecklistItem = function(task, $event, $index, force) {
// Remove item if clicked on trash icon
if (force) {
task._edit.checklist.splice($index, 1);
} else if (!task._edit.checklist[$index].text) {
// User deleted all the text and is now wishing to delete the item
// saveTask will prune the empty item
// Move focus if the list is still non-empty
if ($index > 0)
focusChecklist(task._edit, $index-1);
// Don't allow the backspace key to navigate back now that the field is gone
$event.preventDefault();
}
}
$scope.swapChecklistItems = function(task, oldIndex, newIndex) {
var toSwap = task._edit.checklist.splice(oldIndex, 1)[0];
task._edit.checklist.splice(newIndex, 0, toSwap);
}
$scope.navigateChecklist = function(task,$index,$event){
focusChecklist(task, $event.keyCode == '40' ? $index+1 : $index-1);
}
$scope.checklistCompletion = function(checklist){
return _.reduce(checklist,function(m,i){return m+(i.completed ? 1 : 0);},0)
}
$scope.collapseChecklist = function(task) {
task.collapseChecklist = !task.collapseChecklist;
$scope.saveTask(task,true);
}
/*
------------------------
Items
------------------------
*/
$scope.$watch('user.items.gear.owned', function(){
$scope.itemStore = Shared.updateStore(User.user);
},true);
$scope.healthPotion = Content.potion;
$scope.armoire = Content.armoire;
$scope.buy = function(item) {
playRewardSound(item);
User.buy({params:{key:item.key}});
};
$scope.buyArmoire = function () {
playRewardSound($scope.armoire);
User.buyArmoire();
}
/*
------------------------
Hiding Tasks
------------------------
*/
$scope.shouldShow = function(task, list, prefs){
if (task._editing) // never hide a task while being edited
return true;
var shouldDo = task.type == 'daily' ? habitrpgShared.shouldDo(new Date, task, prefs) : true;
switch (list.view) {
case "yellowred": // Habits
return task.value < 1;
case "greenblue": // Habits
return task.value >= 1;
case "remaining": // Dailies and To-Dos
return !task.completed && shouldDo;
case "complete": // Dailies and To-Dos
return task.completed || !shouldDo;
case "dated": // To-Dos
return !task.completed && task.date;
case "ingamerewards": // All skills/rewards except the user's own
return false; // Because "rewards" list includes only the user's own
case "all":
return true;
}
}
function playRewardSound (task) {
if (task.value <= User.user.stats.gp){
$rootScope.playSound('Reward');
}
}
var isCtrlPressed = function (keyEvent) {
if (CTRL_KEYS.indexOf(keyEvent.keyCode) > -1) {
$scope.ctrlPressed = true;
$scope.$apply();
}
}
var isCtrlLetGo = function (keyEvent) {
if (CTRL_KEYS.indexOf(keyEvent.keyCode) > -1) {
$scope.ctrlPressed = false;
$scope.$apply();
}
}
document.addEventListener('keydown', isCtrlPressed);
document.addEventListener('keyup', isCtrlLetGo);
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams, options){
if (toState.name.indexOf('tasks') < 0) {
document.removeEventListener('keydown', isCtrlPressed);
document.removeEventListener('keyup', isCtrlLetGo);
}
});
/*
------------------------
Tags
------------------------
*/
$scope.updateTaskTags = function (tagId, task) {
var tagIndex = task._edit.tags.indexOf(tagId);
if (tagIndex === -1) {
Tasks.addTagToTask(task._id, tagId);
task.tags.push(tagId);
} else {
Tasks.removeTagFromTask(task._id, tagId);
task.tags.splice(tagIndex, 1);
}
angular.copy(task.tags, task._edit.tags);
}
/*
------------------------
Disabling Spells
------------------------
*/
$scope.spellDisabled = function (skill) {
if (skill === 'frost' && $scope.user.stats.buffs.streaks) {
return true;
} else if (skill === 'stealth' && $scope.user.stats.buffs.stealth >= $scope.user.dailys.length) {
return true;
}
return false;
};
$scope.skillNotes = function (skill) {
var notes = skill.notes();
if (skill.key === 'frost' && $scope.spellDisabled(skill.key)) {
notes = window.env.t('spellWizardFrostAlreadyCast');
} else if (skill.key === 'stealth' && $scope.spellDisabled(skill.key)) {
notes = window.env.t('spellRogueStealthMaxedOut');
} else if (skill.key === 'stealth') {
notes = window.env.t('spellRogueStealthDaliesAvoided', { originalText: notes, number: $scope.user.stats.buffs.stealth });
}
return notes;
};
}]);

View File

@@ -0,0 +1,18 @@
'use strict';
habitrpg.controller("TavernCtrl", ['$scope', 'Groups', 'User', 'Challenges',
function($scope, Groups, User, Challenges) {
Groups.tavern()
.then(function (tavern) {
$scope.group = tavern;
Challenges.getGroupChallenges($scope.group._id)
.then(function (response) {
$scope.group.challenges = response.data.data;
});
})
$scope.toggleUserTier = function($event) {
$($event.target).next().toggle();
}
}
]);

View File

@@ -0,0 +1,87 @@
"use strict";
habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$http', '$state', 'Guide', 'Shared', 'Content', 'Stats', 'Social',
function($rootScope, $scope, $location, User, $http, $state, Guide, Shared, Content, Stats, Social) {
$scope.profile = User.user;
$scope.statCalc = Stats;
$scope.loadWidgets = Social.loadWidgets;
$scope.hideUserAvatar = function() {
$(".userAvatar").hide();
};
$scope.$watch('_editing.profile', function(value){
if(value === true) $scope.editingProfile = angular.copy(User.user.profile);
});
$scope.allocate = function(stat){
User.allocate({query:{stat:stat}});
}
$scope.changeClass = function(klass){
if (!klass) {
if (!confirm(window.env.t('sureReset')))
return;
return User.changeClass({});
}
User.changeClass({query:{class:klass}});
$scope.selectedClass = undefined;
Shared.updateStore(User.user);
Guide.goto('classes', 0,true);
}
$scope.save = function(){
var values = {};
_.each($scope.editingProfile, function(value, key){
// Using toString because we need to compare two arrays (websites)
var curVal = $scope.profile.profile[key];
if(!curVal || $scope.editingProfile[key].toString() !== curVal.toString())
values['profile.' + key] = value;
});
User.set(values);
$scope._editing.profile = false;
}
$scope.acknowledgeHealthWarning = function(){
User.set({'flags.warnedLowHealth':true});
}
/**
* For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase
* @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc.
* Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets
*/
$scope.unlock = function(path){
var fullSet = ~path.indexOf(',');
var cost =
~path.indexOf('background.') ?
(fullSet ? 3.75 : 1.75) : // (Backgrounds) 15G per set, 7G per individual
(fullSet ? 1.25 : 0.5); // (Hair, skin, etc) 5G per set, 2G per individual
if (fullSet) {
if (confirm(window.env.t('purchaseFor',{cost:cost*4})) !== true) return;
if (User.user.balance < cost) return $rootScope.openModal('buyGems');
} else if (!User.user.fns.dotGet('purchased.' + path)) {
if (confirm(window.env.t('purchaseFor',{cost:cost*4})) !== true) return;
if (User.user.balance < cost) return $rootScope.openModal('buyGems');
}
User.unlock({query:{path:path}})
}
$scope.ownsSet = function(type,_set) {
return !_.find(_set,function(v,k){
return !User.user.purchased[type][k];
});
}
$scope.setKeys = function(type,_set){
return _.map(_set, function(v,k){
return type+'.'+k;
}).join(',');
}
}
]);