diff --git a/.afignore b/.afignore deleted file mode 100644 index 08499c2b0a..0000000000 --- a/.afignore +++ /dev/null @@ -1,5 +0,0 @@ -.DS_Store -public/gen/ -#lib/ -*.swp -.idea/ diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000000..244429953a --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "assets/bower_components" +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1835a90679..6be095b526 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/styles/404.styl b/assets/css/404.styl similarity index 100% rename from styles/404.styl rename to assets/css/404.styl diff --git a/styles/app/achievements.styl b/assets/css/app/achievements.styl similarity index 100% rename from styles/app/achievements.styl rename to assets/css/app/achievements.styl diff --git a/styles/app/alerts.styl b/assets/css/app/alerts.styl similarity index 100% rename from styles/app/alerts.styl rename to assets/css/app/alerts.styl diff --git a/styles/app/avatar.styl b/assets/css/app/avatar.styl similarity index 100% rename from styles/app/avatar.styl rename to assets/css/app/avatar.styl diff --git a/styles/app/backer.styl b/assets/css/app/backer.styl similarity index 100% rename from styles/app/backer.styl rename to assets/css/app/backer.styl diff --git a/styles/app/challenges.styl b/assets/css/app/challenges.styl similarity index 100% rename from styles/app/challenges.styl rename to assets/css/app/challenges.styl diff --git a/styles/app/color-vars.styl b/assets/css/app/color-vars.styl similarity index 100% rename from styles/app/color-vars.styl rename to assets/css/app/color-vars.styl diff --git a/styles/app/customizer.styl b/assets/css/app/customizer.styl similarity index 100% rename from styles/app/customizer.styl rename to assets/css/app/customizer.styl diff --git a/styles/app/female_sprites.styl b/assets/css/app/female_sprites.styl similarity index 100% rename from styles/app/female_sprites.styl rename to assets/css/app/female_sprites.styl diff --git a/styles/app/filters.styl b/assets/css/app/filters.styl similarity index 100% rename from styles/app/filters.styl rename to assets/css/app/filters.styl diff --git a/styles/app/game-pane.styl b/assets/css/app/game-pane.styl similarity index 100% rename from styles/app/game-pane.styl rename to assets/css/app/game-pane.styl diff --git a/styles/app/header.styl b/assets/css/app/header.styl similarity index 100% rename from styles/app/header.styl rename to assets/css/app/header.styl diff --git a/styles/app/helpers.styl b/assets/css/app/helpers.styl similarity index 100% rename from styles/app/helpers.styl rename to assets/css/app/helpers.styl diff --git a/styles/app/index.styl b/assets/css/app/index.styl similarity index 90% rename from styles/app/index.styl rename to assets/css/app/index.styl index cf5a6f2fb4..96b21b4726 100644 --- a/styles/app/index.styl +++ b/assets/css/app/index.styl @@ -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"; diff --git a/styles/app/inventory.styl b/assets/css/app/inventory.styl similarity index 100% rename from styles/app/inventory.styl rename to assets/css/app/inventory.styl diff --git a/styles/app/items.styl b/assets/css/app/items.styl similarity index 100% rename from styles/app/items.styl rename to assets/css/app/items.styl diff --git a/styles/app/male_sprites.styl b/assets/css/app/male_sprites.styl similarity index 100% rename from styles/app/male_sprites.styl rename to assets/css/app/male_sprites.styl diff --git a/styles/app/npcs.styl b/assets/css/app/npcs.styl similarity index 100% rename from styles/app/npcs.styl rename to assets/css/app/npcs.styl diff --git a/styles/app/responsive.styl b/assets/css/app/responsive.styl similarity index 100% rename from styles/app/responsive.styl rename to assets/css/app/responsive.styl diff --git a/styles/app/scrollbars.styl b/assets/css/app/scrollbars.styl similarity index 100% rename from styles/app/scrollbars.styl rename to assets/css/app/scrollbars.styl diff --git a/styles/app/shop_sprites.styl b/assets/css/app/shop_sprites.styl similarity index 100% rename from styles/app/shop_sprites.styl rename to assets/css/app/shop_sprites.styl diff --git a/styles/app/tasks.styl b/assets/css/app/tasks.styl similarity index 100% rename from styles/app/tasks.styl rename to assets/css/app/tasks.styl diff --git a/styles/base.styl b/assets/css/base.styl similarity index 100% rename from styles/base.styl rename to assets/css/base.styl diff --git a/styles/bootstrap-responsive.styl b/assets/css/bootstrap-responsive.styl similarity index 100% rename from styles/bootstrap-responsive.styl rename to assets/css/bootstrap-responsive.styl diff --git a/styles/reset.styl b/assets/css/reset.styl similarity index 100% rename from styles/reset.styl rename to assets/css/reset.styl diff --git a/styles/ui.styl b/assets/css/ui.styl similarity index 100% rename from styles/ui.styl rename to assets/css/ui.styl diff --git a/assets/js/app.coffee b/assets/js/app.coffee new file mode 100644 index 0000000000..93946625f4 --- /dev/null +++ b/assets/js/app.coffee @@ -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") diff --git a/src/app/browser.coffee b/assets/js/browser.coffee similarity index 93% rename from src/app/browser.coffee rename to assets/js/browser.coffee index bf7e214dfb..eae7619c5b 100644 --- a/src/app/browser.coffee +++ b/assets/js/browser.coffee @@ -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) diff --git a/assets/js/browser.js b/assets/js/browser.js new file mode 100644 index 0000000000..ab3776ecb2 --- /dev/null +++ b/assets/js/browser.js @@ -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(" - " + rounded + " HP", 'hp'); + } else if (num > 0) { + return statsNotification(" + " + 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(" - " + rounded + " XP", 'xp'); + } else if (num > 0) { + return statsNotification(" + " + 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 + " " + silver + " "; + } else if (gold > 0) { + return "" + gold + " "; + } else if (silver > 0) { + return "" + silver + " "; + } + }; + 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(" Respawn!", "death"); + } + }); + return user.on('set', 'stats.lvl', function(captures, args) { + if (captures > args) { + return statsNotification(' 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(''); + }); + } + }; + + 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 + '
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 = '
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); + } + + } + + }); \ No newline at end of file diff --git a/assets/js/services/sharedServices.js b/assets/js/services/sharedServices.js new file mode 100644 index 0000000000..50c3fdb72d --- /dev/null +++ b/assets/js/services/sharedServices.js @@ -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; + }]); \ No newline at end of file diff --git a/assets/js/services/userServices.js b/assets/js/services/userServices.js new file mode 100644 index 0000000000..602cbe69c3 --- /dev/null +++ b/assets/js/services/userServices.js @@ -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; + + } +]); diff --git a/src/app/tasks.coffee b/assets/js/tasks.coffee similarity index 100% rename from src/app/tasks.coffee rename to assets/js/tasks.coffee diff --git a/assets/js/tasks.js b/assets/js/tasks.js new file mode 100644 index 0000000000..d114eded69 --- /dev/null +++ b/assets/js/tasks.js @@ -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 +*/ diff --git a/assets/js/tasks.map b/assets/js/tasks.map new file mode 100644 index 0000000000..2190d51830 --- /dev/null +++ b/assets/js/tasks.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "tasks.js", + "sourceRoot": "", + "sources": [ + "tasks.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/src/app/unlock.coffee b/assets/js/unlock.coffee similarity index 100% rename from src/app/unlock.coffee rename to assets/js/unlock.coffee diff --git a/assets/js/unlock.js b/assets/js/unlock.js new file mode 100644 index 0000000000..8e81cfe41e --- /dev/null +++ b/assets/js/unlock.js @@ -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 += " [Close]"; + 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 = "\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 = "\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 +*/ diff --git a/assets/js/unlock.map b/assets/js/unlock.map new file mode 100644 index 0000000000..f953012de2 --- /dev/null +++ b/assets/js/unlock.map @@ -0,0 +1,10 @@ +{ + "version": 3, + "file": "unlock.js", + "sourceRoot": "", + "sources": [ + "unlock.coffee" + ], + "names": [], + "mappings": "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" +} \ No newline at end of file diff --git a/bower.json b/bower.json new file mode 100644 index 0000000000..e5b79a0590 --- /dev/null +++ b/bower.json @@ -0,0 +1,27 @@ +{ + "name": "HabitRPG", + "version": "0.1.1", + "homepage": "https://github.com/lefnire/habitrpg", + "authors": [ + "Tyler Renelle " + ], + "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" + } +} diff --git a/package.json b/package.json index 1a72ddcb56..f7bd0ba550 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/server.js b/server.js deleted file mode 100644 index ddfc8bd6b7..0000000000 --- a/server.js +++ /dev/null @@ -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 ", - 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) diff --git a/src/config.coffee b/src/config.coffee new file mode 100644 index 0000000000..220c5b2c87 --- /dev/null +++ b/src/config.coffee @@ -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" \ No newline at end of file diff --git a/src/server/api.coffee b/src/controllers/api.coffee similarity index 99% rename from src/server/api.coffee rename to src/controllers/api.coffee index b7ffeeca86..ce98cc4e65 100644 --- a/src/server/api.coffee +++ b/src/controllers/api.coffee @@ -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 diff --git a/src/server/deprecated.coffee b/src/controllers/deprecated.coffee similarity index 100% rename from src/server/deprecated.coffee rename to src/controllers/deprecated.coffee diff --git a/src/server/private.coffee b/src/controllers/private.coffee similarity index 100% rename from src/server/private.coffee rename to src/controllers/private.coffee diff --git a/src/server/static.coffee b/src/controllers/static.coffee similarity index 100% rename from src/server/static.coffee rename to src/controllers/static.coffee diff --git a/src/errors.coffee b/src/errors.coffee new file mode 100644 index 0000000000..f39cb6ebd6 --- /dev/null +++ b/src/errors.coffee @@ -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 " + to: "tylerrenelle@gmail.com" + subject: "HabitRPG Error" + text: error.stack + + console.log error.stack \ No newline at end of file diff --git a/src/server/middleware.coffee b/src/middleware.coffee similarity index 88% rename from src/server/middleware.coffee rename to src/middleware.coffee index ab8ae9902f..fa2ca2aef5 100644 --- a/src/server/middleware.coffee +++ b/src/middleware.coffee @@ -32,8 +32,4 @@ translate = (req, res, next) -> next() -apiv1Middleware = (req, res, next) -> - req.habit ?= {} - next() - -module.exports = { splash, view, allowCrossDomain, translate, apiv1Middleware} \ No newline at end of file +module.exports = { splash, view, allowCrossDomain, translate} \ No newline at end of file diff --git a/src/server/models/user.coffee b/src/models/user.coffee similarity index 100% rename from src/server/models/user.coffee rename to src/models/user.coffee diff --git a/src/server/routes.coffee b/src/routes.coffee similarity index 98% rename from src/server/routes.coffee rename to src/routes.coffee index 16c32d0e77..1f27990a76 100644 --- a/src/server/routes.coffee +++ b/src/routes.coffee @@ -1,6 +1,6 @@ express = require 'express' router = new express.Router() -api = require './api' +api = require './controllers/api' ### ---------- /api/v1 API ------------ diff --git a/src/server.coffee b/src/server.coffee new file mode 100644 index 0000000000..65c5ac4042 --- /dev/null +++ b/src/server.coffee @@ -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}" diff --git a/src/server/index.coffee b/src/server/index.coffee deleted file mode 100644 index 388aade44e..0000000000 --- a/src/server/index.coffee +++ /dev/null @@ -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}" diff --git a/src/server/serverError.coffee b/src/server/serverError.coffee deleted file mode 100644 index ba5c90b63b..0000000000 --- a/src/server/serverError.coffee +++ /dev/null @@ -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 \ No newline at end of file diff --git a/src/server/store.coffee b/src/server/store.coffee deleted file mode 100644 index 4c81c2775c..0000000000 --- a/src/server/store.coffee +++ /dev/null @@ -1,151 +0,0 @@ -derbyAuth = require('derby-auth/store') - -### -Setup read / write access -@param store -### - -publicAccess = -> - accept = arguments[arguments.length-2] - #return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - accept(true) - -module.exports.customAccessControl = (store) -> - userAccess(store) - groupSystem(store) - REST(store) - -### - General user access -### -userAccess = (store) -> - - store.readPathAccess "users.*", -> # captures, accept, err -> - accept = arguments[arguments.length-2] - err = arguments[arguments.length - 1] -# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - - accept = arguments[arguments.length - 2] - uid = arguments[0] - accept (uid is @session.userId) or derbyAuth.isServer(@) - - store.writeAccess "*", "users.*", -> # captures, value, accept, err -> - accept = arguments[arguments.length-2] - err = arguments[arguments.length - 1] - # return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - - return accept(true) if derbyAuth.isServer(@) - - return accept(false) if derbyAuth.bustedSession(@) - - captures = arguments[0].split('.') - uid = captures.shift() - attrPath = captures.join('.') # new array shifted left, after shift() was run - - if attrPath is 'backer' - return accept(false) # we can only manually set this stuff in the database - - # public access to users.*.party.invitation (TODO, lock down a bit more) - if attrPath.indexOf('invitations.') is 0 - return accept(true) - - # Same session (user.id = this.session.userId) - return accept(true) if uid is @session.userId - - accept(false) - - store.writeAccess "*", "users.*.balance", (id, newBalance, accept, err) -> -# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - - oldBalance = @session.req?._racerModel?.get("users.#{id}.balance") || 0 - purchasingSomethingOnClient = newBalance < oldBalance - accept(purchasingSomethingOnClient or derbyAuth.isServer(@)) - - store.writeAccess "*", "users.*.flags.ads", -> # captures, value, accept, err -> - accept = arguments[arguments.length - 2] - err = arguments[arguments.length - 1] -# return err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@) - return accept(false) if derbyAuth.bustedSession(@) - - accept(derbyAuth.isServer(@)) - - -### - REST - Get user with API token -### -REST = (store) -> - store.query.expose "users", "withIdAndToken", (uid, token) -> - @where("id").equals(uid) - .where('apiToken').equals(token) - .findOne() - - store.queryAccess "users", "withIdAndToken", (uid, token, accept, err) -> - return accept(true) if uid && token - accept(false) # only user has id & token - - -### - Party & Guild Permissions -### -groupSystem = (store) -> - - ### - Public User Info - ### - store.query.expose "users", "publicInfo", (ids) -> - @where("id").within(ids) - .only('stats', - 'items', - 'invitations', - 'profile', - 'achievements', - 'backer', - 'preferences', - 'auth.local.username', - 'auth.facebook.displayName') - store.queryAccess "users", "publicInfo", publicAccess - - ### - Read / Write groups, so they can create new groups - ### - store.readPathAccess "groups.*", publicAccess - store.writeAccess "*", "groups.*", publicAccess - - ### - Public HabitRPG Guild - ### - store.readPathAccess 'groups.habitrpg', publicAccess - store.writeAccess "*", "groups.habitrpg.chat.*", publicAccess - store.writeAccess "*", "groups.habitrpg.challenges.*", publicAccess - - ### - Find group which has member by id - ### - store.query.expose "groups", "withMember", (id, type) -> - q = @where('members').contains([id]).only(['id', 'type', 'name', 'description', 'members' , 'privacy']) - q = q.where('type').equals(type) if type? - store.queryAccess 'groups', 'withMember', publicAccess - - ### - Public Groups Info - ### - store.query.expose "groups", "publicGroups", -> - @where('privacy').equals('public') - .where('type').equals('guild') - .only(['id', 'type', 'name', 'description', 'members' , 'privacy']) - store.queryAccess "groups", "publicGroups", publicAccess - - ### - Fetch group info (ie, they just got invited) - ### - store.query.expose "groups", "withIds", (ids) -> - return unless ids #FIXME this is sometimes null when ids is array (guilds) - if typeof ids is 'string' - @where("id").equals(ids).findOne() # find a single group - else - @where("id").within(ids) # find multiple groups - store.queryAccess "groups", "withIds", publicAccess \ No newline at end of file diff --git a/ui/connectionAlert/index.html b/ui/connectionAlert/index.html deleted file mode 100644 index 21df475b49..0000000000 --- a/ui/connectionAlert/index.html +++ /dev/null @@ -1,15 +0,0 @@ - -
- {#unless connected} -

- {#if canConnect} - Offline - {#unless :self.hideReconnect} - – Reconnect - {/} - {else} - Unable to reconnect – Reload - {/} -

- {/} -
diff --git a/ui/connectionAlert/index.js b/ui/connectionAlert/index.js deleted file mode 100644 index 01808549ba..0000000000 --- a/ui/connectionAlert/index.js +++ /dev/null @@ -1,15 +0,0 @@ -exports.create = function(model) { - - this.connect = function() { - // Hide the reconnect link for a second after clicking it - model.set('hideReconnect', true) - setTimeout(function() { - model.set('hideReconnect', false) - }, 1000) - model.socket.socket.connect() - } - - this.reload = function() { - window.location.reload() - } -} diff --git a/ui/index.js b/ui/index.js deleted file mode 100644 index 229e0ad12e..0000000000 --- a/ui/index.js +++ /dev/null @@ -1,14 +0,0 @@ -var config = { - filename: __filename -, styles: '../styles/ui' -, scripts: { - connectionAlert: require('./connectionAlert') - } -}; - -module.exports = ui -ui.decorate = 'derby' - -function ui(derby, options) { - derby.createLibrary(config, options) -} diff --git a/views/app/header.html b/views/app/header.html deleted file mode 100644 index ae80e5fe5b..0000000000 --- a/views/app/header.html +++ /dev/null @@ -1,43 +0,0 @@ - - diff --git a/views/app/index.html b/views/app/index.html deleted file mode 100644 index dd60c0896d..0000000000 --- a/views/app/index.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - HabitRPG | Gamify Your Life - - - - - - - - - - - -
- - {#if _undo}Undo{/} - - - -
- - - -
-
-
- - {#if _user.preferences.hideHeader}{/} -
- -
-
- -
-
- -
-
-
- diff --git a/views/index.jade b/views/index.jade new file mode 100644 index 0000000000..cae7fbd88c --- /dev/null +++ b/views/index.jade @@ -0,0 +1,28 @@ +extends layout + +block content + div(ng-app="habitrpg", ng-controller="RootCtrl") + // -------- Header -------- + ui:connectionAlert + include ./shared/login + .header-wrap + a.label.undo-button(x-bind='click:undo', ng-show='_undo') Undo + | app:settings:menu + include ./shared/header + span(ng-class='{hidden: _gamePane}') + | app:filters:filters + + br + #notification-area + #wrap + app:alerts:flash + //if they hide the header, we still need user-menu visible + app:settings:menu(ng-show='_user.preferences.hideHeader') + .exp-chart(ng-class='{hidden: _page.charts.exp}') + #main.grid + div(ng-class='{hidden: _gamePane}') + app:tasks:task-lists(habits='{{_habitList}}', dailys='{{_dailyList}}', todos='{{_todoList}}', rewards='{{_rewardList}}', main='true', editable='true') + div(ng-class='{hidden: _gamePane}') + app:game-pane:main + app:footer:footer + diff --git a/views/layout.jade b/views/layout.jade new file mode 100644 index 0000000000..00dfb10bdc --- /dev/null +++ b/views/layout.jade @@ -0,0 +1,55 @@ +doctype 5 +html + head + title HabitRPG | Your Life The Role Playing Game + + // CSS + != css('app/index') + != css('/bower_components/bootstrap/docs/assets/css/bootstrap') + != css('/bower_components/bootstrap/docs/assets/css/bootstrap-responsive') + != css('/bower_components/angular-ui/build/angular-ui') + != css('app/index') + + // JS + != js('/bower_components/jquery/jquery.min') + != js('/bower_components/lodash/lodash') + != js('/bower_components/moment/moment') + != js('/bower_components/angular/angular') + != js('/bower_components/angular-resource/angular-resource') + != js('/bower_components/angular-ui/build/angular-ui') + != js('/bower_components/angular-bootstrap/ui-bootstrap') + != js('/bower_components/angular-bootstrap/ui-bootstrap-tpls') + + // Bootstrap + + // HabitRPG + != js('/bower_components/habitrpg-shared/browser/browser.debug') + + != js('app') + + != js('services/authServices') + != js('services/notificationServices') + != js('services/sharedServices') + != js('services/userServices') + + != js('filters/filters') + + != js('directives/directives') + + != js('controllers/authCtrl') + != js('controllers/characterCtrl') + != js('controllers/menuCtrl') + != js('controllers/notificationCtrl') + != js('controllers/RootCtrl') + != js('controllers/settingsCtrl') + != js('controllers/statsCtrl') + != js('controllers/tasksCtrl') + != js('controllers/userAvatarCtrl') + + //webfonts + link(href='//fonts.googleapis.com/css?family=Lato:300,400,700,400italic,700italic', rel='stylesheet', type='text/css') + meta(name='viewport', content='width=device-width') + meta(name='apple-mobile-web-app-capable', content='yes') + + body + block content \ No newline at end of file diff --git a/views/static/about.html b/views/marketing/about.html similarity index 100% rename from views/static/about.html rename to views/marketing/about.html diff --git a/views/static/extensions.html b/views/marketing/extensions.html similarity index 100% rename from views/static/extensions.html rename to views/marketing/extensions.html diff --git a/views/static/front.html b/views/marketing/front.html similarity index 100% rename from views/static/front.html rename to views/marketing/front.html diff --git a/views/static/head.html b/views/marketing/head.html similarity index 100% rename from views/static/head.html rename to views/marketing/head.html diff --git a/views/static/header.html b/views/marketing/header.html similarity index 100% rename from views/static/header.html rename to views/marketing/header.html diff --git a/views/static/privacy.html b/views/marketing/privacy.html similarity index 100% rename from views/static/privacy.html rename to views/marketing/privacy.html diff --git a/views/static/team.html b/views/marketing/team.html similarity index 100% rename from views/static/team.html rename to views/marketing/team.html diff --git a/views/static/terms.html b/views/marketing/terms.html similarity index 100% rename from views/static/terms.html rename to views/marketing/terms.html diff --git a/views/app/challenges.html b/views/options/challenges.html similarity index 100% rename from views/app/challenges.html rename to views/options/challenges.html diff --git a/views/app/game-pane.html b/views/options/game-pane.html similarity index 100% rename from views/app/game-pane.html rename to views/options/game-pane.html diff --git a/views/app/groups.html b/views/options/groups.html similarity index 100% rename from views/app/groups.html rename to views/options/groups.html diff --git a/views/app/pets.html b/views/options/pets.html similarity index 100% rename from views/app/pets.html rename to views/options/pets.html diff --git a/views/app/settings.html b/views/options/settings.html similarity index 91% rename from views/app/settings.html rename to views/options/settings.html index 094f83ecf9..561181bf65 100644 --- a/views/app/settings.html +++ b/views/options/settings.html @@ -111,22 +111,6 @@ - - {{else}} - - Login / Register With Facebook -

Or

- - - -
-
-
-
-
{{/}} diff --git a/views/app/alerts.html b/views/shared/alerts.html similarity index 100% rename from views/app/alerts.html rename to views/shared/alerts.html diff --git a/views/app/footer.html b/views/shared/footer.html similarity index 100% rename from views/app/footer.html rename to views/shared/footer.html diff --git a/views/shared/header.jade b/views/shared/header.jade new file mode 100644 index 0000000000..90764492f7 --- /dev/null +++ b/views/shared/header.jade @@ -0,0 +1,30 @@ +header.site-header(ng-class='{hidden: user.preferences.hideHeader}', role='banner', data-partysize='{{party.members.length>1 ? truarr(party.members.length) : 0}}') + {{user.auth.local.username}} + // avatar + .herobox-wrap.main-herobox + app:avatar:avatar(profile='{{user}}', main='true') + // stat bars + .hero-stats + .meter.health(title='Health') + .bar(style='width: {{percent(user.stats.hp, 50)}}%;') + span.meter-text + i.icon-heart {{user.stats.hp | number:0}} / 50 + .meter.experience(title='Experience') + .bar(style='width: {{percent(user.stats.exp,tnl(user.stats.lvl))}}%;') + span.meter-text + i.icon-star {{user.stats.exp | number:0}} / {{tnl(user.stats.lvl)}} + // FIXME doesn't look great here, but the "Experience" CSS title rollover covers it where it was before + span(ng-show='user.history.exp') + a(x-bind='click:toggleChart', data-id='exp', rel='tooltip', title='Progress') + i.icon-signal + // party + // I have an idea to use this loop for the user's herobox/avatar as well + NOTE TO SELF: Ask Tyler if some kind of inter-leaving is possible + where we can put ONE of the results of this loop BEFORE the progress bars, and the rest after + .herobox-wrap(ng-repeat='memberId in _party.members', ng-class='{hidden: memberId == userId}') + app:avatar:avatar(ng-hide='memberId == userId', profile='{{_members[memberId]}}', party='true') + // Would be way cleaner as a Derby template `data-content=""`, but it was just printing HTML as text + // + I've re-implemented the rollover using the actual `herobox`/avatar template and data-attributes. + This Derby template idea would have been really handy but this is pretty versatile, and keeps it all in the avatar section + app:alerts:hiding-bailey diff --git a/views/shared/login.jade b/views/shared/login.jade new file mode 100644 index 0000000000..e1789e4f85 --- /dev/null +++ b/views/shared/login.jade @@ -0,0 +1,30 @@ +div(ng-controller='AuthCtrl') + button.btn(ng-click='showing = true') Login / Register + div(modal='showing', close='') + .modal-header + h3 Login / Register + .modal-body + //a(href='/auth/facebook') + img(src='/img/facebook-login-register.jpeg', alt='Login / Register With Facebook') + //h3 Or + tabset + tab(heading='Login') + form(ng-submit='auth()') + .control-group + input(type='text', ng-model='loginUsername', placeholder='{{useUUID ? \'UUID\' : \'Username\'}}') + .control-group + input(type='{{useUUID ? \'text\' : \'password\'}}', ng-model='loginPassword', placeholder='{{useUUID ? \'API Token\' : \'Password\'}}') + .control-group + label.checkbox + input(type='checkbox', ng-click='useUUID = !useUUID') + | Use UUID / API Token (For Facebook Users) + .control-group + input.btn.btn-primary(type='submit', value='Login') + + tab(heading='Register') + | derby-auth:register + + .modal-footer + button.btn.btn-warning.cancel(ng-click='showing = false') Cancel + + diff --git a/views/app/modals.html b/views/shared/modals.html similarity index 100% rename from views/app/modals.html rename to views/shared/modals.html diff --git a/views/app/avatar.html b/views/tasks/avatar.html similarity index 100% rename from views/app/avatar.html rename to views/tasks/avatar.html diff --git a/views/app/filters.html b/views/tasks/filters.html similarity index 100% rename from views/app/filters.html rename to views/tasks/filters.html diff --git a/views/app/rewards.html b/views/tasks/rewards.html similarity index 100% rename from views/app/rewards.html rename to views/tasks/rewards.html diff --git a/views/app/tasks.html b/views/tasks/tasks.html similarity index 100% rename from views/app/tasks.html rename to views/tasks/tasks.html