');
+ });
+ }
+ };
+
+ 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
+*/
diff --git a/assets/js/browser.map b/assets/js/browser.map
new file mode 100644
index 0000000000..b12511ca44
--- /dev/null
+++ b/assets/js/browser.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "browser.js",
+ "sourceRoot": "",
+ "sources": [
+ "browser.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/src/app/challenges.coffee b/assets/js/challenges.coffee
similarity index 100%
rename from src/app/challenges.coffee
rename to assets/js/challenges.coffee
diff --git a/assets/js/challenges.js b/assets/js/challenges.js
new file mode 100644
index 0000000000..399cc712a9
--- /dev/null
+++ b/assets/js/challenges.js
@@ -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
+*/
diff --git a/assets/js/challenges.map b/assets/js/challenges.map
new file mode 100644
index 0000000000..982734a623
--- /dev/null
+++ b/assets/js/challenges.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "challenges.js",
+ "sourceRoot": "",
+ "sources": [
+ "challenges.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/assets/js/controllers/RootCtrl.coffee b/assets/js/controllers/RootCtrl.coffee
new file mode 100644
index 0000000000..5c2e144fb7
--- /dev/null
+++ b/assets/js/controllers/RootCtrl.coffee
@@ -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 ""
\ No newline at end of file
diff --git a/assets/js/controllers/authCtrl.coffee b/assets/js/controllers/authCtrl.coffee
new file mode 100644
index 0000000000..26cae500e9
--- /dev/null
+++ b/assets/js/controllers/authCtrl.coffee
@@ -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
diff --git a/assets/js/controllers/characterCtrl.js b/assets/js/controllers/characterCtrl.js
new file mode 100644
index 0000000000..52a0f9e5a4
--- /dev/null
+++ b/assets/js/controllers/characterCtrl.js
@@ -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()
+ }
+
+ }
+]);
diff --git a/assets/js/controllers/menuCtrl.js b/assets/js/controllers/menuCtrl.js
new file mode 100644
index 0000000000..609551c3b9
--- /dev/null
+++ b/assets/js/controllers/menuCtrl.js
@@ -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())
+
+ }
+]);
diff --git a/assets/js/controllers/notificationCtrl.js b/assets/js/controllers/notificationCtrl.js
new file mode 100644
index 0000000000..c75cd83f5c
--- /dev/null
+++ b/assets/js/controllers/notificationCtrl.js
@@ -0,0 +1,9 @@
+'use strict';
+
+habitrpg.controller('NotificationCtrl',
+ ['$scope', 'Notification',
+ function ($scope, Notification) {
+ $scope.data = Notification.get();
+
+ }
+]);
diff --git a/assets/js/controllers/settingsCtrl.js b/assets/js/controllers/settingsCtrl.js
new file mode 100644
index 0000000000..29ff2cb045
--- /dev/null
+++ b/assets/js/controllers/settingsCtrl.js
@@ -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");
+ }
+ });
+ }
+
+ }
+]);
diff --git a/assets/js/controllers/statsCtrl.js b/assets/js/controllers/statsCtrl.js
new file mode 100644
index 0000000000..a49e9682c6
--- /dev/null
+++ b/assets/js/controllers/statsCtrl.js
@@ -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;
+ }
+]);
diff --git a/assets/js/controllers/taskDetailsCtrl.js b/assets/js/controllers/taskDetailsCtrl.js
new file mode 100644
index 0000000000..5d110bb330
--- /dev/null
+++ b/assets/js/controllers/taskDetailsCtrl.js
@@ -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);
+ };
+ }
+]);
diff --git a/assets/js/controllers/tasksCtrl.js b/assets/js/controllers/tasksCtrl.js
new file mode 100644
index 0000000000..3db9a77af5
--- /dev/null
+++ b/assets/js/controllers/tasksCtrl.js
@@ -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()
+
+ */
+
+ }
+]);
diff --git a/assets/js/controllers/userAvatarCtrl.js b/assets/js/controllers/userAvatarCtrl.js
new file mode 100644
index 0000000000..0ab3c1fcc7
--- /dev/null
+++ b/assets/js/controllers/userAvatarCtrl.js
@@ -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()
+ }
+ }
+]);
diff --git a/src/app/debug.coffee b/assets/js/debug.coffee
similarity index 100%
rename from src/app/debug.coffee
rename to assets/js/debug.coffee
diff --git a/assets/js/debug.js b/assets/js/debug.js
new file mode 100644
index 0000000000..fe676c4d6a
--- /dev/null
+++ b/assets/js/debug.js
@@ -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
+*/
diff --git a/assets/js/debug.map b/assets/js/debug.map
new file mode 100644
index 0000000000..2cb3b0ee28
--- /dev/null
+++ b/assets/js/debug.map
@@ -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"
+}
\ No newline at end of file
diff --git a/assets/js/directives/directives.js b/assets/js/directives/directives.js
new file mode 100644
index 0000000000..ba291f51db
--- /dev/null
+++ b/assets/js/directives/directives.js
@@ -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()});
+ }
+ });
+ }]
+});
\ No newline at end of file
diff --git a/src/app/filters.coffee b/assets/js/filters.coffee
similarity index 100%
rename from src/app/filters.coffee
rename to assets/js/filters.coffee
diff --git a/assets/js/filters.js b/assets/js/filters.js
new file mode 100644
index 0000000000..fc76efc800
--- /dev/null
+++ b/assets/js/filters.js
@@ -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
+*/
diff --git a/assets/js/filters.map b/assets/js/filters.map
new file mode 100644
index 0000000000..a58a3d2102
--- /dev/null
+++ b/assets/js/filters.map
@@ -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"
+}
\ No newline at end of file
diff --git a/assets/js/filters/filters.js b/assets/js/filters/filters.js
new file mode 100644
index 0000000000..0e0d79d0ae
--- /dev/null
+++ b/assets/js/filters/filters.js
@@ -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);
+ }
+ });
\ No newline at end of file
diff --git a/src/app/groups.coffee b/assets/js/groups.coffee
similarity index 100%
rename from src/app/groups.coffee
rename to assets/js/groups.coffee
diff --git a/assets/js/groups.js b/assets/js/groups.js
new file mode 100644
index 0000000000..32aee87167
--- /dev/null
+++ b/assets/js/groups.js
@@ -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
+*/
diff --git a/assets/js/groups.map b/assets/js/groups.map
new file mode 100644
index 0000000000..7182240114
--- /dev/null
+++ b/assets/js/groups.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "groups.js",
+ "sourceRoot": "",
+ "sources": [
+ "groups.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/src/app/i18n.coffee b/assets/js/i18n.coffee
similarity index 100%
rename from src/app/i18n.coffee
rename to assets/js/i18n.coffee
diff --git a/assets/js/i18n.js b/assets/js/i18n.js
new file mode 100644
index 0000000000..fff7539865
--- /dev/null
+++ b/assets/js/i18n.js
@@ -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
+*/
diff --git a/assets/js/i18n.map b/assets/js/i18n.map
new file mode 100644
index 0000000000..0c58a9efc3
--- /dev/null
+++ b/assets/js/i18n.map
@@ -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"
+}
\ No newline at end of file
diff --git a/src/app/index.coffee b/assets/js/index.coffee
similarity index 99%
rename from src/app/index.coffee
rename to assets/js/index.coffee
index cc73beba22..b33304b0c5 100644
--- a/src/app/index.coffee
+++ b/assets/js/index.coffee
@@ -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
diff --git a/assets/js/index.js b/assets/js/index.js
new file mode 100644
index 0000000000..d6546bc9cf
--- /dev/null
+++ b/assets/js/index.js
@@ -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
+*/
diff --git a/assets/js/index.map b/assets/js/index.map
new file mode 100644
index 0000000000..5e457e04d4
--- /dev/null
+++ b/assets/js/index.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "index.js",
+ "sourceRoot": "",
+ "sources": [
+ "index.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/src/app/items.coffee b/assets/js/items.coffee
similarity index 100%
rename from src/app/items.coffee
rename to assets/js/items.coffee
diff --git a/assets/js/items.js b/assets/js/items.js
new file mode 100644
index 0000000000..82a0ebbeac
--- /dev/null
+++ b/assets/js/items.js
@@ -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
+*/
diff --git a/assets/js/items.map b/assets/js/items.map
new file mode 100644
index 0000000000..2dce2ace55
--- /dev/null
+++ b/assets/js/items.map
@@ -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"
+}
\ No newline at end of file
diff --git a/src/app/misc.coffee b/assets/js/misc.coffee
similarity index 100%
rename from src/app/misc.coffee
rename to assets/js/misc.coffee
diff --git a/assets/js/misc.js b/assets/js/misc.js
new file mode 100644
index 0000000000..7abb475820
--- /dev/null
+++ b/assets/js/misc.js
@@ -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
+*/
diff --git a/assets/js/misc.map b/assets/js/misc.map
new file mode 100644
index 0000000000..47b048bcfb
--- /dev/null
+++ b/assets/js/misc.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "misc.js",
+ "sourceRoot": "",
+ "sources": [
+ "misc.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/src/app/pets.coffee b/assets/js/pets.coffee
similarity index 100%
rename from src/app/pets.coffee
rename to assets/js/pets.coffee
diff --git a/assets/js/pets.js b/assets/js/pets.js
new file mode 100644
index 0000000000..93063ffaf4
--- /dev/null
+++ b/assets/js/pets.js
@@ -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
+*/
diff --git a/assets/js/pets.map b/assets/js/pets.map
new file mode 100644
index 0000000000..b6abb49d67
--- /dev/null
+++ b/assets/js/pets.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "pets.js",
+ "sourceRoot": "",
+ "sources": [
+ "pets.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/src/app/profile.coffee b/assets/js/profile.coffee
similarity index 100%
rename from src/app/profile.coffee
rename to assets/js/profile.coffee
diff --git a/assets/js/profile.js b/assets/js/profile.js
new file mode 100644
index 0000000000..168e76aa68
--- /dev/null
+++ b/assets/js/profile.js
@@ -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
+*/
diff --git a/assets/js/profile.map b/assets/js/profile.map
new file mode 100644
index 0000000000..8bc026ac7d
--- /dev/null
+++ b/assets/js/profile.map
@@ -0,0 +1,10 @@
+{
+ "version": 3,
+ "file": "profile.js",
+ "sourceRoot": "",
+ "sources": [
+ "profile.coffee"
+ ],
+ "names": [],
+ "mappings": "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"
+}
\ No newline at end of file
diff --git a/assets/js/services/authServices.js b/assets/js/services/authServices.js
new file mode 100644
index 0000000000..fad0176ecd
--- /dev/null
+++ b/assets/js/services/authServices.js
@@ -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() {}
+ }
+
+ }
+]);
diff --git a/assets/js/services/notificationServices.js b/assets/js/services/notificationServices.js
new file mode 100644
index 0000000000..5d7a47cc6c
--- /dev/null
+++ b/assets/js/services/notificationServices.js
@@ -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 + '
\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 = "- {#if canConnect} - Offline - {#unless :self.hideReconnect} - – Reconnect - {/} - {else} - Unable to reconnect – Reload - {/} -
- {/} -
-