rewrite2 WIP

This commit is contained in:
Tyler Renelle 2013-08-24 21:06:37 -04:00
parent a00bd3dcf1
commit 3c45220e1f
130 changed files with 3452 additions and 558 deletions

View file

@ -1,5 +0,0 @@
.DS_Store
public/gen/
#lib/
*.swp
.idea/

3
.bowerrc Normal file
View file

@ -0,0 +1,3 @@
{
"directory": "assets/bower_components"
}

2
.gitignore vendored
View file

@ -1,12 +1,12 @@
.DS_Store
public/gen
node_modules
#lib/
*.swp
.idea*
config.json
npm-debug.log
lib
assets/bower_components
src/*/*.js
src/*/*.map

View file

@ -1,9 +1,7 @@
@import "nib/vendor";
//@import "nib/vendor";
// Vendor Includes - include first so we can override
@import "../../public/vendor/bootstrap/css/bootstrap.min.css";
@import "../../public/vendor/bootstrap/css/bootstrap-responsive.min.css";
@import "../../public/vendor/datepicker/css/datepicker.css";
@import "../../../public/vendor/datepicker/css/datepicker.css";
// Custom includes
@import "./female_sprites.styl";
@ -15,8 +13,8 @@
@import "./items.styl";
@import "./inventory.styl";
@import "./alerts.styl";
@import "../../public/img/sprites/pet_sprites.css";
@import "../../public/img/sprites/PetEggs.css";
@import "../../../public/img/sprites/pet_sprites.css";
@import "../../../public/img/sprites/PetEggs.css";
@import "./helpers.styl";
@import "./responsive.styl";
@import "./header.styl";

12
assets/js/app.coffee Normal file
View file

@ -0,0 +1,12 @@
"use strict"
###
The main HabitRPG app module.
@type {angular.Module}
###
# .constant('API_URL', 'https://beta.habitrpg.com')
# userServices handles redirect to /login if not authenticated
window.habitrpg = angular.module('habitrpg', ['userServices', 'sharedServices', 'authServices', 'notificationServices', 'ui.bootstrap'])
.constant("API_URL", "http://localhost:3000")

View file

@ -14,19 +14,19 @@ loadJavaScripts = (model) ->
###
Internal Scripts
###
require "../../public/vendor/jquery-ui-1.10.2/jquery-1.9.1"
require "../../public/vendor/jquery.cookie.min"
require "../../public/vendor/bootstrap/js/bootstrap.min"
require "../../public/vendor/jquery.bootstrap-growl.min"
require "../../public/vendor/datepicker/js/bootstrap-datepicker"
require "../../public/vendor/bootstrap-tour/bootstrap-tour"
require "../vendor/jquery-ui-1.10.2/jquery-1.9.1.js"
require "../vendor/jquery.cookie.min.js"
require "../vendor/bootstrap/js/bootstrap.min.js"
require "../vendor/jquery.bootstrap-growl.min.js"
require "../vendor/datepicker/js/bootstrap-datepicker"
require "../vendor/bootstrap-tour/bootstrap-tour"
unless (model.get('_mobileDevice') is true)
require "../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.core"
require "../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.widget"
require "../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.mouse"
require "../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.sortable"
require "../../public/vendor/sticky"
require "../vendor/jquery-ui-1.10.2/ui/jquery.ui.core.js"
require "../vendor/jquery-ui-1.10.2/ui/jquery.ui.widget.js"
require "../vendor/jquery-ui-1.10.2/ui/jquery.ui.mouse.js"
require "../vendor/jquery-ui-1.10.2/ui/jquery.ui.sortable.js"
require "../vendor/sticky"
# note: external script loading is handled in app.on('render') near the bottom of this file (see https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/x8FwdTLEuXo)

304
assets/js/browser.js Normal file
View file

@ -0,0 +1,304 @@
// Generated by CoffeeScript 1.6.3
(function() {
var amazonAffiliate, googleAnalytics, googleCharts, growlNotification, initStickyHeader, loadJavaScripts, moment, setupGrowlNotifications, setupSortable, setupTooltips, setupTour, _;
_ = require('lodash');
moment = require('moment');
/*
Loads JavaScript files from public/vendor/*
Use require() to min / concatinate for faster page load
*/
loadJavaScripts = function(model) {
/*
Internal Scripts
*/
require("../vendor/jquery-ui-1.10.2/jquery-1.9.1.js");
require("../vendor/jquery.cookie.min.js");
require("../vendor/bootstrap/js/bootstrap.min.js");
require("../vendor/jquery.bootstrap-growl.min.js");
require("../vendor/datepicker/js/bootstrap-datepicker");
require("../vendor/bootstrap-tour/bootstrap-tour");
if (!(model.get('_mobileDevice') === true)) {
require("../vendor/jquery-ui-1.10.2/ui/jquery.ui.core.js");
require("../vendor/jquery-ui-1.10.2/ui/jquery.ui.widget.js");
require("../vendor/jquery-ui-1.10.2/ui/jquery.ui.mouse.js");
require("../vendor/jquery-ui-1.10.2/ui/jquery.ui.sortable.js");
return require("../vendor/sticky");
}
};
/*
Setup jQuery UI Sortable
*/
setupSortable = function(model) {
if (!(model.get('_mobileDevice') === true)) {
return ['habit', 'daily', 'todo', 'reward'].forEach(function(type) {
return $("ul." + type + "s").sortable({
dropOnEmpty: false,
cursor: "move",
items: "li",
scroll: true,
axis: 'y',
update: function(e, ui) {
var domId, id, item, to;
item = ui.item[0];
domId = item.id;
id = item.getAttribute('data-id');
to = $("ul." + type + "s").children().index(item);
return model.at("_" + type + "List").pass({
ignore: domId
}).move({
id: id
}, to);
}
});
});
}
};
setupTooltips = module.exports.setupTooltips = function() {
$('[rel=tooltip]').tooltip();
$('[rel=popover]').popover();
$('.popover-auto-show').popover('show');
return $('.priority-multiplier-help').popover({
title: "How difficult is this task?",
trigger: "hover",
content: "This multiplies its point value. Use sparingly, rely instead on our organic value-adjustment algorithms. But some tasks are grossly more valuable (Write Thesis vs Floss Teeth). Click for more info."
});
};
setupTour = function(model) {
var tour, tourSteps;
tourSteps = [
{
element: ".main-herobox",
title: "Welcome to HabitRPG",
content: "Welcome to HabitRPG, a habit-tracker which treats your goals like a Role Playing Game."
}, {
element: "#bars",
title: "Achieve goals and level up",
content: "As you accomplish goals, you level up. If you fail your goals, you lose hit points. Lose all your HP and you die."
}, {
element: "ul.habits",
title: "Habits",
content: "Habits are goals that you constantly track.",
placement: "bottom"
}, {
element: "ul.dailys",
title: "Dailies",
content: "Dailies are goals that you want to complete once a day.",
placement: "bottom"
}, {
element: "ul.todos",
title: "Todos",
content: "Todos are one-off goals which need to be completed eventually.",
placement: "bottom"
}, {
element: "ul.rewards",
title: "Rewards",
content: "As you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits.",
placement: "bottom"
}, {
element: "ul.habits li:first-child",
title: "Hover over comments",
content: "Different task-types have special properties. Hover over each task's comment for more information. When you're ready to get started, delete the existing tasks and add your own.",
placement: "right"
}
];
$('.main-herobox').popover('destroy');
tour = new Tour();
tourSteps.forEach(function(step) {
return tour.addStep(_.defaults(step, {
html: true
}));
});
if (isNaN(tour._current)) {
tour._current = 0;
}
return tour.start();
};
initStickyHeader = function(model) {
return $('.header-wrap').sticky({
topSpacing: 0
});
};
growlNotification = module.exports.growlNotification = function(html, type) {
return $.bootstrapGrowl(html, {
ele: '#notification-area',
type: type,
top_offset: 20,
align: 'right',
width: 250,
delay: 3000,
allow_dismiss: true,
stackup_spacing: 10
});
};
/*
Sets up "+1 Exp", "Level Up", etc notifications
*/
setupGrowlNotifications = function(model) {
var showCoins, statsNotification, user;
if (typeof jQuery === "undefined" || jQuery === null) {
return;
}
user = model.at('_user');
statsNotification = function(html, type) {
if (user.get('stats.lvl') === 0) {
return;
}
return growlNotification(html, type);
};
user.on('set', 'stats.hp', function(captures, args) {
var num, rounded;
num = captures - args;
rounded = Math.abs(num.toFixed(1));
if (num < 0) {
return statsNotification("<i class='icon-heart'></i> - " + rounded + " HP", 'hp');
} else if (num > 0) {
return statsNotification("<i class='icon-heart'></i> + " + rounded + " HP", 'hp');
}
});
user.on('set', 'stats.exp', function(captures, args, isLocal, silent) {
var num, rounded;
if (silent == null) {
silent = false;
}
num = captures - args;
rounded = Math.abs(num.toFixed(1));
if (num < 0 && num > -50) {
return statsNotification("<i class='icon-star'></i> - " + rounded + " XP", 'xp');
} else if (num > 0) {
return statsNotification("<i class='icon-star'></i> + " + rounded + " XP", 'xp');
}
});
/*
Show "+ 5 {gold_coin} 3 {silver_coin}"
*/
showCoins = function(money) {
var absolute, gold, silver;
absolute = Math.abs(money);
gold = Math.floor(absolute);
silver = Math.floor((absolute - gold) * 100);
if (gold && silver > 0) {
return "" + gold + " <i class='icon-gold'></i> " + silver + " <i class='icon-silver'></i>";
} else if (gold > 0) {
return "" + gold + " <i class='icon-gold'></i>";
} else if (silver > 0) {
return "" + silver + " <i class='icon-silver'></i>";
}
};
user.on('set', 'stats.gp', function(captures, args) {
var bonus, money, sign;
money = captures - args;
if (!money) {
return;
}
sign = money < 0 ? '-' : '+';
statsNotification("" + sign + " " + (showCoins(money)), 'gp');
bonus = model.get('_streakBonus');
if ((money > 0) && !!bonus) {
if (bonus < 0.01) {
bonus = 0.01;
}
statsNotification("+ " + (showCoins(bonus)) + " Streak Bonus!");
return model.del('_streakBonus');
}
});
user.on('set', 'items.*', function(item, after, before) {
if ((item === 'armor' || item === 'weapon' || item === 'shield' || item === 'head') && parseInt(after) < parseInt(before)) {
if (item === 'head') {
item = 'helm';
}
return statsNotification("<i class='icon-death'></i> Respawn!", "death");
}
});
return user.on('set', 'stats.lvl', function(captures, args) {
if (captures > args) {
return statsNotification('<i class="icon-chevron-up"></i> Level Up!', 'lvl');
}
});
};
module.exports.resetDom = function(model) {
window.DERBY.app.dom.clear();
return window.DERBY.app.view.render(model, window.DERBY.app.view._lastRender.ns, window.DERBY.app.view._lastRender.context);
};
googleAnalytics = function(model) {
if (model.flags.nodeEnv === 'production') {
window._gaq = [["_setAccount", "UA-33510635-1"], ["_setDomainName", "habitrpg.com"], ["_trackPageview"]];
return $.getScript(("https:" === document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js");
}
};
amazonAffiliate = function(model) {
if (model.get('_loggedIn') && (model.get('_user.flags.ads') !== 'hide')) {
return $.getScript('//wms.assoc-amazon.com/20070822/US/js/link-enhancer-common.js?tag=ha0d2-20').fail(function() {
return $('body').append('<img src="//wms.assoc-amazon.com/20070822/US/img/noscript.gif?tag=ha0d2-20" alt="" />');
});
}
};
googleCharts = function() {
return $.getScript("//www.google.com/jsapi", function() {
return google.load("visualization", "1", {
packages: ["corechart"],
callback: function() {}
});
});
};
module.exports.app = function(appExports, model, app) {
loadJavaScripts(model);
if (!model.get('_mobileDevice')) {
setupGrowlNotifications(model);
}
return app.on('render', function(ctx) {
if (!model.get('_mobileDevice')) {
setupTooltips(model);
initStickyHeader(model);
setupSortable(model);
setupTour(model);
}
$('.datepicker').datepicker({
autoclose: true,
todayBtn: true
}).on('changeDate', function(ev) {
return model.at(ev.target).set('date', moment(ev.date).format('MM/DD/YYYY'));
});
/*
External Scripts
JS files not needed right away (google charts) or entirely optional (analytics)
Each file getsload asyncronously via $.getScript, so it doesn't bog page-load
These need to be handled in app.on('render'), see https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/x8FwdTLEuXo
*/
$.getScript('//checkout.stripe.com/v2/checkout.js');
if (!(model.get('_mobileDevice') === true)) {
$.getScript("//s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire");
googleCharts();
}
googleAnalytics(model);
return amazonAffiliate(model);
});
};
}).call(this);
/*
//@ sourceMappingURL=browser.map
*/

10
assets/js/browser.map Normal file

File diff suppressed because one or more lines are too long

148
assets/js/challenges.js Normal file
View file

@ -0,0 +1,148 @@
// Generated by CoffeeScript 1.6.3
(function() {
var helpers, _;
_ = require('lodash');
helpers = require('habitrpg-shared/script/helpers');
module.exports.app = function(appExports, model) {
var browser, challengeDiscard, user;
browser = require('./browser');
user = model.at('_user');
$('#profile-challenges-tab-link').on('show', function(e) {
return _.each(model.get('groups'), function(g) {
return _.each(g.challenges, function(chal) {
return _.each(['habit', 'daily', 'todo'], function(type) {
return _.each(chal["" + type + "s"], function(task) {
return _.each(chal.users, function(member) {
var chart, data, history, options, _ref, _ref1;
if ((history = member != null ? (_ref = member["" + type + "s"]) != null ? (_ref1 = _ref[task.id]) != null ? _ref1.history : void 0 : void 0 : void 0) && !!history) {
data = google.visualization.arrayToDataTable(_.map(history, function(h) {
return [h.date, h.value];
}));
options = {
backgroundColor: {
fill: 'transparent'
},
width: 150,
height: 50,
chartArea: {
width: '80%',
height: '80%'
},
axisTitlePosition: 'none',
legend: {
position: 'bottom'
},
hAxis: {
gridlines: {
color: 'transparent'
}
},
vAxis: {
gridlines: {
color: 'transparent'
}
}
};
chart = new google.visualization.LineChart($(".challenge-" + chal.id + "-member-" + member.id + "-history-" + task.id)[0]);
return chart.draw(data, options);
}
});
});
});
});
});
});
appExports.challengeCreate = function(e, el) {
var gid, type, _ref;
_ref = [$(el).attr('data-type'), $(el).attr('data-gid')], type = _ref[0], gid = _ref[1];
return model.set('_challenge.new', {
name: '',
habits: [],
dailys: [],
todos: [],
rewards: [],
id: model.id(),
uid: user.get('id'),
user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')),
group: {
type: type,
id: gid
},
timestamp: +(new Date)
});
};
appExports.challengeSave = function() {
var gid;
gid = model.get('_challenge.new.group.id');
return model.unshift("groups." + gid + ".challenges", model.get('_challenge.new'), function() {
browser.growlNotification('Challenge Created', 'success');
return challengeDiscard();
});
};
appExports.toggleChallengeEdit = function(e, el) {
var path;
path = "_editing.challenges." + ($(el).attr('data-id'));
return model.set(path, !model.get(path));
};
appExports.challengeDiscard = challengeDiscard = function() {
return model.del('_challenge.new');
};
appExports.challengeSubscribe = function(e) {
var chal, tags, userChallenges;
chal = e.get();
tags = user.get('tags');
if (!(tags && _.find(tags, {
id: chal.id
}))) {
model.push('_user.tags', {
id: chal.id,
name: chal.name,
challenge: true
});
}
tags = {};
tags[chal.id] = true;
userChallenges = user.get('challenges');
if (!(userChallenges && (userChallenges.indexOf(chal.id) !== -1))) {
user.unshift('challenges', chal.id);
}
return _.each(['habit', 'daily', 'todo', 'reward'], function(type) {
return _.each(chal["" + type + "s"], function(task) {
task.tags = tags;
task.challenge = chal.id;
task.group = {
id: chal.group.id,
type: chal.group.type
};
model.push("_" + type + "List", task);
return true;
});
});
};
return appExports.challengeUnsubscribe = function(e) {
var chal, i, _ref;
chal = e.get();
i = (_ref = user.get('challenges')) != null ? _ref.indexOf(chal.id) : void 0;
if ((i != null) && i !== -1) {
user.remove("challenges." + i);
}
return _.each(['habit', 'daily', 'todo', 'reward'], function(type) {
return _.each(chal["" + type + "s"], function(task) {
model.remove("_" + type + "List", _.findIndex(model.get("_" + type + "List", {
id: task.id
})));
model.del("_user.tasks." + task.id);
return true;
});
});
};
};
}).call(this);
/*
//@ sourceMappingURL=challenges.map
*/

10
assets/js/challenges.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "challenges.js",
"sourceRoot": "",
"sources": [
"challenges.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,13 @@
"use strict"
# Make user and settings available for everyone through root scope.
habitrpg.controller "RootCtrl", ($scope, $rootScope, $location, User) ->
$rootScope.User = User
$rootScope.user = User.user
$rootScope.settings = User.settings
# FIXME this is dangerous, organize helpers.coffee better, so we can group them by which controller needs them,
# and then simply _.defaults($scope, Helpers.user) kinda thing
_.defaults $rootScope, window.habitrpgShared.helpers
$rootScope.authenticated = ->
User.settings.auth.apiId isnt ""

View file

@ -0,0 +1,55 @@
"use strict"
###
The authentication controller (login & facebook)
###
habitrpg.controller "AuthCtrl", ($scope, $rootScope, Facebook, LocalAuth, User, $http, $location, API_URL) ->
$scope.useUUID = false
debugger
$scope.toggleUUID = ->
if showedFacebookMessage is false
alert "Until we add Facebook, use your UUID and API Token to log in (found at https://habitrpg.com > Options > Settings)."
showedFacebookMessage = true
$scope.useUUID = not $scope.useUUID
$scope.register = ->
#TODO highlight invalid inputs
# we have this as a workaround for https://github.com/HabitRPG/habitrpg-mobile/issues/64
return if $scope.registrationForm.$invalid
$http.post(API_URL + "/api/v1/register", $scope.registerVals).success((data, status, headers, config) ->
User.authenticate data.id, data.apiToken, (err) ->
$location.path "/habit"
).error (data, status, headers, config) ->
if status is 0
alert "Server not currently reachable, try again later"
else if !!data and !!data.err
alert data.err
else
alert "ERROR: " + status
$scope.auth = ->
data =
username: $scope.loginUsername
password: $scope.loginPassword
runAuth = (id, token) ->
User.authenticate id, token, (err) ->
$location.path "/habit"
if $scope.useUUID
runAuth $scope.loginUsername, $scope.loginPassword
else
$http.post(API_URL + "/api/v1/user/auth/local", data)
.success((data, status, headers, config) ->
runAuth data.id, data.token
$scope.showing = false
).error (data, status, headers, config) ->
if status is 0
alert "Server not currently reachable, try again later"
else if !!data and !!data.err
alert data.err
else
alert "ERROR: " + status

View file

@ -0,0 +1,40 @@
'use strict';
/**
* The character controller:
*
*/
habitrpg.controller('CharacterCtrl',
['$scope', '$location', 'User',
function($scope, $location, User) {
$scope.user = User.user;
$scope.equipped = function(user, type) {
var tier = (user.backer && user.backer.tier)
return window.habitrpgShared.helpers.equipped(type, user.items[type], user.preferences, tier);
}
$scope.$watch('user.tasks', function(){
$scope.hpPercent = function(hp) {
return (hp / 50) * 100;
}
$scope.expPercent = function(exp, level) {
return (exp / window.habitrpgShared.algos.tnl(level)) * 100;
}
})
$scope.floor = Math.floor;
$scope.count = function(arr) {
return _.size(arr);
}
$scope.tnl = window.habitrpgShared.algos.tnl;
$scope.showUserAvatar = function() {
$('.userAvatar').show()
}
}
]);

View file

@ -0,0 +1,66 @@
'use strict';
/**
* The menu controller:
* - sets the menu options, should we do it dynamic so it generates the menu like: width = 1/elements * 100 ?
* - exposes the model to the template and provides event handlers
*/
habitrpg.controller('MenuCtrl',
['$scope', '$rootScope', '$location', 'User',
function($scope, $rootScope, $location, User) {
$scope.swiperight = function(){
$scope.menuopen = true;
}
$scope.swipeleft = function(){
$scope.menuopen = false;
}
$scope.menuClick = function(button) {
$scope.menuopen = false;
$location.url(button.link);
}
/**
* Show title according to the location
*/
$rootScope.$on('$routeChangeSuccess', function(){
var found = _.find($scope.nav, function(obj){
return obj.link === $location.path();
});
if (found) {
$rootScope.taskContext = {
name: found.name,
type: found.link.substr(1) // remove trailing /
};
$rootScope.menuopen = false;
}
});
$scope.nav = [
{ link:'/habit', name:'Habits', lowercase:'habits' },
{ link:'/daily', name:'Dailies', lowercase:'dailies' },
{ link:'/todo', name:'Todos', lowercase:'todos' },
{ link:'/reward', name:'Rewards', lowercase:'rewards' },
{ link:'/profile', name:'Profile', lowercase:'profile' },
{ link:'/settings', name:'Settings',lowercase:'settings' },
{ link:'/help', name:'Help', lowercase:'help' }
]
$scope.refreshing = function () {
return User.settings.fetching ? "spin" : ""
};
$scope.queueLength = function () {
return User.settings.sync.queue.length || User.settings.sync.sent.length
};
$scope.stats = User.user.stats;
$('#main_nav').css('height', $(window).height())
$('#wrapper').css('height', $(window).height())
}
]);

View file

@ -0,0 +1,9 @@
'use strict';
habitrpg.controller('NotificationCtrl',
['$scope', 'Notification',
function ($scope, Notification) {
$scope.data = Notification.get();
}
]);

View file

@ -0,0 +1,21 @@
'use strict';
// Make user and settings available for everyone through root scope.
habitrpg.controller('SettingsCtrl',
['$scope', 'User', '$location',
function($scope, User, $location) {
$scope.resetApp = function () {
localStorage.clear();
location.reload();
};
$scope.auth = function (id, token) {
User.authenticate(id, token, function (err) {
if (!err) {
alert('Login successful!');
$location.path("/habit");
}
});
}
}
]);

View file

@ -0,0 +1,14 @@
'use strict';
habitrpg.controller('StatsCtrl',
['$scope', 'User',
function($scope, User) {
$scope.refreshing = function () {
return User.settings.fetching ? "spin" : ""
};
$scope.queueLength = function () {
return User.settings.sync.queue.length || User.settings.sync.sent.length
};
$scope.stats = User.user.stats;
}
]);

View file

@ -0,0 +1,78 @@
'use strict';
habitrpg.controller('TaskDetailsCtrl',
['$scope', '$rootScope', '$location', 'User',
function($scope, $rootScope, $location, User) {
$scope.task = $rootScope.selectedTask;
$scope.editing = false;
$scope.editedTask = null;
$scope.goBack = function () {
$rootScope.selectedTask = null;
$location.path('/' + $scope.task.type);
};
$scope.edit = function () {
$scope.originalTask = _.clone($scope.task); // TODO deep clone?;
$scope.editedTask = $scope.task;
$scope.editing = true;
};
$scope.save = function () {
var task = $scope.task,
log = [];
function setVal(k,v){
if (typeof v !== "undefined") {
var op = {op: 'set', data:{}};
op.data["tasks." + task.id + "." + k] = v;
log.push(op);
}
}
setVal("text", task.text);
setVal("notes", task.notes);
setVal("priority", task.priority);
if (task.type == 'habit') {
setVal("up", task.up);
setVal("down", task.down);
} else if (task.type == 'daily') {
setVal("repeat", task.repeat);
// _.each(task.repeat, function(v, k) {
// setVal("repeat." + k, v);
// })
} else if (task.type == 'todo') {
setVal("date", task.date);
} else if (task.type == 'reward') {
setVal("value", task.value);
}
User.log(log);
$rootScope.selectedTask = null;
$location.path('/' + $scope.task.type);
$scope.editing = false;
};
$scope.cancel = function () {
// reset $scope.task to $scope.originalTask
for (var key in $scope.task) {
$scope.task[key] = $scope.originalTask[key];
}
$scope.originalTask = null;
$scope.editedTask = null;
$scope.editing = false;
};
$scope.delete = function () {
var confirmed = window.confirm("Delete this task?");
if (confirmed !== true) return;
var task = $scope.task;
var tasks = User.user[task.type+'s'];
User.log({op: 'delTask', data: task});
$scope.goBack();
delete tasks.splice(tasks.indexOf(task),1);
};
}
]);

View file

@ -0,0 +1,188 @@
'use strict';
habitrpg.controller('TasksCtrl',
['$scope', '$rootScope', '$location', 'filterFilter', 'User', 'Algos', 'Helpers', 'Notification',
function($scope, $rootScope, $location, filterFilter, User, Algos, Helpers, Notification) {
$scope.user = User.user;
$scope.taskTypeTitleSingular = function () {
// show title according to the location, singular form
return $rootScope.taskContext.type.charAt(0).toUpperCase() + $rootScope.taskContext.type.slice(1);
};
$scope.taskType = function () {
return $location.path().split('/')[1]
};
$scope.tasks = function () {
//return task array based on our location i.e. /habit will return user.habits[]
return User.user[$scope.taskType() + 's'];
};
$scope.showedTasks = []
$scope.taskFilter = function (task) {
return ($location.path() == '/todo') ? !task.completed :
($location.path() == '/todo/completed') ? task.completed :
true;
};
$scope.score = function (task, direction) {
//save current stats to compute the difference after scoring.
var statsDiff = {};
var oldStats = _.clone(User.user.stats);
Algos.score(User.user, task, direction);
//compute the stats change.
_.each(oldStats, function (value, key) {
var newValue = User.user.stats[key];
if (newValue !== value) {
statsDiff[key] = newValue - value;
}
});
//notify user if there are changes in stats.
if (Object.keys(statsDiff).length > 0) {
Notification.push({type: 'stats', stats: statsDiff});
}
if (task.type == 'reward' && _.isEmpty(statsDiff)) {
Notification.push({type: 'text', text: 'Not enough GP.'});
}
User.log({op: 'score', data: task, dir: direction});
};
$scope.notDue = function(task) {
if (task.type == 'daily') {
return !window.habitrpgShared.helpers.shouldDo(moment(), task.repeat);
} else {
return false
}
}
$scope.getClass = function(value) {
var out = ''
if (value < -20)
out += ' color-worst'
else if (value < -10)
out += ' color-worse'
else if (value < -1)
out += ' color-bad'
else if (value < 1)
out += ' color-neutral'
else if (value < 5)
out += ' color-good'
else if (value < 10)
out += ' color-better'
else
out += ' color-best'
return out
}
$scope.addTask = function () {
if (!$scope.newTask.length) {
return;
}
var defaults = {
text: $scope.newTask,
type: $scope.taskType(),
value: $scope.taskType() == 'reward' ? 20 : 0
},
extra = {};
switch ($scope.taskType()) {
case 'habit':
extra = {up: true, down: true};
break;
case 'daily':
case 'todo':
extra = {completed: false};
break;
}
var newTask = _.defaults(extra, defaults);
newTask.id = Helpers.uuid();
User.user[newTask.type + 's'].unshift(newTask)
$scope.showedTasks.unshift(newTask)
User.log({op: 'addTask', data: newTask});
$scope.newTask = '';
//Add the new task to the actions log
};
$scope.clearDoneTodos = function () {
//We can't alter $scope.user.tasks here. We have to invoke API call.
//To be implemented
};
$scope.selectTask = function (task) {
$rootScope.selectedTask = task;
$location.path('/tasks/' + task.id)
}
$scope.changeCheck = function (task) {
// This is calculated post-change, so task.completed=true if they just checked it
if (task.completed) {
$scope.score(task, 'up')
} else {
$scope.score(task, 'down')
}
}
$('.taskWell').css('height', $(window).height() - 61)
// TODO this should be somewhere else, but fits the html location better here
$rootScope.revive = function() {
window.habitrpgShared.algos.revive(User.user);
User.log({op:'revive'});
}
var counter = 0;
/**
* ------------------------
* Items
* ------------------------
*/
$scope.$watch('user.items', function(){
$scope.itemStore = window.habitrpgShared.items.updateStore($scope.user);
});
$scope.buy = function(type) {
var hasEnough = window.habitrpgShared.items.buyItem($scope.user, type);
if (hasEnough) {
User.log({op:'buy', type:type});
Notification.push({type:'text', text:"Item bought!"})
} else {
Notification.push({type:'text', text:"Not enough GP."})
}
}
/*
$scope.loadMore = function() {
var length = $scope.showedTasks.length
if (typeof $scope.tasks() != 'undefined') {
for (var i = length; i < length+7; i++) {
if (typeof $scope.tasks()[i] != 'undefined') {
$scope.showedTasks.push($scope.tasks()[i]);
}
}
}
};
$scope.loadMore()
*/
}
]);

View file

@ -0,0 +1,33 @@
'use strict';
habitrpg.controller('userAvatarCtrl',
['$scope', '$location', 'filterFilter', 'User',
function($scope, $location, filterFilter, User) {
$scope.user = User.user;
$scope.changeHair = function(color) {
User.user.preferences.hair = color;
User.log({op:"set",data:{"preferences.hair":color}})
}
$scope.changeSkin = function(color) {
User.user.preferences.skin = color
User.log({op:"set",data:{"preferences.skin":color}})
}
$scope.changeSex = function(gender) {
User.user.preferences.gender = gender
User.log({op:"set",data:{"preferences.gender":gender}})
}
$scope.changeArmor = function(armor) {
User.user.preferences.armorSet = armor
User.log({op:"set",data:{"preferences.armorSet":armor}})
}
$scope.hideUserAvatar = function() {
$('.userAvatar').hide()
}
}
]);

34
assets/js/debug.js Normal file
View file

@ -0,0 +1,34 @@
// Generated by CoffeeScript 1.6.3
(function() {
var algos, moment;
moment = require('moment');
algos = require('habitrpg-shared/script/algos');
module.exports.app = function(appExports, model) {
var user;
user = model.at('_user');
appExports.emulateNextDay = function() {
var yesterday;
yesterday = +moment().subtract('days', 1).toDate();
user.set('lastCron', yesterday);
return window.location.reload();
};
appExports.emulateTenDays = function() {
var yesterday;
yesterday = +moment().subtract('days', 10).toDate();
user.set('lastCron', yesterday);
return window.location.reload();
};
return appExports.cheat = function() {
user.incr('stats.exp', algos.tnl(user.get('stats.lvl')));
return user.incr('stats.gp', 1000);
};
};
}).call(this);
/*
//@ sourceMappingURL=debug.map
*/

10
assets/js/debug.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "debug.js",
"sourceRoot": "",
"sources": [
"debug.coffee"
],
"names": [],
"mappings": ";AAAA;CAAA,KAAA,OAAA;;CAAA,CAAA,CAAS,GAAT,CAAS,CAAA;;CAAT,CACA,CAAQ,EAAR,EAAQ,uBAAA;;CADR,CAGA,CAAA,EAAqB,CAAf,CAAQ,EAAQ,CAAD;CACnB,GAAA,IAAA;CAAA,CAAO,CAAA,CAAP,CAAY,EAAL;CAAP,EAE4B,CAA5B,KAA4B,CAAlB,IAAV;CACE,QAAA,CAAA;AAAa,CAAb,CAAuC,CAA3B,GAAZ,EAAa,CAAb;CAAA,CACqB,CAArB,CAAI,EAAJ,GAAA,CAAA;CACO,KAAD,EAAS,KAAf;CALF,IAE4B;CAF5B,EAO4B,CAA5B,KAA4B,CAAlB,IAAV;CACE,QAAA,CAAA;AAAa,CAAb,CAAuC,CAA3B,GAAZ,EAAa,CAAb;CAAA,CACqB,CAArB,CAAI,EAAJ,GAAA,CAAA;CACO,KAAD,EAAS,KAAf;CAVF,IAO4B;CAKjB,EAAQ,EAAnB,IAAmB,CAAT,CAAV;CACE,CAAuB,CAAA,CAAnB,CAAwB,CAA5B,KAAA;CACK,CAAiB,EAAlB,MAAJ,GAAA;CAfiB,IAaA;CAhBrB,EAGqB;CAHrB"
}

View file

@ -0,0 +1,63 @@
'use strict';
/**
* Directive that places focus on the element it is applied to when the expression it binds to evaluates to true.
*/
habitrpg.directive('taskFocus',
['$timeout',
function($timeout) {
return function(scope, elem, attrs) {
scope.$watch(attrs.taskFocus, function(newval) {
if ( newval ) {
$timeout(function() {
elem[0].focus();
}, 0, false);
}
});
};
}
]);
/**
* Directive that executes an expression when the element it is applied to loses focus.
*/
habitrpg.directive('taskBlur', function() {
return function(scope, elem, attrs) {
elem.bind('blur', function() {
scope.$apply(attrs.taskBlur);
});
};
});
habitrpg.directive('whenScrolled', function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.whenScrolled);
}
});
};
});
/**
* Add sortable
*/
habitrpg.directive('sort', function (User) {
return ['$scope', '$rootScope', 'element', 'attrs', 'ngModel',
function($scope, $rootScope, element, attrs, ngModel) {
$(element).sortable({
axis: "y",
start: function (event, ui) {
ui.item.data('startIndex', ui.item.index());
},
stop: function (event, ui) {
var taskType = $rootScope.taskContext.type;
var startIndex = ui.item.data('startIndex');
var task = User.user[taskType][startIndex];
User.log({op: 'sortTask', data: task, from: startIndex, to: ui.item.index()});
}
});
}]
});

55
assets/js/filters.js Normal file
View file

@ -0,0 +1,55 @@
// Generated by CoffeeScript 1.6.3
(function() {
var _;
_ = require('lodash');
module.exports.app = function(appExports, model) {
var user;
user = model.at('_user');
appExports.toggleFilterByTag = function(e, el) {
var path, tagId;
tagId = $(el).attr('data-tag-id');
path = 'filters.' + tagId;
return user.set(path, !(user.get(path)));
};
appExports.filtersNewTag = function() {
user.setNull('tags', []);
user.push('tags', {
id: model.id(),
name: model.get("_newTag")
});
return model.set('_newTag', '');
};
appExports.toggleEditingTags = function() {
return model.set('_editingTags', !model.get('_editingTags'));
};
appExports.clearFilters = function() {
return user.set('filters', {});
};
return appExports.filtersDeleteTag = function(e, el) {
var tag, tagId, tags;
tags = user.get('tags');
tag = e.at("_user.tags." + $(el).attr('data-index'));
tagId = tag.get('id');
if (!tagId) {
user.set('tags', _.filter(tags, (function(t) {
return t != null ? t.id : void 0;
})));
user.set('filters', {});
return;
}
model.del("_user.filters." + tagId);
tag.remove();
return _.each(user.get("tasks"), function(task) {
user.del("tasks." + task.id + ".tags." + tagId);
return true;
});
};
};
}).call(this);
/*
//@ sourceMappingURL=filters.map
*/

10
assets/js/filters.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "filters.js",
"sourceRoot": "",
"sources": [
"filters.coffee"
],
"names": [],
"mappings": ";AAAA;CAAA,KAAA;;CAAA,CAAA,CAAI,IAAA,CAAA;;CAAJ,CAEA,CAAA,EAAqB,CAAf,CAAQ,EAAQ,CAAD;CACnB,GAAA,IAAA;CAAA,CAAO,CAAA,CAAP,CAAY,EAAL;CAAP,CAEmC,CAAJ,CAA/B,KAAgC,CAAtB,OAAV;CACE,SAAA,CAAA;CAAA,CAAQ,CAAA,CAAA,CAAR,CAAA,OAAQ;CAAR,EACO,CAAP,CADA,CACA,IAAO;AACS,CAAX,CAAU,CAAf,CAAI,SAAJ;CALF,IAE+B;CAF/B,EAO2B,CAA3B,KAA2B,CAAjB,GAAV;CACE,CAAqB,EAAjB,EAAJ,CAAA;CAAA,CACkB,EAAd,EAAJ;CAAkB,CAAC,GAAS,GAAT;CAAD,CAAuB,CAAA,CAAN,CAAW,GAAX,CAAM;CADzC,OACA;CACM,CAAe,CAArB,EAAK,IAAL,IAAA;CAVF,IAO2B;CAP3B,EAY+B,CAA/B,KAA+B,CAArB,OAAV;AAC6B,CAArB,CAAoB,CAA1B,EAAK,QAAL,CAAA;CAbF,IAY+B;CAZ/B,EAe0B,CAA1B,KAA0B,CAAhB,EAAV;CACO,CAAe,CAApB,CAAI,KAAJ,IAAA;CAhBF,IAe0B;CAGf,CAAuB,CAAJ,MAAC,CAArB,CAAV,KAAA;CACE,SAAA,MAAA;CAAA,EAAO,CAAP,EAAA;CAAA,CACM,CAAN,CAA2B,EAA3B,MAA2B,CAAhB;CADX,EAEQ,CAAA,CAAR,CAAA;AAGO,CAAP,GAAA,CAAA,CAAA;CACE,CAAiB,CAAjB,CAAI,EAAJ,EAAA,CAAmC;CAAM,EAAD;CAAP,QAAC;CAAlC,CACoB,CAApB,CAAI,IAAJ,CAAA;CACA,aAAA;QARF;CAAA,EAUA,EAAK,CAAL,UAAW;CAVX,EAWG,GAAH;CAGC,CAAyB,CAAnB,CAAP,GAAO,EAAoB,IAA3B;CAAoC,CAAU,CAAV,CAAI,CAAJ,GAAA;CAAV,cAAqD;CAA/E,MAA0B;CAlCT,IAmBW;CArBhC,EAEqB;CAFrB"
}

View file

@ -0,0 +1,10 @@
angular.module('habitrpg')
.filter('gold', function () {
return function (gp) {
return Math.floor(gp);
}
}).filter('silver', function () {
return function (gp) {
return Math.floor((gp - Math.floor(gp))*100);
}
});

280
assets/js/groups.js Normal file
View file

@ -0,0 +1,280 @@
// Generated by CoffeeScript 1.6.3
(function() {
var helpers, _,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
_ = require('lodash');
helpers = require('habitrpg-shared/script/helpers');
module.exports.app = function(appExports, model, app) {
var browser, joinGroup, user, _currentTime;
browser = require('./browser');
_currentTime = model.at('_currentTime');
_currentTime.setNull(+(new Date));
setInterval((function() {
return _currentTime.set(+(new Date));
}), 60000);
user = model.at('_user');
appExports.groupCreate = function(e, el) {
var newGroup, type;
type = $(el).attr('data-type');
newGroup = {
name: model.get("_new.group.name"),
description: model.get("_new.group.description"),
leader: user.get('id'),
members: [user.get('id')],
type: type
};
if (type === 'party') {
return model.add('groups', newGroup, function() {
return location.reload();
});
}
if (!(user.get('balance') >= 1)) {
return $('#more-gems-modal').modal('show');
}
if (confirm("Create Guild for 4 Gems?")) {
if (type === 'guild') {
newGroup.privacy = model.get("_new.group.privacy") || 'public';
}
newGroup.balance = 1;
return model.add('groups', newGroup, function() {
return user.incr('balance', -1, function() {
return location.reload();
});
});
}
};
appExports.toggleGroupEdit = function(e, el) {
var path;
path = "_editing.groups." + ($(el).attr('data-gid'));
return model.set(path, !model.get(path));
};
appExports.toggleLeaderMessageEdit = function(e, el) {
var path;
path = "_editing.leaderMessage." + ($(el).attr('data-gid'));
return model.set(path, !model.get(path));
};
appExports.groupAddWebsite = function(e, el) {
var test;
test = e.get();
e.at().unshift('websites', model.get('_newGroupWebsite'));
return model.del('_newGroupWebsite');
};
appExports.groupInvite = function(e, el) {
var uid;
uid = model.get('_groupInvitee').replace(/[\s"]/g, '');
model.set('_groupInvitee', '');
if (_.isEmpty(uid)) {
return;
}
return model.query('users').publicInfo([uid]).fetch(function(err, profiles) {
var profile;
if (err) {
throw err;
}
profile = profiles.at(0).get();
if (!profile) {
return model.set("_groupError", "User with id " + uid + " not found.");
}
return model.query('groups').withMember(uid).fetch(function(err, g) {
var gid, group, groupError, groups, invite, name, type, _ref, _ref1;
if (err) {
throw err;
}
group = e.get();
groups = g.get();
type = group.type, name = group.name;
gid = group.id;
groupError = function(msg) {
return model.set("_groupError", msg);
};
invite = function() {
$.bootstrapGrowl("Invitation Sent.");
switch (type) {
case 'guild':
return model.push("users." + uid + ".invitations.guilds", {
id: gid,
name: name
}, function() {
return location.reload();
});
case 'party':
return model.set("users." + uid + ".invitations.party", {
id: gid,
name: name
}, function() {
return location.reload();
});
}
};
switch (type) {
case 'guild':
if (((_ref = profile.invitations) != null ? _ref.guilds : void 0) && _.find(profile.invitations.guilds, {
id: gid
})) {
return groupError("User already invited to that group");
} else if (__indexOf.call(group.members, uid) >= 0) {
return groupError("User already in that group");
} else {
return invite();
}
break;
case 'party':
if ((_ref1 = profile.invitations) != null ? _ref1.party : void 0) {
return groupError("User already pending invitation.");
} else if (_.find(groups, {
type: 'party'
})) {
return groupError("User already in a party.");
} else {
return invite();
}
}
});
});
};
joinGroup = function(gid) {
return model.push("groups." + gid + ".members", user.get('id'), function() {
return location.reload();
});
};
appExports.joinGroup = function(e, el) {
return joinGroup(e.get('id'));
};
appExports.acceptInvitation = function(e, el) {
var gid;
gid = e.get('id');
if ($(el).attr('data-type') === 'party') {
return user.set('invitations.party', null, function() {
return joinGroup(gid);
});
} else {
return e.at().remove(function() {
return joinGroup(gid);
});
}
};
appExports.rejectInvitation = function(e, el) {
var clear;
clear = function() {
return browser.resetDom(model);
};
if (e.at().path().indexOf('party') !== -1) {
return model.del(e.at().path(), clear);
} else {
return e.at().remove(clear);
}
};
appExports.groupLeave = function(e, el) {
var group, index, uid;
if (confirm("Leave this group, are you sure?") === true) {
uid = user.get('id');
group = model.at("groups." + ($(el).attr('data-id')));
index = group.get('members').indexOf(uid);
if (index !== -1) {
return group.remove('members', index, 1, function() {
var updated;
updated = group.get();
if (_.isEmpty(updated.members) && (updated.type === 'party')) {
return group.del(function() {
return location.reload();
});
} else if (updated.leader === uid) {
return group.set("leader", updated.members[0], function() {
return location.reload();
});
} else {
return location.reload();
}
});
}
}
};
/*
Chat Functionality
*/
model.on('unshift', '_party.chat', function() {
return $('.chat-message').tooltip();
});
model.on('unshift', '_habitrpg.chat', function() {
return $('.chat-message').tooltip();
});
appExports.sendChat = function(e, el) {
var chat, group, members, message, messages, text, type, uniqMembers;
text = model.get('_chatMessage');
if (!/\S/.test(text)) {
return;
}
group = e.at();
members = group.get('members');
uniqMembers = _.uniq(members);
if (!_.isEqual(uniqMembers, members)) {
group.set('members', uniqMembers);
}
chat = group.at('chat');
model.set('_chatMessage', '');
message = {
id: model.id(),
uuid: user.get('id'),
contributor: user.get('backer.contributor'),
npc: user.get('backer.npc'),
text: text,
user: helpers.username(model.get('_user.auth'), model.get('_user.profile.name')),
timestamp: +(new Date)
};
messages = chat.get() || [];
messages.unshift(message);
messages.splice(200);
chat.set(messages);
type = $(el).attr('data-type');
if (group.get('type') === 'party') {
return model.set('_user.party.lastMessageSeen', chat.get()[0].id);
}
};
appExports.chatKeyup = function(e, el, next) {
if (e.keyCode !== 13) {
return next();
}
return appExports.sendChat(e, el);
};
appExports.deleteChatMessage = function(e) {
if (confirm("Delete chat message?") === true) {
return e.at().remove();
}
};
app.on('render', function(ctx) {
return $('#party-tab-link').on('shown', function(e) {
var messages;
messages = model.get('_party.chat');
if (!((messages != null ? messages.length : void 0) > 0)) {
return false;
}
return model.set('_user.party.lastMessageSeen', messages[0].id);
});
});
appExports.gotoPartyChat = function() {
return model.set('_gamePane', true, function() {
return $('#party-tab-link').tab('show');
});
};
return appExports.assignGroupLeader = function(e, el) {
var newLeader;
newLeader = model.get('_new.groupLeader');
if (newLeader && (confirm("Assign new leader, you sure?") === true)) {
if (newLeader) {
return e.at().set('leader', newLeader, function() {
return browser.resetDom(model);
});
}
}
};
};
}).call(this);
/*
//@ sourceMappingURL=groups.map
*/

10
assets/js/groups.map Normal file

File diff suppressed because one or more lines are too long

25
assets/js/i18n.js Normal file
View file

@ -0,0 +1,25 @@
// Generated by CoffeeScript 1.6.3
(function() {
var i18n;
i18n = require('derby-i18n');
i18n.plurals.add('he', function(n) {
return n;
});
i18n.plurals.add('bg', function(n) {
return n;
});
i18n.plurals.add('nl', function(n) {
return n;
});
module.exports = i18n;
}).call(this);
/*
//@ sourceMappingURL=i18n.map
*/

10
assets/js/i18n.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "i18n.js",
"sourceRoot": "",
"sources": [
"i18n.coffee"
],
"names": [],
"mappings": ";AAAA;CAAA,GAAA,EAAA;;CAAA,CAAA,CAAO,CAAP,GAAO,KAAA;;CAAP,CAEA,CAAA,CAAI,GAAQ,EAAY;CAAD,UAAO;CAA9B,EAAuB;;CAFvB,CAGA,CAAA,CAAI,GAAQ,EAAY;CAAD,UAAO;CAA9B,EAAuB;;CAHvB,CAIA,CAAA,CAAI,GAAQ,EAAY;CAAD,UAAO;CAA9B,EAAuB;;CAJvB,CAMA,CAAiB,CANjB,EAMM,CAAN;CANA"
}

View file

@ -2,7 +2,7 @@ derby = require 'derby'
# Include library components
derby.use require('derby-ui-boot'), {styles: []}
derby.use require '../../ui'
derby.use require '../'
derby.use require 'derby-auth/components'
# Init app & reference its functions

184
assets/js/index.js Normal file
View file

@ -0,0 +1,184 @@
// Generated by CoffeeScript 1.6.3
(function() {
var algos, app, async, derby, get, i18n, misc, ready, view, _;
derby = require('derby');
derby.use(require('derby-ui-boot'), {
styles: []
});
derby.use(require('../'));
derby.use(require('derby-auth/components'));
app = derby.createApp(module);
get = app.get, view = app.view, ready = app.ready;
i18n = require('./i18n');
i18n.localize(app, {
availableLocales: ['en', 'he', 'bg', 'nl'],
defaultLocale: 'en',
urlScheme: false,
checkHeader: true
});
misc = require('./misc');
misc.viewHelpers(view);
_ = require('lodash');
algos = require('habitrpg-shared/script/algos');
async = require('async');
get('/', function(page, model, params, next) {
var uuid, _ref, _ref1;
if (((_ref = page.params) != null ? (_ref1 = _ref.query) != null ? _ref1.play : void 0 : void 0) != null) {
return page.redirect('/');
}
/*
Subscribe to the user, the users's party (meta info like party name, member ids, etc), and the party's members. 3 subscriptions.
*/
uuid = model.get('_userId') || model.session.userId;
return async.waterfall([
function(cb) {
var myGroupsQuery, publicGroupsQuery;
publicGroupsQuery = model.query('groups').publicGroups();
myGroupsQuery = model.query('groups').withMember(uuid);
return model.fetch(publicGroupsQuery, myGroupsQuery, cb);
}, function(publicGroups, groups, cb) {
var groupsInfo, groupsObj, guildsQ, membersQ, partyQ;
model.set('_publicGroups', _.sortBy(publicGroups.get(), function(g) {
return -_.size(g.members);
}));
groupsObj = groups.get();
if (_.isEmpty(groupsObj)) {
return cb(true);
}
groupsInfo = _.reduce(groupsObj, (function(m, g) {
if (g.type === 'guild') {
m.guildIds.push(g.id);
} else {
m.partyId = g.id;
}
m.members = m.members.concat(g.members);
return m;
}), {
guildIds: [],
partyId: null,
members: []
});
membersQ = model.query('users').publicInfo(groupsInfo.members);
partyQ = model.query('groups').withIds(groupsInfo.partyId);
guildsQ = model.query('groups').withIds(groupsInfo.guildIds);
return model.fetch(membersQ, partyQ, guildsQ, cb);
}
], function(err, members, party, guilds) {
var mObj;
if (err && err !== true) {
return next(err);
}
if (members) {
mObj = members.get();
model.set("_members", _.object(_.pluck(mObj, 'id'), mObj));
model.set("_membersArray", mObj);
}
if (party) {
model.ref('_party', party);
}
if (guilds) {
model.ref('_guilds', guilds);
}
return model.subscribe("users." + uuid, 'groups.habitrpg', function(err, user, tavern) {
if (err) {
return next(err);
}
model.ref('_user', user);
model.ref('_habitRPG', tavern);
if (!user.get()) {
console.error("User not found - this shouldn't be happening!");
return page.redirect('/logout');
}
require('./items').server(model);
_.each(['habit', 'daily', 'todo', 'reward'], function(type) {
model.refList("_" + type + "List", "_user.tasks", "_user." + type + "Ids");
return true;
});
return page.render();
});
});
});
ready(function(model) {
var browser, tz, user;
user = model.at('_user');
misc.fixCorruptUser(model);
browser = require('./browser');
require('./tasks').app(exports, model);
require('./items').app(exports, model);
require('./groups').app(exports, model, app);
require('./profile').app(exports, model);
require('./pets').app(exports, model);
require('../server/private').app(exports, model);
if (model.flags.nodeEnv !== 'production') {
require('./debug').app(exports, model);
}
browser.app(exports, model, app);
require('./unlock').app(exports, model);
require('./filters').app(exports, model);
require('./challenges').app(exports, model);
exports.removeAt = function(e, el) {
var confirmMessage;
if ((confirmMessage = $(el).attr('data-confirm')) != null) {
if (confirm(confirmMessage) !== true) {
return;
}
}
e.at().remove();
if ($(el).attr('data-refresh')) {
return browser.resetDom(model);
}
};
tz = user.get("preferences.timezoneOffset");
if (!(tz && tz === (new Date()).getTimezoneOffset())) {
user.set('preferences.timezoneOffset', (new Date()).getTimezoneOffset());
}
/*
Cron
*/
return misc.batchTxn(model, function(uObj, paths) {
_.each(['habit', 'daily', 'todo', 'reward'], function(type) {
uObj["" + type + "s"] = _.where(uObj.tasks, {
type: type
});
return true;
});
algos.cron(uObj, {
paths: paths
});
if (_.isEmpty(paths) || (paths['lastCron'] && _.size(paths) === 1)) {
return;
}
if (paths['stats.hp']) {
delete paths['stats.hp'];
return setTimeout(function() {
browser.resetDom(model);
return user.set('stats.hp', uObj.stats.hp);
}, 750);
}
}, {
cron: true
});
});
}).call(this);
/*
//@ sourceMappingURL=index.map
*/

10
assets/js/index.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "index.js",
"sourceRoot": "",
"sources": [
"index.coffee"
],
"names": [],
"mappings": "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"
}

64
assets/js/items.js Normal file
View file

@ -0,0 +1,64 @@
// Generated by CoffeeScript 1.6.3
(function() {
var items, updateStore, _;
items = require('habitrpg-shared/script/items');
_ = require('lodash');
updateStore = function(model) {
var nextItems;
nextItems = items.updateStore(model.get('_user'));
return _.each(nextItems, function(v, k) {
model.set("_items.next." + k, v);
return true;
});
};
/*
server exports
*/
module.exports.server = function(model) {
model.set('_items', items.items);
return updateStore(model);
};
/*
app exports
*/
module.exports.app = function(appExports, model) {
var misc;
misc = require('./misc');
model.on("set", "_user.items.*", function() {
return updateStore(model);
});
appExports.buyItem = function(e, el) {
return misc.batchTxn(model, function(uObj, paths) {
var ret;
ret = items.buyItem(uObj, $(el).attr('data-type'), {
paths: paths
});
if (ret === false) {
return alert("Not enough GP");
}
});
};
appExports.activateRewardsTab = function() {
model.set('_activeTabRewards', true);
return model.set('_activeTabPets', false);
};
return appExports.activatePetsTab = function() {
model.set('_activeTabPets', true);
return model.set('_activeTabRewards', false);
};
};
}).call(this);
/*
//@ sourceMappingURL=items.map
*/

10
assets/js/items.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "items.js",
"sourceRoot": "",
"sources": [
"items.coffee"
],
"names": [],
"mappings": ";AAAA;CAAA,KAAA,eAAA;;CAAA,CAAA,CAAQ,EAAR,EAAQ,uBAAA;;CAAR,CACA,CAAI,IAAA,CAAA;;CADJ,CAGA,CAAc,EAAA,IAAC,EAAf;CACE,OAAA,CAAA;CAAA,EAAY,CAAZ,CAAiB,EAAa,EAA9B,EAAY;CACX,CAAiB,CAAA,CAAlB,KAAA,EAAA;CAA2B,CAA6B,CAA7B,EAAK,CAAL,QAAW;CAApB,YAA0C;CAA5D,IAAkB;CALpB,EAGc;;CAId;;;CAPA;;CAAA,CAUA,CAAwB,EAAA,CAAlB,CAAQ,EAAW;CACvB,CAAoB,CAApB,CAAA,CAAK,GAAL;CACY,IAAZ,MAAA;CAZF,EAUwB;;CAIxB;;;CAdA;;CAAA,CAiBA,CAAA,EAAqB,CAAf,CAAQ,EAAQ,CAAD;CACnB,GAAA,IAAA;CAAA,EAAO,CAAP,GAAO,CAAA;CAAP,CAEA,CAAiC,CAAjC,CAAK,IAA4B,MAAjC;CAAgD,IAAZ,MAAA,EAAA;CAApC,IAAiC;CAFjC,CAIyB,CAAJ,CAArB,GAAA,EAAsB,CAAZ;CACH,CAAgB,CAAA,CAAjB,CAAJ,GAAA,CAAsB,IAAtB;CACE,EAAA,SAAA;CAAA,CAA0B,CAA1B,CAAM,CAAK,EAAL,CAAN,GAA0B;CAAyB,CAAC,GAAD,KAAC;CAApD,SAAM;CACN,EAA0B,CAAA,CAAO,GAAjC;CAAM,IAAN,UAAA,EAAA;UAFmB;CAArB,MAAqB;CALvB,IAIqB;CAJrB,EASgC,CAAhC,KAAgC,CAAtB,QAAV;CACE,CAA+B,CAA/B,CAAA,CAAK,CAAL,aAAA;CACM,CAAsB,CAA5B,EAAK,QAAL,GAAA;CAXF,IASgC;CAGrB,EAAkB,MAAA,CAAnB,CAAV,IAAA;CACE,CAA4B,CAA5B,CAAA,CAAK,CAAL,UAAA;CACM,CAAyB,CAA/B,EAAK,QAAL,MAAA;CAfiB,IAaU;CA9B/B,EAiBqB;CAjBrB"
}

323
assets/js/misc.js Normal file
View file

@ -0,0 +1,323 @@
// Generated by CoffeeScript 1.6.3
(function() {
var algos, batchTxn, helpers, indexedPath, items, taskInChallenge, _;
_ = require('lodash');
algos = require('habitrpg-shared/script/algos');
items = require('habitrpg-shared/script/items').items;
helpers = require('habitrpg-shared/script/helpers');
module.exports.batchTxn = batchTxn = function(model, cb, options) {
var batch, paths, ret, setOps, uObj, user;
if (options == null) {
options = {};
}
_.defaults(options, {
user: model.at("_user"),
cron: false,
done: function() {}
});
user = options.user;
uObj = helpers.hydrate(user.get());
batch = {
set: function(k, v) {
helpers.dotSet(k, v, uObj);
return paths[k] = true;
},
get: function(k) {
return helpers.dotGet(k, uObj);
}
};
paths = {};
model._dontPersist = true;
ret = cb(uObj, paths, batch);
_.each(paths, function(v, k) {
user.pass({
cron: options.cron
}).set(k, batch.get(k));
return true;
});
model._dontPersist = false;
if (!_.isEmpty(paths)) {
setOps = _.reduce(paths, (function(m, v, k) {
m[k] = batch.get(k);
return m;
}), {});
user.set("update__", setOps, options.done);
} else {
options.done();
}
return ret;
};
/*
We can't always use refLists, but we often still need to get a positional path by id: eg, users.1234.tasks.5678.value
For arrays (which use indexes, not id-paths), here's a helper function so we can run indexedPath('users',:user.id,'tasks',:task.id,'value)
*/
indexedPath = function() {
var _this = this;
return _.reduce(arguments, function(m, v) {
if (!m) {
return v;
}
if (_.isString(v)) {
return "" + m + "." + v;
}
return ("" + m + ".") + _.findIndex(_this.model.get(m), v);
}, '');
};
taskInChallenge = function(task) {
if (!(task != null ? task.challenge : void 0)) {
return void 0;
}
return this.model.at(indexedPath.call(this, "groups." + task.group.id + ".challenges", {
id: task.challenge
}, "" + task.type + "s", {
id: task.id
}));
};
/*
algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the
object properties, and it's very difficult to diff the two objects and find dot-separated paths to set. So we to first
clone our user object (if we don't do that, it screws with model.on() listeners, ping Tyler for an explaination),
perform the updates while tracking paths, then all the values at those paths
*/
module.exports.score = function(model, taskId, direction, allowUndo) {
var delta, drop;
if (allowUndo == null) {
allowUndo = false;
}
drop = void 0;
delta = batchTxn(model, function(uObj, paths) {
var chal, chalTask, chalUser, cu, previousUndo, tObj, tObjBefore, timeoutId, _ref, _ref1, _ref2, _ref3, _ref4;
tObj = uObj.tasks[taskId];
if (allowUndo) {
tObjBefore = _.cloneDeep(tObj);
if ((_ref = tObjBefore.type) === 'daily' || _ref === 'todo') {
tObjBefore.completed = !tObjBefore.completed;
}
previousUndo = model.get('_undo');
if (previousUndo != null ? previousUndo.timeoutId : void 0) {
clearTimeout(previousUndo.timeoutId);
}
timeoutId = setTimeout((function() {
return model.del('_undo');
}), 20000);
model.set('_undo', {
stats: _.cloneDeep(uObj.stats),
task: tObjBefore,
timeoutId: timeoutId
});
}
delta = algos.score(uObj, tObj, direction, {
paths: paths
});
if ((_ref1 = uObj._tmp) != null ? _ref1.streakBonus : void 0) {
model.set('_streakBonus', uObj._tmp.streakBonus);
}
drop = (_ref2 = uObj._tmp) != null ? _ref2.drop : void 0;
if ((chalTask = taskInChallenge.call({
model: model
}, tObj)) && (chalTask != null ? chalTask.get() : void 0)) {
model._dontPersist = false;
chalTask.incr("value", delta);
chal = model.at(indexedPath.call({
model: model
}, "groups." + tObj.group.id + ".challenges", {
id: tObj.challenge
}));
chalUser = function() {
return indexedPath.call({
model: model
}, chal.path(), 'users', {
id: uObj.id
});
};
cu = model.at(chalUser());
if (!(cu != null ? cu.get() : void 0)) {
chal.push("users", {
id: uObj.id,
name: helpers.username(uObj.auth, (_ref3 = uObj.profile) != null ? _ref3.name : void 0)
});
cu = model.at(chalUser());
} else {
cu.set('name', helpers.username(uObj.auth, (_ref4 = uObj.profile) != null ? _ref4.name : void 0));
}
cu.set("" + tObj.type + "s." + tObj.id, {
value: tObj.value,
history: tObj.history
});
return model._dontPersist = true;
}
}, {
done: function() {
if (drop && (typeof $ !== "undefined" && $ !== null)) {
model.set('_drop', drop);
return $('#item-dropped-modal').modal('show');
}
}
});
return delta;
};
/*
Cleanup task-corruption (null tasks, rogue/invisible tasks, etc)
Obviously none of this should be happening, but we'll stop-gap until we can find & fix
Gotta love refLists! see https://github.com/lefnire/habitrpg/issues/803 & https://github.com/lefnire/habitrpg/issues/6343
*/
module.exports.fixCorruptUser = function(model) {
var resetDom, tasks, user;
user = model.at('_user');
tasks = user.get('tasks');
_.each(tasks, function(task, key) {
if (!(((task != null ? task.id : void 0) != null) && ((task != null ? task.type : void 0) != null))) {
user.del("tasks." + key);
delete tasks[key];
}
return true;
});
resetDom = false;
batchTxn(model, function(uObj, paths, batch) {
var uniqInvites, uniqPets, _ref;
uniqPets = _.uniq(uObj.items.pets);
if (!_.isEqual(uniqPets, uObj.items.pets)) {
batch.set('items.pets', uniqPets);
}
if ((_ref = uObj.invitations) != null ? _ref.guilds : void 0) {
uniqInvites = _.uniq(uObj.invitations.guilds);
if (!_.isEqual(uniqInvites, uObj.invitations.guilds)) {
batch.set('invitations.guilds', uniqInvites);
}
}
['habit', 'daily', 'todo', 'reward'].forEach(function(type) {
var idList, preened, taskIds, union;
idList = uObj["" + type + "Ids"];
taskIds = _.pluck(_.where(tasks, {
type: type
}), 'id');
union = _.union(idList, taskIds);
preened = _.filter(union, function(id) {
return id && _.contains(taskIds, id);
});
if (!_.isEqual(idList, preened)) {
batch.set("" + type + "Ids", preened);
console.error(uObj.id + ("'s " + type + "s were corrupt."));
}
return true;
});
return resetDom = !_.isEmpty(paths);
});
if (resetDom) {
return require('./browser').resetDom(model);
}
};
module.exports.viewHelpers = function(view) {
view.fn("percent", function(x, y) {
if (x === 0) {
x = 1;
}
return Math.round(x / y * 100);
});
view.fn('indexOf', function(str1, str2) {
if (!(str1 && str2)) {
return false;
}
return str1.indexOf(str2) !== -1;
});
view.fn("round", Math.round);
view.fn("floor", Math.floor);
view.fn("ceil", Math.ceil);
view.fn("lt", function(a, b) {
return a < b;
});
view.fn('gt', function(a, b) {
return a > b;
});
view.fn("mod", function(a, b) {
return parseInt(a) % parseInt(b) === 0;
});
view.fn("notEqual", function(a, b) {
return a !== b;
});
view.fn("and", function() {
return _.reduce(arguments, function(cumm, curr) {
return cumm && curr;
});
});
view.fn("or", function() {
return _.reduce(arguments, function(cumm, curr) {
return cumm || curr;
});
});
view.fn("truarr", function(num) {
return num - 1;
});
view.fn('count', function(arr) {
return (arr != null ? arr.length : void 0) || 0;
});
view.fn('int', {
get: function(num) {
return num;
},
set: function(num) {
return [parseInt(num)];
}
});
view.fn('indexedPath', indexedPath);
view.fn("encodeiCalLink", helpers.encodeiCalLink);
view.fn("gems", function(balance) {
return balance * 4;
});
view.fn("username", helpers.username);
view.fn("tnl", algos.tnl);
view.fn('equipped', helpers.equipped);
view.fn("gold", helpers.gold);
view.fn("silver", helpers.silver);
view.fn('userStr', helpers.userStr);
view.fn('totalStr', helpers.totalStr);
view.fn('userDef', helpers.userDef);
view.fn('totalDef', helpers.totalDef);
view.fn('itemText', helpers.itemText);
view.fn('itemStat', helpers.itemStat);
view.fn('ownsPet', helpers.ownsPet);
view.fn('taskClasses', helpers.taskClasses);
view.fn('friendlyTimestamp', helpers.friendlyTimestamp);
view.fn('newChatMessages', helpers.newChatMessages);
view.fn('relativeDate', helpers.relativeDate);
view.fn('noTags', helpers.noTags);
view.fn('appliedTags', helpers.appliedTags);
view.fn('taskInChallenge', function(task) {
var _ref;
return (_ref = taskInChallenge.call(this, task)) != null ? _ref.get() : void 0;
});
view.fn('taskAttrFromChallenge', function(task, attr) {
var _ref;
return (_ref = taskInChallenge.call(this, task)) != null ? _ref.get(attr) : void 0;
});
view.fn('brokenChallengeLink', function(task) {
var _ref;
return (task != null ? task.challenge : void 0) && !((_ref = taskInChallenge.call(this, task)) != null ? _ref.get() : void 0);
});
return view.fn('challengeMemberScore', function(member, tType, tid) {
var _ref, _ref1;
return Math.round((_ref = member["" + tType + "s"]) != null ? (_ref1 = _ref[tid]) != null ? _ref1.value : void 0 : void 0);
});
};
}).call(this);
/*
//@ sourceMappingURL=misc.map
*/

10
assets/js/misc.map Normal file

File diff suppressed because one or more lines are too long

103
assets/js/pets.js Normal file
View file

@ -0,0 +1,103 @@
// Generated by CoffeeScript 1.6.3
(function() {
var hatchingPotions, pets, randomVal, _, _ref;
_ = require('lodash');
randomVal = require('habitrpg-shared/script/helpers').randomVal;
_ref = require('habitrpg-shared/script/items').items, pets = _ref.pets, hatchingPotions = _ref.hatchingPotions;
/*
app exports
*/
module.exports.app = function(appExports, model) {
var user;
user = model.at('_user');
appExports.chooseEgg = function(e, el) {
return model.ref('_hatchEgg', e.at());
};
appExports.hatchEgg = function(e, el) {
var egg, eggIdx, eggs, hatchingPotionIdx, hatchingPotionName, myHatchingPotion, myPets;
hatchingPotionName = $(el).children('select').val();
myHatchingPotion = user.get('items.hatchingPotions');
egg = model.get('_hatchEgg');
eggs = user.get('items.eggs');
myPets = user.get('items.pets');
hatchingPotionIdx = myHatchingPotion.indexOf(hatchingPotionName);
eggIdx = eggs.indexOf(egg);
if (hatchingPotionIdx === -1) {
return alert("You don't own that hatching potion yet, complete more tasks!");
}
if (eggIdx === -1) {
return alert("You don't own that egg yet, complete more tasks!");
}
if (myPets && myPets.indexOf("" + egg.name + "-" + hatchingPotionName) !== -1) {
return alert("You already have that pet, hatch a different combo.");
}
user.push('items.pets', egg.name + '-' + hatchingPotionName, function() {
eggs.splice(eggIdx, 1);
myHatchingPotion.splice(hatchingPotionIdx, 1);
user.set('items.eggs', eggs);
return user.set('items.hatchingPotions', myHatchingPotion);
});
return alert('Your egg hatched! Visit your stable to equip your pet.');
};
appExports.choosePet = function(e, el, next) {
var modifier, name, pet, petStr, _ref1;
petStr = $(el).attr('data-pet');
if (user.get('items.pets').indexOf(petStr) === -1) {
return next();
}
if (user.get('items.currentPet.str') === petStr) {
return user.set('items.currentPet', {});
}
_ref1 = petStr.split('-'), name = _ref1[0], modifier = _ref1[1];
pet = _.find(pets, {
name: name
});
pet.modifier = modifier;
pet.str = petStr;
return user.set('items.currentPet', pet);
};
appExports.buyHatchingPotion = function(e, el) {
var gems, name, newHatchingPotion;
name = $(el).attr('data-hatchingPotion');
newHatchingPotion = _.find(hatchingPotions, {
name: name
});
gems = user.get('balance') * 4;
if (gems >= newHatchingPotion.value) {
if (confirm("Buy this hatching potion with " + newHatchingPotion.value + " of your " + gems + " Gems?")) {
user.push('items.hatchingPotions', newHatchingPotion.name);
return user.set('balance', (gems - newHatchingPotion.value) / 4);
}
} else {
return $('#more-gems-modal').modal('show');
}
};
return appExports.buyEgg = function(e, el) {
var gems, name, newEgg;
name = $(el).attr('data-egg');
newEgg = _.find(pets, {
name: name
});
gems = user.get('balance') * 4;
if (gems >= newEgg.value) {
if (confirm("Buy this egg with " + newEgg.value + " of your " + gems + " Gems?")) {
user.push('items.eggs', newEgg);
return user.set('balance', (gems - newEgg.value) / 4);
}
} else {
return $('#more-gems-modal').modal('show');
}
};
};
}).call(this);
/*
//@ sourceMappingURL=pets.map
*/

10
assets/js/pets.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "pets.js",
"sourceRoot": "",
"sources": [
"pets.coffee"
],
"names": [],
"mappings": "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"
}

135
assets/js/profile.js Normal file
View file

@ -0,0 +1,135 @@
// Generated by CoffeeScript 1.6.3
(function() {
var algos, browser, helpers, items, misc, _;
helpers = require('habitrpg-shared/script/helpers');
algos = require('habitrpg-shared/script/algos');
browser = require('./browser');
items = require('./items');
misc = require('./misc');
_ = require('lodash');
module.exports.app = function(appExports, model) {
var toggleGamePane, user;
user = model.at('_user');
appExports.revive = function() {
var paths, uObj, _ref;
_ref = [user.get(), {}], uObj = _ref[0], paths = _ref[1];
algos.revive(uObj, {
paths: paths
});
return _.each(paths, (function(v, k) {
return user.set(k, helpers.dotGet(k, uObj));
}));
};
appExports.reset = function(e, el) {
misc.batchTxn(model, function(uObj, paths, batch) {
batch.set('tasks', {});
['habit', 'daily', 'todo', 'reward'].forEach(function(type) {
return batch.set("" + type + "Ids", []);
});
_.each({
hp: 50,
lvl: 1,
gp: 0,
exp: 0
}, function(v, k) {
return batch.set("stats." + k, v);
});
return _.each({
armor: 0,
weapon: 0,
head: 0,
shield: 0
}, function(v, k) {
return batch.set("items." + k, v);
});
});
return browser.resetDom(model);
};
appExports.closeNewStuff = function(e, el) {
return user.set('flags.newStuff', 'hide');
};
appExports.customizeGender = function(e, el) {
return user.set('preferences.gender', $(el).attr('data-value'));
};
appExports.customizeHair = function(e, el) {
return user.set('preferences.hair', $(el).attr('data-value'));
};
appExports.customizeSkin = function(e, el) {
return user.set('preferences.skin', $(el).attr('data-value'));
};
appExports.customizeArmorSet = function(e, el) {
return user.set('preferences.armorSet', $(el).attr('data-value'));
};
appExports.restoreSave = function() {
return misc.batchTxn(model, function(uObj, paths, batch) {
return $('#restore-form input').each(function() {
var path, val, _ref;
_ref = [$(this).attr('data-for'), parseInt($(this).val() || 1)], path = _ref[0], val = _ref[1];
return batch.set(path, val);
});
});
};
appExports.toggleHeader = function(e, el) {
return user.set('preferences.hideHeader', !user.get('preferences.hideHeader'));
};
appExports.deleteAccount = function(e, el) {
return model.del("users." + (user.get('id')), function() {
return location.href = "/logout";
});
};
appExports.profileAddWebsite = function(e, el) {
var newWebsite;
newWebsite = model.get('_newProfileWebsite');
if (/^(\s)*$/.test(newWebsite)) {
return;
}
user.unshift('profile.websites', newWebsite);
return model.set('_newProfileWebsite', '');
};
appExports.profileEdit = function(e, el) {
return model.set('_profileEditing', true);
};
appExports.profileSave = function(e, el) {
return model.set('_profileEditing', false);
};
appExports.profileRemoveWebsite = function(e, el) {
var i, sites;
sites = user.get('profile.websites');
i = sites.indexOf($(el).attr('data-website'));
sites.splice(i, 1);
return user.set('profile.websites', sites);
};
toggleGamePane = function() {
return model.set('_gamePane', !model.get('_gamePane'), function() {
return browser.setupTooltips();
});
};
appExports.clickAvatar = function(e, el) {
var uid;
uid = $(el).attr('data-uid');
if (uid === model.get('_userId')) {
return toggleGamePane();
} else {
return $("#avatar-modal-" + uid).modal('show');
}
};
appExports.toggleGamePane = function() {
return toggleGamePane();
};
return appExports.toggleResting = function() {
return model.set('_user.flags.rest', !model.get('_user.flags.rest'));
};
};
}).call(this);
/*
//@ sourceMappingURL=profile.map
*/

10
assets/js/profile.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "profile.js",
"sourceRoot": "",
"sources": [
"profile.coffee"
],
"names": [],
"mappings": "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"
}

View file

@ -0,0 +1,103 @@
'use strict';
/**
* Services that persists and retrieves user from localStorage.
*/
var facebook = {}
angular.module('authServices', ['userServices']).
factory('Facebook',
['$http', '$location', 'User', 'API_URL',
function($http, $location, User, API_URL) {
//TODO FB.init({appId: '${section.parameters['facebook.app.id']}', status: true, cookie: true, xfbml: true});
var auth, user = User.user;
facebook.handleStatusChange = function(session) {
if (session.authResponse) {
FB.api('/me', {
fields: 'name, picture, email'
}, function(response) {
console.log(response.error)
if (!response.error) {
var data = {
name: response.name,
facebook_id: response.id,
email: response.email
}
$http.post(API_URL + '/api/v1/user/auth/facebook', data).success(function(data, status, headers, config) {
User.authenticate(data.id, data.token, function(err) {
if (!err) {
alert('Login successful!');
$location.path("/habit");
}
});
}).error(function(response) {
console.log('error')
})
} else {
alert('napaka')
}
//clearAction();
});
} else {
document.body.className = 'not_connected';
//clearAction();
}
}
return {
authUser: function() {
FB.Event.subscribe('auth.statusChange', facebook.handleStatusChange);
},
getAuth: function() {
return auth;
},
login: function() {
FB.login(null, {
scope: 'email'
});
},
logout: function() {
FB.logout(function(response) {
window.location.reload();
});
}
}
}
])
.factory('LocalAuth',
['$http', 'User',
function($http, User) {
var auth,
user = User.user;
return {
getAuth: function() {
return auth;
},
login: function() {
user.id = '';
user.apiToken = '';
User.authenticate();
return;
},
logout: function() {}
}
}
]);

View file

@ -0,0 +1,75 @@
angular.module('notificationServices', []).
factory('Notification', function () {
var data = {message:''};
var active = false;
var timer = null;
return {
hide: function () {
$('#notification').fadeOut(function () {
$('#notification').css('webkit-transform', 'none')
$('#notification').css('top', '-63px')
$('#notification').css('left', '0px');
setTimeout(function() {
$('#notification').show()
}, 190)
});
active = false;
timer = null;
},
animate: function () {
if (timer) {
clearTimeout(timer);
timer = setTimeout(this.hide, 2000)
}
if (active == false) {
active = true;
$('#notification').transition({ y: 63, x: 0 });
timer = setTimeout(this.hide, 2000);
}
},
push: function (message) {
data.message = ''
switch(message.type) {
case 'stats':
if (message.stats.exp != null && message.stats.gp != null)
data.message = 'Experience: ' + message.stats.exp + '<br />GP: ' + message.stats.gp.toFixed(2)
if (message.stats.hp)
data.message = 'HP: ' + message.stats.hp.toFixed(2)
if (message.stats.gp && message.stats.exp == null)
data.message = '<br />GP: ' + message.stats.gp.toFixed(2)
break;
case 'text':
data.message = message.text
break;
}
this.animate()
},
get: function () {
return data;
},
clearTimer: function () {
clearTimeout(timer);
timer = null;
active = false;
},
init: function () {
timer = setTimeout(this.hide, 2000);
}
}
});

View file

@ -0,0 +1,16 @@
'use strict';
/**
* Services that persists and retrieves user from localStorage.
*/
angular.module('sharedServices', [] ).
factory("Items", ['$rootScope', function($rootScope){
return window.habitrpgShared.items;
}]).
factory("Algos", ['$rootScope', function($rootScope){
return window.habitrpgShared.algos;
}]).
factory("Helpers", ['$rootScope', function($rootScope){
return window.habitrpgShared.helpers;
}]);

View file

@ -0,0 +1,169 @@
'use strict';
/**
* Services that persists and retrieves user from localStorage.
*/
angular.module('userServices', []).
factory('User', ['$http', '$location', 'Notification', 'API_URL',
function($http, $location, Notification, API_URL) {
var STORAGE_ID = 'habitrpg-user',
HABIT_MOBILE_SETTINGS = 'habit-mobile-settings',
authenticated = false,
defaultSettings = {
auth: { apiId: '', apiToken: ''},
sync: {
queue: [], //here OT will be queued up, this is NOT call-back queue!
sent: [] //here will be OT which have been sent, but we have not got reply from server yet.
},
fetching: false, // whether fetch() was called or no. this is to avoid race conditions
online: false
},
settings = {}, //habit mobile settings (like auth etc.) to be stored here
user = {}; // this is stored as a reference accessible to all controllers, that way updates propagate
//first we populate user with schema
_.extend(user, window.habitrpgShared.helpers.newUser());
user.apiToken = user._id = ''; // we use id / apitoken to determine if registered
//than we try to load localStorage
if (localStorage.getItem(STORAGE_ID)) {
_.extend(user, JSON.parse(localStorage.getItem(STORAGE_ID)));
}
var syncQueue = function (cb) {
if (!authenticated) {
alert("Not authenticated, can't sync, go to settings first.");
return;
}
var queue = settings.sync.queue;
var sent = settings.sync.sent;
if (queue.length === 0) {
console.log('Sync: Queue is empty');
return;
}
if (settings.fetching) {
console.log('Sync: Already fetching');
return;
}
if (settings.online!==true) {
console.log('Sync: Not online');
return;
}
settings.fetching = true;
// move all actions from queue array to sent array
_.times(queue.length, function () {
sent.push(queue.shift());
});
$http.post(API_URL + '/api/v1/user/batch-update' + '?date=' + new Date().getTime(), sent)
.success(function (data, status, heacreatingders, config) {
data.tasks = _.toArray(data.tasks);
//make sure there are no pending actions to sync. If there are any it is not safe to apply model from server as we may overwrite user data.
if (!queue.length) {
//we can't do user=data as it will not update user references in all other angular controllers.
_.extend(user, data);
// FIXME handle this somewhere else, we don't need to check every single time
var offset = moment().zone(); // eg, 240 - this will be converted on server as -(offset/60)
if (!user.preferences.timezoneOffset || user.preferences.timezoneOffset !== offset) {
userServices.log({op:'set', data: {'preferences.timezoneOffset': offset}});
}
}
sent.length = 0;
settings.fetching = false;
save();
if (cb) {
cb(false)
}
syncQueue(); // call syncQueue to check if anyone pushed more actions to the queue while we were talking to server.
})
.error(function (data, status, headers, config) {
//move sent actions back to queue
_.times(sent.length, function () {
queue.push(sent.shift())
});
settings.fetching = false;
Notification.push({type:'text', text:"We're offline"})
});
}
var save = function () {
localStorage.setItem(STORAGE_ID, JSON.stringify(user));
localStorage.setItem(HABIT_MOBILE_SETTINGS, JSON.stringify(settings));
};
var userServices = {
user: user,
online: function (status) {
if (status===true) {
settings.online = true;
syncQueue();
} else {
settings.online = false;
};
},
authenticate: function (uuid, token, cb) {
if (!!uuid && !!token) {
$http.defaults.headers.common = {'Content-Type': "application/json;charset=utf-8"};
$http.defaults.headers.common['x-api-user'] = uuid;
$http.defaults.headers.common['x-api-key'] = token;
authenticated = true;
settings.auth.apiId = uuid;
settings.auth.apiToken = token;
settings.online = true;
this.log({}, cb);
} else {
alert('Please enter your ID and Token in settings.')
}
},
log: function (action, cb) {
//push by one buy one if an array passed in.
if (_.isArray(action)) {
action.forEach(function (a) {
settings.sync.queue.push(a);
});
} else {
settings.sync.queue.push(action);
}
save();
syncQueue(cb);
},
settings: settings
};
//load settings if we have them
if (localStorage.getItem(HABIT_MOBILE_SETTINGS)) {
//use extend here to make sure we keep object reference in other angular controllers
_.extend(settings, JSON.parse(localStorage.getItem(HABIT_MOBILE_SETTINGS)));
//if settings were saved while fetch was in process reset the flag.
settings.fetching = false;
//create and load if not
} else {
localStorage.setItem(HABIT_MOBILE_SETTINGS, JSON.stringify(defaultSettings));
_.extend(settings, defaultSettings);
}
//If user does not have ApiID that forward him to settings.
if (!settings.auth.apiId || !settings.auth.apiToken) {
$location.path("/login");
} else {
userServices.authenticate(settings.auth.apiId, settings.auth.apiToken)
}
return userServices;
}
]);

222
assets/js/tasks.js Normal file
View file

@ -0,0 +1,222 @@
// Generated by CoffeeScript 1.6.3
(function() {
var algos, helpers, misc, moment, _;
algos = require('habitrpg-shared/script/algos');
helpers = require('habitrpg-shared/script/helpers');
_ = require('lodash');
moment = require('moment');
misc = require('./misc');
/*
Make scoring functionality available to the app
*/
module.exports.app = function(appExports, model) {
var user;
user = model.at('_user');
appExports.addTask = function(e, el) {
var newModel, newTask, text, type;
type = $(el).attr('data-task-type');
newModel = model.at('_new' + type.charAt(0).toUpperCase() + type.slice(1));
text = newModel.get();
if (/^(\s)*$/.test(text) || text === void 0) {
return;
}
newTask = {
id: model.id(),
type: type,
text: text,
notes: '',
value: 0
};
newTask.tags = _.reduce(user.get('filters'), (function(memo, v, k) {
if (v) {
memo[k] = v;
}
return memo;
}), {});
switch (type) {
case 'habit':
newTask = _.defaults({
up: true,
down: true
}, newTask);
break;
case 'reward':
newTask = _.defaults({
value: 20
}, newTask);
break;
case 'daily':
newTask = _.defaults({
repeat: {
su: true,
m: true,
t: true,
w: true,
th: true,
f: true,
s: true
},
completed: false
}, newTask);
break;
case 'todo':
newTask = _.defaults({
completed: false
}, newTask);
}
e.at().unshift(newTask);
return newModel.set('');
};
appExports.del = function(e) {
if (confirm("Are you sure you want to delete this task?") !== true) {
return;
}
$('[rel=tooltip]').tooltip('hide');
user.del("tasks." + (e.get('id')));
return e.at().remove();
};
appExports.clearCompleted = function(e, el) {
var completedIds, todoIds;
completedIds = _.pluck(_.where(model.get('_todoList'), {
completed: true
}), 'id');
todoIds = user.get('todoIds');
_.each(completedIds, function(id) {
user.del("tasks." + id);
return true;
});
return user.set('todoIds', _.difference(todoIds, completedIds));
};
appExports.toggleDay = function(e, el) {
var task;
task = model.at(e.target);
if (/active/.test($(el).attr('class'))) {
return task.set('repeat.' + $(el).attr('data-day'), false);
} else {
return task.set('repeat.' + $(el).attr('data-day'), true);
}
};
appExports.toggleTaskEdit = function(e, el) {
var chartPath, editPath, id, _ref;
id = e.get('id');
_ref = ["_tasks.editing." + id, "_page.charts." + id], editPath = _ref[0], chartPath = _ref[1];
model.set(editPath, !(model.get(editPath)));
return model.set(chartPath, false);
};
appExports.toggleChart = function(e, el) {
var chart, data, history, historyPath, id, matrix, options, togglePath, _ref, _ref1, _ref2, _ref3;
id = $(el).attr('data-id');
_ref = ['', ''], historyPath = _ref[0], togglePath = _ref[1];
switch (id) {
case 'exp':
_ref1 = ['_page.charts.exp', '_user.history.exp'], togglePath = _ref1[0], historyPath = _ref1[1];
break;
case 'todos':
_ref2 = ['_page.charts.todos', '_user.history.todos'], togglePath = _ref2[0], historyPath = _ref2[1];
break;
default:
_ref3 = ["_page.charts." + id, "_user.tasks." + id + ".history"], togglePath = _ref3[0], historyPath = _ref3[1];
model.set("_tasks.editing." + id, false);
}
history = model.get(historyPath);
model.set(togglePath, !(model.get(togglePath)));
matrix = [['Date', 'Score']];
_.each(history, function(obj) {
return matrix.push([moment(obj.date).format('MM/DD/YY'), obj.value]);
});
data = google.visualization.arrayToDataTable(matrix);
options = {
title: 'History',
backgroundColor: {
fill: 'transparent'
}
};
chart = new google.visualization.LineChart($("." + id + "-chart")[0]);
return chart.draw(data, options);
};
appExports.todosShowRemaining = function() {
return model.set('_showCompleted', false);
};
appExports.todosShowCompleted = function() {
return model.set('_showCompleted', true);
};
/*
Call scoring functions for habits & rewards (todos & dailies handled below)
*/
appExports.score = function(e, el) {
var direction, id;
id = $(el).parents('li').attr('data-id');
direction = $(el).attr('data-direction');
return misc.score(model, id, direction, true);
};
/*
This is how we handle appExports.score for todos & dailies. Due to Derby's special handling of `checked={:task.completd}`,
the above function doesn't work so we need a listener here
*/
user.on('set', 'tasks.*.completed', function(i, completed, previous, isLocal, passed) {
var direction;
if (!isLocal || (passed != null ? passed.cron : void 0)) {
return;
}
direction = completed ? 'up' : 'down';
return misc.score(model, i, direction, true);
});
/*
Undo
*/
appExports.undo = function() {
var taskPath, undo;
undo = model.get('_undo');
if (undo != null ? undo.timeoutId : void 0) {
clearTimeout(undo.timeoutId);
}
model.del('_undo');
_.each(undo.stats, function(val, key) {
user.set("stats." + key, val);
return true;
});
taskPath = "tasks." + undo.task.id;
return _.each(undo.task, function(val, key) {
if (key === 'id' || key === 'type') {
return true;
}
if (key === 'completed') {
user.pass({
cron: true
}).set("" + taskPath + ".completed", val);
} else {
user.set("" + taskPath + "." + key, val);
}
return true;
});
};
appExports.tasksToggleAdvanced = function(e, el) {
return $(el).next('.advanced-option').toggleClass('visuallyhidden');
};
appExports.tasksSaveAndClose = function() {
$('[rel=tooltip]').tooltip();
return $('[rel=popover]').popover();
};
return appExports.tasksSetPriority = function(e, el) {
var dataId;
dataId = $(el).parent('[data-id]').attr('data-id');
return model.at(e.target).set('priority', $(el).attr('data-priority'));
};
};
}).call(this);
/*
//@ sourceMappingURL=tasks.map
*/

10
assets/js/tasks.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "tasks.js",
"sourceRoot": "",
"sources": [
"tasks.coffee"
],
"names": [],
"mappings": "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"
}

135
assets/js/unlock.js Normal file
View file

@ -0,0 +1,135 @@
// Generated by CoffeeScript 1.6.3
(function() {
var hatchingPotions, pets, randomVal, _, _ref;
_ = require('lodash');
randomVal = require('habitrpg-shared/script/helpers').randomVal;
_ref = require('habitrpg-shared/script/items').items, pets = _ref.pets, hatchingPotions = _ref.hatchingPotions;
/*
Listeners to enabled flags, set notifications to the user when they've unlocked features
*/
module.exports.app = function(appExports, model) {
var alreadyShown, showPopover, user;
user = model.at('_user');
alreadyShown = function(before, after) {
return !(!before && after === true);
};
showPopover = function(selector, title, html, placement) {
if (placement == null) {
placement = 'bottom';
}
$(selector).popover('destroy');
html += " <a href='#' onClick=\"$('" + selector + "').popover('hide');return false;\">[Close]</a>";
return $(selector).popover({
title: title,
placement: placement,
trigger: 'manual',
html: true,
content: html
}).popover('show');
};
user.on('set', 'flags.customizationsNotification', function(after, before) {
var html;
if (alreadyShown(before, after)) {
return;
}
$('.main-herobox').popover('destroy');
html = "Click your avatar to customize your appearance.";
return showPopover('.main-herobox', 'Customize Your Avatar', html, 'bottom');
});
user.on('set', 'flags.itemsEnabled', function(after, before) {
var html;
if (alreadyShown(before, after)) {
return;
}
html = "<img src='/vendor/BrowserQuest/client/img/1/chest.png' />\nCongratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information.";
return showPopover('div.rewards', 'Item Store Unlocked', html, 'left');
});
user.on('set', 'flags.petsEnabled', function(after, before) {
var html;
if (alreadyShown(before, after)) {
return;
}
html = "<img src='/img/sprites/wolf_border.png' style='width:30px;height:30px;float:left;padding-right:5px' />\nYou have unlocked Pets! You can now buy pets with Gems (note, you replenish Gems with real-life money - so chose your pets wisely!)";
return showPopover('#rewardsTabs', 'Pets Unlocked', html, 'left');
});
user.on('set', 'flags.partyEnabled', function(after, before) {
var html;
if (user.get('party.current') || alreadyShown(before, after)) {
return;
}
html = "Be social, join a party and play Habit with your friends! You'll be better at your habits with accountability partners. Click User -> Options -> Party, and follow the instructions. LFG anyone?";
return showPopover('.user-menu', 'Party System', html, 'bottom');
});
user.on('set', 'flags.dropsEnabled', function(after, before) {
var dontPersist, egg;
if (alreadyShown(before, after)) {
return;
}
egg = randomVal(pets);
dontPersist = model._dontPersist;
model._dontPersist = false;
user.push('items.eggs', egg);
model._dontPersist = dontPersist;
return $('#drops-enabled-modal').modal('show');
});
user.on('push', 'items.pets', function(after, before) {
var dontPersist;
if (user.get('achievements.beastMaster')) {
return;
}
if (before >= 90) {
dontPersist = model._dontPersist;
model._dontPersist = false;
user.set('achievements.beastMaster', true, (function() {
return model._dontPersist = dontPersist;
}));
return $('#beastmaster-achievement-modal').modal('show');
}
});
user.on('set', 'items.*', function(after, before) {
var dontPersist, items;
if (user.get('achievements.ultimateGear')) {
return;
}
items = user.get('items');
if (parseInt(items.weapon) >= 6 && parseInt(items.armor) >= 5 && parseInt(items.head) >= 5 && parseInt(items.shield) >= 5) {
dontPersist = model._dontPersist;
model._dontPersist = false;
user.set('achievements.ultimateGear', true, (function() {
return model._dontPersist = dontPersist;
}));
return $('#max-gear-achievement-modal').modal('show');
}
});
return user.on('set', 'tasks.*.streak', function(id, after, before) {
var dontPersist;
if (after > 0) {
if ((after % 21) === 0) {
dontPersist = model._dontPersist;
model._dontPersist = false;
user.incr('achievements.streak', 1, (function() {
return model._dontPersist = dontPersist;
}));
return $('#streak-achievement-modal').modal('show');
} else if ((before - after === 1) && (before % 21 === 0)) {
dontPersist = model._dontPersist;
model._dontPersist = false;
return user.incr('achievements.streak', -1, (function() {
return model._dontPersist = dontPersist;
}));
}
}
});
};
}).call(this);
/*
//@ sourceMappingURL=unlock.map
*/

10
assets/js/unlock.map Normal file
View file

@ -0,0 +1,10 @@
{
"version": 3,
"file": "unlock.js",
"sourceRoot": "",
"sources": [
"unlock.coffee"
],
"names": [],
"mappings": "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"
}

27
bower.json Normal file
View file

@ -0,0 +1,27 @@
{
"name": "HabitRPG",
"version": "0.1.1",
"homepage": "https://github.com/lefnire/habitrpg",
"authors": [
"Tyler Renelle <tylerrenelle@gmail.com>"
],
"private": true,
"ignore": [
"**/.*",
"node_modules",
"public/bower_components",
"test",
"tests"
],
"dependencies": {
"jquery": "~2.0.3",
"angular": "1.2.0-rc.1",
"angular-resource": "1.2.0-rc.1",
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared.git#master",
"bootstrap": "v2.3.2",
"angular-ui": "~0.4.0",
"angular-bootstrap": "~0.5.0",
"lodash": "~1.3.1",
"moment": "~2.1.0"
}
}

View file

@ -5,10 +5,6 @@
"main": "./server.js",
"dependencies": {
"habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared#master",
"derby": "git://github.com/lefnire/derby#habitrpg",
"racer": "git://github.com/lefnire/racer#habitrpg",
"racer-db-mongo": "git://github.com/lefnire/racer-db-mongo#habitrpg",
"derby-ui-boot": "git://github.com/HabitRPG/derby-ui-boot#habit0.3",
"derby-auth": "git://github.com/lefnire/derby-auth#master",
"connect-mongo": "*",
"passport-facebook": "*",
@ -17,19 +13,20 @@
"guid": "*",
"moment": "*",
"stripe": "*",
"coffee-script": "1.4.x",
"mongoskin": "*",
"coffee-script": "*",
"nconf": "*",
"icalendar": "git://github.com/lefnire/node-icalendar#master",
"superagent": "~0.12.4",
"resolve": "~0.2.3",
"expect.js": "~0.2.0",
"derby-i18n": "git://github.com/switz/derby-i18n#master",
"relative-date": "~1.1.1",
"lodash": "~1.3.1",
"async": "~0.2.9",
"optimist": "~0.5.2",
"mongoose": "~3.6.18"
"mongoose": "~3.6.18",
"stylus": "~0.37.0",
"connect-assets": "~2.5.2",
"bower": "~1.2.4"
},
"private": true,
"subdomain": "habitrpg",
@ -42,7 +39,7 @@
"npm": "1.1.x"
},
"scripts": {
"start": "server.js",
"start": "nodemon src/server.coffee",
"test": "mocha test/api.mocha.coffee"
}
}

View file

@ -1,78 +0,0 @@
// Load nconf and define default configuration values if config.json or ENV vars are not found
var conf = require('nconf');
conf.argv().env().file({ file: __dirname + "/config.json" }).defaults({
'PORT': 3000,
'IP': '0.0.0.0',
'BASE_URL': 'http://localhost',
'NODE_ENV': 'development'
});
// Override normal ENV values with nconf ENV values (ENV values are used the same way without nconf)
process.env.IP = conf.get("IP");
process.env.PORT = conf.get("PORT");
process.env.BASE_URL = conf.get("BASE_URL");
process.env.FACEBOOK_KEY = conf.get("FACEBOOK_KEY");
process.env.FACEBOOK_SECRET = conf.get("FACEBOOK_SECRET");
process.env.NODE_DB_URI = conf.get("NODE_DB_URI");
process.env.NODE_ENV = conf.get("NODE_ENV");
process.env.SESSION_SECRET = conf.get("SESSION_SECRET");
process.env.SMTP_USER = conf.get("SMTP_USER");
process.env.SMTP_PASS = conf.get("SMTP_PASS");
process.env.SMTP_SERVICE = conf.get("SMTP_SERVICE");
process.env.STRIPE_API_KEY = conf.get("STRIPE_API_KEY");
process.env.STRIPE_PUB_KEY = conf.get("STRIPE_PUB_KEY");
/*var agent;
if (process.env.NODE_ENV === 'development') {
// Follow these instructions for profiling / debugging leaks
// * https://developers.google.com/chrome-developer-tools/docs/heap-profiling
// * https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101
agent = require('webkit-devtools-agent');
console.log("To debug memory leaks:" +
"\n\t(1) Run `kill -SIGUSR2 " + process.pid + "`" +
"\n\t(2) open http://c4milo.github.com/node-webkit-agent/21.0.1180.57/inspector.html?host=localhost:1337&page=0");
}*/
if (process.env.NODE_ENV === 'development') Error.stackTraceLimit = Infinity;
process.on('uncaughtException', function (error) {
function sendEmail(mailData) {
var nodemailer = require("derby-auth/node_modules/nodemailer");
// create reusable transport method (opens pool of SMTP connections)
// TODO derby-auth isn't currently configurable here, if you need customizations please send pull request
var smtpTransport = nodemailer.createTransport("SMTP",{
service: process.env.SMTP_SERVICE,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS
}
});
// send mail with defined transport object
smtpTransport.sendMail(mailData, function(error, response){
if(error){
console.log(error);
}else{
console.log("Message sent: " + response.message);
}
smtpTransport.close(); // shut down the connection pool, no more messages
});
}
sendEmail({
from: "HabitRPG <admin@habitrpg.com>",
to: "tylerrenelle@gmail.com",
subject: "HabitRPG Error",
text: error.stack
});
console.log(error.stack);
});
require('coffee-script') // remove intermediate compilation requirement
require('./src/server').listen(process.env.PORT || 3000, process.env.IP || '0.0.0.0');
// Note: removed "up" module, which is default for development (but interferes with and production + PaaS)
// Restore to 5310bb0 if I want it back (see https://github.com/codeparty/derby/issues/165#issuecomment-10405693)

20
src/config.coffee Normal file
View file

@ -0,0 +1,20 @@
# Load nconf and define default configuration values if config.json or ENV vars are not found
conf = require("nconf")
path = require("path")
conf
.argv()
.env()
.file('defaults', path.join(path.resolve __dirname, '../config.json.example'))
.file('user', path.join(path.resolve __dirname, '../config.json'))
#var agent;
#if (process.env.NODE_ENV === 'development') {
# // Follow these instructions for profiling / debugging leaks
# // * https://developers.google.com/chrome-developer-tools/docs/heap-profiling
# // * https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101
# agent = require('webkit-devtools-agent');
# console.log("To debug memory leaks:" +
# "\n\t(1) Run `kill -SIGUSR2 " + process.pid + "`" +
# "\n\t(2) open http://c4milo.github.com/node-webkit-agent/21.0.1180.57/inspector.html?host=localhost:1337&page=0");
#}
Error.stackTraceLimit = Infinity if conf.get('NODE_ENV') is "development"

View file

@ -9,9 +9,8 @@ validator = require 'derby-auth/node_modules/validator'
check = validator.check
sanitize = validator.sanitize
utils = require 'derby-auth/utils'
misc = require '../app/misc'
derbyAuthUtil = require('derby-auth/utils')
User = require('./models/user').model
User = require('./../models/user').model
api = module.exports

30
src/errors.coffee Normal file
View file

@ -0,0 +1,30 @@
nconf = require('nconf')
process.on "uncaughtException", (error) ->
sendEmail = (mailData) ->
nodemailer = require("derby-auth/node_modules/nodemailer")
# create reusable transport method (opens pool of SMTP connections)
# TODO derby-auth isn't currently configurable here, if you need customizations please send pull request
smtpTransport = nodemailer.createTransport("SMTP",
service: nconf.get('SMTP_SERVICE')
auth:
user: nconf.get('SMTP_USER')
pass: nconf.get('SMTP_PASS')
)
# send mail with defined transport object
smtpTransport.sendMail mailData, (error, response) ->
if error
console.log error
else
console.log "Message sent: " + response.message
smtpTransport.close() # shut down the connection pool, no more messages
sendEmail
from: "HabitRPG <admin@habitrpg.com>"
to: "tylerrenelle@gmail.com"
subject: "HabitRPG Error"
text: error.stack
console.log error.stack

View file

@ -32,8 +32,4 @@ translate = (req, res, next) ->
next()
apiv1Middleware = (req, res, next) ->
req.habit ?= {}
next()
module.exports = { splash, view, allowCrossDomain, translate, apiv1Middleware}
module.exports = { splash, view, allowCrossDomain, translate}

View file

@ -1,6 +1,6 @@
express = require 'express'
router = new express.Router()
api = require './api'
api = require './controllers/api'
###
---------- /api/v1 API ------------

96
src/server.coffee Normal file
View file

@ -0,0 +1,96 @@
express = require("express")
http = require("http")
path = require("path")
app = express()
nconf = require('nconf')
middleware = require("./middleware")
require('./config') # Setup configurations
require('./errors')
###
MongoDB Configuration
###
mongoose = require('mongoose')
require('./models/user') # load up the user schema - TODO is this necessary?
module.exports = server
# Connect using Mongoose too for API purposes, we'll eventually phase out Derby and only use mongoose
mongoose.connect nconf.get('NODE_DB_URI'), (err) ->
throw err if (err)
console.info('Connected with Mongoose')
###
Server Configuration
###
# all environments
app.set "port", nconf.get('PORT')
app.set "views", __dirname + "/../views"
app.set "view engine", "jade"
app.use express.favicon()
app.use express.logger("dev")
app.use express.bodyParser()
app.use require('connect-assets')()
app.use express.methodOverride()
app.use app.router
app.use express['static'](path.join(__dirname, "/../public"))
# development only
app.use express.errorHandler() if "development" is app.get("env")
# Custom Directives
app.get '/', (req, res) ->
res.render 'index', {'HabitRPG | Your Life, The Role Playing Game'}
app.use('/api/v1', require('./routes').middleware)
app.use(require('./controllers/deprecated').middleware)
server = http.createServer(app).listen app.get("port"), ->
console.log "Express server listening on port " + app.get("port")
module.exports = server
#ONE_YEAR = 1000 * 60 * 60 * 24 * 365
#TWO_WEEKS = 1000 * 60 * 60 * 24 * 14
#root = path.dirname path.dirname __dirname
#publicPath = path.join root, 'public'
#
#
#expressApp
# .use(middleware.allowCrossDomain)
# .use(express.favicon("#{publicPath}/favicon.ico"))
# # Gzip static files and serve from memory
# .use(gzippo.staticGzip(publicPath, maxAge: ONE_YEAR))
# # Gzip dynamically rendered content
# .use(express.compress())
# .use(express.bodyParser())
# .use(express.methodOverride())
# # Uncomment and supply secret to add Derby session handling
# # Derby session middleware creates req.session and socket.io sessions
# .use(express.cookieParser())
# .use(store.sessionMiddleware
# secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE'
# cookie: { maxAge: TWO_WEEKS } # defaults to 2 weeks? aka, can delete this line?
# store: mongo_store
# )
# # Adds req.getModel method
# .use(store.modelMiddleware())
# .use(middleware.translate)
# # API should be hit before all other routes
# # Show splash page for newcomers
# .use(middleware.splash)
# .use(priv.middleware)
# .use(middleware.view)
# .use(auth.middleware(strategies, options))
# # Creates an express middleware from the app's routes
# .use(app.router())
# .use(require('./static').middleware)
# .use(expressApp.router)
# .use(serverError(root))
#
#
## Errors
#expressApp.all '*', (req) ->
# throw "404: #{req.url}"

View file

@ -1,105 +0,0 @@
http = require 'http'
path = require 'path'
express = require 'express'
gzippo = require 'gzippo'
derby = require 'derby'
racer = require 'racer'
auth = require 'derby-auth'
app = require '../app'
serverError = require './serverError'
MongoStore = require('connect-mongo')(express)
priv = require './private'
habitrpgStore = require './store'
middleware = require './middleware'
helpers = require("habitrpg-shared/script/helpers")
# The first-fruits of our derby-expulsion, API-only for now
mongoose = require('mongoose')
require('./models/user') # load up the user schema - TODO is this necessary?
## RACER CONFIGURATION ##
#racer.io.set('transports', ['xhr-polling'])
racer.ioClient.set('reconnection limit', 300000) # max reconect timeout to 5 minutes
racer.set('bundleTimeout', 40000)
#unless process.env.NODE_ENV == 'production'
# racer.use(racer.logPlugin)
# derby.use(derby.logPlugin)
## SERVER CONFIGURATION ##
expressApp = express()
server = http.createServer expressApp
module.exports = server
derby.use require('racer-db-mongo')
module.exports.habitStore = store = derby.createStore
db: {type: 'Mongo', uri: process.env.NODE_DB_URI, safe:true, autoreconnect: true}
listen: server
# Connect using Mongoose too for API purposes, we'll eventually phase out Derby and only use mongoose
mongoose.connect process.env.NODE_DB_URI, (err) ->
throw err if (err)
console.info('Connected with Mongoose')
ONE_YEAR = 1000 * 60 * 60 * 24 * 365
TWO_WEEKS = 1000 * 60 * 60 * 24 * 14
root = path.dirname path.dirname __dirname
publicPath = path.join root, 'public'
# Authentication setup
strategies =
facebook:
strategy: require("passport-facebook").Strategy
conf:
clientID: process.env.FACEBOOK_KEY
clientSecret: process.env.FACEBOOK_SECRET
options =
domain: process.env.BASE_URL || 'http://localhost:3000'
allowPurl: true
schema: helpers.newUser(true)
# This has to happen before our middleware stuff
auth.store(store, habitrpgStore.customAccessControl)
mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
expressApp
.use(middleware.allowCrossDomain)
.use(express.favicon("#{publicPath}/favicon.ico"))
# Gzip static files and serve from memory
.use(gzippo.staticGzip(publicPath, maxAge: ONE_YEAR))
# Gzip dynamically rendered content
.use(express.compress())
.use(express.bodyParser())
.use(express.methodOverride())
# Uncomment and supply secret to add Derby session handling
# Derby session middleware creates req.session and socket.io sessions
.use(express.cookieParser())
.use(store.sessionMiddleware
secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE'
cookie: { maxAge: TWO_WEEKS } # defaults to 2 weeks? aka, can delete this line?
store: mongo_store
)
# Adds req.getModel method
.use(store.modelMiddleware())
.use(middleware.translate)
# API should be hit before all other routes
.use(middleware.apiv1Middleware)
.use('/api/v1', require('./routes').middleware)
.use(require('./deprecated').middleware)
# Show splash page for newcomers
.use(middleware.splash)
.use(priv.middleware)
.use(middleware.view)
.use(auth.middleware(strategies, options))
# Creates an express middleware from the app's routes
.use(app.router())
.use(require('./static').middleware)
.use(expressApp.router)
.use(serverError(root))
priv.routes(expressApp)
# Errors
expressApp.all '*', (req) ->
throw "404: #{req.url}"

View file

@ -1,20 +0,0 @@
derby = require 'derby'
{isProduction} = derby.util
module.exports = (root) ->
staticPages = derby.createStatic root
return (err, req, res, next) ->
return next() unless err?
console.log(if err.stack then err.stack else err)
## Customize error handling here ##
message = err.message || err.toString()
status = parseInt message
if status is 404
staticPages.render '404', res, {url: req.url}, 404
else if status >= 400 and status < 600
res.send status
else
staticPages.render 'error', res, {message, status}, status

Some files were not shown because too many files have changed in this diff Show more