mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-08-02 04:00:36 +00:00
Merge branch 'develop' into toebu-tags-filters
Conflicts: src/app/index.coffee src/app/tasks.coffee styles/app/index.styl views/app/index.html views/app/tasks.html
This commit is contained in:
commit
f332c5d9a8
70 changed files with 4040 additions and 2488 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -5,3 +5,4 @@ node_modules
|
|||
*.swp
|
||||
.idea*
|
||||
config.json
|
||||
npm-debug.log
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
|
|
@ -19,3 +19,6 @@
|
|||
[submodule "public/vendor/BrowserQuest"]
|
||||
path = public/vendor/BrowserQuest
|
||||
url = https://github.com/mozilla/BrowserQuest.git
|
||||
[submodule "public/vendor/bootstrap-tour"]
|
||||
path = public/vendor/bootstrap-tour
|
||||
url = git://github.com/sorich87/bootstrap-tour.git
|
||||
|
|
|
|||
2
CHANGELOG.md
Normal file
2
CHANGELOG.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
3/3/2013
|
||||
* Add custom day start: https://trello.com/card/custom-day-start/50e5d3684fe3a7266b0036d6/15
|
||||
13
README.md
13
README.md
|
|
@ -1,18 +1,11 @@
|
|||
#[HabitRPG](http://habitrpg.com/)
|
||||
|
||||
[HabitRPG](http://habitrpg.com/) is a habit tracker app which treats your goals like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
||||
HabitRPG is a habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.
|
||||
|
||||
[Read more](https://habitrpg.com/static/about)
|
||||
|
||||

|
||||
|
||||
###[About](https://habitrpg.com/splash.html)
|
||||
###[Running Locally](https://github.com/lefnire/habitrpg/wiki/Running-Locally)
|
||||
###[API](https://github.com/lefnire/habitrpg/wiki/API)
|
||||
|
||||
##Contact
|
||||
###[Bugs](https://github.com/lefnire/habitrpg/issues)
|
||||
###[New Features](https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6)
|
||||
###[Email](mailto:tylerrenelle@gmail.com)
|
||||
|
||||
##License
|
||||
Code is licensed under GNU GPL v3. Content is licensed under CC-BY-SA 3.0.
|
||||
See the LICENSE file for details.
|
||||
|
|
|
|||
39
migrations/20130307_exp_overflow.js
Normal file
39
migrations/20130307_exp_overflow.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_normalize_algo_values.js
|
||||
|
||||
/**
|
||||
* Make sure people aren't overflowing their exp with the new system
|
||||
*/
|
||||
db.users.find().forEach(function(user){
|
||||
function oldTnl(level) {
|
||||
return (Math.pow(level,2)*10)+(level*10)+80
|
||||
}
|
||||
|
||||
function newTnl(level) {
|
||||
var value = 0;
|
||||
if (level >= 100) {
|
||||
value = 0
|
||||
} else {
|
||||
value = Math.round(((Math.pow(level,2)*0.25)+(10 * level) + 139.75)/10)*10; // round to nearest 10
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
var newTnl = newTnl(user.stats.lvl);
|
||||
if (user.stats.exp > newTnl) {
|
||||
var percent = user.stats.exp / oldTnl(user.stats.lvl);
|
||||
percent = (percent>1) ? 1 : percent;
|
||||
user.stats.exp = newTnl * percent;
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set: {'stats.exp': user.stats.exp}},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
47
migrations/20130307_normalize_algo_values.js
Normal file
47
migrations/20130307_normalize_algo_values.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_normalize_algo_values.js
|
||||
|
||||
/**
|
||||
* Users were experiencing a lot of extreme Exp multiplication (https://github.com/lefnire/habitrpg/issues/594).
|
||||
* This sets things straight, and in preparation for another algorithm overhaul
|
||||
*/
|
||||
db.users.find().forEach(function(user){
|
||||
if (user.stats.exp >= 3580) {
|
||||
user.stats.exp = 0;
|
||||
}
|
||||
|
||||
if (user.stats.lvl > 100) {
|
||||
user.stats.lvl = 100;
|
||||
}
|
||||
|
||||
_.each(user.tasks, function(task, key){
|
||||
// remove corrupt tasks
|
||||
if (!task) {
|
||||
delete user.tasks[key];
|
||||
return;
|
||||
}
|
||||
|
||||
// Fix busted values
|
||||
if (task.value > 21.27) {
|
||||
task.value = 21.27;
|
||||
}
|
||||
else if (task.value < -47.27) {
|
||||
task.value = -47.27;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set:
|
||||
{
|
||||
'stats.lvl': user.stats.lvl,
|
||||
'stats.exp': user.stats.exp,
|
||||
'tasks' : user.tasks
|
||||
}
|
||||
},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
28
migrations/20130307_remove_duff_histories.js
Normal file
28
migrations/20130307_remove_duff_histories.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Remove duff histories for dailies
|
||||
*/
|
||||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/20130307_remove_duff_histories.js
|
||||
db.users.find().forEach(function(user){
|
||||
|
||||
|
||||
_.each(user.tasks, function(task, key){
|
||||
if (task.type === "daily") {
|
||||
// remove busted history entries
|
||||
task.history = _.filter(task.history, function(h){return !!h.value})
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
db.users.update(
|
||||
{_id:user._id},
|
||||
{$set:
|
||||
{
|
||||
'tasks' : user.tasks
|
||||
}
|
||||
},
|
||||
{multi:true}
|
||||
);
|
||||
} catch(e) {
|
||||
print(e);
|
||||
}
|
||||
})
|
||||
11
migrations/find_unique_user.js
Normal file
11
migrations/find_unique_user.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
// mongo habitrpg ./node_modules/underscore/underscore.js ./migrations/find_unique_user.js
|
||||
|
||||
/**
|
||||
* There are some rare instances of lost user accounts, due to a corrupt user auth variable (see https://github.com/lefnire/habitrpg/wiki/User-ID)
|
||||
* Past in the text of a unique habit here to find the user, then you can restore their UUID
|
||||
*/
|
||||
|
||||
db.users.find().forEach(function(user){
|
||||
var found = _.findWhere(user.tasks, {text: "Replace Me"})
|
||||
if (found) printjson({id:user._id, auth:user.auth});
|
||||
})
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"version": "0.0.0-150",
|
||||
"main": "./server.js",
|
||||
"dependencies": {
|
||||
"derby": "git://github.com/lefnire/derby#habitrpg",
|
||||
"derby": "git://github.com/Unroll-Me/derby#master",
|
||||
"racer": "git://github.com/lefnire/racer#habitrpg",
|
||||
"racer-db-mongo": "git://github.com/lefnire/racer-db-mongo#habitrpg",
|
||||
"derby-ui-boot": "git://github.com/codeparty/derby-ui-boot#master",
|
||||
|
|
@ -23,9 +23,10 @@
|
|||
"mongoskin": "*",
|
||||
"nconf": "*",
|
||||
"icalendar": "git://github.com/lefnire/node-icalendar#master",
|
||||
"superagent": "~0.12.4",
|
||||
"resolve": "~0.2.3",
|
||||
"browserify": "1.17.3",
|
||||
"webkit-devtools-agent": "*"
|
||||
"expect.js": "~0.2.0"
|
||||
},
|
||||
"private": true,
|
||||
"subdomain": "habitrpg",
|
||||
|
|
@ -38,6 +39,7 @@
|
|||
"npm": "1.1.x"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "server.js"
|
||||
"start": "server.js",
|
||||
"test": "mocha test/api.mocha.coffee"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>HabitRPG | Gamify Your Life</title>
|
||||
<title>HabitRPG | Error</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Le styles -->
|
||||
<link href="/vendor/bootstrap/docs/assets/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="/vendor/bootstrap/docs/assets/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<!-- CDN -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||
|
||||
<link href="/vendor/bootstrap/docs/assets/css/docs.css" rel="stylesheet">
|
||||
<link href="/css/static-pages.css" rel="stylesheet">
|
||||
|
||||
<style type="text/css">
|
||||
.jumbotron {
|
||||
background-color: #1b1b1b;
|
||||
background-image: -moz-linear-gradient(top, #222222, #111111);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
|
||||
background-image: -webkit-linear-gradient(top, #222222, #111111);
|
||||
background-image: -o-linear-gradient(top, #222222, #111111);
|
||||
background-image: linear-gradient(to bottom, #222222, #111111);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #252525;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
|
||||
|
||||
}
|
||||
.rotate-img {
|
||||
transform:rotate(90deg);
|
||||
-ms-transform:rotate(90deg); /* IE 9 */
|
||||
|
|
@ -37,7 +27,7 @@
|
|||
<div class='marketing'>
|
||||
<img class='rotate-img' src="/img/sprites/armor3_m.png" />
|
||||
<h2>The server is experiencing issues.</h2>
|
||||
<p><a href="/">Try again</a> in a few, the developer has been notified. The most likely culprit is <a href="https://github.com/lefnire/habitrpg/issues/165">this issue</a> which Tyler is working frantically to fix. (Any memory leak experts?)</p>
|
||||
<p><a href="/">Try again</a> in a few, the developer has been notified. The most likely culprit is <a href="https://github.com/lefnire/habitrpg/issues/165">this issue</a> which Tyler is working to fix. (Any memory leak experts?)</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
|||
76
public/css/footer.css
Normal file
76
public/css/footer.css
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
|
||||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
/* The html and body elements cannot have any padding or margin. */
|
||||
}
|
||||
|
||||
/* Wrapper for page content to push down footer */
|
||||
#wrap {
|
||||
min-height: 100%;
|
||||
height: auto !important;
|
||||
height: 100%;
|
||||
/* Negative indent footer by it's height */
|
||||
margin: 0 auto -60px;
|
||||
}
|
||||
|
||||
/* Set the fixed height of the footer here */
|
||||
#push,
|
||||
#footer {
|
||||
height: 60px;
|
||||
}
|
||||
#footer {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
/* Lastly, apply responsive CSS fixes as necessary */
|
||||
@media (max-width: 767px) {
|
||||
#footer {
|
||||
margin-left: -20px;
|
||||
margin-right: -20px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Custom page CSS
|
||||
-------------------------------------------------- */
|
||||
/* Not required for template or sticky footer method. */
|
||||
|
||||
#wrap > .container {
|
||||
padding-top: 60px;
|
||||
}
|
||||
.container .credit {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* HabitRPG Custom CSS
|
||||
-------------------------------------------------- */
|
||||
|
||||
.footer {
|
||||
/*padding: 70px 0;*/
|
||||
border-top: 1px solid #e5e5e5;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.footer li {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.footer h4 {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.footer td {
|
||||
align: left !important;
|
||||
}
|
||||
17
public/css/static-pages.css
Normal file
17
public/css/static-pages.css
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.jumbotron {
|
||||
background-color: #1b1b1b;
|
||||
background-image: -moz-linear-gradient(top, #222222, #111111);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
|
||||
background-image: -webkit-linear-gradient(top, #222222, #111111);
|
||||
background-image: -o-linear-gradient(top, #222222, #111111);
|
||||
background-image: linear-gradient(to bottom, #222222, #111111);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #252525;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
|
||||
background:none;
|
||||
color:black;
|
||||
|
||||
}
|
||||
body {
|
||||
padding-top:0px;
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 789 B |
BIN
public/img/sprites/Egg_Sprite_Sheet.png
Normal file
BIN
public/img/sprites/Egg_Sprite_Sheet.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.3 KiB |
17
public/img/sprites/PetEggs.css
Normal file
17
public/img/sprites/PetEggs.css
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#PetEggs {
|
||||
/* egg sprite size */
|
||||
width: 48px;
|
||||
height: 51px;
|
||||
/* link to sprite sheet image */
|
||||
background-image: url(Egg_Sprite_Sheet.png);
|
||||
}
|
||||
/* defines where in the sheet the eggs are */
|
||||
.WolfEgg{background-position: 0px 0px;}
|
||||
.TigerEgg{background-position: 0px -51px;}
|
||||
.PolarBearEgg{background-position: 0px -102px;}
|
||||
.PandaEgg{background-position: 0px -153px;}
|
||||
.LionEgg{background-position: 0px -204px;}
|
||||
.FlyingPigEgg{background-position: 0px -255px;}
|
||||
.DrakeEgg{background-position: 0px -306px;}
|
||||
.CactusEgg{background-position: 0px -357px;}
|
||||
.BearEgg{background-position: 0px -408px;}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>HabitRPG | Gamify Your Life</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- Le styles -->
|
||||
<link href="/vendor/bootstrap/docs/assets/css/bootstrap.css" rel="stylesheet">
|
||||
<link href="/vendor/bootstrap/docs/assets/css/bootstrap-responsive.css" rel="stylesheet">
|
||||
<link href="/vendor/bootstrap/docs/assets/css/docs.css" rel="stylesheet">
|
||||
|
||||
<script type="text/javascript" src="vendor/jquery-ui/jquery-1.9.1.js"></script>
|
||||
<script type="text/javascript">
|
||||
$.getScript("https://s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire");
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
.jumbotron {
|
||||
background-color: #1b1b1b;
|
||||
background-image: -moz-linear-gradient(top, #222222, #111111);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#222222), to(#111111));
|
||||
background-image: -webkit-linear-gradient(top, #222222, #111111);
|
||||
background-image: -o-linear-gradient(top, #222222, #111111);
|
||||
background-image: linear-gradient(to bottom, #222222, #111111);
|
||||
background-repeat: repeat-x;
|
||||
border-color: #252525;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff111111', GradientType=0);
|
||||
background:none;
|
||||
color:black;
|
||||
|
||||
}
|
||||
body {
|
||||
padding-top:0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class='jumbotron masthead'>
|
||||
<div class='container'>
|
||||
<h1><img src="/img/logo/habitrpg_pixel.png" alt="HabitRPG"/></h1>
|
||||
<p>Habit tracking which treats your goals like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.</p>
|
||||
<a href="/?play=1" class='btn btn-primary btn-small'>Play</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bs-docs-social">
|
||||
<div class="container">
|
||||
<ul class="bs-docs-social-buttons">
|
||||
<li>
|
||||
<table>
|
||||
<tr>
|
||||
<td><!-- Github -->
|
||||
<iframe src="/vendor/github-buttons/github-btn.html?user=lefnire&repo=habitrpg&type=watch&count=true"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="85px" height="20px"></iframe>
|
||||
</td>
|
||||
<td>
|
||||
<div class="addthis_toolbox addthis_default_style "
|
||||
addthis:url="https://habitrpg.com"
|
||||
addthis:title="HabitRPG: Role Playing Game for Self Improvement">
|
||||
<a class="addthis_button_facebook_like" fb:like:layout="button_count"></a>
|
||||
<a class="addthis_button_tweet" tw:via="habitrpg"></a>
|
||||
</div>
|
||||
</td>
|
||||
<td><a href="http://habitrpg.tumblr.com/">Blog</a> - </td>
|
||||
<td><a href="https://github.com/lefnire/habitrpg/wiki/FAQ">FAQ</a> - </td>
|
||||
<td><a href="https://github.com/lefnire/habitrpg/wiki/API">API</a> - </td>
|
||||
<td><a href="https://github.com/lefnire/habitrpg#contact">Contact</a></td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class='container'>
|
||||
<div class='marketing'>
|
||||
<p> </p>
|
||||
<p><iframe src="http://player.vimeo.com/video/57639356" width="960" height="539" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></p>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>Habits</h1>
|
||||
</div>
|
||||
<p class="lead">Habits are goals that you constantly track. For some habits, it only makes sense to gain points (eg, "1h Productive Work"). For others, it only makes sense to lose points (like "Eat Junk Food"). For the rest, both gain and loss apply (eg, for "Take The Stairs", stairs is a gain, elevator is a loss).</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>Dailies</h1>
|
||||
</div>
|
||||
<p class="lead">Dailies are goals that you want to complete once a day. At the end of each day, non-completed Dailies dock you points. If you are doing well, they turn green and are less valuable (experience, gold) and less damaging (HP). This means you can ease up on them for a bit. But if you are doing poorly, they turn red. The worse you do, the more valuable (exp, gold) and more damaging (HP) these goals become. This encourages you to focus on your shortcomings, the reds.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>Todos</h1>
|
||||
</div>
|
||||
<p class="lead">Todos are one-off goals which need to be completed eventually. Non-completed Todos won’t hurt you, but they will become more valuable over time. This will encourage you to wrap up stale Todos.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>Rewards</h1>
|
||||
</div>
|
||||
<p class='lead'>As you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits. But only buy if you have enough gold - you lose HP otherwise.</p>
|
||||
<p class='lead'>After you’ve played for a while, you unlock the <strong>Item Store</strong> under the rewards column. You can now buy weapons, armor, potions, etc. Armor decreases HP loss (by an increasing amount wich each upgrade). Weapons increase experience gain. Potions recover 15 HP.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>License & Credits</h1>
|
||||
</div>
|
||||
<p class='lead'>Code is licensed under GNU GPL v3. Content is licensed under CC-BY-SA 3.0. See the LICENSE file for details.</p>
|
||||
|
||||
<p class='lead'>Content and assets comes from Mozilla’s <a href="http://browserquest.mozilla.org/">BrowserQuest</a> (<a href="http://mozilla.org">Mozilla</a>, <a href="http://www.littleworkshop.fr">Little Workshop</a>). They seriously rock.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
2
public/vendor/bootstrap
vendored
2
public/vendor/bootstrap
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 8c7f9c66a7d12f47f50618ef420868fe836d0c33
|
||||
Subproject commit eb24718add4dd36fe92fdbdb79e6ff4ce5919300
|
||||
1
public/vendor/bootstrap-tour
vendored
Submodule
1
public/vendor/bootstrap-tour
vendored
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 4f94fa056c88c6099dea135b644d8f95d38ac9e1
|
||||
271
public/vendor/bootstrap-tour.js
vendored
271
public/vendor/bootstrap-tour.js
vendored
|
|
@ -1,271 +0,0 @@
|
|||
|
||||
/* ============================================================
|
||||
# bootstrap-tour.js v0.1
|
||||
# http://pushly.github.com/bootstrap-tour/
|
||||
# ==============================================================
|
||||
# Copyright 2012 Push.ly
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
(function($, window) {
|
||||
var Tour, document;
|
||||
document = window.document;
|
||||
Tour = (function() {
|
||||
|
||||
function Tour(options) {
|
||||
var _this = this;
|
||||
this._options = $.extend({
|
||||
name: 'tour',
|
||||
labels: {
|
||||
end: 'End tour',
|
||||
next: 'Next »',
|
||||
prev: '« Prev'
|
||||
},
|
||||
keyboard: true,
|
||||
afterSetState: function(key, value) {},
|
||||
afterGetState: function(key, value) {},
|
||||
onShow: function(tour) {},
|
||||
onHide: function(tour) {},
|
||||
onShown: function(tour) {}
|
||||
}, options);
|
||||
this._steps = [];
|
||||
this.setCurrentStep();
|
||||
this._onresize(function() {
|
||||
if (!_this.ended) return _this.showStep(_this._current);
|
||||
});
|
||||
}
|
||||
|
||||
Tour.prototype.setState = function(key, value) {
|
||||
$.cookie("" + this._options.name + "_" + key, value, {
|
||||
expires: 36500,
|
||||
path: '/'
|
||||
});
|
||||
return this._options.afterSetState(key, value);
|
||||
};
|
||||
|
||||
Tour.prototype.getState = function(key) {
|
||||
var value;
|
||||
value = $.cookie("" + this._options.name + "_" + key);
|
||||
this._options.afterGetState(key, value);
|
||||
return value;
|
||||
};
|
||||
|
||||
Tour.prototype.addStep = function(step) {
|
||||
return this._steps.push(step);
|
||||
};
|
||||
|
||||
Tour.prototype.getStep = function(i) {
|
||||
if (this._steps[i] != null) {
|
||||
return $.extend({
|
||||
path: "",
|
||||
placement: "right",
|
||||
title: "",
|
||||
content: "",
|
||||
next: i === this._steps.length - 1 ? -1 : i + 1,
|
||||
prev: i - 1,
|
||||
animation: true,
|
||||
onShow: this._options.onShow,
|
||||
onHide: this._options.onHide,
|
||||
onShown: this._options.onShown
|
||||
}, this._steps[i]);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype.start = function(force) {
|
||||
var _this = this;
|
||||
if (force == null) force = false;
|
||||
if (this.ended() && !force) return;
|
||||
$(document).off("click.bootstrap-tour", ".popover .next").on("click.bootstrap-tour", ".popover .next", function(e) {
|
||||
e.preventDefault();
|
||||
return _this.next();
|
||||
});
|
||||
$(document).off("click.bootstrap-tour", ".popover .prev").on("click.bootstrap-tour", ".popover .prev", function(e) {
|
||||
e.preventDefault();
|
||||
return _this.prev();
|
||||
});
|
||||
$(document).off("click.bootstrap-tour", ".popover .end").on("click.bootstrap-tour", ".popover .end", function(e) {
|
||||
e.preventDefault();
|
||||
return _this.end();
|
||||
});
|
||||
this._setupKeyboardNavigation();
|
||||
return this.showStep(this._current);
|
||||
};
|
||||
|
||||
Tour.prototype.next = function() {
|
||||
this.hideStep(this._current);
|
||||
return this.showNextStep();
|
||||
};
|
||||
|
||||
Tour.prototype.prev = function() {
|
||||
this.hideStep(this._current);
|
||||
return this.showPrevStep();
|
||||
};
|
||||
|
||||
Tour.prototype.end = function() {
|
||||
this.hideStep(this._current);
|
||||
$(document).off(".bootstrap-tour");
|
||||
return this.setState("end", "yes");
|
||||
};
|
||||
|
||||
Tour.prototype.ended = function() {
|
||||
return !!this.getState("end");
|
||||
};
|
||||
|
||||
Tour.prototype.restart = function() {
|
||||
this.setState("current_step", null);
|
||||
this.setState("end", null);
|
||||
this.setCurrentStep(0);
|
||||
return this.start();
|
||||
};
|
||||
|
||||
Tour.prototype.hideStep = function(i) {
|
||||
var step;
|
||||
step = this.getStep(i);
|
||||
if (step.onHide != null) step.onHide(this);
|
||||
return $(step.element).popover("hide");
|
||||
};
|
||||
|
||||
Tour.prototype.showStep = function(i) {
|
||||
var step;
|
||||
step = this.getStep(i);
|
||||
if (!step) return;
|
||||
this.setCurrentStep(i);
|
||||
if (step.path !== "" && document.location.pathname !== step.path && document.location.pathname.replace(/^.*[\\\/]/, '') !== step.path) {
|
||||
document.location.href = step.path;
|
||||
return;
|
||||
}
|
||||
if (step.onShow != null) step.onShow(this);
|
||||
if (!((step.element != null) && $(step.element).length !== 0 && $(step.element).is(":visible"))) {
|
||||
this.showNextStep();
|
||||
return;
|
||||
}
|
||||
this._showPopover(step, i);
|
||||
if (step.onShown != null) return step.onShown(this);
|
||||
};
|
||||
|
||||
Tour.prototype.setCurrentStep = function(value) {
|
||||
if (value != null) {
|
||||
this._current = value;
|
||||
return this.setState("current_step", value);
|
||||
} else {
|
||||
this._current = this.getState("current_step");
|
||||
if (this._current === null || this._current === "null") {
|
||||
return this._current = 0;
|
||||
} else {
|
||||
return this._current = parseInt(this._current);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype.showNextStep = function() {
|
||||
var step;
|
||||
step = this.getStep(this._current);
|
||||
return this.showStep(step.next);
|
||||
};
|
||||
|
||||
Tour.prototype.showPrevStep = function() {
|
||||
var step;
|
||||
step = this.getStep(this._current);
|
||||
return this.showStep(step.prev);
|
||||
};
|
||||
|
||||
Tour.prototype._showPopover = function(step, i) {
|
||||
var content, nav, options, tip,
|
||||
_this = this;
|
||||
content = "" + step.content + "<br /><p>";
|
||||
options = $.extend({}, this._options);
|
||||
if (step.options) $.extend(options, step.options);
|
||||
if (step.reflex) {
|
||||
$(step.element).css("cursor", "pointer");
|
||||
$(step.element).on("click", function(e) {
|
||||
$(step.element).css("cursor", "auto");
|
||||
return _this.next();
|
||||
});
|
||||
}
|
||||
nav = [];
|
||||
if (step.prev >= 0) {
|
||||
nav.push("<a href='#" + step.prev + "' class='prev'>" + options.labels.prev + "</a>");
|
||||
}
|
||||
if (step.next >= 0) {
|
||||
nav.push("<a href='#" + step.next + "' class='next'>" + options.labels.next + "</a>");
|
||||
}
|
||||
content += nav.join(" | ");
|
||||
content += "<a href='#' class='pull-right end'>" + options.labels.end + "</a>";
|
||||
$(step.element).popover({
|
||||
placement: step.placement,
|
||||
trigger: "manual",
|
||||
title: step.title,
|
||||
content: content,
|
||||
html: true,
|
||||
animation: step.animation
|
||||
}).popover("show");
|
||||
tip = $(step.element).data("popover").tip();
|
||||
this._reposition(tip);
|
||||
return this._scrollIntoView(tip);
|
||||
};
|
||||
|
||||
Tour.prototype._reposition = function(tip) {
|
||||
var offsetBottom, offsetRight, tipOffset;
|
||||
tipOffset = tip.offset();
|
||||
offsetBottom = $(document).outerHeight() - tipOffset.top - $(tip).outerHeight();
|
||||
if (offsetBottom < 0) tipOffset.top = tipOffset.top + offsetBottom;
|
||||
offsetRight = $(document).outerWidth() - tipOffset.left - $(tip).outerWidth();
|
||||
if (offsetRight < 0) tipOffset.left = tipOffset.left + offsetRight;
|
||||
if (tipOffset.top < 0) tipOffset.top = 0;
|
||||
if (tipOffset.left < 0) tipOffset.left = 0;
|
||||
return tip.offset(tipOffset);
|
||||
};
|
||||
|
||||
Tour.prototype._scrollIntoView = function(tip) {
|
||||
var tipRect;
|
||||
tipRect = tip.get(0).getBoundingClientRect();
|
||||
if (!(tipRect.top > 0 && tipRect.bottom < $(window).height() && tipRect.left > 0 && tipRect.right < $(window).width())) {
|
||||
return tip.get(0).scrollIntoView(true);
|
||||
}
|
||||
};
|
||||
|
||||
Tour.prototype._onresize = function(cb, timeout) {
|
||||
return $(window).resize(function() {
|
||||
clearTimeout(timeout);
|
||||
return timeout = setTimeout(cb, 100);
|
||||
});
|
||||
};
|
||||
|
||||
Tour.prototype._setupKeyboardNavigation = function() {
|
||||
var _this = this;
|
||||
if (this._options.keyboard) {
|
||||
return $(document).on("keyup.bootstrap-tour", function(e) {
|
||||
if (!e.which) return;
|
||||
switch (e.which) {
|
||||
case 39:
|
||||
e.preventDefault();
|
||||
if (_this._current < _this._steps.length - 1) return _this.next();
|
||||
break;
|
||||
case 37:
|
||||
e.preventDefault();
|
||||
if (_this._current > 0) return _this.prev();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return Tour;
|
||||
|
||||
})();
|
||||
return window.Tour = Tour;
|
||||
})(jQuery, window);
|
||||
|
||||
}).call(this);
|
||||
|
|
@ -22,7 +22,7 @@ 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;
|
||||
/*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
|
||||
|
|
@ -31,7 +31,7 @@ if (process.env.NODE_ENV === 'development') {
|
|||
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");
|
||||
}
|
||||
}*/
|
||||
|
||||
process.on('uncaughtException', function (error) {
|
||||
|
||||
|
|
|
|||
67
src/app/algos.coffee
Normal file
67
src/app/algos.coffee
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
XP = 15
|
||||
HP = 2
|
||||
|
||||
priorityValue = (priority='!') ->
|
||||
switch priority
|
||||
when '!' then 1
|
||||
when '!!' then 1.5
|
||||
when '!!!' then 2
|
||||
else 1
|
||||
|
||||
module.exports.tnl = (level) ->
|
||||
if level >= 100
|
||||
value = 0
|
||||
else
|
||||
value = Math.round(((Math.pow(level,2)*0.25)+(10 * level) + 139.75)/10)*10 # round to nearest 10
|
||||
return value
|
||||
|
||||
###
|
||||
Calculates Exp modificaiton based on level and weapon strength
|
||||
{value} task.value for exp gain
|
||||
{weaponStrength) weapon strength
|
||||
{level} current user level
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.expModifier = (value, weaponStr, level, priority='!') ->
|
||||
str = (level-1) / 2 # ultimately get this from user
|
||||
totalStr = (str + weaponStr) / 100
|
||||
strMod = 1 + totalStr
|
||||
exp = value * XP * strMod * priorityValue(priority)
|
||||
return Math.round(exp)
|
||||
|
||||
###
|
||||
Calculates HP modification based on level and armor defence
|
||||
{value} task.value for hp loss
|
||||
{armorDefense} defense from armor
|
||||
{helmDefense} defense from helm
|
||||
{level} current user level
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.hpModifier = (value, armorDef, helmDef, shieldDef, level, priority='!') ->
|
||||
def = (level-1) / 2 # ultimately get this from user?
|
||||
totalDef = (def + armorDef + helmDef + shieldDef) / 100 #ultimate get this from user
|
||||
defMod = 1 - totalDef
|
||||
hp = value * HP * defMod * priorityValue(priority)
|
||||
return Math.round(hp * 10)/10 # round to 1dp
|
||||
|
||||
###
|
||||
Future use
|
||||
{priority} user-defined priority multiplier
|
||||
###
|
||||
module.exports.gpModifier = (value, modifier, priority='!') ->
|
||||
return value * modifier * priorityValue(priority)
|
||||
|
||||
###
|
||||
Calculates the next task.value based on direction
|
||||
Uses a capped inverse log y=.95^x, y>= -5
|
||||
{currentValue} the current value of the task
|
||||
{direction} up or down
|
||||
###
|
||||
module.exports.taskDeltaFormula = (currentValue, direction) ->
|
||||
if currentValue < -47.27 then currentValue = -47.27
|
||||
else if currentValue > 21.27 then currentValue = 21.27
|
||||
delta = Math.pow(0.9747,currentValue)
|
||||
return delta if direction is 'up'
|
||||
return -delta
|
||||
|
||||
|
||||
|
|
@ -1,75 +1,36 @@
|
|||
_ = require 'underscore'
|
||||
moment = require 'moment'
|
||||
#algos = require './algos'
|
||||
|
||||
module.exports.restoreRefs = restoreRefs = (model) ->
|
||||
# tnl function
|
||||
model.fn '_tnl', '_user.stats.lvl', (lvl) ->
|
||||
# see https://github.com/lefnire/habitrpg/issues/4
|
||||
# also update in scoring.coffee. TODO create a function accessible in both locations
|
||||
(lvl*100)/5
|
||||
|
||||
restoreRefs = module.exports.restoreRefs = (model) ->
|
||||
#refLists
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
|
||||
module.exports.resetDom = (model) ->
|
||||
window.DERBY.app.dom.clear()
|
||||
restoreRefs(model)
|
||||
window.DERBY.app.view.render(model)
|
||||
reconstructPage model
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
reconstructPage model
|
||||
|
||||
reconstructPage = (model) ->
|
||||
loadJavaScripts(model)
|
||||
setupSortable(model)
|
||||
setupTooltips(model)
|
||||
setupTour(model)
|
||||
setupGrowlNotifications(model) unless model.get('_view.mobileDevice')
|
||||
$('.datepicker').datepicker({autoclose:true, todayBtn:true})
|
||||
.on 'changeDate', (ev) ->
|
||||
#for some reason selecting a date doesn't fire a change event on the field, meaning our changes aren't saved
|
||||
#FIXME also, it saves as a day behind??
|
||||
model.at(ev.target).set 'date', moment(ev.date).add('d',1).format('MM/DD/YYYY')
|
||||
|
||||
###
|
||||
Loads JavaScript files from (1) public/js/* and (2) external sources
|
||||
We use this file (instead of <Scripts:> or <Tail:> inside .html) so we can utilize require() to concatinate for
|
||||
faster page load, and $.getScript for asyncronous external script loading
|
||||
If a library is available in a CDN, we put it in <Scripts:> (index.html) for better caching. If not, we use
|
||||
this function to utilize require() to concatinate for faster page load, and $.getScript for asyncronous external script loading
|
||||
###
|
||||
loadJavaScripts = (model) ->
|
||||
|
||||
require '../../public/vendor/jquery-ui/jquery-1.9.1'
|
||||
unless model.get('_view.mobileDevice')
|
||||
require '../../public/vendor/jquery-ui/ui/jquery.ui.core'
|
||||
require '../../public/vendor/jquery-ui/ui/jquery.ui.widget'
|
||||
require '../../public/vendor/jquery-ui/ui/jquery.ui.mouse'
|
||||
require '../../public/vendor/jquery-ui/ui/jquery.ui.sortable'
|
||||
|
||||
# Bootstrap
|
||||
require '../../public/vendor/bootstrap/docs/assets/js/bootstrap'
|
||||
# require '../../public/vendor/bootstrap/js/bootstrap-tooltip'
|
||||
# require '../../public/vendor/bootstrap/js/bootstrap-tab'
|
||||
# require '../../public/vendor/bootstrap/js/bootstrap-popover'
|
||||
# require '../../public/vendor/bootstrap/js/bootstrap-modal'
|
||||
# require '../../public/vendor/bootstrap/js/bootstrap-dropdown'
|
||||
|
||||
|
||||
require '../../public/vendor/jquery-cookie/jquery.cookie'
|
||||
require '../../public/vendor/bootstrap-tour' #https://raw.github.com/pushly/bootstrap-tour/master/bootstrap-tour.js
|
||||
require '../../public/vendor/bootstrap-datepicker/js/bootstrap-datepicker'
|
||||
require '../../public/vendor/bootstrap-growl/jquery.bootstrap-growl.min'
|
||||
|
||||
require '../../public/vendor/bootstrap-tour/bootstrap-tour'
|
||||
|
||||
# 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
|
||||
unless model.get('_view.mobileDevice')
|
||||
|
||||
$.getScript("https://s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire");
|
||||
$.getScript("//s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire");
|
||||
|
||||
# Google Charts
|
||||
$.getScript "https://www.google.com/jsapi", ->
|
||||
$.getScript "//www.google.com/jsapi", ->
|
||||
# Specifying callback in options param is vital! Otherwise you get blank screen, see http://stackoverflow.com/a/12200566/362790
|
||||
google.load "visualization", "1", {packages:["corechart"], callback: ->}
|
||||
|
||||
|
|
@ -81,15 +42,11 @@ loadJavaScripts = (model) ->
|
|||
###
|
||||
setupSortable = (model) ->
|
||||
unless (model.get('_view.mobileDevice') == true) #don't do sortable on mobile
|
||||
# Make the lists draggable using jQuery UI
|
||||
# Note, have to setup helper function here and call it for each type later
|
||||
# due to variable binding of "type"
|
||||
setupSortable = (type) ->
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) ->
|
||||
$("ul.#{type}s").sortable
|
||||
dropOnEmpty: false
|
||||
cursor: "move"
|
||||
items: "li"
|
||||
opacity: 0.4
|
||||
scroll: true
|
||||
axis: 'y'
|
||||
update: (e, ui) ->
|
||||
|
|
@ -102,16 +59,15 @@ setupSortable = (model) ->
|
|||
# Also, note that refList index arguments can either be an index
|
||||
# or the item's id property
|
||||
model.at("_#{type}List").pass(ignore: domId).move {id}, to
|
||||
_.each ['habit', 'daily', 'todo', 'reward'], (type) -> setupSortable(type)
|
||||
|
||||
setupTooltips = (model) ->
|
||||
$('[rel=tooltip]').tooltip()
|
||||
$('[rel=popover]').popover()
|
||||
# FIXME: this isn't very efficient, do model.on set for specific attrs for popover
|
||||
model.on 'set', '*', ->
|
||||
$('[rel=tooltip]').tooltip()
|
||||
$('[rel=popover]').popover()
|
||||
|
||||
$('.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 = (model) ->
|
||||
tourSteps = [
|
||||
|
|
@ -160,12 +116,8 @@ setupTour = (model) ->
|
|||
$('.main-avatar').popover('destroy') #remove previous popovers
|
||||
tour = new Tour()
|
||||
_.each tourSteps, (step) ->
|
||||
tour.addStep
|
||||
html: true
|
||||
element: step.element
|
||||
title: step.title
|
||||
content: step.content
|
||||
placement: step.placement
|
||||
tour.addStep _.defaults step, {html:true}
|
||||
tour._current = 0 if isNaN(tour._current) #bootstrap-tour bug
|
||||
tour.start()
|
||||
|
||||
###
|
||||
|
|
@ -180,7 +132,7 @@ setupGrowlNotifications = (model) ->
|
|||
return if user.get('stats.lvl') == 0
|
||||
$.bootstrapGrowl html,
|
||||
ele: '#notification-area',
|
||||
type: type # (null, 'info', 'error', 'success', 'gp', 'xp', 'hp', 'lvl')
|
||||
type: type # (null, 'info', 'error', 'success', 'gp', 'xp', 'hp', 'lvl','death')
|
||||
top_offset: 20
|
||||
align: 'right' # ('left', 'right', or 'center')
|
||||
width: 250 # (integer, or 'auto')
|
||||
|
|
@ -193,21 +145,55 @@ setupGrowlNotifications = (model) ->
|
|||
num = captures - args
|
||||
rounded = Math.abs(num.toFixed(1))
|
||||
if num < 0
|
||||
statsNotification "<i class='icon-heart'></i> -#{rounded} HP", 'hp' # lost hp from purchase
|
||||
statsNotification "<i class='icon-heart'></i> - #{rounded} HP", 'hp' # lost hp from purchase
|
||||
else if num > 0
|
||||
statsNotification "<i class='icon-heart'></i> + #{rounded} HP", 'hp' # gained hp from potion/level?
|
||||
|
||||
user.on 'set', 'stats.exp', (captures, args, isLocal, silent=false) ->
|
||||
# unless silent
|
||||
num = captures - args
|
||||
rounded = Math.abs(num.toFixed(1))
|
||||
if num < 0 and num > -50 # TODO fix hackey negative notification supress
|
||||
statsNotification "<i class='icon-star'></i> - #{rounded} XP", 'xp'
|
||||
else if num > 0
|
||||
statsNotification "<i class='icon-star'></i> + #{rounded} XP", 'xp'
|
||||
|
||||
user.on 'set', 'stats.gp', (captures, args) ->
|
||||
num = captures - args
|
||||
rounded = Math.abs(num.toFixed(1))
|
||||
# made purchase
|
||||
if num < 0
|
||||
# FIXME use 'warning' when unchecking an accidently completed daily/todo, and notify of exp too
|
||||
statsNotification "<i class='icon-star'></i> -#{rounded} GP", 'gp'
|
||||
# gained gp (and thereby exp)
|
||||
else if num > 0
|
||||
num = Math.abs(num)
|
||||
statsNotification "<i class='icon-star'></i> +#{rounded} XP", 'xp'
|
||||
statsNotification "<i class='icon-gp'></i> +#{rounded} GP", 'gp'
|
||||
absolute = Math.abs(num)
|
||||
gold = Math.floor(absolute)
|
||||
silver = Math.floor((absolute-gold)*100)
|
||||
sign = if num < 0 then '-' else '+'
|
||||
if gold and silver > 0
|
||||
statsNotification "#{sign} #{gold} <i class='icon-gold'></i> #{silver} <i class='icon-silver'></i>", 'gp'
|
||||
else if gold > 0
|
||||
statsNotification "#{sign} #{gold} <i class='icon-gold'></i>", 'gp'
|
||||
else if silver > 0
|
||||
statsNotification "#{sign} #{silver} <i class='icon-silver'></i>", 'gp'
|
||||
|
||||
user.on 'set', 'stats.lvl', (captures, args) ->
|
||||
if captures > args
|
||||
statsNotification('<i class="icon-chevron-up"></i> Level Up!', 'lvl')
|
||||
if captures is 1 and args is 0
|
||||
statsNotification '<i class="icon-death"></i> You died! Game over.', 'death'
|
||||
else
|
||||
statsNotification '<i class="icon-chevron-up"></i> Level Up!', 'lvl'
|
||||
|
||||
|
||||
module.exports.resetDom = (model) ->
|
||||
DERBY.app.dom.clear()
|
||||
DERBY.app.view.render(model, DERBY.app.view._lastRender.ns, DERBY.app.view._lastRender.context);
|
||||
|
||||
module.exports.app = (appExports, model, app) ->
|
||||
loadJavaScripts(model)
|
||||
setupGrowlNotifications(model) unless model.get('_view.mobileDevice')
|
||||
|
||||
app.on 'render', (ctx) ->
|
||||
#restoreRefs(model)
|
||||
setupSortable(model)
|
||||
setupTooltips(model)
|
||||
setupTour(model)
|
||||
$('.datepicker').datepicker({autoclose:true, todayBtn:true})
|
||||
.on 'changeDate', (ev) ->
|
||||
#for some reason selecting a date doesn't fire a change event on the field, meaning our changes aren't saved
|
||||
#FIXME also, it saves as a day behind??
|
||||
model.at(ev.target).set 'date', moment(ev.date).add('d',1).format('MM/DD/YYYY')
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
character = require './character'
|
||||
browser = require './browser'
|
||||
items = require './items'
|
||||
algos = require './algos'
|
||||
|
||||
moment = require 'moment'
|
||||
_ = require 'underscore'
|
||||
|
|
@ -21,6 +22,9 @@ module.exports.username = username = (auth) ->
|
|||
module.exports.view = (view) ->
|
||||
view.fn "username", (auth) -> username(auth)
|
||||
|
||||
view.fn "tnl", algos.tnl
|
||||
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at '_user'
|
||||
|
||||
|
|
@ -57,8 +61,14 @@ module.exports.app = (appExports, model) ->
|
|||
batch.commit()
|
||||
browser.resetDom(model)
|
||||
|
||||
appExports.closeCelebrationNofitication = (e, el) ->
|
||||
user.set('flags.celebrationEvent', 'hide')
|
||||
appExports.closeAlgosNotification = (e, el) ->
|
||||
user.set('flags.algosNotification', 'hide')
|
||||
|
||||
appExports.closeOnliesNotification = (e, el) ->
|
||||
user.set('flags.onliesNotification', 'hide')
|
||||
|
||||
appExports.closePriorityNotification = (e, el) ->
|
||||
user.set('flags.priorityNotification', 'hide')
|
||||
|
||||
appExports.customizeGender = (e, el) ->
|
||||
user.set 'preferences.gender', $(el).attr('data-value')
|
||||
|
|
@ -76,9 +86,16 @@ module.exports.app = (appExports, model) ->
|
|||
batch = new BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
$('#restore-form input').each ->
|
||||
batch.set $(this).attr('data-for'), parseInt($(this).val())
|
||||
batch.set $(this).attr('data-for'), parseInt($(this).val() || 1)
|
||||
batch.commit()
|
||||
|
||||
appExports.toggleHeader = (e, el) ->
|
||||
user.set 'preferences.hideHeader', !user.get('preferences.hideHeader')
|
||||
|
||||
appExports.deleteAccount = (e, el) ->
|
||||
model.del "users.#{user.get('id')}", ->
|
||||
window.location.href = "/logout"
|
||||
|
||||
user.on 'set', 'flags.customizationsNotification', (captures, args) ->
|
||||
return unless captures == true
|
||||
$('.main-avatar').popover('destroy') #remove previous popovers
|
||||
|
|
@ -99,7 +116,7 @@ userSchema =
|
|||
stats: { gp: 0, exp: 0, lvl: 1, hp: 50 }
|
||||
party: { current: null, invitation: null }
|
||||
items: { weapon: 0, armor: 0, head: 0, shield: 0 }
|
||||
preferences: { gender: 'm', skin: 'white', hair: 'blond', armorSet: 'v1' }
|
||||
preferences: { gender: 'm', skin: 'white', hair: 'blond', armorSet: 'v1', dayStart:0, showHelm: true }
|
||||
habitIds: []
|
||||
dailyIds: []
|
||||
todoIds: []
|
||||
|
|
@ -119,14 +136,18 @@ module.exports.newUserObject = ->
|
|||
newUser = lodash.cloneDeep userSchema
|
||||
newUser.apiToken = derby.uuid()
|
||||
|
||||
repeat = {m:true,t:true,w:true,th:true,f:true,s:true,su:true}
|
||||
defaultTasks = [
|
||||
{type: 'habit', text: '1h Productive Work', notes: '-- Habits: Constantly Track --\nFor some habits, it only makes sense to *gain* points (like this one).', value: 0, up: true, down: false }
|
||||
{type: 'habit', text: 'Eat Junk Food', notes: 'For others, it only makes sense to *lose* points', value: 0, up: false, down: true}
|
||||
{type: 'habit', text: 'Take The Stairs', notes: 'For the rest, both + and - make sense (stairs = gain, elevator = lose)', value: 0, up: true, down: true}
|
||||
{type: 'daily', text: '1h Personal Project', notes: '-- Dailies: Complete Once a Day --\nAt the end of each day, non-completed Dailies dock you points.', value: 0, completed: false }
|
||||
{type: 'daily', text: 'Exercise', notes: "If you are doing well, they turn green and are less valuable (experience, gold) and less damaging (HP). This means you can ease up on them for a bit.", value: 3, completed: false }
|
||||
{type: 'daily', text: '45m Reading', notes: 'But if you are doing poorly, they turn red. The worse you do, the more valuable (exp, gold) and more damaging (HP) these goals become. This encourages you to focus on your shortcomings, the reds.', value: -10, completed: false }
|
||||
|
||||
{type: 'daily', text: '1h Personal Project', notes: '-- Dailies: Complete Once a Day --\nAt the end of each day, non-completed Dailies dock you points.', value: 0, completed: false, repeat: repeat }
|
||||
{type: 'daily', text: 'Exercise', notes: "If you are doing well, they turn green and are less valuable (experience, gold) and less damaging (HP). This means you can ease up on them for a bit.", value: 3, completed: false, repeat: repeat }
|
||||
{type: 'daily', text: '45m Reading', notes: 'But if you are doing poorly, they turn red. The worse you do, the more valuable (exp, gold) and more damaging (HP) these goals become. This encourages you to focus on your shortcomings, the reds.', value: -10, completed: false, repeat: repeat }
|
||||
|
||||
{type: 'todo', text: 'Call Mom', notes: "-- Todos: Complete Eventually --\nNon-completed Todos won't hurt you, but they will become more valuable over time. This will encourage you to wrap up stale Todos.", value: -3, completed: false }
|
||||
|
||||
{type: 'reward', text: '1 Episode of Game of Thrones', notes: '-- Rewards: Treat Yourself! --\nAs you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits.', value: 20 }
|
||||
{type: 'reward', text: 'Cake', notes: 'But only buy if you have enough gold - you lose HP otherwise.', value: 10 }
|
||||
]
|
||||
|
|
@ -177,7 +198,7 @@ module.exports.updateUser = (model) ->
|
|||
union = _.union obj[type + 'Ids'], taskIds
|
||||
|
||||
# 2. remove empty (grey) tasks
|
||||
preened = _.filter(union, (val) -> _.contains(taskIds, val))
|
||||
preened = _.filter union, (val) -> _.contains(taskIds, val) and val?
|
||||
|
||||
# There were indeed issues found, set the new list
|
||||
batch.set("#{type}Ids", preened) # if _.difference(preened, userObj[path]).length != 0
|
||||
|
|
@ -233,6 +254,7 @@ module.exports.BatchUpdate = BatchUpdate = (model) ->
|
|||
commit: ->
|
||||
model._dontPersist = false
|
||||
# some hackery in our own branched racer-db-mongo, see findAndModify of lefnire/racer-db-mongo#habitrpg index.js
|
||||
# pass true if we have levelled to supress xp notification
|
||||
user.set "update__", updates
|
||||
transactionInProgress = false
|
||||
updates = {}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
moment = require 'moment'
|
||||
algos = require './algos'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
|
@ -8,6 +9,20 @@ module.exports.app = (appExports, model) ->
|
|||
user.set 'lastCron', yesterday
|
||||
window.location.reload()
|
||||
|
||||
appExports.emulateTenDays = ->
|
||||
yesterday = +moment().subtract('days', 10).toDate()
|
||||
user.set 'lastCron', yesterday
|
||||
window.location.reload()
|
||||
|
||||
appExports.cheat = ->
|
||||
user.incr 'stats.exp', 20
|
||||
user.incr 'stats.exp', algos.tnl(user.get('stats.lvl'))
|
||||
user.incr 'stats.gp', 1000
|
||||
|
||||
appExports.reset = ->
|
||||
user.set 'stats.exp', 0
|
||||
user.set 'stats.lvl', 0
|
||||
user.set 'stats.gp', 0
|
||||
user.set 'items.weapon', 0
|
||||
user.set 'items.armor', 0
|
||||
user.set 'items.head', 0
|
||||
user.set 'items.shield', 0
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
moment = require('moment')
|
||||
moment = require 'moment'
|
||||
_ = require 'underscore'
|
||||
|
||||
# Absolute diff between two dates, based on 12am for both days
|
||||
module.exports.daysBetween = (a, b) ->
|
||||
Math.abs(moment(a).startOf('day').diff(moment(b).startOf('day'), 'days'))
|
||||
# Absolute diff between two dates
|
||||
module.exports.daysBetween = (yesterday, now, dayStart) ->
|
||||
#sanity-check reset-time (is it 24h time?)
|
||||
dayStart = 0 unless (dayStart? and (dayStart = parseInt(dayStart)) and dayStart >= 0 and dayStart <= 24)
|
||||
Math.abs moment(yesterday).startOf('day').add('h', dayStart).diff(moment(now), 'days')
|
||||
|
||||
module.exports.dayMapping = dayMapping = {0:'su',1:'m',2:'t',3:'w',4:'th',5:'f',6:'s',7:'su'}
|
||||
|
||||
|
|
@ -15,6 +18,12 @@ module.exports.viewHelpers = (view) ->
|
|||
view.fn "round", (num) ->
|
||||
Math.round num
|
||||
|
||||
view.fn "floor", (num) ->
|
||||
Math.floor num
|
||||
|
||||
view.fn "ceil", (num) ->
|
||||
Math.ceil num
|
||||
|
||||
view.fn "lt", (a, b) ->
|
||||
a < b
|
||||
view.fn 'gt', (a, b) -> a > b
|
||||
|
|
@ -29,4 +38,8 @@ module.exports.viewHelpers = (view) ->
|
|||
loc = window?.location.host or process.env.BASE_URL
|
||||
encodeURIComponent "http://#{loc}/v1/users/#{uid}/calendar.ics?apiToken=#{apiToken}"
|
||||
|
||||
view.fn "notEqual", (a, b) -> (a != b)
|
||||
view.fn "and", -> _.reduce arguments, (cumm, curr) -> cumm && curr
|
||||
view.fn "or", -> _.reduce arguments, (cumm, curr) -> cumm || curr
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
derby = require 'derby'
|
||||
{get, view, ready} = derby.createApp module
|
||||
derby.use require('derby-ui-boot'), {styles: ['bootstrap', 'responsive']}
|
||||
app = derby.createApp module
|
||||
{get, view, ready} = app
|
||||
derby.use require('derby-ui-boot'), {styles: []}
|
||||
derby.use require '../../ui'
|
||||
derby.use require 'derby-auth/components'
|
||||
|
||||
|
|
@ -46,13 +47,12 @@ get '/', (page, model, params, next) ->
|
|||
|
||||
ready (model) ->
|
||||
user = model.at('_user')
|
||||
score = new scoring.Scoring(model)
|
||||
|
||||
#set cron immediately
|
||||
lastCron = user.get('lastCron')
|
||||
user.set('lastCron', +new Date) if (!lastCron? or lastCron == 'new')
|
||||
|
||||
score.cron()
|
||||
scoring.cron(model)
|
||||
|
||||
character.app(exports, model)
|
||||
tasks.app(exports, model)
|
||||
|
|
@ -61,6 +61,6 @@ ready (model) ->
|
|||
profile.app(exports, model)
|
||||
require('../server/private').app(exports, model)
|
||||
require('./debug').app(exports, model) if model.get('_view.nodeEnv') != 'production'
|
||||
browser.app(exports, model)
|
||||
filters.app(exports, model)
|
||||
browser.app(exports, model, app)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,40 +2,40 @@ _ = require 'underscore'
|
|||
|
||||
items = module.exports.items =
|
||||
weapon: [
|
||||
{index: 0, text: "Training Sword", classes: "weapon_0", notes:'Training weapon.', modifier: 0.00, value:0}
|
||||
{index: 1, text: "Sword", classes:'weapon_1', notes:'Increases experience gain by 3%.', modifier: 0.03, value:20}
|
||||
{index: 2, text: "Axe", classes:'weapon_2', notes:'Increases experience gain by 6%.', modifier: 0.06, value:30}
|
||||
{index: 3, text: "Morningstar", classes:'weapon_3', notes:'Increases experience gain by 9%.', modifier: 0.09, value:45}
|
||||
{index: 4, text: "Blue Sword", classes:'weapon_4', notes:'Increases experience gain by 12%.', modifier: 0.12, value:65}
|
||||
{index: 5, text: "Red Sword", classes:'weapon_5', notes:'Increases experience gain by 15%.', modifier: 0.15, value:90}
|
||||
{index: 6, text: "Golden Sword", classes:'weapon_6', notes:'Increases experience gain by 18%.', modifier: 0.18, value:120}
|
||||
{index: 0, text: "Training Sword", classes: "weapon_0", notes:'Training weapon.', strength: 0, value:0}
|
||||
{index: 1, text: "Sword", classes:'weapon_1', notes:'Increases experience gain by 3%.', strength: 3, value:20}
|
||||
{index: 2, text: "Axe", classes:'weapon_2', notes:'Increases experience gain by 6%.', strength: 6, value:30}
|
||||
{index: 3, text: "Morningstar", classes:'weapon_3', notes:'Increases experience gain by 9%.', strength: 9, value:45}
|
||||
{index: 4, text: "Blue Sword", classes:'weapon_4', notes:'Increases experience gain by 12%.', strength: 12, value:65}
|
||||
{index: 5, text: "Red Sword", classes:'weapon_5', notes:'Increases experience gain by 15%.', strength: 15, value:90}
|
||||
{index: 6, text: "Golden Sword", classes:'weapon_6', notes:'Increases experience gain by 18%.', strength: 18, value:120}
|
||||
]
|
||||
armor: [
|
||||
{index: 0, text: "Cloth Armor", classes: 'armor_0', notes:'Training armor.', modifier: 0.00, value:0}
|
||||
{index: 1, text: "Leather Armor", classes: 'armor_1', notes:'Decreases HP loss by 4%.', modifier: 0.04, value:30}
|
||||
{index: 2, text: "Chain Mail", classes: 'armor_2', notes:'Decreases HP loss by 6%.', modifier: 0.06, value:45}
|
||||
{index: 3, text: "Plate Mail", classes: 'armor_3', notes:'Decreases HP loss by 7%.', modifier: 0.07, value:65}
|
||||
{index: 4, text: "Red Armor", classes: 'armor_4', notes:'Decreases HP loss by 8%.', modifier: 0.08, value:90}
|
||||
{index: 5, text: "Golden Armor", classes: 'armor_5', notes:'Decreases HP loss by 10%.', modifier: 0.1, value:120}
|
||||
{index: 0, text: "Cloth Armor", classes: 'armor_0', notes:'Training armor.', defense: 0, value:0}
|
||||
{index: 1, text: "Leather Armor", classes: 'armor_1', notes:'Decreases HP loss by 4%.', defense: 4, value:30}
|
||||
{index: 2, text: "Chain Mail", classes: 'armor_2', notes:'Decreases HP loss by 6%.', defense: 6, value:45}
|
||||
{index: 3, text: "Plate Mail", classes: 'armor_3', notes:'Decreases HP loss by 7%.', defense: 7, value:65}
|
||||
{index: 4, text: "Red Armor", classes: 'armor_4', notes:'Decreases HP loss by 8%.', defense: 8, value:90}
|
||||
{index: 5, text: "Golden Armor", classes: 'armor_5', notes:'Decreases HP loss by 10%.', defense: 10, value:120}
|
||||
]
|
||||
head: [
|
||||
{index: 0, text: "No Helm", classes: 'head_0', notes:'Training helm.', modifier: 0.00, value:0}
|
||||
{index: 1, text: "Leather Helm", classes: 'head_1', notes:'Decreases HP loss by 2%.', modifier: 0.02, value:15}
|
||||
{index: 2, text: "Chain Coif", classes: 'head_2', notes:'Decreases HP loss by 3%.', modifier: 0.03, value:25}
|
||||
{index: 3, text: "Plate Helm", classes: 'head_3', notes:'Decreases HP loss by 4%.', modifier: 0.04, value:45}
|
||||
{index: 4, text: "Red Helm", classes: 'head_4', notes:'Decreases HP loss by 5%.', modifier: 0.05, value:60}
|
||||
{index: 5, text: "Golden Helm", classes: 'head_5', notes:'Decreases HP loss by 6%.', modifier: 0.06, value:80}
|
||||
{index: 0, text: "No Helm", classes: 'head_0', notes:'Training helm.', defense: 0, value:0}
|
||||
{index: 1, text: "Leather Helm", classes: 'head_1', notes:'Decreases HP loss by 2%.', defense: 2, value:15}
|
||||
{index: 2, text: "Chain Coif", classes: 'head_2', notes:'Decreases HP loss by 3%.', defense: 3, value:25}
|
||||
{index: 3, text: "Plate Helm", classes: 'head_3', notes:'Decreases HP loss by 4%.', defense: 4, value:45}
|
||||
{index: 4, text: "Red Helm", classes: 'head_4', notes:'Decreases HP loss by 5%.', defense: 5, value:60}
|
||||
{index: 5, text: "Golden Helm", classes: 'head_5', notes:'Decreases HP loss by 6%.', defense: 6, value:80}
|
||||
]
|
||||
shield: [
|
||||
{index: 0, text: "No Shield", classes: 'shield_0', notes:'No Shield.', modifier: 0.00, value:0}
|
||||
{index: 1, text: "Wooden Shield", classes: 'shield_1', notes:'Decreases HP loss by 3%', modifier: 0.03, value:20}
|
||||
{index: 2, text: "Buckler", classes: 'shield_2', notes:'Decreases HP loss by 4%.', modifier: 0.04, value:35}
|
||||
{index: 3, text: "Enforced Shield", classes: 'shield_3', notes:'Decreases HP loss by 5%.', modifier: 0.05, value:55}
|
||||
{index: 4, text: "Red Shield", classes: 'shield_4', notes:'Decreases HP loss by 7%.', modifier: 0.07, value:70}
|
||||
{index: 5, text: "Golden Shield", classes: 'shield_5', notes:'Decreases HP loss by 8%.', modifier: 0.08, value:90}
|
||||
{index: 0, text: "No Shield", classes: 'shield_0', notes:'No Shield.', defense: 0, value:0}
|
||||
{index: 1, text: "Wooden Shield", classes: 'shield_1', notes:'Decreases HP loss by 3%', defense: 3, value:20}
|
||||
{index: 2, text: "Buckler", classes: 'shield_2', notes:'Decreases HP loss by 4%.', defense: 4, value:35}
|
||||
{index: 3, text: "Enforced Shield", classes: 'shield_3', notes:'Decreases HP loss by 5%.', defense: 5, value:55}
|
||||
{index: 4, text: "Red Shield", classes: 'shield_4', notes:'Decreases HP loss by 7%.', defense: 7, value:70}
|
||||
{index: 5, text: "Golden Shield", classes: 'shield_5', notes:'Decreases HP loss by 8%.', defense: 8, value:90}
|
||||
]
|
||||
potion: {type: 'potion', text: "Potion", notes: "Recover 15 HP", value: 25, classes: 'potion'}
|
||||
reroll: {type: 'reroll', text: "Re-Roll", classes: 'reroll', notes: "Resets your tasks. When you're struggling and everything's red, use for a clean slate.", value:0 }
|
||||
reroll: {type: 'reroll', text: "Re-Roll", classes: 'reroll', notes: "Resets your task values back to 0 (yellow). Useful when everything's red and it's hard to stay alive.", value:0 }
|
||||
|
||||
pets: [
|
||||
{index: 0, text: 'Bear Cub', name: 'bearcub', icon: 'Pet-BearCub-Base.png', value: 3}
|
||||
|
|
@ -165,22 +165,22 @@ module.exports.app = (appExports, model) ->
|
|||
model.set '_view.activeTabPets', true
|
||||
model.set '_view.activeTabRewards', false
|
||||
|
||||
user.on 'set', 'flags.itemsEnabled', (captures, args) ->
|
||||
return unless captures == true
|
||||
model.on 'set', '_user.flags.itemsEnabled', (captures, args) ->
|
||||
return unless captures is true
|
||||
html = """
|
||||
<div class='item-store-popover'>
|
||||
<img src='/vendor/BrowserQuest/client/img/1chest.png' />
|
||||
<img src='/vendor/BrowserQuest/client/img/1/chest.png' />
|
||||
Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information.
|
||||
<a href='#' onClick="$('ul.items').popover('hide');return false;">[Close]</a>
|
||||
<a href='#' onClick="$('div.rewards').popover('hide');return false;">[Close]</a>
|
||||
</div>
|
||||
"""
|
||||
$('ul.items').popover
|
||||
$('div.rewards').popover({
|
||||
title: "Item Store Unlocked"
|
||||
placement: 'left'
|
||||
trigger: 'manual'
|
||||
html: true
|
||||
content: html
|
||||
$('ul.items').popover 'show'
|
||||
}).popover 'show'
|
||||
|
||||
user.on 'set', 'flags.petsEnabled', (captures, args) ->
|
||||
return unless captures == true
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ module.exports.partySubscribe = partySubscribe = (page, model, params, next, cb)
|
|||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
||||
user.on 'set', 'flags.partyEnabled', (captures, args) ->
|
||||
return unless captures == true
|
||||
unlockPartiesNotification = ->
|
||||
$('.main-avatar').popover('destroy') #remove previous popovers
|
||||
html = """
|
||||
<div class='party-system-popover'>
|
||||
|
|
@ -89,6 +88,14 @@ module.exports.app = (appExports, model) ->
|
|||
content: html
|
||||
$('.main-avatar').popover 'show'
|
||||
|
||||
appExports.manuallyUnlockParties = ->
|
||||
$("#settings-modal").modal("hide")
|
||||
user.set('flags.partyEnabled', true)
|
||||
unlockPartiesNotification()
|
||||
|
||||
user.on 'set', 'flags.partyEnabled', (captures, args) ->
|
||||
unlockPartiesNotification() if captures == true
|
||||
|
||||
#TODO implement this when we have unsubscribe working properly
|
||||
model.on 'set', '_user.party.invitation', (after, before) ->
|
||||
if !before? and after? # they just got invited
|
||||
|
|
|
|||
|
|
@ -20,6 +20,6 @@ module.exports.app = (appExports, model) ->
|
|||
appExports.profileChangeActive = (e, el) ->
|
||||
uid = $(el).attr('data-uid')
|
||||
model.ref '_profileActive', model.at("users.#{uid}")
|
||||
model.set '_profileActiveMain', model.get('_user.id') == uid
|
||||
model.set '_profileActiveMain', user.get('id') is uid
|
||||
model.set '_profileActiveUsername', character.username model.get('_profileActive.auth')
|
||||
|
||||
|
|
|
|||
|
|
@ -5,59 +5,122 @@ helpers = require './helpers'
|
|||
browser = require './browser'
|
||||
character = require './character'
|
||||
items = require './items'
|
||||
algos = require './algos'
|
||||
|
||||
module.exports.Scoring = (model) ->
|
||||
MODIFIER = algos.MODIFIER # each new level, armor, weapon add 2% modifier (this mechanism will change)
|
||||
|
||||
MODIFIER = .02 # each new level, armor, weapon add 2% modifier (this mechanism will change)
|
||||
# {taskId} task you want to score
|
||||
# {direction} 'up' or 'down'
|
||||
# {times} # times to call score on this task (1 unless cron, usually)
|
||||
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
||||
score = (model, taskId, direction, times, batch, cron) ->
|
||||
user = model.at '_user'
|
||||
|
||||
###
|
||||
Calculates Exp & GP modification based on weapon & lvl
|
||||
{value} task.value for gain
|
||||
{modifiers} may manually pass in stats as {weapon, exp}. This is used for testing
|
||||
###
|
||||
expModifier = (value, modifiers = {}) ->
|
||||
weapon = modifiers.weapon || user.get('items.weapon')
|
||||
lvl = modifiers.lvl || user.get('stats.lvl')
|
||||
dmg = items.items.weapon[weapon].modifier # each new weapon increases exp gain
|
||||
dmg += (lvl-1) * MODIFIER # same for lvls
|
||||
modified = value + (value * dmg)
|
||||
return modified
|
||||
commit = false
|
||||
unless batch?
|
||||
commit = true
|
||||
batch = new character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
obj = batch.obj()
|
||||
|
||||
###
|
||||
Calculates HP-loss modification based on armor & lvl
|
||||
{value} task.value which is hurting us
|
||||
{modifiers} may manually pass in modifier as {armor, lvl}. This is used for testing
|
||||
###
|
||||
hpModifier = (value, modifiers = {}) ->
|
||||
armor = modifiers.armor || user.get('items.armor')
|
||||
head = modifiers.head || user.get('items.head')
|
||||
shield = modifiers.shield || user.get('items.shield')
|
||||
lvl = modifiers.lvl || user.get('stats.lvl')
|
||||
ac = items.items.armor[armor].modifier + items.items.head[head].modifier + items.items.shield[shield].modifier # each new armor decreases HP loss
|
||||
ac += (lvl-1) * MODIFIER # same for lvls
|
||||
modified = value - (value * ac)
|
||||
return modified
|
||||
{gp, hp, exp, lvl} = obj.stats
|
||||
|
||||
###
|
||||
Calculates the next task.value based on direction
|
||||
For negative values, use a line: something like y=-.1x+1
|
||||
For positibe values, taper off with inverse log: y=.9^x
|
||||
Would love to use inverse log for the whole thing, but after 13 fails it hits infinity. Revisit this formula later
|
||||
{currentValue} the current value of the task, determines it's next value
|
||||
{direction} 'up' or 'down'
|
||||
###
|
||||
taskDeltaFormula = (currentValue, direction) ->
|
||||
sign = if (direction == "up") then 1 else -1
|
||||
delta = if (currentValue < 0) then (( -0.1 * currentValue + 1 ) * sign) else (( Math.pow(0.9,currentValue) ) * sign)
|
||||
taskPath = "tasks.#{taskId}"
|
||||
taskObj = obj.tasks[taskId]
|
||||
{type, value} = taskObj
|
||||
priority = taskObj.priority or '!'
|
||||
|
||||
# If they're trying to purhcase a too-expensive reward, confirm they want to take a hit for it
|
||||
if taskObj.value > obj.stats.gp and taskObj.type is 'reward'
|
||||
r = confirm "Not enough GP to purchase this reward, buy anyway and lose HP? (Punishment for taking a reward you didn't earn)."
|
||||
unless r
|
||||
batch.commit()
|
||||
return
|
||||
|
||||
delta = 0
|
||||
times ?= 1
|
||||
calculateDelta = (adjustvalue=true) ->
|
||||
# If multiple days have passed, multiply times days missed
|
||||
_.times times, (n) ->
|
||||
# Each iteration calculate the delta (nextDelta), which is then accumulated in delta
|
||||
# (aka, the total delta). This weirdness won't be necessary when calculating mathematically
|
||||
# rather than iteratively
|
||||
nextDelta = algos.taskDeltaFormula(value, direction)
|
||||
value += nextDelta if adjustvalue
|
||||
delta += nextDelta
|
||||
|
||||
addPoints = ->
|
||||
level = user.get('stats.lvl')
|
||||
weaponStrength = items.items.weapon[user.get('items.weapon')].strength
|
||||
exp += algos.expModifier(delta,weaponStrength,level, priority) / 2 # / 2 hack for now bcause people leveling too fast
|
||||
gp += algos.gpModifier(delta, 1, priority)
|
||||
|
||||
subtractPoints = ->
|
||||
level = user.get('stats.lvl')
|
||||
armorDefense = items.items.armor[user.get('items.armor')].defense
|
||||
helmDefense = items.items.head[user.get('items.head')].defense
|
||||
shieldDefense = items.items.shield[user.get('items.shield')].defense
|
||||
hp += algos.hpModifier(delta,armorDefense,helmDefense,shieldDefense,level, priority)
|
||||
|
||||
switch type
|
||||
when 'habit'
|
||||
calculateDelta()
|
||||
# Add habit value to habit-history (if different)
|
||||
if (delta > 0) then addPoints() else subtractPoints()
|
||||
taskObj.history ?= []
|
||||
if taskObj.value != value
|
||||
historyEntry = { date: +new Date, value: value }
|
||||
taskObj.history.push historyEntry
|
||||
batch.set "#{taskPath}.history", taskObj.history
|
||||
|
||||
when 'daily'
|
||||
if cron? # cron
|
||||
calculateDelta()
|
||||
subtractPoints()
|
||||
else
|
||||
calculateDelta(false)
|
||||
if delta != 0
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
|
||||
when 'todo'
|
||||
if cron? #cron
|
||||
calculateDelta()
|
||||
#don't touch stats on cron
|
||||
else
|
||||
calculateDelta()
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
|
||||
when 'reward'
|
||||
# Don't adjust values for rewards
|
||||
calculateDelta(false)
|
||||
# purchase item
|
||||
gp -= Math.abs(taskObj.value)
|
||||
num = parseFloat(taskObj.value).toFixed(2)
|
||||
# if too expensive, reduce health & zero gp
|
||||
if gp < 0
|
||||
hp += gp # hp - gp difference
|
||||
gp = 0
|
||||
|
||||
taskObj.value = value
|
||||
batch.set "#{taskPath}.value", taskObj.value
|
||||
origStats = _.clone obj.stats
|
||||
updateStats model, {hp: hp, exp: exp, gp: gp}, batch
|
||||
if commit
|
||||
# newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*)
|
||||
newStats = _.clone batch.obj().stats
|
||||
_.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key]
|
||||
batch.setStats(newStats)
|
||||
# batch.setStats()
|
||||
batch.commit()
|
||||
return delta
|
||||
|
||||
###
|
||||
###
|
||||
Updates user stats with new stats. Handles death, leveling up, etc
|
||||
{stats} new stats
|
||||
{update} if aggregated changes, pass in userObj as update. otherwise commits will be made immediately
|
||||
###
|
||||
updateStats = (newStats, batch) ->
|
||||
###
|
||||
updateStats = (model, newStats, batch) ->
|
||||
user = model.at '_user'
|
||||
obj = batch.obj()
|
||||
|
||||
# if user is dead, dont do anything
|
||||
|
|
@ -73,14 +136,30 @@ module.exports.Scoring = (model) ->
|
|||
obj.stats.hp = newStats.hp
|
||||
|
||||
if newStats.exp?
|
||||
tnl = algos.tnl(obj.stats.lvl)
|
||||
#silent = false
|
||||
# if we're at level 100, turn xp to gold
|
||||
if obj.stats.lvl >= 100
|
||||
newStats.gp += newStats.exp / 15
|
||||
newStats.exp = 0
|
||||
obj.stats.lvl = 100
|
||||
else
|
||||
# level up & carry-over exp
|
||||
tnl = model.get '_tnl'
|
||||
if newStats.exp >= tnl
|
||||
#silent = true # push through the negative xp silently
|
||||
user.set('stats.exp', newStats.exp) # push normal + notification
|
||||
while newStats.exp >= tnl and obj.stats.lvl < 100 # keep levelling up
|
||||
newStats.exp -= tnl
|
||||
obj.stats.lvl++
|
||||
tnl = algos.tnl(obj.stats.lvl)
|
||||
if obj.stats.lvl== 100
|
||||
newStats.exp = 0
|
||||
obj.stats.hp = 50
|
||||
|
||||
obj.stats.exp = newStats.exp
|
||||
#if silent
|
||||
#console.log("pushing silent :" + obj.stats.exp)
|
||||
#user.pass(true).set('stats.exp', obj.stats.exp)
|
||||
|
||||
# Set flags when they unlock features
|
||||
if !obj.flags.customizationsNotification and (obj.stats.exp > 10 or obj.stats.lvl > 1)
|
||||
|
|
@ -102,109 +181,14 @@ module.exports.Scoring = (model) ->
|
|||
gp = 0.0 if (!gp? or gp<0)
|
||||
obj.stats.gp = newStats.gp
|
||||
|
||||
# {taskId} task you want to score
|
||||
# {direction} 'up' or 'down'
|
||||
# {times} # times to call score on this task (1 unless cron, usually)
|
||||
# {update} if we're running updates en-mass (eg, cron on server) pass in userObj
|
||||
score = (taskId, direction, times, batch, cron) ->
|
||||
|
||||
commit = false
|
||||
unless batch?
|
||||
commit = true
|
||||
batch = new character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
obj = batch.obj()
|
||||
|
||||
{gp, hp, exp, lvl} = obj.stats
|
||||
|
||||
taskPath = "tasks.#{taskId}"
|
||||
taskObj = obj.tasks[taskId]
|
||||
{type, value} = taskObj
|
||||
|
||||
# If they're trying to purhcase a too-expensive reward, confirm they want to take a hit for it
|
||||
if taskObj.value > obj.stats.gp and taskObj.type is 'reward'
|
||||
r = confirm "Not enough GP to purchase this reward, buy anyway and lose HP? (Punishment for taking a reward you didn't earn)."
|
||||
unless r
|
||||
batch.commit()
|
||||
return
|
||||
|
||||
delta = 0
|
||||
times ?= 1
|
||||
calculateDelta = (adjustvalue=true) ->
|
||||
# If multiple days have passed, multiply times days missed
|
||||
_.times times, (n) ->
|
||||
# Each iteration calculate the delta (nextDelta), which is then accumulated in delta
|
||||
# (aka, the total delta). This weirdness won't be necessary when calculating mathematically
|
||||
# rather than iteratively
|
||||
nextDelta = taskDeltaFormula(value, direction)
|
||||
value += nextDelta if adjustvalue
|
||||
delta += nextDelta
|
||||
|
||||
addPoints = ->
|
||||
modified = expModifier(delta)
|
||||
exp += modified
|
||||
gp += modified
|
||||
|
||||
subtractPoints = ->
|
||||
modified = hpModifier(delta)
|
||||
hp += modified
|
||||
|
||||
switch type
|
||||
when 'habit'
|
||||
# Don't adjust values for habits that don't have both + and -
|
||||
adjustvalue = if (taskObj.up==false or taskObj.down==false) then false else true
|
||||
calculateDelta(adjustvalue)
|
||||
# Add habit value to habit-history (if different)
|
||||
if (delta > 0) then addPoints() else subtractPoints()
|
||||
taskObj.history ?= []
|
||||
if taskObj.value != value
|
||||
historyEntry = { date: +new Date, value: value }
|
||||
taskObj.history.push historyEntry
|
||||
batch.set "#{taskPath}.history", taskObj.history
|
||||
|
||||
when 'daily'
|
||||
calculateDelta()
|
||||
if cron? # cron
|
||||
subtractPoints()
|
||||
else
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
|
||||
when 'todo'
|
||||
calculateDelta()
|
||||
unless cron? # don't touch stats on cron
|
||||
addPoints() # obviously for delta>0, but also a trick to undo accidental checkboxes
|
||||
|
||||
when 'reward'
|
||||
# Don't adjust values for rewards
|
||||
calculateDelta(false)
|
||||
# purchase item
|
||||
gp -= Math.abs(taskObj.value)
|
||||
num = parseFloat(taskObj.value).toFixed(2)
|
||||
# if too expensive, reduce health & zero gp
|
||||
if gp < 0
|
||||
hp += gp # hp - gp difference
|
||||
gp = 0
|
||||
|
||||
taskObj.value = value
|
||||
batch.set "#{taskPath}.value", taskObj.value
|
||||
origStats = _.clone obj.stats
|
||||
updateStats {hp: hp, exp: exp, gp: gp}, batch
|
||||
if commit
|
||||
# newStats / origStats is a glorious hack to trick Derby into seeing the change in model.on(*)
|
||||
newStats = _.clone batch.obj().stats
|
||||
_.each Object.keys(origStats), (key) -> obj.stats[key] = origStats[key]
|
||||
batch.setStats(newStats)
|
||||
# batch.setStats()
|
||||
batch.commit()
|
||||
return delta
|
||||
|
||||
###
|
||||
###
|
||||
At end of day, add value to all incomplete Daily & Todo tasks (further incentive)
|
||||
For incomplete Dailys, deduct experience
|
||||
###
|
||||
cron = () ->
|
||||
###
|
||||
cron = (model) ->
|
||||
user = model.at '_user'
|
||||
today = +new Date
|
||||
daysPassed = helpers.daysBetween(today, user.get('lastCron'))
|
||||
daysPassed = helpers.daysBetween(user.get('lastCron'), today, user.get('preferences.dayStart'))
|
||||
if daysPassed > 0
|
||||
batch = new character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
|
|
@ -229,17 +213,26 @@ module.exports.Scoring = (model) ->
|
|||
thatDay = moment().subtract('days', n+1)
|
||||
if repeat[helpers.dayMapping[thatDay.day()]]==true
|
||||
daysFailed++
|
||||
score id, 'down', daysFailed, batch, true
|
||||
|
||||
score model, id, 'down', daysFailed, batch, true
|
||||
if type == 'daily'
|
||||
if completed #set OHV for completed dailies
|
||||
newValue = taskObj.value + algos.taskDeltaFormula(taskObj.value,'up')
|
||||
batch.set "tasks.#{taskObj.id}.value", newValue
|
||||
|
||||
taskObj.history ?= []
|
||||
taskObj.history.push { date: +new Date, value: value }
|
||||
taskObj.history.push { date: +new Date, value: taskObj.value }
|
||||
batch.set "tasks.#{taskObj.id}.history", taskObj.history
|
||||
batch.set "tasks.#{taskObj.id}.completed", false
|
||||
else
|
||||
value = obj.tasks[taskObj.id].value #get updated value
|
||||
absVal = if (completed) then Math.abs(value) else value
|
||||
todoTally += absVal
|
||||
else if type is 'habit' # slowly reset 'onlies' value to 0
|
||||
if taskObj.up==false or taskObj.down==false
|
||||
if Math.abs(taskObj.value) < 0.1
|
||||
batch.set "tasks.#{taskObj.id}.value", 0
|
||||
else
|
||||
batch.set "tasks.#{taskObj.id}.value", taskObj.value / 2
|
||||
|
||||
# Finished tallying
|
||||
obj.history ?= {}; obj.history.todos ?= []; obj.history.exp ?= []
|
||||
|
|
@ -249,7 +242,7 @@ module.exports.Scoring = (model) ->
|
|||
lvl = 0 #iterator
|
||||
while lvl < (obj.stats.lvl-1)
|
||||
lvl++
|
||||
expTally += (lvl*100)/5
|
||||
expTally += algos.tnl(lvl)
|
||||
obj.history.exp.push { date: today, value: expTally }
|
||||
|
||||
# Set the new user specs, and animate HP loss
|
||||
|
|
@ -261,13 +254,12 @@ module.exports.Scoring = (model) ->
|
|||
setTimeout (-> user.set 'stats.hp', hpAfter), 1000 # animate hp loss
|
||||
|
||||
|
||||
return {
|
||||
MODIFIER: MODIFIER
|
||||
module.exports = {
|
||||
score: score
|
||||
cron: cron
|
||||
|
||||
# testing stuff
|
||||
expModifier: expModifier
|
||||
hpModifier: hpModifier
|
||||
taskDeltaFormula: taskDeltaFormula
|
||||
}
|
||||
expModifier: algos.expModifier
|
||||
hpModifier: algos.hpModifier
|
||||
taskDeltaFormula: algos.taskDeltaFormula
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,48 +2,45 @@ scoring = require './scoring'
|
|||
helpers = require './helpers'
|
||||
_ = require 'underscore'
|
||||
moment = require 'moment'
|
||||
character = require './character'
|
||||
|
||||
module.exports.view = (view) ->
|
||||
view.fn 'taskClasses', (type, completed, value, repeat, tags = {}, filters = {}) ->
|
||||
#TODO figure out how to just pass in the task model, so i can access all these properties from one object
|
||||
if type != 'reward'
|
||||
view.fn 'taskClasses', (task, filters) ->
|
||||
return unless task
|
||||
{type, completed, value, repeat} = task
|
||||
|
||||
for filter, enabled of filters
|
||||
if enabled and not tags[filter]
|
||||
if enabled and not task.tags?[filter]
|
||||
# All the other classes don't matter
|
||||
return 'filtered-out'
|
||||
return 'hide'
|
||||
|
||||
classes = type
|
||||
|
||||
# show as completed if completed (naturally) or not required for today
|
||||
if type in ['todo', 'daily']
|
||||
if completed or (repeat and repeat[helpers.dayMapping[moment().day()]]==false)
|
||||
classes += " completed"
|
||||
else
|
||||
classes += " uncompleted"
|
||||
|
||||
switch
|
||||
when value<-8 then classes += ' color-worst'
|
||||
when value>=-8 and value<-5 then classes += ' color-worse'
|
||||
when value>=-5 and value<-1 then classes += ' color-bad'
|
||||
when value>=-1 and value<1 then classes += ' color-neutral'
|
||||
when value>=1 and value<5 then classes += ' color-good'
|
||||
when value>=5 and value<10 then classes += ' color-better'
|
||||
when value>=10 then classes += ' color-best'
|
||||
if value < -20
|
||||
classes += ' color-worst'
|
||||
else if value < -10
|
||||
classes += ' color-worse'
|
||||
else if value < -1
|
||||
classes += ' color-bad'
|
||||
else if value < 1
|
||||
classes += ' color-neutral'
|
||||
else if value < 5
|
||||
classes += ' color-good'
|
||||
else if value < 10
|
||||
classes += ' color-better'
|
||||
else
|
||||
classes += ' color-best'
|
||||
return classes
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
score = new scoring.Scoring(model)
|
||||
|
||||
user.on 'set', 'tasks.*.completed', (i, completed, previous, isLocal, passed) ->
|
||||
return if passed? && passed.cron # Don't do this stuff on cron
|
||||
direction = () ->
|
||||
return 'up' if completed==true and previous == false
|
||||
return 'down' if completed==false and previous == true
|
||||
throw new Error("Direction neither 'up' nor 'down' on checkbox set.")
|
||||
|
||||
# Score the user based on todo task
|
||||
task = user.at("tasks.#{i}")
|
||||
score.score(i, direction())
|
||||
|
||||
appExports.addTask = (e, el, next) ->
|
||||
type = $(el).attr('data-task-type')
|
||||
|
|
@ -76,7 +73,7 @@ module.exports.app = (appExports, model) ->
|
|||
|
||||
appExports.del = (e, el) ->
|
||||
# Derby extends model.at to support creation from DOM nodes
|
||||
task = model.at(e.target)
|
||||
task = e.at()
|
||||
id = task.get('id')
|
||||
|
||||
history = task.get('history')
|
||||
|
|
@ -88,7 +85,7 @@ module.exports.app = (appExports, model) ->
|
|||
return # Cancel. Don't delete, don't hurt user
|
||||
else
|
||||
task.set('type','habit') # hack to make sure it hits HP, instead of performing "undo checkbox"
|
||||
score.score(id, direction:'down')
|
||||
scoring.score(model, id, direction:'down')
|
||||
|
||||
# prevent accidently deleting long-standing tasks
|
||||
else
|
||||
|
|
@ -124,8 +121,8 @@ module.exports.app = (appExports, model) ->
|
|||
appExports.toggleTaskEdit = (e, el) ->
|
||||
hideId = $(el).attr('data-hide-id')
|
||||
toggleId = $(el).attr('data-toggle-id')
|
||||
$(document.getElementById(hideId)).hide()
|
||||
$(document.getElementById(toggleId)).toggle()
|
||||
$(document.getElementById(hideId)).addClass('visuallyhidden')
|
||||
$(document.getElementById(toggleId)).toggleClass('visuallyhidden')
|
||||
|
||||
appExports.toggleChart = (e, el) ->
|
||||
hideSelector = $(el).attr('data-hide-id')
|
||||
|
|
@ -180,9 +177,70 @@ module.exports.app = (appExports, model) ->
|
|||
path = "_user.tasks.#{taskId}.tags.#{tagId}"
|
||||
model.set path, !(model.get path)
|
||||
|
||||
appExports.score = (e, el, next) ->
|
||||
|
||||
setUndo = (stats, task) ->
|
||||
previousUndo = model.get('_undo')
|
||||
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
|
||||
timeoutId = setTimeout (-> model.del('_undo')), 10000
|
||||
model.set '_undo', {stats:stats, task:task, timeoutId: timeoutId}
|
||||
|
||||
|
||||
###
|
||||
Call scoring functions for habits & rewards (todos & dailies handled below)
|
||||
###
|
||||
appExports.score = (e, el) ->
|
||||
task= model.at $(el).parents('li')[0]
|
||||
taskObj = task.get()
|
||||
direction = $(el).attr('data-direction')
|
||||
direction = 'up' if direction == 'true/'
|
||||
direction = 'down' if direction == 'false/'
|
||||
task = model.at $(el).parents('li')[0]
|
||||
score.score(task.get('id'), direction)
|
||||
|
||||
# set previous state for undo
|
||||
setUndo _.clone(user.get('stats')), _.clone(taskObj)
|
||||
|
||||
scoring.score(model, taskObj.id, direction)
|
||||
|
||||
###
|
||||
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', (i, completed, previous, isLocal, passed) ->
|
||||
return if passed? && passed.cron # Don't do this stuff on cron
|
||||
direction = if completed then 'up' else 'down'
|
||||
|
||||
# set previous state for undo
|
||||
taskObj = _.clone user.get("tasks.#{i}")
|
||||
taskObj.completed = previous
|
||||
setUndo _.clone(user.get('stats')), taskObj
|
||||
|
||||
scoring.score(model, i, direction)
|
||||
|
||||
###
|
||||
Undo
|
||||
###
|
||||
appExports.undo = () ->
|
||||
undo = model.get '_undo'
|
||||
clearTimeout(undo.timeoutId) if undo?.timeoutId
|
||||
batch = character.BatchUpdate(model)
|
||||
batch.startTransaction()
|
||||
model.del '_undo'
|
||||
_.each undo.stats, (val, key) -> batch.set "stats.#{key}", val
|
||||
taskPath = "tasks.#{undo.task.id}"
|
||||
_.each undo.task, (val, key) ->
|
||||
return if key in ['id', 'type'] # strange bugs in this world: https://workflowy.com/shared/a53582ea-43d6-bcce-c719-e134f9bf71fd/
|
||||
if key is 'completed'
|
||||
user.pass({cron:true}).set("#{taskPath}.completed",val)
|
||||
else
|
||||
batch.set "#{taskPath}.#{key}", val
|
||||
batch.commit()
|
||||
|
||||
appExports.tasksToggleAdvanced = (e, el) ->
|
||||
$(el).next('.advanced-option').toggleClass('visuallyhidden')
|
||||
|
||||
appExports.tasksSaveAndClose = ->
|
||||
# When they update their notes, re-establish tooltip & popover
|
||||
$('[rel=tooltip]').tooltip()
|
||||
$('[rel=popover]').popover()
|
||||
|
||||
appExports.tasksSetPriority = (e, el) ->
|
||||
dataId = $(el).parent('[data-id]').attr('data-id')
|
||||
#"_user.tasks.#{dataId}"
|
||||
model.at(e.target).set 'priority', $(el).attr('data-priority')
|
||||
|
|
|
|||
|
|
@ -3,35 +3,168 @@ router = new express.Router()
|
|||
|
||||
scoring = require '../app/scoring'
|
||||
_ = require 'underscore'
|
||||
icalendar = require('icalendar')
|
||||
{ tnl } = require '../app/algos'
|
||||
validator = require 'derby-auth/node_modules/validator'
|
||||
check = validator.check
|
||||
sanitize = validator.sanitize
|
||||
|
||||
# ---------- /v1 API ------------
|
||||
# Every url added beneath router is prefaced by /v1
|
||||
NO_TOKEN_OR_UID = err: "You must include a token and uid (user id) in your request"
|
||||
NO_USER_FOUND = err: "No user found."
|
||||
|
||||
# ---------- /api/v1 API ------------
|
||||
# Every url added beneath router is prefaced by /api/v1
|
||||
|
||||
###
|
||||
v1 API. Requires user-id and apiToken, task-id, direction. Test with:
|
||||
curl -X POST -H "Content-Type:application/json" -d '{"apiToken":"{TOKEN}"}' localhost:3000/v1/users/{UID}/tasks/productivity/up
|
||||
v1 API. Requires api-v1-user (user id) and api-v1-key (api key) headers, Test with:
|
||||
$ cd node_modules/racer && npm install && cd ../..
|
||||
$ mocha test/api.mocha.coffee
|
||||
###
|
||||
|
||||
router.post '/users/:uid/tasks/:taskId/:direction', (req, res) ->
|
||||
{uid, taskId, direction} = req.params
|
||||
{apiToken, title, service, icon} = req.body
|
||||
console.log {params:req.params, body:req.body} if process.env.NODE_ENV == 'development'
|
||||
###
|
||||
API Status
|
||||
###
|
||||
router.get '/status', (req, res) ->
|
||||
res.json status: 'up'
|
||||
|
||||
###
|
||||
beforeEach auth interceptor
|
||||
###
|
||||
auth = (req, res, next) ->
|
||||
uid = req.headers['x-api-user']
|
||||
token = req.headers['x-api-key']
|
||||
return res.json 401, NO_TOKEN_OR_UID unless uid || token
|
||||
|
||||
model = req.getModel()
|
||||
query = model.query('users').withIdAndToken(uid, token)
|
||||
|
||||
query.fetch (err, user) ->
|
||||
return res.json err: err if err
|
||||
req.user = user
|
||||
req.userObj = user.get()
|
||||
return res.json 401, NO_USER_FOUND if !req.userObj || _.isEmpty(req.userObj)
|
||||
req._isServer = true
|
||||
next()
|
||||
###
|
||||
GET /user
|
||||
###
|
||||
router.get '/user', auth, (req, res) ->
|
||||
user = req.userObj
|
||||
|
||||
user.stats.toNextLevel = tnl user.stats.lvl
|
||||
user.stats.maxHealth = 50
|
||||
|
||||
delete user.apiToken
|
||||
|
||||
res.json user
|
||||
|
||||
###
|
||||
GET /user/task/:id
|
||||
###
|
||||
router.get '/user/task/:id', auth, (req, res) ->
|
||||
task = req.userObj.tasks[req.params.id]
|
||||
return res.json 400, err: "No task found." if !task || _.isEmpty(task)
|
||||
|
||||
res.json 200, task
|
||||
|
||||
###
|
||||
validate task
|
||||
###
|
||||
validateTask = (req, res, next) ->
|
||||
task = {}
|
||||
newTask = { type, text, notes, value, up, down, completed } = req.body
|
||||
|
||||
# If we're updating, get the task from the user
|
||||
if req.method is 'PUT' or req.method is 'DELETE'
|
||||
task = req.userObj?.tasks[req.params.id]
|
||||
return res.json 400, err: "No task found." if !task || _.isEmpty(task)
|
||||
# Strip for now
|
||||
type = undefined
|
||||
delete newTask.type
|
||||
else if req.method is 'POST'
|
||||
unless /^(habit|todo|daily|reward)$/.test type
|
||||
return res.json 400, err: 'type must be habit, todo, daily, or reward'
|
||||
|
||||
text = sanitize(text).xss()
|
||||
notes = sanitize(notes).xss()
|
||||
value = sanitize(value).toInt()
|
||||
|
||||
switch type
|
||||
when 'habit'
|
||||
newTask.up = true unless typeof up is 'boolean'
|
||||
newTask.down = true unless typeof down is 'boolean'
|
||||
when 'daily', 'todo'
|
||||
newTask.completed = false unless typeof completed is 'boolean'
|
||||
|
||||
_.extend task, newTask
|
||||
req.task = task
|
||||
next()
|
||||
|
||||
###
|
||||
PUT /user/task/:id
|
||||
###
|
||||
router.put '/user/task/:id', auth, validateTask, (req, res) ->
|
||||
req.user.set "tasks.#{req.task.id}", req.task
|
||||
|
||||
res.json 200, req.task
|
||||
|
||||
###
|
||||
DELETE /user/task/:id
|
||||
###
|
||||
router.delete '/user/task/:id', auth, validateTask, (req, res) ->
|
||||
taskIds = req.user.get "#{req.task.type}Ids"
|
||||
|
||||
req.user.del "tasks.#{req.task.id}"
|
||||
# Remove one id from array of typeIds
|
||||
req.user.remove "#{req.task.type}Ids", taskIds.indexOf(req.task.id), 1
|
||||
|
||||
res.send 204
|
||||
|
||||
###
|
||||
POST /user/task/
|
||||
###
|
||||
router.post '/user/task', auth, validateTask, (req, res) ->
|
||||
task = req.task
|
||||
type = task.type
|
||||
|
||||
model = req.getModel()
|
||||
model.ref '_user', req.user
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
model.at("_#{type}List").push task
|
||||
|
||||
res.json 201, task
|
||||
|
||||
###
|
||||
GET /user/tasks
|
||||
###
|
||||
router.get '/user/tasks', auth, (req, res) ->
|
||||
user = req.userObj
|
||||
return res.json 400, NO_USER_FOUND if !user || _.isEmpty(user)
|
||||
|
||||
model = req.getModel()
|
||||
model.ref '_user', req.user
|
||||
tasks = []
|
||||
types = ['habit','todo','daily','reward']
|
||||
if /^(habit|todo|daily|reward)$/.test req.query.type
|
||||
types = [req.query.type]
|
||||
for type in types
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
tasks = tasks.concat model.get("_#{type}List")
|
||||
|
||||
res.json 200, tasks
|
||||
|
||||
###
|
||||
This is called form deprecated.coffee's score function, and the req.headers are setup properly to handle the login
|
||||
###
|
||||
scoreTask = (req, res, next) ->
|
||||
{taskId, direction} = req.params
|
||||
{title, service, icon} = req.body
|
||||
|
||||
# Send error responses for improper API call
|
||||
return res.send(500, 'request body "apiToken" required') unless apiToken
|
||||
return res.send(500, ':uid required') unless uid
|
||||
return res.send(500, ':taskId required') unless taskId
|
||||
return res.send(500, ":direction must be 'up' or 'down'") unless direction in ['up','down']
|
||||
|
||||
req._isServer = true
|
||||
model = req.getModel()
|
||||
model.fetch model.query('users').withIdAndToken(uid, apiToken), (err, result) ->
|
||||
return res.send(500, err) if err
|
||||
user = result
|
||||
userObj = user.get()
|
||||
if _.isEmpty(userObj)
|
||||
return res.send(500, "User with uid=#{uid}, token=#{apiToken} not found. Make sure you're not using your username, but your User Id")
|
||||
{user, userObj} = req
|
||||
|
||||
model.ref('_user', user)
|
||||
|
||||
|
|
@ -48,37 +181,13 @@ router.post '/users/:uid/tasks/:taskId/:direction', (req, res) ->
|
|||
down: true
|
||||
notes: "This task was created by a third-party service. Feel free to edit, it won't harm the connection to that service. Additionally, multiple services may piggy-back off this task."
|
||||
|
||||
score = scoring.Scoring(model)
|
||||
delta = score.score(taskId, direction)
|
||||
delta = scoring.score(model, taskId, direction)
|
||||
result = model.get ('_user.stats')
|
||||
result.delta = delta
|
||||
res.send(result)
|
||||
|
||||
router.get '/users/:uid/calendar.ics', (req, res) ->
|
||||
#return next() #disable for now
|
||||
{uid} = req.params
|
||||
{apiToken} = req.query
|
||||
|
||||
model = req.getModel()
|
||||
query = model.query('users').withIdAndToken(uid, apiToken)
|
||||
query.fetch (err, result) ->
|
||||
return res.send(500, err) if err
|
||||
tasks = result.get('tasks')
|
||||
# tasks = result[0].tasks
|
||||
tasksWithDates = _.filter tasks, (task) -> !!task.date
|
||||
return res.send(500, "No events found") if _.isEmpty(tasksWithDates)
|
||||
|
||||
ical = new icalendar.iCalendar()
|
||||
ical.addProperty('NAME', 'HabitRPG')
|
||||
_.each tasksWithDates, (task) ->
|
||||
event = new icalendar.VEvent(task.id);
|
||||
event.setSummary(task.text);
|
||||
d = new Date(task.date)
|
||||
d.date_only = true
|
||||
event.setDate d
|
||||
ical.addComponent event
|
||||
res.type('text/calendar')
|
||||
formattedIcal = ical.toString().replace(/DTSTART\:/g, 'DTSTART;VALUE=DATE:')
|
||||
res.send(200, formattedIcal)
|
||||
router.post '/user/tasks/:taskId/:direction', auth, scoreTask
|
||||
|
||||
module.exports = router
|
||||
module.exports.auth = auth
|
||||
module.exports.scoreTask = scoreTask # export so deprecated can call it
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
express = require 'express'
|
||||
router = new express.Router()
|
||||
|
||||
scoring = require '../app/scoring'
|
||||
_ = require 'underscore'
|
||||
icalendar = require('icalendar')
|
||||
api = require './api'
|
||||
|
||||
# ---------- Deprecated Paths ------------
|
||||
|
||||
deprecatedMessage = 'This API is no longer supported, see https://github.com/lefnire/habitrpg/wiki/API for new protocol'
|
||||
|
|
@ -9,4 +14,39 @@ router.get '/:uid/up/:score?', (req, res) -> res.send(500, deprecatedMessage)
|
|||
router.get '/:uid/down/:score?', (req, res) -> res.send(500, deprecatedMessage)
|
||||
router.post '/users/:uid/tasks/:taskId/:direction', (req, res) -> res.send(500, deprecatedMessage)
|
||||
|
||||
# Redirect to new API
|
||||
initDeprecated = (req, res, next) ->
|
||||
req.headers['x-api-user'] = req.params.uid
|
||||
req.headers['x-api-key'] = req.body.apiToken
|
||||
next()
|
||||
|
||||
router.post '/v1/users/:uid/tasks/:taskId/:direction', initDeprecated, api.auth, api.scoreTask
|
||||
|
||||
router.get '/v1/users/:uid/calendar.ics', (req, res) ->
|
||||
#return next() #disable for now
|
||||
{uid} = req.params
|
||||
{apiToken} = req.query
|
||||
|
||||
model = req.getModel()
|
||||
query = model.query('users').withIdAndToken(uid, apiToken)
|
||||
query.fetch (err, result) ->
|
||||
return res.send(500, err) if err
|
||||
tasks = result.get('tasks')
|
||||
# tasks = result[0].tasks
|
||||
tasksWithDates = _.filter tasks, (task) -> !!task.date
|
||||
return res.send(500, "No events found") if _.isEmpty(tasksWithDates)
|
||||
|
||||
ical = new icalendar.iCalendar()
|
||||
ical.addProperty('NAME', 'HabitRPG')
|
||||
_.each tasksWithDates, (task) ->
|
||||
event = new icalendar.VEvent(task.id);
|
||||
event.setSummary(task.text);
|
||||
d = new Date(task.date)
|
||||
d.date_only = true
|
||||
event.setDate d
|
||||
ical.addComponent event
|
||||
res.type('text/calendar')
|
||||
formattedIcal = ical.toString().replace(/DTSTART\:/g, 'DTSTART;VALUE=DATE:')
|
||||
res.send(200, formattedIcal)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ server = http.createServer expressApp
|
|||
module.exports = server
|
||||
|
||||
derby.use require('racer-db-mongo')
|
||||
store = derby.createStore
|
||||
module.exports.habitStore = store = derby.createStore
|
||||
db: {type: 'Mongo', uri: process.env.NODE_DB_URI, safe:true}
|
||||
listen: server
|
||||
|
||||
|
|
@ -57,6 +57,7 @@ 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))
|
||||
|
|
@ -74,6 +75,9 @@ mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
|
|||
)
|
||||
# Adds req.getModel method
|
||||
.use(store.modelMiddleware())
|
||||
# API should be hit before all other routes
|
||||
.use('/api/v1', require('./api').middleware)
|
||||
.use(require('./deprecated').middleware)
|
||||
# Show splash page for newcomers
|
||||
.use(middleware.splash)
|
||||
.use(priv.middleware)
|
||||
|
|
@ -81,9 +85,7 @@ mongo_store = new MongoStore {url: process.env.NODE_DB_URI}, ->
|
|||
.use(auth.middleware(strategies, options))
|
||||
# Creates an express middleware from the app's routes
|
||||
.use(app.router())
|
||||
.use('/v1', require('./api').middleware)
|
||||
.use(require('./static').middleware)
|
||||
.use(require('./deprecated').middleware)
|
||||
.use(expressApp.router)
|
||||
.use(serverError(root))
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
module.exports.splash = (req, res, next) ->
|
||||
# This was an API call, not a page load
|
||||
return next() if req.is('json')
|
||||
|
||||
unless req.query?.play? or req.getModel().get('_userId')
|
||||
res.redirect('/splash.html')
|
||||
isStatic = req.url.split('/')[1] is 'static'
|
||||
unless req.query?.play? or req.getModel().get('_userId') or isStatic
|
||||
res.redirect('/static/front')
|
||||
else
|
||||
next()
|
||||
|
||||
|
|
@ -15,3 +13,15 @@ module.exports.view = (req, res, next) ->
|
|||
_view.nodeEnv = process.env.NODE_ENV
|
||||
model.set '_view', _view
|
||||
next()
|
||||
|
||||
#CORS middleware
|
||||
module.exports.allowCrossDomain = (req, res, next) ->
|
||||
res.header "Access-Control-Allow-Origin", (req.headers.origin || "*")
|
||||
res.header "Access-Control-Allow-Methods", "OPTIONS,GET,POST,PUT,HEAD,DELETE"
|
||||
res.header "Access-Control-Allow-Headers", "Content-Type,X-Requested-With,x-api-user,x-api-key"
|
||||
|
||||
# wtf is this for?
|
||||
if req.method is 'OPTIONS'
|
||||
res.send(200);
|
||||
else
|
||||
next()
|
||||
|
|
@ -7,10 +7,18 @@ derby = require 'derby'
|
|||
# ---------- Static Pages ------------
|
||||
staticPages = derby.createStatic path.dirname(path.dirname(__dirname))
|
||||
|
||||
router.get '/privacy', (req, res) ->
|
||||
staticPages.render 'privacy', res
|
||||
beforeEach = (req, res, next) ->
|
||||
req.getModel().set '_view', {nodeEnv: 'production'} # we don't want cheat buttons on static pages
|
||||
next()
|
||||
|
||||
router.get '/terms', (req, res) ->
|
||||
staticPages.render 'terms', res
|
||||
router.get '/splash.html', (req, res) -> return res.redirect('/static/front')
|
||||
router.get '/static/front', beforeEach, (req, res) -> staticPages.render 'static/front', res
|
||||
router.get '/static/about', beforeEach, (req, res) -> staticPages.render 'static/about', res
|
||||
router.get '/static/team', beforeEach, (req, res) -> staticPages.render 'static/team', res
|
||||
router.get '/static/extensions', beforeEach, (req, res) -> staticPages.render 'static/extensions', res
|
||||
router.get '/static/faq', beforeEach, (req, res) -> staticPages.render 'static/faq', res
|
||||
|
||||
router.get '/static/privacy', beforeEach, (req, res) -> staticPages.render 'static/privacy', res
|
||||
router.get '/static/terms', beforeEach, (req, res) -> staticPages.render 'static/terms', res
|
||||
|
||||
module.exports = router
|
||||
|
|
|
|||
|
|
@ -28,7 +28,10 @@ userAccess = (store) ->
|
|||
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 err(derbyAuth.SESSION_INVALIDATED_ERROR) if derbyAuth.bustedSession(@)
|
||||
|
||||
return accept(true) if derbyAuth.isServer(@)
|
||||
|
||||
return accept(false) if derbyAuth.bustedSession(@)
|
||||
|
||||
captures = arguments[0].split('.')
|
||||
|
|
@ -40,8 +43,7 @@ userAccess = (store) ->
|
|||
return accept(true)
|
||||
|
||||
# Same session (user.id = this.session.userId)
|
||||
if (uid is @session.userId) or derbyAuth.isServer(@)
|
||||
return accept(true)
|
||||
return accept(true) if uid is @session.userId
|
||||
|
||||
accept(false)
|
||||
|
||||
|
|
@ -51,9 +53,10 @@ userAccess = (store) ->
|
|||
|
||||
oldBalance = @session.req?._racerModel?.get("users.#{id}.balance") || 0
|
||||
purchasingSomethingOnClient = newBalance < oldBalance
|
||||
accept(purchasingSomethingOnClient or @session.req?._isServer)
|
||||
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(@)
|
||||
|
|
@ -67,7 +70,7 @@ userAccess = (store) ->
|
|||
###
|
||||
REST = (store) ->
|
||||
store.query.expose "users", "withIdAndToken", (uid, token) ->
|
||||
@byId(uid)
|
||||
@where("id").equals(uid)
|
||||
.where('apiToken').equals(token)
|
||||
.findOne()
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ lvlColor = #d9edf7
|
|||
lvlText = #3a87ad
|
||||
xpColor = #DCC0FB
|
||||
xpText = #635673
|
||||
deathColor = #4E4E4E
|
||||
deathText = #ABABAB
|
||||
borderDarken = 20%
|
||||
|
||||
// alert styles
|
||||
|
|
@ -32,11 +34,36 @@ borderDarken = 20%
|
|||
border-color: darken(xpColor,borderDarken)
|
||||
color: xpText
|
||||
|
||||
.alert-death
|
||||
background-color: deathColor
|
||||
border-color: deathColor
|
||||
color: deathText
|
||||
|
||||
// alert icons
|
||||
|
||||
.icon-gp
|
||||
background: url("img/sprites/shop_sprites.png") no-repeat
|
||||
background-size: 400px
|
||||
background-position: -386px 0
|
||||
.icon-gold
|
||||
background: url("img/coin_single_gold.png") no-repeat
|
||||
background-position: center center
|
||||
background-size: 18px
|
||||
width: 14px
|
||||
height: 14px
|
||||
|
||||
.icon-silver
|
||||
background: url("img/coin_single_silver.png") no-repeat
|
||||
background-position: center center
|
||||
background-size: 18px
|
||||
width: 14px
|
||||
height: 14px
|
||||
|
||||
.icon-death
|
||||
background: url("img/sprites/dead.png") no-repeat
|
||||
background-position: center center
|
||||
background-size: 14px
|
||||
width: 14px
|
||||
height: 14px
|
||||
|
||||
|
||||
.undo-button
|
||||
position:absolute
|
||||
left:5px
|
||||
top:5px
|
||||
32
styles/app/helpers.styl
Normal file
32
styles/app/helpers.styl
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Hide from both screenreaders and browsers: h5bp.com/u
|
||||
.hidden
|
||||
display: none !important
|
||||
visibility: hidden
|
||||
|
||||
|
||||
// Hide only visually, but have it available for screenreaders: h5bp.com/v
|
||||
.visuallyhidden
|
||||
border: 0
|
||||
clip: rect(0 0 0 0)
|
||||
height: 1px
|
||||
margin: -1px
|
||||
overflow: hidden
|
||||
padding: 0
|
||||
position: absolute
|
||||
width: 1px
|
||||
|
||||
|
||||
// Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p
|
||||
.visuallyhidden.focusable:active,
|
||||
.visuallyhidden.focusable:focus
|
||||
clip: auto
|
||||
height: auto
|
||||
margin: 0
|
||||
overflow: visible
|
||||
position: static
|
||||
width: auto
|
||||
|
||||
|
||||
// Hide visually and from screenreaders, but maintain layout
|
||||
.invisible
|
||||
visibility: hidden
|
||||
|
|
@ -8,13 +8,31 @@
|
|||
@import "./items.styl";
|
||||
@import "./alerts.styl";
|
||||
@import "./filters.styl";
|
||||
@import "./helpers.styl";
|
||||
@import "./responsive.styl";
|
||||
@import "./scrollbars.styl";
|
||||
|
||||
@import "../../public/vendor/bootstrap-datepicker/css/datepicker.css";
|
||||
|
||||
// fix exploding to very wide for some reason
|
||||
.datepicker
|
||||
width: 210px
|
||||
|
||||
html,body,p,h1,ul,li,table,tr,th,td
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
*
|
||||
-webkit-box-sizing: border-box
|
||||
-moz-box-sizing: border-box
|
||||
box-sizing: border-box
|
||||
|
||||
hr
|
||||
border-top: 0
|
||||
border-bottom: 1px solid #ddd
|
||||
border-color: rgba(0,0,0,0.1)
|
||||
|
||||
|
||||
/* Header
|
||||
-------------------------------------------------- */
|
||||
|
||||
|
|
@ -35,7 +53,7 @@ html,body,p,h1,ul,li,table,tr,th,td
|
|||
box-sizing: border-box
|
||||
z-index: 1000
|
||||
|
||||
@media (max-width: 480px) {
|
||||
@media (max-width: 600px) {
|
||||
#head {
|
||||
position: static;
|
||||
}
|
||||
|
|
@ -97,16 +115,6 @@ html,body,p,h1,ul,li,table,tr,th,td
|
|||
/* Footer
|
||||
-------------------------------------------------- */
|
||||
|
||||
.footer ul li
|
||||
list-style:none
|
||||
float:left
|
||||
.footer ul li:after
|
||||
content: "\00A0\00A0|\00A0\00A0"
|
||||
.footer ul li:last-child:after
|
||||
content: ""
|
||||
.footer table td
|
||||
vertical-align: top
|
||||
|
||||
/* Bootstrap website boilerplate */
|
||||
.footer {
|
||||
padding: 70px 0;
|
||||
|
|
@ -132,7 +140,7 @@ html,body,p,h1,ul,li,table,tr,th,td
|
|||
/*overflow:auto;*/
|
||||
padding-bottom: 250px; /* don't know why this works, sticky footers are weird */
|
||||
|
||||
@media (max-width: 480px) {
|
||||
@media (max-width: 600px) {
|
||||
#wrap {
|
||||
margin-top: 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,83 @@
|
|||
/* ----- Items, Weapons, Armor -----*/
|
||||
.item-store-popover img
|
||||
float:left
|
||||
padding-right:7px;
|
||||
// money styles
|
||||
.money
|
||||
display: inline-block
|
||||
line-height: 1.5em
|
||||
padding-left: 0.75em
|
||||
|
||||
.item .task-text img
|
||||
max-height: 16px
|
||||
[class^="shop_"]
|
||||
vertical-align: top
|
||||
display: inline-block
|
||||
|
||||
.buy-link, .item-buy-link
|
||||
background-color: #DEE5F2
|
||||
padding: 2px
|
||||
margin-right: 10px
|
||||
border-radius: 5px
|
||||
.btn-buy
|
||||
width: 4.5em
|
||||
height: 3em
|
||||
padding: 0.75em 0 0
|
||||
text-align: center
|
||||
vertical-align: top
|
||||
background-color: $better
|
||||
|
||||
img
|
||||
height:20px
|
||||
color: #555
|
||||
&:hover, &:focus
|
||||
background-color: $bad
|
||||
text-decoration: none
|
||||
|
||||
.item-buy-link
|
||||
background-color: #ccffcc
|
||||
.rewards
|
||||
margin-bottom: 1.5em
|
||||
padding-bottom: 1.5em
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1)
|
||||
|
||||
.item
|
||||
position:relative
|
||||
.reward-item
|
||||
background: white
|
||||
|
||||
.shop-table
|
||||
position:absolute
|
||||
top: 0
|
||||
left: 50px
|
||||
.reward-cost
|
||||
display: inline
|
||||
|
||||
#money
|
||||
position:relative
|
||||
.shop_gold, .shop_silver, .shop_copper
|
||||
float:right
|
||||
.money-table
|
||||
position:absolute
|
||||
top: 0
|
||||
|
||||
.shop-pet
|
||||
mouse: pointer
|
||||
.shop-pet-owned-check
|
||||
position:absolute
|
||||
right:0px
|
||||
bottom:0px
|
||||
// store items
|
||||
.btn-reroll
|
||||
background-color: $better
|
||||
cursor: pointer
|
||||
box-shadow: inset -1px -1px 0 rgba(0,0,0,0.1)
|
||||
&:hover, &:focus
|
||||
background-color: $bad
|
||||
|
||||
.item-img
|
||||
display: inline-block
|
||||
vertical-align: top
|
||||
// background-color: $bad
|
||||
height: 3em
|
||||
margin-left: -0.5em
|
||||
margin-right: 0.5em
|
||||
// border: 1px solid rgba(0,0,0,0.1)
|
||||
border-top: 0
|
||||
border-bottom: 0
|
||||
|
||||
.token-wallet
|
||||
cursor: pointer
|
||||
.tile
|
||||
background-color: darken($neutral, 10%)
|
||||
.add-token-btn
|
||||
opacity: 0
|
||||
&:hover, &:focus
|
||||
.add-token-btn
|
||||
opacity: 1
|
||||
background-color: $good
|
||||
.tile
|
||||
opacity: 1
|
||||
|
||||
|
||||
// pets (this will all change when pet system is overhauled)
|
||||
.pet-grid
|
||||
padding-top: 1.5em
|
||||
border-top: rgba(0,0,0,0.1)
|
||||
table
|
||||
width: 100%
|
||||
td
|
||||
padding: 0.5em
|
||||
width: 25%
|
||||
&.active-pet
|
||||
background-color: $bad
|
||||
outline: 1px solid rgba(0,0,0,0.1)
|
||||
outline-offset: -1px
|
||||
&:hover, &:focus
|
||||
background-color: darken($better, 10%)
|
||||
31
styles/app/responsive.styl
Normal file
31
styles/app/responsive.styl
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
.grid
|
||||
padding: 0 1.5em 1.5em 0
|
||||
letter-spacing: -4px
|
||||
|
||||
.module
|
||||
display: inline-block
|
||||
letter-spacing: normal
|
||||
vertical-align: top
|
||||
width: 25%
|
||||
padding: 0 0 1.5em 1.5em
|
||||
-moz-box-sizing: border-box
|
||||
box-sizing: border-box
|
||||
|
||||
// at 960px when 4 columns really starts to break
|
||||
@media (max-width: 60em)
|
||||
.module
|
||||
width: 50%
|
||||
.task-column
|
||||
max-height: 18em
|
||||
overflow-y: scroll
|
||||
|
||||
// at 600px when 2col starts breaking
|
||||
@media (max-width: 37.5em)
|
||||
.grid
|
||||
padding-right: 0
|
||||
.module
|
||||
width: 100%
|
||||
padding-left: 0
|
||||
.task-column
|
||||
max-height: none
|
||||
overflow: visible
|
||||
82
styles/app/scrollbars.styl
Normal file
82
styles/app/scrollbars.styl
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/* Gmail style scrollbar */
|
||||
.task-column::-webkit-scrollbar {
|
||||
width: 12px
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb {
|
||||
border-width: 1px 1px 1px 2px
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track {
|
||||
border-width: 0
|
||||
}
|
||||
.task-column::-webkit-scrollbar {
|
||||
height: 16px;
|
||||
overflow: visible;
|
||||
width: 16px;
|
||||
}
|
||||
.task-column::-webkit-scrollbar-button {
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track {
|
||||
background-clip: padding-box;
|
||||
border: solid transparent;
|
||||
border-width: 0 0 0 4px;
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:horizontal {
|
||||
border-width: 4px 0 0
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:hover {
|
||||
background-color: rgba(150,150,150,.05);
|
||||
box-shadow: inset 1px 0 0 rgba(150,150,150,.1);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:horizontal:hover {
|
||||
box-shadow: inset 0 1px 0 rgba(150,150,150,.1)
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:active {
|
||||
background-color: rgba(150,150,150,.05);
|
||||
box-shadow: inset 1px 0 0 rgba(150,150,150,.14),inset -1px 0 0 rgba(150,150,150,.07);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:horizontal:active {
|
||||
box-shadow: inset 0 1px 0 rgba(150,150,150,.14),inset 0 -1px 0 rgba(150,150,150,.07)
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(150,150,150,.2);
|
||||
background-clip: padding-box;
|
||||
border: solid transparent;
|
||||
border-width: 1px 1px 1px 6px;
|
||||
min-height: 28px;
|
||||
padding: 100px 0 0;
|
||||
box-shadow: inset 1px 1px 0 rgba(150,150,150,.1),inset 0 -1px 0 rgba(150,150,150,.07);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb:horizontal {
|
||||
border-width: 6px 1px 1px;
|
||||
padding: 0 0 0 100px;
|
||||
box-shadow: inset 1px 1px 0 rgba(150,150,150,.1),inset -1px 0 0 rgba(150,150,150,.07);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(150,150,150,.4);
|
||||
box-shadow: inset 1px 1px 1px rgba(150,150,150,.25);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb:active {
|
||||
background-color: rgba(150,150,150,0.5);
|
||||
box-shadow: inset 1px 1px 3px rgba(150,150,150,0.35);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track {
|
||||
border-width: 0 1px 0 6px
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:horizontal {
|
||||
border-width: 6px 0 1px
|
||||
}
|
||||
.task-column::-webkit-scrollbar-track:hover {
|
||||
background-color: rgba(150,150,150,.035);
|
||||
box-shadow: inset 1px 1px 0 rgba(150,150,150,.14),inset -1px -1px 0 rgba(150,150,150,.07);
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb {
|
||||
border-width: 0 1px 0 6px
|
||||
}
|
||||
.task-column::-webkit-scrollbar-thumb:horizontal {
|
||||
border-width: 6px 0 1px
|
||||
}
|
||||
.task-column::-webkit-scrollbar-corner {
|
||||
background: transparent
|
||||
}
|
||||
|
|
@ -49,6 +49,6 @@
|
|||
.shop_armor_1 {background-position: -800px 0; width: 40px; height: 40px}
|
||||
.shop_reroll {background-position: -840px 0; width: 40px; height: 40px}
|
||||
.shop_potion {background-position: -880px 0; width: 40px; height: 40px}
|
||||
.shop_copper {background-position: -920px 0; width: 40px; height: 40px}
|
||||
.shop_silver {background-position: -960px 0; width: 40px; height: 40px}
|
||||
.shop_gold {background-position: -1000px 0; width: 40px; height: 40px}
|
||||
.shop_copper {background-position: -923px -8px; width: 32px; height: 22px}
|
||||
.shop_silver {background-position: -963px -8px; width: 32px; height: 22px}
|
||||
.shop_gold {background-position: -1003px -8px; width: 32px; height: 22px}
|
||||
|
|
|
|||
|
|
@ -1,42 +1,354 @@
|
|||
.color-worst pre
|
||||
background-color: rgb(230, 184, 175)
|
||||
.color-worse pre
|
||||
background-color: rgb(244, 204, 204)
|
||||
.color-bad pre
|
||||
background-color: rgb(252, 229, 205)
|
||||
.color-neutral pre
|
||||
background-color: rgb(255, 242, 204)
|
||||
.color-good pre
|
||||
background-color: rgb(217, 234, 211)
|
||||
.color-better pre
|
||||
background-color: rgb(208, 224, 227)
|
||||
.color-best pre
|
||||
background-color: rgb(201, 218, 248)
|
||||
.completed pre
|
||||
background-color: rgb(217, 217, 217)
|
||||
color: rgb(153, 153, 153)
|
||||
.reward pre
|
||||
// 1. Set up color classes
|
||||
// ========================
|
||||
|
||||
// color variables
|
||||
$worst = rgb(230, 184, 175)
|
||||
$worse = rgb(244, 204, 204)
|
||||
$bad = rgb(252, 229, 205)
|
||||
$neutral = rgb(255, 242, 204)
|
||||
$good = rgb(217, 234, 211)
|
||||
$better = rgb(208, 224, 227)
|
||||
$best = rgb(201, 218, 248)
|
||||
$completed = rgb(217, 217, 217)
|
||||
|
||||
// array of keywords and their associated color vars
|
||||
$stages = (worst $worst) (worse $worse) (bad $bad) (neutral $neutral) (good $good) (better $better) (best $best)
|
||||
|
||||
// for each color stage, generate a named class w/ the appropriate color
|
||||
for $stage in $stages
|
||||
.color-{$stage[0]}
|
||||
background-color: $stage[1]
|
||||
.action-yesno label,
|
||||
.task-action-btn
|
||||
background-color: darken($stage[1], 30%)
|
||||
&:hover, &:focus
|
||||
background-color: darken($stage[1], 40%)
|
||||
|
||||
// completed has to be outside the loop to override the color class
|
||||
.completed
|
||||
background-color: $completed
|
||||
color: #999
|
||||
.task-checker label
|
||||
background-color: darken($completed, 30%)
|
||||
.task-action-btn
|
||||
background-color: darken($completed, 30%)
|
||||
|
||||
.reward
|
||||
background-color: white
|
||||
|
||||
label.checkbox.inline{
|
||||
width: 40px
|
||||
|
||||
|
||||
// 2. Columns & Tasks
|
||||
// ===================
|
||||
|
||||
// main columns
|
||||
// ------------
|
||||
.task-column
|
||||
padding: 1.5em
|
||||
background: #f5f5f5
|
||||
border: 1px solid #ccc
|
||||
font-family: 'Lato', sans-serif
|
||||
|
||||
.task-column_title
|
||||
margin: 0 0 0.5em
|
||||
padding: 0
|
||||
|
||||
|
||||
// add new task form
|
||||
// -----------------
|
||||
.addtask-form
|
||||
margin-bottom: 0.75em
|
||||
outline: 1px solid rgba(0,0,0,0.15)
|
||||
outline-offset: -1px
|
||||
background-color: white
|
||||
// box-shadow: 0 0 3px rgba(0,0,0,0.15)
|
||||
position: relative
|
||||
|
||||
// the input field
|
||||
.addtask-field
|
||||
display: block
|
||||
width: 100%
|
||||
input
|
||||
font-family: 'Lato', sans-serif
|
||||
border: 0
|
||||
outline: 0
|
||||
border-radius: 0
|
||||
box-shadow: none
|
||||
background-color: white
|
||||
height: 3em
|
||||
padding: 0 0 0 0.5em
|
||||
width: 100%
|
||||
&:focus
|
||||
box-shadow: inset 0 0 3px darken($best, 20%),inset -1px 0 1px darken($best, 30%)
|
||||
|
||||
// the button
|
||||
.addtask-btn
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0
|
||||
// important for overriding bootstrap, remove later
|
||||
width: 1.81818em !important
|
||||
height: 1.88em
|
||||
padding: 0
|
||||
font-size: 1.61em
|
||||
line-height: 1.7
|
||||
outline: 0
|
||||
border: 0
|
||||
border-radius: 0 //required to nuke BB-playbook user agent styles
|
||||
background-color: darken($best, 20%)
|
||||
opacity: 0.75
|
||||
&:hover,
|
||||
&:focus
|
||||
opacity: 1
|
||||
background-color: darken($best, 20%)
|
||||
&:active
|
||||
background-color: darken($best, 30%)
|
||||
opacity: 1
|
||||
|
||||
|
||||
// an individual task entry
|
||||
// ------------------------
|
||||
.task
|
||||
overflow: hidden
|
||||
list-style: none
|
||||
clear: both
|
||||
padding: 0 0.5em 0 0.5em
|
||||
min-height: 3em
|
||||
line-height: 3
|
||||
margin-bottom: 0.75em
|
||||
outline: 1px solid rgba(0,0,0,0.1)
|
||||
outline-offset: -1px
|
||||
&:hover
|
||||
cursor: move
|
||||
&:last-child
|
||||
margin-bottom: 0
|
||||
|
||||
// task content
|
||||
.task-text
|
||||
display: inline
|
||||
|
||||
// when a task is being dragged
|
||||
.task.ui-sortable-helper {
|
||||
box-shadow: 0 0 3px rgba(0,0,0,0.15), 0 0 5px rgba(0,0,0,0.1)
|
||||
transform: scale(1.05)
|
||||
outline: 1px solid rgba(0,0,0,0.2)
|
||||
}
|
||||
|
||||
.todos
|
||||
.nav-pills > .active > a, .nav-pills > .active > a:hover
|
||||
color: #005580
|
||||
background-color: #DEE5F2
|
||||
// primary task commands
|
||||
// ----------------------
|
||||
.task-controls
|
||||
display: inline
|
||||
margin-left: -0.5em
|
||||
margin-right: 0.5em
|
||||
|
||||
.dailys
|
||||
.repeat-days > .btn:not(.active)
|
||||
background-color: #aaa;
|
||||
background-image: -moz-linear-gradient(top, #eee, #aaa);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#eee), to(#aaa));
|
||||
background-image: -webkit-linear-gradient(top, #eee, #aaa);
|
||||
background-image: -o-linear-gradient(top, #eee, #aaa);
|
||||
background-image: linear-gradient(to bottom, #eee, #aaa);
|
||||
background-repeat: repeat-x;
|
||||
// plus/minus commands
|
||||
.task-action-btn
|
||||
display: inline-block
|
||||
width: 2.12765em
|
||||
height: 2.12765em
|
||||
padding: 0
|
||||
font-size: 1.41em
|
||||
line-height: 2.12765
|
||||
text-align: center
|
||||
color: #222
|
||||
vertical-align: top
|
||||
border-right: 1px solid rgba(0,0,0,0.25)
|
||||
&:last-child
|
||||
border: 0
|
||||
&:hover, &:focus
|
||||
color: #222
|
||||
text-decoration: none
|
||||
|
||||
// checkbox
|
||||
.task-checker input[type=checkbox]
|
||||
margin: 0
|
||||
padding: 0
|
||||
visibility: hidden
|
||||
|
||||
.task-checker label
|
||||
display: inline-block
|
||||
width: 3em
|
||||
height: 3em
|
||||
margin: 0
|
||||
vertical-align: top
|
||||
cursor: pointer
|
||||
|
||||
// plus/minus checkbox
|
||||
.action-plusminus
|
||||
label
|
||||
margin-right: 1.5em
|
||||
label:after
|
||||
font-size: 1.41em
|
||||
width: 2.12765em
|
||||
height: 2.12765em
|
||||
line-height: 2.12765
|
||||
text-align: center
|
||||
display: inline-block
|
||||
vertical-align top
|
||||
opacity: 0.3
|
||||
background-color: #ddd
|
||||
outline: 1px solid rgba(0,0,0,0.5)
|
||||
color: #222
|
||||
label[for$="plus"]:after
|
||||
content: '+'
|
||||
label[for$="minus"]:after
|
||||
content: '−'
|
||||
|
||||
&.select-toggle input[type=checkbox]:checked + label:after
|
||||
opacity: 1
|
||||
|
||||
// yes/no checkbox
|
||||
.action-yesno
|
||||
position: relative
|
||||
// uncompleted icon
|
||||
label:after, label:before
|
||||
// content: '◯'
|
||||
content: ''
|
||||
display: block
|
||||
height: 1.714285714em
|
||||
width: 1.714285714em
|
||||
font-size: 1.75em
|
||||
line-height: 1.714285714em
|
||||
text-align: center
|
||||
color: black
|
||||
opacity: 0.2
|
||||
|
||||
label:after
|
||||
content: '▨'
|
||||
label:before
|
||||
position: absolute
|
||||
left: 0
|
||||
// hint at completed icon
|
||||
label:hover:before,
|
||||
label:focus:before
|
||||
content: ''
|
||||
label:hover:after,
|
||||
label:focus:after
|
||||
content: '¬'
|
||||
font-size: 3.2em
|
||||
line-height: 0.9375em
|
||||
height: 0.9375em
|
||||
width: 1.15em
|
||||
text-align: center
|
||||
transform: rotate(135deg)
|
||||
opacity: 0.5 !important
|
||||
label:active:after
|
||||
opacity: 0.75 !important
|
||||
|
||||
// completed icon
|
||||
input[type=checkbox]:checked + label:after
|
||||
content: '¬'
|
||||
font-size: 3.2em
|
||||
line-height: 0.9375em
|
||||
height: 0.9375em
|
||||
width: 1.15em
|
||||
text-align: center
|
||||
transform: rotate(135deg)
|
||||
opacity: 0.75
|
||||
|
||||
|
||||
// secondary task commands
|
||||
// -----------------------
|
||||
.task-meta-controls
|
||||
float: right
|
||||
margin-left: 0.25em
|
||||
opacity: 0.25
|
||||
a > i
|
||||
margin-left: 0.125em
|
||||
.task-notes
|
||||
margin-left: 0.125em
|
||||
|
||||
.task:hover .task-meta-controls
|
||||
opacity: 1
|
||||
|
||||
|
||||
// task editing
|
||||
// ------------
|
||||
.task-options
|
||||
padding: 0 0.5em
|
||||
margin-top: 1.25em
|
||||
color: #333
|
||||
|
||||
.option-group
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1)
|
||||
padding: 0 0 1em
|
||||
margin-bottom: 1em
|
||||
|
||||
.option-title
|
||||
font-size: 1em
|
||||
margin: 0 0 0.5em
|
||||
line-height: 1.5
|
||||
border: 0
|
||||
padding: 0
|
||||
color: #333
|
||||
font-style: italic
|
||||
&:not(.mega):after
|
||||
content: ':'
|
||||
&.mega
|
||||
cursor: pointer
|
||||
font-style: normal
|
||||
font-weight: bold
|
||||
text-decoration: underline
|
||||
|
||||
.option-content
|
||||
height: 2.5em
|
||||
width: 100%
|
||||
margin: 0 0 1em
|
||||
padding: 0 0 0 0.5em
|
||||
outline: 0
|
||||
border: 1px solid rgba(0,0,0,0.2)
|
||||
box-shadow: none
|
||||
border-radius: 0
|
||||
font-family: 'Lato', sans-serif //bootstrap override
|
||||
|
||||
textarea.option-content
|
||||
height: 5em
|
||||
padding-top: 0.25em
|
||||
resize: none
|
||||
margin-bottom: 0
|
||||
|
||||
// button-group
|
||||
.task-controls.tile-group
|
||||
display: block
|
||||
text-align: center
|
||||
margin: 0
|
||||
|
||||
.task-action-btn.tile
|
||||
border: 0
|
||||
font-size: 1.15em
|
||||
font-weight: 300
|
||||
outline: 1px solid rgba(0,0,0,0.2)
|
||||
outline-offset: -1px
|
||||
margin: 0 0 0 3px
|
||||
font-family: 'Lato', sans-serif
|
||||
text-align: inherit
|
||||
opacity: 0.5
|
||||
width: auto
|
||||
padding: 0 0.5em
|
||||
&.active
|
||||
opacity: 1
|
||||
|
||||
.tile.spacious
|
||||
margin: 0.75em 0
|
||||
font-size: 1.5em
|
||||
opacity: 1
|
||||
.tile.bright
|
||||
background-color: lighten($best, 20%)
|
||||
&:hover, &:focus
|
||||
background-color: lighten($best, 40%)
|
||||
.tile.flush
|
||||
margin-left: 0
|
||||
border: 1px solid rgba(0,0,0,0.2)
|
||||
outline: 0
|
||||
line-height: 2em
|
||||
&:first-child
|
||||
border-right: 0
|
||||
|
||||
|
||||
// todos ui
|
||||
// --------
|
||||
|
||||
// context switching
|
||||
.context-enabled
|
||||
.display-context-dependant,
|
||||
.task-list > li
|
||||
|
|
@ -50,39 +362,19 @@ label.checkbox.inline{
|
|||
.show-for-uncompleted, .uncompleted
|
||||
display: block
|
||||
|
||||
.help-icon
|
||||
float:right;
|
||||
|
||||
.task:hover
|
||||
cursor: move
|
||||
|
||||
li:hover .task-meta-controls .hover-show
|
||||
display: inline
|
||||
|
||||
.task
|
||||
list-style:none
|
||||
|
||||
pre
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif
|
||||
font-size: 13px
|
||||
line-height: 18px
|
||||
color: black
|
||||
word-break: normal
|
||||
|
||||
.task-meta-controls
|
||||
float:right
|
||||
i
|
||||
margin-left:5px
|
||||
|
||||
.task-controls,.task-text
|
||||
display:inline
|
||||
margin-right:10px
|
||||
|
||||
.task-meta-controls .hover-show
|
||||
display: none;
|
||||
|
||||
.vote-up, .vote-down
|
||||
text-decoration:none;
|
||||
|
||||
.new-task-form
|
||||
margin-bottom:5px;
|
||||
// nav tabs
|
||||
.nav-tabs
|
||||
margin-top: 1.5em
|
||||
.nav-tabs > li
|
||||
&.active > a
|
||||
color: #333
|
||||
&:hover
|
||||
margin-top: 1px
|
||||
> a
|
||||
border-radius: 0 !important
|
||||
margin-top: 1px
|
||||
color: #333
|
||||
&:hover
|
||||
border-top: 0
|
||||
margin-top: 2px
|
||||
|
|
|
|||
273
test/api.mocha.coffee
Normal file
273
test/api.mocha.coffee
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
_ = require 'underscore'
|
||||
request = require 'superagent'
|
||||
expect = require 'expect.js'
|
||||
require 'coffee-script'
|
||||
|
||||
conf = require("nconf")
|
||||
conf.argv().env().file({file: __dirname + '../config.json'}).defaults
|
||||
|
||||
# Override normal ENV values with nconf ENV values (ENV values are used the same way without nconf)
|
||||
#FIXME can't get nconf file above to load...
|
||||
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 = 'mongodb://localhost/habitrpg'
|
||||
|
||||
## monkey-patch expect.js for better diffs on mocha
|
||||
## see: https://github.com/LearnBoost/expect.js/pull/34
|
||||
|
||||
origBe = expect.Assertion::be
|
||||
expect.Assertion::be = expect.Assertion::equal = (obj) ->
|
||||
@_expected = obj
|
||||
origBe.call this, obj
|
||||
|
||||
# Custom modules
|
||||
character = require '../src/app/character'
|
||||
|
||||
###### Helpers & Variables ######
|
||||
|
||||
model = null
|
||||
uuid = null
|
||||
taskPath = null
|
||||
baseURL = 'http://localhost:1337/api/v1'
|
||||
|
||||
###### Specs ######
|
||||
|
||||
describe 'API', ->
|
||||
server = null
|
||||
store = null
|
||||
model = null
|
||||
user = null
|
||||
uid = null
|
||||
|
||||
before (done) ->
|
||||
server = require '../src/server'
|
||||
server.listen '1337', '0.0.0.0'
|
||||
server.on 'listening', (data) ->
|
||||
store = server.habitStore
|
||||
#store.flush()
|
||||
model = store.createModel()
|
||||
model.set '_userId', uid = model.id()
|
||||
user = character.newUserObject()
|
||||
user.apiToken = model.id()
|
||||
model.session = {userId:uid}
|
||||
model.set "users.#{uid}", user
|
||||
delete model.session
|
||||
# Crappy hack to let server start before tests run
|
||||
setTimeout done, 2000
|
||||
|
||||
describe 'Without token or user id', ->
|
||||
|
||||
it '/api/v1/status', (done) ->
|
||||
request.get("#{baseURL}/status")
|
||||
.set('Accept', 'application/json')
|
||||
.end (res) ->
|
||||
expect(res.statusCode).to.be 200
|
||||
expect(res.body.status).to.be 'up'
|
||||
done()
|
||||
|
||||
it '/api/v1/user', (done) ->
|
||||
request.get("#{baseURL}/user")
|
||||
.set('Accept', 'application/json')
|
||||
.end (res) ->
|
||||
expect(res.statusCode).to.be 401
|
||||
expect(res.body.err).to.be 'You must include a token and uid (user id) in your request'
|
||||
done()
|
||||
|
||||
describe 'With token and user id', ->
|
||||
params = null
|
||||
currentUser = null
|
||||
|
||||
before ->
|
||||
user = model.at("users.#{uid}")
|
||||
currentUser = user.get()
|
||||
params =
|
||||
title: 'Title'
|
||||
text: 'Text'
|
||||
type: 'habit'
|
||||
|
||||
beforeEach ->
|
||||
currentUser = user.get()
|
||||
|
||||
it 'GET /api/v1/user', (done) ->
|
||||
request.get("#{baseURL}/user")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
expect(res.body.id).not.to.be.empty()
|
||||
self = _.clone(currentUser)
|
||||
delete self.apiToken
|
||||
self.stats.toNextLevel = 150
|
||||
self.stats.maxHealth = 50
|
||||
|
||||
expect(res.body).to.eql self
|
||||
done()
|
||||
|
||||
it 'GET /api/v1/user/task/:id', (done) ->
|
||||
tid = _.pluck(currentUser.tasks, 'id')[0]
|
||||
request.get("#{baseURL}/user/task/#{tid}")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
expect(res.body).to.eql currentUser.tasks[tid]
|
||||
done()
|
||||
|
||||
it 'POST /api/v1/user/task', (done) ->
|
||||
request.post("#{baseURL}/user/task")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send(params)
|
||||
.end (res) ->
|
||||
query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
|
||||
query.fetch (err, user) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 201
|
||||
expect(res.body.id).not.to.be.empty()
|
||||
# Ensure that user owns the newly created object
|
||||
expect(user.get().tasks[res.body.id]).to.be.an('object')
|
||||
done()
|
||||
|
||||
it 'POST /api/v1/user/task (without type)', (done) ->
|
||||
request.post("#{baseURL}/user/task")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send({})
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be 'type must be habit, todo, daily, or reward'
|
||||
expect(res.statusCode).to.be 400
|
||||
done()
|
||||
|
||||
it 'POST /api/v1/user/task (only type)', (done) ->
|
||||
request.post("#{baseURL}/user/task")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send(type: 'habit')
|
||||
.end (res) ->
|
||||
query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
|
||||
query.fetch (err, user) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 201
|
||||
expect(res.body.id).not.to.be.empty()
|
||||
# Ensure that user owns the newly created object
|
||||
expect(user.get().tasks[res.body.id]).to.be.an('object')
|
||||
done()
|
||||
|
||||
it 'PUT /api/v1/user/task/:id', (done) ->
|
||||
tid = _.pluck(currentUser.tasks, 'id')[0]
|
||||
request.put("#{baseURL}/user/task/#{tid}")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send(text: 'bye')
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
currentUser.tasks[tid].text = 'bye'
|
||||
expect(res.body).to.eql currentUser.tasks[tid]
|
||||
done()
|
||||
|
||||
it 'PUT /api/v1/user/task/:id (shouldnt update type)', (done) ->
|
||||
tid = _.pluck(currentUser.tasks, 'id')[1]
|
||||
type = if currentUser.tasks[tid].type is 'habit' then 'daily' else 'habit'
|
||||
request.put("#{baseURL}/user/task/#{tid}")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send(type: type, text: 'fishman')
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
currentUser.tasks[tid].text = 'fishman'
|
||||
expect(res.body).to.eql currentUser.tasks[tid]
|
||||
done()
|
||||
|
||||
it 'PUT /api/v1/user/task/:id (update notes)', (done) ->
|
||||
tid = _.pluck(currentUser.tasks, 'id')[2]
|
||||
request.put("#{baseURL}/user/task/#{tid}")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.send(text: 'hi',notes:'foobar matey')
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
currentUser.tasks[tid].text = 'hi'
|
||||
currentUser.tasks[tid].notes = 'foobar matey'
|
||||
expect(res.body).to.eql currentUser.tasks[tid]
|
||||
done()
|
||||
|
||||
it 'GET /api/v1/user/tasks', (done) ->
|
||||
request.get("#{baseURL}/user/tasks")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.end (res) ->
|
||||
query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
|
||||
query.fetch (err, user) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
model.ref '_user', user
|
||||
tasks = []
|
||||
for type in ['habit','todo','daily','reward']
|
||||
model.refList "_#{type}List", "_user.tasks", "_user.#{type}Ids"
|
||||
tasks = tasks.concat model.get("_#{type}List")
|
||||
# Ensure that user owns the tasks
|
||||
expect(res.body.length).to.equal tasks.length
|
||||
# Ensure that the two sets are equal
|
||||
expect(_.difference(_.pluck(res.body,'id'), _.pluck(tasks,'id')).length).to.equal 0
|
||||
done()
|
||||
|
||||
it 'GET /api/v1/user/tasks (todos)', (done) ->
|
||||
request.get("#{baseURL}/user/tasks")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.query(type:'todo')
|
||||
.end (res) ->
|
||||
query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
|
||||
query.fetch (err, user) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 200
|
||||
model.ref '_user', user
|
||||
model.refList "_todoList", "_user.tasks", "_user.todoIds"
|
||||
tasks = model.get("_todoList")
|
||||
# Ensure that user owns the tasks
|
||||
expect(res.body.length).to.equal tasks.length
|
||||
# Ensure that the two sets are equal
|
||||
expect(_.difference(_.pluck(res.body,'id'), _.pluck(tasks,'id')).length).to.equal 0
|
||||
done()
|
||||
|
||||
it 'DELETE /api/v1/user/task/:id', (done) ->
|
||||
tid = currentUser.habitIds[2]
|
||||
request.del("#{baseURL}/user/task/#{tid}")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.end (res) ->
|
||||
expect(res.body.err).to.be undefined
|
||||
expect(res.statusCode).to.be 204
|
||||
query = model.query('users').withIdAndToken(currentUser.id, currentUser.apiToken)
|
||||
query.fetch (err, user) ->
|
||||
expect(user.get('habitIds').indexOf(tid)).to.be -1
|
||||
expect(user.get("tasks.#{tid}")).to.be undefined
|
||||
done()
|
||||
|
||||
it 'DELETE /api/v1/user/task/:id (no task found)', (done) ->
|
||||
tid = "adsfasdfjunkshouldntbeatask"
|
||||
request.del("#{baseURL}/user/task/#{tid}")
|
||||
.set('Accept', 'application/json')
|
||||
.set('X-API-User', currentUser.id)
|
||||
.set('X-API-Key', currentUser.apiToken)
|
||||
.end (res) ->
|
||||
expect(res.statusCode).to.be 400
|
||||
expect(res.body.err).to.be 'No task found.'
|
||||
done()
|
||||
|
|
@ -12,8 +12,8 @@ casper.start "#{url}/?play=1", ->
|
|||
@fill 'form#derby-auth-register',
|
||||
username: user1.id
|
||||
email: "{user1.id}@gmail.com"
|
||||
'email-confirmation': "{user1.id}@gmail.com"
|
||||
password: 'habitrpg123'
|
||||
'password-confirmation': "habitrpg123"
|
||||
, true
|
||||
casper.thenOpen "#{url}/logout"
|
||||
casper.thenOpen "#{url}/?play=1", ->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
--colors
|
||||
--reporter spec
|
||||
--timeout 1200
|
||||
--timeout 2800
|
||||
--ignore-leaks
|
||||
--growl
|
||||
--debug
|
||||
--compilers coffee:coffee-script
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
{expect} = require 'derby/node_modules/racer/test/util'
|
||||
{BrowserModel: Model} = require 'derby/node_modules/racer/test/util/model'
|
||||
derby = require 'derby'
|
||||
clone = require 'clone'
|
||||
lodash = require 'lodash'
|
||||
_ = require 'underscore'
|
||||
moment = require 'moment'
|
||||
|
||||
# Custom modules
|
||||
scoring = require '../src/app/scoring'
|
||||
schema = require '../src/app/schema'
|
||||
schema = require '../src/app/character'
|
||||
helpers = require '../src/app/helpers'
|
||||
|
||||
###### Helpers & Variables ######
|
||||
|
||||
|
|
@ -19,8 +20,8 @@ taskPath = null
|
|||
# Otherwise, using model.get(path) will give the same object before as after
|
||||
pathSnapshots = (paths) ->
|
||||
if _.isString(paths)
|
||||
return clone(model.get(paths))
|
||||
_.map paths, (path) -> clone(model.get(path))
|
||||
return lodash.cloneDeep(model.get(paths))
|
||||
_.map paths, (path) -> lodash.cloneDeep(model.get(path))
|
||||
statsTask = -> pathSnapshots(['_user.stats', taskPath]) # quick snapshot of user.stats & task
|
||||
|
||||
cleanUserObj = ->
|
||||
|
|
@ -67,6 +68,18 @@ modificationsLookup = (direction, options = {}) ->
|
|||
|
||||
###### Specs ######
|
||||
|
||||
describe 'Cron', ->
|
||||
it 'calculates day differences with dayStart properly', ->
|
||||
dayStart = 4
|
||||
yesterday = moment().subtract('d', 1).add('h', dayStart)
|
||||
now = moment().startOf('day').add('h', dayStart-1) #today
|
||||
console.log {yesterday: yesterday.format('MM/DD HH:00'), now: now.format('MM/DD HH:00')}
|
||||
console.log {diff: Math.abs(moment(yesterday).diff(moment(now), 'days'))}
|
||||
expect(helpers.daysBetween(yesterday, now, dayStart)).to.eql 0
|
||||
now = moment().startOf('day').add('h', dayStart)
|
||||
console.log {now: now.format('MM/DD HH:00')}
|
||||
expect(helpers.daysBetween(yesterday, now, dayStart)).to.eql 1
|
||||
|
||||
describe 'User', ->
|
||||
model = null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,32 @@
|
|||
<alerts:>
|
||||
{#if _flash.error}
|
||||
<ul class="alert alert-error">
|
||||
<ul class="unstyled alert alert-error">
|
||||
{#each _flash.error as :error}<li>{:error}</li>{/}
|
||||
</ul>
|
||||
{/}
|
||||
|
||||
{#if equal(_user.flags.algosNotification,'show')}
|
||||
<div class='alert alert-success'>
|
||||
<a x-bind="click:closeAlgosNotification" class=pull-right>[x]</a>
|
||||
<p>New features! Custom day start, new and better algorithms, authentication enhancements. See <a target="_blank" href="http://habitrpg.tumblr.com/post/44468869138/custom-day-start-better-algorithms-auth-enhancements">details here.</a>
|
||||
"Hey, I had more Exp before this roll-out!" <a data-target="#restore-modal" data-toggle=modal>Restore your Exp here.</a></p>
|
||||
</div>
|
||||
{/}
|
||||
|
||||
{#if equal(_user.flags.onliesNotification, 'show')}
|
||||
<div class='alert alert-success'>
|
||||
<a x-bind="click:closeOnliesNotification " class=pull-right>[x]</a>
|
||||
<p>
|
||||
"Onlies" now gain / lose value just like other habits, and are reset back to 0 at the beginning of each day.
|
||||
See <a target="_blank" href="https://trello.com/card/shading-onlies/50e5d3684fe3a7266b0036d6/67">details here</a>, and
|
||||
chime in with your recommendations on this mechanic.
|
||||
</p>
|
||||
</div>
|
||||
{/}
|
||||
|
||||
{#if equal(_user.flags.priorityNotification, 'show')}
|
||||
<div class='alert alert-success'>
|
||||
<a x-bind="click:closePriorityNotification" class=pull-right>[x]</a>
|
||||
<p>New Feature: Priority Multiplier! You can now multiply "more important" tasks on a 1x, 1.5x, or 2x scale. <a target="_blank" href="https://trello.com/card/priority-multiplier/50e5d3684fe3a7266b0036d6/17">See details</a>.</p>
|
||||
</div>
|
||||
{/}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
<modals:>
|
||||
<app:modals:modal modalId="avatar-modal">
|
||||
{#if _profileActiveMain}
|
||||
{#with _user.preferences}
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle='tab' href="#profileCustomize">Avatar</a></li>
|
||||
<li><a data-toggle='tab' href="#profileEdit">Profile</a></li>
|
||||
|
|
@ -14,7 +13,6 @@
|
|||
|
||||
<div class="tab-pane" id="profileEdit"><app:avatar:profile user="{_user}" main="true" /></div>
|
||||
</div>
|
||||
{/}
|
||||
{else}
|
||||
<app:avatar:profile profile={_profileActive} />
|
||||
{/}
|
||||
|
|
@ -25,17 +23,26 @@
|
|||
|
||||
<avatar:>
|
||||
<div class="avatar {{#if @main}}main-avatar{{/}} {{#if @party}}party-avatar{{/}}" data-toggle='modal' data-target='#avatar-modal' data-uid="{@profile.id}" x-bind="click:profileChangeActive">
|
||||
{#unless @minimal}{#if @profile.items.pet}<img src='img/sprites/{@profile.items.pet.icon}' />{/}{/}
|
||||
<div class='character-sprites'>
|
||||
|
||||
{#if and(@profile.items.pet, not(@minimal))}
|
||||
<img src='/img/sprites/{@profile.items.pet.icon}' />
|
||||
{/}
|
||||
|
||||
{#with @profile.preferences as :p}
|
||||
<div class='character-sprites'>
|
||||
<span class='{:p.gender}_skin_{:p.skin}'></span>
|
||||
<span class='{:p.gender}_hair_{:p.hair}'></span>
|
||||
<span class="{equipped(@profile, 'armor')}"></span>
|
||||
{#if :p.showHelm}
|
||||
<span class="{equipped(@profile, 'head')}"></span>
|
||||
{else}
|
||||
<span class="{:p.gender}_head_0"></span>
|
||||
{/}
|
||||
<span class='{:p.gender}_shield_{@profile.items.shield}'></span>
|
||||
<span class='{:p.gender}_weapon_{@profile.items.weapon}'></span>
|
||||
{/}
|
||||
</div>
|
||||
{/}
|
||||
|
||||
{{#unless @minimal}}
|
||||
<div class="lvl"><span class="badge badge-info">Lvl {@profile.stats.lvl}</span></div>
|
||||
{{/}}
|
||||
|
|
@ -49,33 +56,38 @@
|
|||
</figure>
|
||||
|
||||
<!-- customization options -->
|
||||
{#with _user.preferences as :p}
|
||||
<menu type="list">
|
||||
<!-- gender -->
|
||||
<li class="customize-menu">
|
||||
<menu label="Gender">
|
||||
<menu label="Head">
|
||||
<button type="button" class="m_head_0 customize-option" data-value="m" x-bind="click:customizeGender"></button>
|
||||
<button type="button" class="f_head_0 customize-option" data-value="f" x-bind="click:customizeGender"></button>
|
||||
</menu>
|
||||
<label class="checkbox">
|
||||
<input type=checkbox checked="{_user.preferences.showHelm}" /> Show Helm
|
||||
</label>
|
||||
|
||||
</li>
|
||||
|
||||
<!-- hair -->
|
||||
<li class="customize-menu">
|
||||
<menu label="Hair">
|
||||
<button type="button" class="{.gender}_hair_blond customize-option" data-value="blond" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{.gender}_hair_black customize-option" data-value="black" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{.gender}_hair_brown customize-option" data-value="brown" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{.gender}_hair_white customize-option" data-value="white" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{:p.gender}_hair_blond customize-option" data-value="blond" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{:p.gender}_hair_black customize-option" data-value="black" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{:p.gender}_hair_brown customize-option" data-value="brown" x-bind="click:customizeHair"></button>
|
||||
<button type="button" class="{:p.gender}_hair_white customize-option" data-value="white" x-bind="click:customizeHair"></button>
|
||||
</menu>
|
||||
</li>
|
||||
|
||||
<!-- skin -->
|
||||
<li class="customize-menu">
|
||||
<menu label="Skin">
|
||||
<button type="button" class='{.gender}_skin_dead customize-option' data-value="dead" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{.gender}_skin_orc customize-option' data-value="orc" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{.gender}_skin_asian customize-option' data-value="asian" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{.gender}_skin_black customize-option' data-value="black" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{.gender}_skin_white customize-option' data-value="white" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{:p.gender}_skin_dead customize-option' data-value="dead" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{:p.gender}_skin_orc customize-option' data-value="orc" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{:p.gender}_skin_asian customize-option' data-value="asian" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{:p.gender}_skin_black customize-option' data-value="black" x-bind="click:customizeSkin"></button>
|
||||
<button type="button" class='{:p.gender}_skin_white customize-option' data-value="white" x-bind="click:customizeSkin"></button>
|
||||
</menu>
|
||||
</li>
|
||||
</menu>
|
||||
|
|
@ -90,12 +102,12 @@
|
|||
</li>
|
||||
</menu>
|
||||
{/}
|
||||
{/}
|
||||
|
||||
<profile:>
|
||||
<div class='row-fluid'>
|
||||
<div class='span6 well'>
|
||||
{#if _profileEditing}
|
||||
{#if _profileActiveMain}
|
||||
{#if and(_profileEditing, _profileActiveMain)}
|
||||
<a class='btn btn-success' x-bind="click:profileSave">Save</a>
|
||||
<!-- TODO use photo-upload instead: https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/xMmADvxBOak -->
|
||||
<div class="control-group">
|
||||
|
|
@ -135,11 +147,12 @@
|
|||
|
||||
</div>
|
||||
<a class='btn btn-success' x-bind="click:profileSave">Save</a>
|
||||
{/}
|
||||
{else}
|
||||
<h3>
|
||||
{#if _profileActive.profile.name}{_profileActive.profile.name}
|
||||
{else}{username(_profileActive.auth)}
|
||||
{#if _profileActive.profile.name}
|
||||
{_profileActive.profile.name}
|
||||
{else}
|
||||
{username(_profileActive.auth)}
|
||||
{/}
|
||||
{#if _profileActiveMain}<a class='btn pull-right' x-bind="click:profileEdit">Edit</a>{/}
|
||||
</h3>
|
||||
|
|
|
|||
|
|
@ -1,41 +1,71 @@
|
|||
<footer:>
|
||||
<footer class=footer>
|
||||
<div class=container>
|
||||
{#if equal(_view.nodeEnv, 'production')}
|
||||
<table class=pull-right><tr>
|
||||
<td><!-- Github -->
|
||||
<iframe src="/vendor/github-buttons/github-btn.html?user=lefnire&repo=habitrpg&type=watch&count=true"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="85px" height="20px"></iframe>
|
||||
</td>
|
||||
<td>
|
||||
<div class='row'>
|
||||
|
||||
<div class='span3'>
|
||||
<h4>Company</h4>
|
||||
<ul class='unstyled'>
|
||||
<li><a href="/static/about">About</a></li>
|
||||
<li><a target="_blank" href="http://habitrpg.tumblr.com/">Blog</a></li>
|
||||
<li><a href="/static/team">Team</a></li>
|
||||
<li><a href="/static/extensions">Extensions</a></li>
|
||||
<li><a href="/static/faq">FAQ</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class='span3'>
|
||||
<h4>Legal</h4>
|
||||
<ul class='unstyled'>
|
||||
<li><a href="/static/privacy">Privacy</a></li>
|
||||
<li><a href="/static/terms">Terms</a></li>
|
||||
<li><strong>© 2012 OCDevel LLC</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class='span3'>
|
||||
<h4>Community</h4>
|
||||
<ul class='unstyled'>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg/issues?state=open">Submit Bug</a></li>
|
||||
<li><a target="_blank" href="https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6">Request Feature</a></li>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg/wiki/API">API</a></li>
|
||||
<li><a href="/static/extensions">Add-ons / Extensions</a></li>
|
||||
<li><a target="_blank" href="http://www.kickstarter.com/projects/lefnire/habitrpg-mobile">Kickstarter</a></li>
|
||||
<li><a target="_blank" href="https://www.facebook.com/Habitrpg">Facebook Page</a></li>
|
||||
<li><a target="_blank" href="http://www.reddit.com/r/habitrpg/">Reddit</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class='span3'>
|
||||
{{#if equal(_view.nodeEnv, 'development')}}
|
||||
<h4>Cheat</h4>
|
||||
<ul class='unstyled'>
|
||||
<li><button class='btn' x-bind="click:emulateNextDay">Emulate Next Day</button></li>
|
||||
<li><button class='btn' x-bind="click:emulateTenDays">Emulate 10 Days</button></li>
|
||||
<li><button class='btn' x-bind="click:cheat">Insta Level</button></li>
|
||||
<li><button class='btn' x-bind='click:reset'>Reset Level</button></li>
|
||||
</ul>
|
||||
{{else}}
|
||||
<h4>Social</h4>
|
||||
<div class="addthis_toolbox addthis_default_style "
|
||||
addthis:url="https://habitrpg.com"
|
||||
addthis:title="HabitRPG - Gamify Your Life">
|
||||
<a class="addthis_button_facebook_like" fb:like:layout="button_count"></a>
|
||||
<a class="addthis_button_tweet" tw:via="habitrpg"></a>
|
||||
<table>
|
||||
<tr><td><a class="addthis_button_facebook_like" fb:like:layout="button_count"></a></td></tr>
|
||||
<tr><td><a class="addthis_button_tweet" tw:via="habitrpg"></a></td></tr>
|
||||
<tr><td align='left'>
|
||||
<iframe src="/vendor/github-buttons/github-btn.html?user=lefnire&repo=habitrpg&type=watch&count=true"
|
||||
allowtransparency="true" frameborder="0" scrolling="0" width="85px" height="20px" ></iframe>
|
||||
</td></tr>
|
||||
<tr><td><a class="addthis_button_google_plusone" g:plusone:size="medium"></a> </td></tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr></table>
|
||||
{else}
|
||||
<div class='pull-right'>
|
||||
<button class='btn' x-bind="click:emulateNextDay">Emulate Next Day</button>
|
||||
<button class='btn' x-bind="click:cheat">Add GP & Exp</button>
|
||||
{{/}}
|
||||
|
||||
</div>
|
||||
{/}
|
||||
<div>
|
||||
<ul>
|
||||
<li>Copyright © 2012 OCDevel LLC</li>
|
||||
<li><a href="/splash.html">About</a></li>
|
||||
<li><a target="_blank" href="http://habitrpg.tumblr.com/">Blog</a></li>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg/issues?state=open">Bugs</a></li>
|
||||
<li><a target="_blank" href="https://trello.com/board/habitrpg/50e5d3684fe3a7266b0036d6">Features</a></li>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg/wiki/FAQ">FAQ</a></li>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg/wiki/API">API</a></li>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg/wiki/Add-ons-and-Extensions">Add-ons / Extensions</a></li>
|
||||
<li><a target="_blank" href="https://github.com/lefnire/habitrpg#contact">Contact</a></li>
|
||||
<li><a href="/privacy">Privacy</a></li>
|
||||
<li><a href="/terms">Terms</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
<header:>
|
||||
{#unless equal(_user.preferences.hideHeader,true)}
|
||||
<div class="row-fluid">
|
||||
<div class='char-status {#if gt(_partyMembers.length,1)}span8 offset2 has-party{else}span6 offset3{/}'>
|
||||
|
||||
|
|
@ -8,28 +9,44 @@
|
|||
</figure>
|
||||
|
||||
<!-- party -->
|
||||
{#each _partyMembers as :member}
|
||||
{#unless equal(:member.id, _userId)}
|
||||
<figure class="party-avatar-wrap" rel="tooltip" title="{username(:member.auth)}" data-placement="bottom">
|
||||
<app:avatar:avatar profile={:member} party="true" />
|
||||
{{#each _partyMembers as :member}}
|
||||
<!-- #each requires an element, so #if / #unless blocks nested immediately inside cause issues if they return false -->
|
||||
<!-- see https://github.com/codeparty/derby/issues/215 -->
|
||||
<span>
|
||||
{{#unless equal(:member.id, _userId)}}
|
||||
<figure class="party-avatar-wrap" rel="popover" data-title="{{username(:member.auth)}}" data-placement="bottom" data-trigger="hover" data-html="true" data-content="
|
||||
<div>
|
||||
<div class='progress progress-danger' style='height:5px;'>
|
||||
<div class='bar' style='height: 5px; width: {percent(:member.stats.hp, 50)}%;'></div>
|
||||
</div>
|
||||
<div class='progress progress-warning' style='height:5px;'>
|
||||
<div class='bar' style='height: 5px; width: {percent(:member.stats.exp, tnl(:member.stats.lvl))}%;'></div>
|
||||
</div>
|
||||
<div>GP: {{floor(:member.stats.gp)}}</div>
|
||||
</div>
|
||||
">
|
||||
<!-- Would be way cleaner as a Derby template `data-content="<app:party:member-stats profile={{:member}} />"`, but it was just printing HTML as text -->
|
||||
|
||||
<app:avatar:avatar profile={{:member}} party="true" />
|
||||
</figure>
|
||||
{/}
|
||||
{/}
|
||||
{{/}}
|
||||
</span>
|
||||
{{/}}
|
||||
|
||||
<!-- progress bars -->
|
||||
<div class="progress-bars">
|
||||
<div class="progress progress-danger" rel=tooltip data-placement=bottom title="Health">
|
||||
<div class="bar" style="width: {percent(_user.stats.hp, 50)}%;"></div>
|
||||
<span class="progress-text"><i class=icon-heart></i> {round(_user.stats.hp)} / 50</span>
|
||||
<span class="progress-text"><i class=icon-heart></i> {ceil(_user.stats.hp)} / 50</span>
|
||||
</div>
|
||||
|
||||
<div class="progress progress-warning" rel=tooltip data-placement=bottom title="Experience">
|
||||
<div class="bar" style="width: {percent(_user.stats.exp,_tnl)}%;"></div>
|
||||
<div class="bar" style="width: {percent(_user.stats.exp,tnl(_user.stats.lvl))}%;"></div>
|
||||
<span class="progress-text">
|
||||
{#if _user.history.exp}
|
||||
<a x-bind=click:toggleChart data-toggle-id="exp-chart" data-history-path="_user.history.exp" rel=tooltip title="Progress"><i class=icon-signal></i></a>
|
||||
{/}
|
||||
<i class=icon-star></i> {round(_user.stats.exp)} / {_tnl}
|
||||
<i class=icon-star></i> {floor(_user.stats.exp)} / {tnl(_user.stats.lvl)}
|
||||
</span>
|
||||
</div>
|
||||
<div id="exp-chart" style="display:none;"></div>
|
||||
|
|
@ -38,3 +55,4 @@
|
|||
</div>
|
||||
<!-- end .row-fluid -->
|
||||
</div>
|
||||
{/}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
<import: src="modals">
|
||||
<import: src="tasks" ns="">
|
||||
<import: src="tasks">
|
||||
<import: src="header">
|
||||
<import: src="alerts">
|
||||
<import: src="avatar">
|
||||
<import: src="rewards" ns="">
|
||||
<import: src="rewards">
|
||||
<import: src="footer">
|
||||
<import: src="settings">
|
||||
<import: src="party">
|
||||
|
|
@ -13,13 +13,23 @@
|
|||
HabitRPG | Gamify Your Life
|
||||
|
||||
<Head:>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
|
||||
<!-- webfonts -->
|
||||
<link href='//fonts.googleapis.com/css?family=Lato:300,400,700,400italic,700italic' rel='stylesheet' type='text/css'>
|
||||
|
||||
<!-- CDN -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||
|
||||
<Header:>
|
||||
<app:modals:modals />
|
||||
<ui:connectionAlert>
|
||||
<div id="head" class="container-fluid">
|
||||
|
||||
{#if _undo}<a x-bind="click:undo" class='label undo-button'>Undo</a>{/}
|
||||
|
||||
<app:settings:menu />
|
||||
<app:header:header />
|
||||
</div>
|
||||
|
|
@ -27,25 +37,36 @@
|
|||
<Body:>
|
||||
<br/>
|
||||
<div id="notification-area"></div>
|
||||
<div id=wrap class="container-fluid">
|
||||
<div id="wrap">
|
||||
<app:alerts:alerts />
|
||||
<app:filters:filters />
|
||||
<div id=main class="row-fluid">
|
||||
<app:taskLists />
|
||||
<div id=main class="grid">
|
||||
<app:tasks:taskLists />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app:footer:footer />
|
||||
|
||||
<Scripts:>
|
||||
<!-- scripts go in /src/app/browser.coffee, that way we can get min/concat -->
|
||||
<!-- only the ones that are remote and can't be done async are here -->
|
||||
<!-- we load as many CDN-available javascript files here as possible. For the rest,
|
||||
we use /src/app/browser.coffee -> loadJavaScripts() which concats/minifies /public/vendor javascript libraries,
|
||||
as well as runs async loads -->
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.3.1/jquery.cookie.min.js"></script>
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-growl/1.0.0/jquery.bootstrap-growl.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.0.0/js/bootstrap-datepicker.min.js"></script>
|
||||
|
||||
<script src="https://checkout.stripe.com/v2/checkout.js"></script>
|
||||
|
||||
{#if equal(_view.nodeEnv,"production")}
|
||||
<!-- Google Analytics -->
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-33510635-1']);
|
||||
_gaq.push(['_setDomainName', 'habitrpg.com']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<!-- Re-Roll modal -->
|
||||
<app:modals:modal modalId='reroll-modal' header="Reset Your Tasks">
|
||||
<app:userTokens/>
|
||||
<app:rewards:userTokens/>
|
||||
<p>Highly discouraged because red tasks provide good incentive to improve (<a target="_blank" href="https://github.com/lefnire/habitrpg#all-my-tasks-are-red-im-dying-too-fast">read more</a>). However, this becomes necessary after long bouts of bad habits.</p>
|
||||
<@footer>
|
||||
{#if lt(_user.balance,1)}
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
<!-- Buy more tokens modal -->
|
||||
<app:modals:modal modalId='more-tokens-modal' header="Out Of Tokens">
|
||||
<app:userTokens/>
|
||||
<app:rewards:userTokens/>
|
||||
<p>You're out of tokens, which are used to buy pets and mounts.</p>
|
||||
<@footer>
|
||||
<a data-dismiss="modal" x-bind="click:showStripe" class="btn btn-success btn-large">Buy More Tokens</a><span class='token-cost'>Not enough tokens</span>
|
||||
|
|
|
|||
|
|
@ -1,65 +1,142 @@
|
|||
<rewardsTab:>
|
||||
<ul class='rewards'>
|
||||
{#each _rewardList as :task}<app:task />{/}
|
||||
<rewardsColumn:>
|
||||
<div class="module">
|
||||
<div class="task-column rewards">
|
||||
<!-- cash or tokens -->
|
||||
<span class='option-box pull-right wallet'>
|
||||
{#unless _view.activeTabPets}
|
||||
<div class="money">
|
||||
{gold(_user.stats.gp)}
|
||||
<span class='shop_gold' rel='tooltip' title='Gold'></span>
|
||||
</div>
|
||||
<div class="money">
|
||||
{silver(_user.stats.gp)}
|
||||
<span class='shop_silver' rel='tooltip' title='Silver'></span>
|
||||
</div>
|
||||
<!-- <div class="money">
|
||||
{copper(_user.stats.gp)}
|
||||
<span class='shop_copper' rel='tooltip' title='Copper'></span>
|
||||
</div> -->
|
||||
{else}
|
||||
<app:rewards:userTokens />
|
||||
{/}
|
||||
</span>
|
||||
<h2 class="task-column_title">Rewards</h2>
|
||||
|
||||
<!-- if pets are enabled, make tabs -->
|
||||
{#if equal(_user.flags.petsEnabled,true)}
|
||||
<div class='tabbable tabs-below'>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="rewards-tab">
|
||||
<app:rewards:rewardsTab />
|
||||
</div>
|
||||
|
||||
<!-- pets pane -->
|
||||
<div class="tab-pane pet-grid" id="pets-tab">
|
||||
{#with _view.items.pets as :pets}
|
||||
<table>
|
||||
<tr>
|
||||
{#with :pets[0]}<app:rewards:pet />{/}
|
||||
{#with :pets[1]}<app:rewards:pet />{/}
|
||||
{#with :pets[2]}<app:rewards:pet />{/}
|
||||
{#with :pets[3]}<app:rewards:pet />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :pets[4]}<app:rewards:pet />{/}
|
||||
{#with :pets[5]}<app:rewards:pet />{/}
|
||||
{#with :pets[6]}<app:rewards:pet />{/}
|
||||
{#with :pets[7]}<app:rewards:pet />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :pets[8]}<app:rewards:pet />{/}
|
||||
{#with :pets[9]}<app:rewards:pet />{/}
|
||||
{#with :pets[10]}<app:rewards:pet />{/}
|
||||
{#with :pets[11]}<app:rewards:pet />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :pets[12]}<app:rewards:pet />{/}
|
||||
{#with :pets[13]}<app:rewards:pet />{/}
|
||||
{#with :pets[14]}<app:rewards:pet />{/}
|
||||
{#with :pets[15]}<app:rewards:pet />{/}
|
||||
</tr>
|
||||
<tr>
|
||||
{#with :pets[16]}<app:rewards:pet />{/}
|
||||
</tr>
|
||||
</table>
|
||||
{/}
|
||||
</div>
|
||||
<!--<div class="tab-pane" id="mounts-tab">...</div>-->
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-tabs" id="rewardsTabs">
|
||||
<li class="active"><a href="#rewards-tab" data-toggle="tab" x-bind="click:activateRewardsTab">Rewards</a></li>
|
||||
<li><a href="#pets-tab" data-toggle="tab" x-bind='click:activatePetsTab'>Pets</a></li>
|
||||
<!--<li><a href="#mounts-tab" data-toggle="tab">Mounts</a></li>-->
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- if pets are disabled, just do rewards tab -->
|
||||
{else}
|
||||
<app:rewards:rewardsTab />
|
||||
{/}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<rewardsTab:>
|
||||
<!-- add new reward -->
|
||||
<app:tasks:newTask type="reward" inputValue="{_newReward}" placeHolder="New Reward" />
|
||||
|
||||
{#if _rewardList}
|
||||
<ul class='rewards'>
|
||||
{#each _rewardList as :task}<app:tasks:task />{/}
|
||||
</ul>
|
||||
{/}
|
||||
|
||||
|
||||
{#if _user.flags.itemsEnabled}
|
||||
<ul class='items'>
|
||||
{#with _view.items.weapon as :item}<app:item />{/}
|
||||
{#with _view.items.armor as :item}<app:item />{/}
|
||||
{#with _view.items.head as :item}<app:item />{/}
|
||||
{#with _view.items.shield as :item}<app:item />{/}
|
||||
{#with _view.items.potion as :item}<app:item />{/}
|
||||
{#with _view.items.reroll as :item}<app:item reroll=true />{/}
|
||||
{#with _view.items.weapon as :item}<app:rewards:item />{/}
|
||||
{#with _view.items.armor as :item}<app:rewards:item />{/}
|
||||
{#with _view.items.head as :item}<app:rewards:item />{/}
|
||||
{#with _view.items.shield as :item}<app:rewards:item />{/}
|
||||
{#with _view.items.potion as :item}<app:rewards:item />{/}
|
||||
{#with _view.items.reroll as :item}<app:rewards:item reroll=true />{/}
|
||||
</ul>
|
||||
{/}
|
||||
|
||||
<pet:>
|
||||
<div class="span3 shop-pet" rel=tooltip title="{.text} - {.value} Tokens">
|
||||
<div style='position:relative'>
|
||||
{#if _user.items.pets[.name]}<i class='icon-ok shop-pet-owned-check'></i>{/}
|
||||
<img x-bind="click:selectPet" data-pet='{.name}' src='img/sprites/{.icon}'/>
|
||||
</div>
|
||||
</div>
|
||||
<td class="{#if _user.items.pets[.name]}active-pet{/}">
|
||||
<img rel=tooltip title="{.text} - {.value} Tokens" x-bind="click:selectPet" data-pet='{.name}' src='img/sprites/{.icon}'/>
|
||||
</td>
|
||||
|
||||
|
||||
<userTokens:>
|
||||
<div class="pull-right">
|
||||
<div class="input-append">
|
||||
<span class="uneditable-input" style="width:auto;">{tokens(_user.balance)}</span>
|
||||
<span class="add-on">Tokens</span>
|
||||
</div>
|
||||
</div>
|
||||
<!--<span class="well pull-right">Tokens: {tokens(_user.balance)}</span>-->
|
||||
<a class="pull-right token-wallet">
|
||||
<span class="task-action-btn tile flush bright add-token-btn">+</span>
|
||||
<span class="task-action-btn tile flush neutral">{tokens(_user.balance)} Tokens</span>
|
||||
</a>
|
||||
|
||||
|
||||
|
||||
<item:>
|
||||
{#unless :item.hide}
|
||||
<li class="task reward item">
|
||||
<pre>
|
||||
<li class="task reward-item {#if :item.hide}hide{/}">
|
||||
<!-- right-hand side control buttons -->
|
||||
<div class="task-meta-controls">
|
||||
<span rel="popover" data-trigger="hover" data-placement="left" data-content="{:item.notes}" data-original-title="{:item.text}" class='task-notes'><i class="icon-comment"></i></span>
|
||||
</div>
|
||||
|
||||
<!-- left-hand size commands -->
|
||||
<div class="task-controls">
|
||||
{#if @reroll}
|
||||
<a href="#" data-toggle="modal" data-target="#reroll-modal" class="item-buy-link" style='cursor:pointer'><i class=icon-retweet></i></a>
|
||||
<a class="task-action-btn btn-reroll" data-toggle="modal" data-target="#reroll-modal">⟲</a>
|
||||
{else}
|
||||
<a x-bind=click:buyItem class="item-buy-link" data-type={:item.type} data-value={:item.value} data-index={:item.index}>{:item.value}<img src="/img/coin_single_gold.png"/></a>
|
||||
<a class="money btn-buy item-btn" x-bind=click:buyItem data-type={:item.type} data-value={:item.value} data-index={:item.index}>
|
||||
<span class="reward-cost">{:item.value}</span>
|
||||
<span class='shop_gold'></span>
|
||||
</a>
|
||||
{/}
|
||||
</div>
|
||||
<div class="task-text">
|
||||
{#if :item.icon}
|
||||
<img src="/img/sprites/{:item.icon}" /> {:item.text}
|
||||
{else}
|
||||
<table class='shop-table'><tr>
|
||||
<td><div class="shop_{:item.classes} shop-sprite"></div></td>
|
||||
<td>{:item.text}</td>
|
||||
</tr></table>
|
||||
{/}
|
||||
|
||||
</div>
|
||||
</pre>
|
||||
<!-- main content -->
|
||||
<span class="shop_{:item.classes} shop-sprite item-img"></span>
|
||||
<p class="task-text">{:item.text}</p>
|
||||
</li>
|
||||
{/}
|
||||
|
|
@ -1,22 +1,48 @@
|
|||
<modals:>
|
||||
{{#if _loggedIn}}
|
||||
<app:modals:modal modalId="settings-modal" header="Settings">
|
||||
<h4>API</h4>
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active"><a data-toggle="tab" href="#" data-target="#settings-settings">General</a></li>
|
||||
<li><a data-toggle="tab" href="#" data-target="#settings-api">API</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="settings-settings">
|
||||
<h4>Custom Day Start</h4>
|
||||
<div class="input-append">
|
||||
<input class="span2" type="number" min=0 max=24 value={_user.preferences.dayStart} />
|
||||
<span class="add-on">:00 (24h)</span>
|
||||
</div>
|
||||
<div>
|
||||
<small>Habit defaults to check and reset your dailies at midnight each day. You can customize that here (Enter number between 0 and 24).</small>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<h4>Misc</h4>
|
||||
<button x-bind="click:toggleHeader" class="btn">{#if _user.preferences.hideHeader}Show Header{else}Hide Header{/}</button>
|
||||
{#unless _user.flags.partyEnabled} <button x-bind="click:manuallyUnlockParties" data-dismiss='modal' rel=tooltip title="Parties are unlocked automatically for usability reasons, but you can override that here.">Enable Parties</button>{/}
|
||||
|
||||
{{#if _user.auth.local}}
|
||||
<hr/>
|
||||
<h4>Change Password</h4>
|
||||
<derby-auth:changePassword />
|
||||
{{/}}
|
||||
|
||||
<hr/>
|
||||
<h4>Danger Zone</h4>
|
||||
<a class='btn btn-danger' data-target="#reset-modal" data-toggle="modal" rel=tooltip title="Resets your entire account (dangerous).">Reset</a>
|
||||
<a class='btn btn-danger' data-target="#restore-modal" data-toggle="modal" rel=tooltip title="Restores attributes to your character.">Restore</a>
|
||||
<a class='btn btn-danger' data-target="#delete-modal" data-toggle="modal" rel=tooltip title="Delete your account.">Delete</a>
|
||||
</div>
|
||||
<div class="tab-pane" id="settings-api">
|
||||
<small>Copy these for use in third party applications.</small>
|
||||
<h6>User ID</h6>
|
||||
<pre class=prettyprint>{_user.id}</pre>
|
||||
|
||||
<h6>API Token</h6>
|
||||
<pre class=prettyprint>{_user.apiToken}</pre>
|
||||
|
||||
{{#if _user.auth.local}}
|
||||
<hr/>
|
||||
<derby-auth:changePassword />
|
||||
{{/}}
|
||||
|
||||
<hr/>
|
||||
<a class='btn btn-danger' data-target="#reset-modal" data-toggle="modal" rel=tooltip title="Resets your entire account (dangerous).">Reset</a>
|
||||
<a class='btn btn-danger' data-target="#restore-modal" data-toggle="modal" rel=tooltip title="Restores attributes to your character.">Restore</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@footer>
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
|
|
@ -81,6 +107,15 @@
|
|||
</@footer>
|
||||
</app:modals:modal>
|
||||
|
||||
<app:modals:modal modalId="delete-modal" header="Delete Account">
|
||||
<p>Woa woa woa! Are you sure? This will seriously delete your account forever, and it can never be restored. If you're absolutely certain, type <strong>DELETE</strong> into the text-box below.</p>
|
||||
<p><input type="text" value="{_deleteAccount}" /></p>
|
||||
<@footer>
|
||||
<button class="btn btn-large" data-dismiss="modal" aria-hidden="true">Cancel</button>
|
||||
<button data-dismiss="modal" x-bind="click:deleteAccount" class="btn btn-danger btn-small {#unless equal(_deleteAccount,'DELETE')}disabled{/}">Delete Account</button>
|
||||
</@footer>
|
||||
</app:modals:modal>
|
||||
|
||||
{{else}}
|
||||
<app:modals:modal modalId="login-modal" header="Login / Register">
|
||||
<a href="/auth/facebook"><img src='/img/facebook-login-register.jpeg' alt="Login / Register With Facebook"/></a>
|
||||
|
|
|
|||
|
|
@ -1,220 +1,197 @@
|
|||
<taskLists:>
|
||||
<!--helpTitle & helpContent moved to tour -->
|
||||
<div class="span3 well habits">
|
||||
<h2>Habits</h2>
|
||||
<app:newTask type="habit" inputValue="{_newHabit}" placeHolder="New Habit" />
|
||||
<ul class="habits">{#each _habitList as :task}<app:task />{/}</ul>
|
||||
|
||||
<!-- Habits Column -->
|
||||
<div class="module">
|
||||
<div class="task-column habits">
|
||||
<h2 class="task-column_title">Habits</h2>
|
||||
<app:tasks:newTask type="habit" inputValue="{_newHabit}" placeHolder="New Habit" />
|
||||
<ul class="habits">{#each _habitList as :task}<app:tasks:task />{/}</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span3 well dailys">
|
||||
<h2>Daily</h2>
|
||||
<app:newTask type="daily" inputValue="{_newDaily}" placeHolder="New Daily" />
|
||||
<ul class='dailys'>{#each _dailyList as :task}<app:task />{/}</ul>
|
||||
<!-- Dailys Column -->
|
||||
<div class="module">
|
||||
<div class="task-column dailys">
|
||||
<h2 class="task-column_title">Daily</h2>
|
||||
<app:tasks:newTask type="daily" inputValue="{_newDaily}" placeHolder="New Daily" />
|
||||
<ul class='dailys'>{#each _dailyList as :task}<app:tasks:task />{/}</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span3 well todos context-enabled context-uncompleted tabbable tabs-below" id="todo-well">
|
||||
<h2>Todos</h2>
|
||||
<div class="display-context-dependant show-for-uncompleted">
|
||||
<app:newTask type="todo" inputValue="{_newTodo}" placeHolder="New Todo" />
|
||||
</div>
|
||||
<ul class='todos task-list'>
|
||||
{#each _todoList as :task}<app:task />{/}
|
||||
</ul>
|
||||
<div class="display-context-dependant show-for-completed">
|
||||
<a class='btn' x-bind=click:clearCompleted>Clear Completed</a>
|
||||
<p> </p>
|
||||
</div>
|
||||
<span class='pull-right'>
|
||||
<!-- Todos Column -->
|
||||
<div class="module">
|
||||
<div class="task-column todos context-enabled context-uncompleted tabbable tabs-below" id="todo-well">
|
||||
<!-- todo export/graph options -->
|
||||
<span class='option-box pull-right'>
|
||||
{#if _user.history.todos}
|
||||
<a x-bind=click:toggleChart data-toggle-id="todos-chart" data-history-path="_user.history.todos" rel=tooltip title="Progress"><i class=icon-signal></i></a>
|
||||
<a class="option-action" x-bind=click:toggleChart data-toggle-id="todos-chart" data-history-path="_user.history.todos" rel=tooltip title="Progress"><i class=icon-signal></i></a>
|
||||
{/}
|
||||
<a href="/v1/users/{{_user.id}}/calendar.ics?apiToken={{_user.apiToken}}"><img src='/img/calendar_ical.png' title="iCal" alt="iCal" rel=tooltip /></a>
|
||||
<!-- <a target="_blank" href="https://www.google.com/calendar/render?cid={{encodeiCalLink(_user.id, _user.apiToken)}}"><img src='/img/calendar_google.png' title="Google Calendar" alt="Google Calendar" rel=tooltip /></a>-->
|
||||
<a class="option-action" href="/v1/users/{{_user.id}}/calendar.ics?apiToken={{_user.apiToken}}" rel=tooltip title="iCal"><i class=icon-calendar></i></a>
|
||||
<!-- <a href="https://www.google.com/calendar/render?cid={{encodeiCalLink(_user.id, _user.apiToken)}}" rel=tooltip title="Google Calendar"><i class=icon-calendar></i></a> -->
|
||||
</span>
|
||||
<h2 class="task-column_title">Todos</h2>
|
||||
|
||||
<!-- create new todo -->
|
||||
<div class="display-context-dependant show-for-uncompleted">
|
||||
<app:tasks:newTask type="todo" inputValue="{_newTodo}" placeHolder="New Todo" />
|
||||
</div>
|
||||
|
||||
<!-- list of all the todos -->
|
||||
<ul class='todos task-list'>
|
||||
{#each _todoList as :task}<app:tasks:task />{/}
|
||||
</ul>
|
||||
|
||||
<button class='task-action-btn tile spacious bright display-context-dependant show-for-completed' x-bind=click:clearCompleted>Clear Completed</button>
|
||||
<div id="todos-chart" style="display:none;"></div>
|
||||
<ul class="nav nav-tabs">
|
||||
|
||||
<!-- remaining/completed tabs -->
|
||||
<ul class="todo-status-toggler nav nav-tabs">
|
||||
<li class="active"><a x-bind=click:changeContext data-target="#todo-well" data-context="context-uncompleted">Remaining</a></li>
|
||||
<li><a x-bind=click:changeContext data-target="#todo-well" data-context="context-completed">Complete</a></li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="span3 well rewards">
|
||||
|
||||
<!--Title -->
|
||||
<div class="row-fluid">
|
||||
<div class="span6"><h2>Rewards</h2></div>
|
||||
<div class="span6">
|
||||
{#unless _view.activeTabPets}
|
||||
<table><tr>
|
||||
<td>{gold(_user.stats.gp)} </td>
|
||||
<td><div class='shop_gold' rel='tooltip' title='Gold'></div></td>
|
||||
<td>{silver(_user.stats.gp)} </td>
|
||||
<td><div class='shop_silver' rel='tooltip' title='Silver'></div></td>
|
||||
<!--<td>{copper(_user.stats.gp)} </td>
|
||||
<td><div class='shop_copper' rel='tooltip' title='shop_copper'></div></td>-->
|
||||
</tr></table>
|
||||
{else}
|
||||
<app:userTokens />
|
||||
{/}
|
||||
</div>
|
||||
</div>
|
||||
<app:newTask type="reward" inputValue="{_newReward}" placeHolder="New Reward" />
|
||||
|
||||
{#if _user.flags.petsEnabled}
|
||||
<div class='tabbable tabs-below'>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="rewards-tab">
|
||||
<app:rewardsTab />
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="pets-tab">
|
||||
{#with _view.items.pets as :pets}
|
||||
|
||||
<div class="row-fluid">
|
||||
{#with :pets[0]}<app:pet />{/}
|
||||
{#with :pets[1]}<app:pet />{/}
|
||||
{#with :pets[2]}<app:pet />{/}
|
||||
{#with :pets[3]}<app:pet />{/}
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
{#with :pets[4]}<app:pet />{/}
|
||||
{#with :pets[5]}<app:pet />{/}
|
||||
{#with :pets[6]}<app:pet />{/}
|
||||
{#with :pets[7]}<app:pet />{/}
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
{#with :pets[8]}<app:pet />{/}
|
||||
{#with :pets[9]}<app:pet />{/}
|
||||
{#with :pets[10]}<app:pet />{/}
|
||||
{#with :pets[11]}<app:pet />{/}
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
{#with :pets[12]}<app:pet />{/}
|
||||
{#with :pets[13]}<app:pet />{/}
|
||||
{#with :pets[14]}<app:pet />{/}
|
||||
{#with :pets[15]}<app:pet />{/}
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
{#with :pets[16]}<app:pet />{/}
|
||||
</div>
|
||||
|
||||
|
||||
{/}
|
||||
</div>
|
||||
<!--<div class="tab-pane" id="mounts-tab">...</div>-->
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="nav nav-tabs" id="rewardsTabs">
|
||||
<li class="active"><a href="#rewards-tab" data-toggle="tab" x-bind="click:activateRewardsTab">Rewards</a></li>
|
||||
<li><a href="#pets-tab" id='pets-tab' data-toggle="tab" x-bind='click:activatePetsTab'>Pets</a></li>
|
||||
<!--<li><a href="#mounts-tab" data-toggle="tab">Mounts</a></li>-->
|
||||
</ul>
|
||||
</div>
|
||||
{else}
|
||||
<app:rewardsTab />
|
||||
{/}
|
||||
</div>
|
||||
<!-- Rewards Column -->
|
||||
<app:rewards:rewardsColumn />
|
||||
|
||||
<!-- Templates -->
|
||||
<newTask:>
|
||||
<form class="form-inline new-task-form" id="new-{@type}" data-task-type="{@type}" x-bind="submit:addTask">
|
||||
<input value="{@inputValue}" type="text" name="new-task" placeholder="{@placeHolder}"/>
|
||||
<input class="btn" type="submit" value="Add" />
|
||||
<form class="addtask-form form-inline new-task-form" id="new-{@type}" data-task-type="{@type}" x-bind="submit:addTask">
|
||||
<span class="addtask-field"><input value="{@inputValue}" type="text" name="new-task" placeholder="{@placeHolder}"/></span>
|
||||
<input class="addtask-btn" type="submit" value="+">
|
||||
</form>
|
||||
<hr>
|
||||
|
||||
<!-- all the parts of a single task -->
|
||||
<task:>
|
||||
<li data-id={{:task.id}} class="task {taskClasses(:task.type, :task.completed, :task.value, :task.repeat, :task.tags, _user._tagFilters)}">
|
||||
<pre>
|
||||
<li data-id={{:task.id}} class="task {taskClasses(:task, _user._tagFilters)}">
|
||||
|
||||
<!-- right-hand side control buttons -->
|
||||
<div class="task-meta-controls">
|
||||
|
||||
<div class="hover-show">
|
||||
<!-- edit -->
|
||||
<a x-bind=click:toggleTaskEdit data-hide-id="{{:task.id}}-chart" data-toggle-id="{{:task.id}}-edit" rel=tooltip title="Edit"><i class="icon-pencil"></i></a>
|
||||
<!-- delete -->
|
||||
<a x-bind=click:del rel=tooltip title="Delete"><i class="icon-trash"></i></a>
|
||||
<!-- chart -->
|
||||
<!-- removing for now cuz it's broken -->
|
||||
{#if :task.history}
|
||||
<a x-bind=click:toggleChart data-toggle-id="{{:task.id}}-chart" data-hide-id="{{:task.id}}-edit" data-history-path="_user.tasks.{{:task.id}}.history" rel="tooltip" title="Progress">
|
||||
<i class="icon-signal"></i>
|
||||
</a>
|
||||
<!-- <a x-bind=click:toggleChart data-toggle-id="{{:task.id}}-chart" data-hide-id="{{:task.id}}-edit" data-history-path="_user.tasks.{{:task.id}}.history" rel="tooltip" title="Progress"><i class="icon-signal"></i></a> -->
|
||||
{/}
|
||||
</div>
|
||||
|
||||
<!-- notes -->
|
||||
{#if :task.notes}
|
||||
<span rel="popover" data-trigger="hover" data-placement="left" data-content="{:task.notes}" data-original-title="{:task.text}" class='task-notes'><i class="icon-comment"></i></span>
|
||||
{/}
|
||||
</div>
|
||||
|
||||
<!-- left-hand side checkbox -->
|
||||
<div class="task-controls">
|
||||
<!-- Habits -->
|
||||
{#if equal(:task.type, 'habit')}
|
||||
{#if :task.up}<a data-direction=up x-bind=click:score><img src="/img/add.png" /></a>{/}
|
||||
{#if :task.down}<a data-direction=down x-bind=click:score><img src="/img/remove.png" /></a>{/}
|
||||
{#if :task.up}<a class="task-action-btn" data-direction=up x-bind=click:score>+</a>{/}
|
||||
{#if :task.down}<a class="task-action-btn" data-direction=down x-bind=click:score>−</a>{/}
|
||||
|
||||
<!-- Rewards -->
|
||||
{else if equal(:task.type, 'reward')}
|
||||
<a x-bind=click:score class="buy-link" data-direction=down>{:task.value}<img src="/img/coin_single_gold.png"/></a>
|
||||
<a class="money btn-buy" x-bind=click:score data-direction=down>
|
||||
<span class="reward-cost">{:task.value}</span>
|
||||
<span class='shop_gold'></span>
|
||||
</a>
|
||||
|
||||
<!-- Daily & Todos -->
|
||||
{else}
|
||||
<input type=checkbox checked="{:task.completed}"/>
|
||||
<span class="task-checker action-yesno">
|
||||
<input type=checkbox id="box-{{:task.id}}" class="visuallyhidden focusable" checked="{:task.completed}">
|
||||
<label for="box-{{:task.id}}"></label>
|
||||
</span>
|
||||
{/}
|
||||
</div>
|
||||
<div class="task-text">
|
||||
|
||||
<!-- main content -->
|
||||
<p class="task-text">
|
||||
{:task.text}
|
||||
{#each _user.tags as :tag}
|
||||
{#if :task.tags[:tag.id]}
|
||||
<span class="label {#if _user._tagFilters[:tag.id]}label-info{/}">{:tag.name}</span>
|
||||
{/}
|
||||
{/}
|
||||
</div>
|
||||
</p>
|
||||
|
||||
<app:taskMeta />
|
||||
</pre>
|
||||
<!-- edit/options dialog -->
|
||||
<app:tasks:taskMeta />
|
||||
</li>
|
||||
|
||||
<!-- task edit/options -->
|
||||
<taskMeta:>
|
||||
<div style="display:none;" id={{:task.id}}-edit>
|
||||
<hr/>
|
||||
<div id="{{:task.id}}-edit" class="task-options visuallyhidden">
|
||||
<form x-bind=submit:toggleTaskEdit data-toggle-id="{{:task.id}}-edit">
|
||||
<div class=control-group>
|
||||
<label>Text</label><input type=text value={:task.text} />
|
||||
<label>Notes</label><textarea rows=3>{:task.notes}</textarea>
|
||||
</div>
|
||||
<!-- text & notes -->
|
||||
<fieldset class="option-group">
|
||||
<label class="option-title">Text</label><input class="option-content" type=text value={:task.text}>
|
||||
<label class="option-title">Extra Notes</label><textarea class="option-content" rows=3>{:task.notes}</textarea>
|
||||
</fieldset>
|
||||
|
||||
<!-- if Habit, plus/minus command options -->
|
||||
{#if equal(:task.type, 'habit')}
|
||||
<div class="control-group">
|
||||
<label class="checkbox inline"><input type=checkbox checked={:task.up}>Up</label>
|
||||
<label class="checkbox inline"><input type=checkbox checked={:task.down}>Down</label>
|
||||
</div>
|
||||
<fieldset class="option-group">
|
||||
<legend class="option-title">Direction/Actions</legend>
|
||||
<span class="task-checker action-plusminus select-toggle">
|
||||
<input class="visuallyhidden focusable" type=checkbox id="{{:task.id}}-option-plus" checked={:task.up}>
|
||||
<label for="{{:task.id}}-option-plus"></label>
|
||||
</span>
|
||||
<span class="task-checker action-plusminus select-toggle">
|
||||
<input class="visuallyhidden focusable" type=checkbox id="{{:task.id}}-option-minus" checked={:task.down}>
|
||||
<label for="{{:task.id}}-option-minus"></label>
|
||||
</span>
|
||||
</fieldset>
|
||||
|
||||
<!-- if Daily, calendar -->
|
||||
{else if equal(:task.type, 'daily')}
|
||||
<label>Repeat</label>
|
||||
<div class="control-group btn-group repeat-days">
|
||||
<fieldset class="option-group">
|
||||
<legend class="option-title">Repeat</legend>
|
||||
<div class="task-controls tile-group repeat-days">
|
||||
<!-- note, does not use data-toggle="buttons-checkbox" - it would interfere with our own click binding -->
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.su}active{/}" data-day='su' x-bind=click:toggleDay>Su</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.m}active{/}" data-day='m' x-bind=click:toggleDay>M</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.t}active{/}" data-day='t' x-bind=click:toggleDay>T</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.w}active{/}" data-day='w' x-bind=click:toggleDay>W</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.th}active{/}" data-day='th' x-bind=click:toggleDay>Th</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.f}active{/}" data-day='f' x-bind=click:toggleDay>F</button>
|
||||
<button type="button" class="btn btn-info {#if :task.repeat.s}active{/}" data-day='s' x-bind=click:toggleDay>S</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.su}active{/}" data-day='su' x-bind=click:toggleDay>Su</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.m}active{/}" data-day='m' x-bind=click:toggleDay>M</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.t}active{/}" data-day='t' x-bind=click:toggleDay>T</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.w}active{/}" data-day='w' x-bind=click:toggleDay>W</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.th}active{/}" data-day='th' x-bind=click:toggleDay>Th</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.f}active{/}" data-day='f' x-bind=click:toggleDay>F</button>
|
||||
<button type="button" class="task-action-btn tile {#if :task.repeat.s}active{/}" data-day='s' x-bind=click:toggleDay>S</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<!-- if Reward, pricing -->
|
||||
{else if equal(:task.type, 'reward')}
|
||||
<div class=control-group>
|
||||
<label>Price
|
||||
<label>Price</label>
|
||||
<div class="input-append">
|
||||
<input class="span5" size="16" type="number" min="0" value={:task.value}><span class="add-on">Gold</span>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- if Todo, the due date -->
|
||||
{else if equal(:task.type, 'todo')}
|
||||
<div class=control-group>
|
||||
<label>Due Date</label>
|
||||
<input type="text" value="{:task.date}" data-date-format="mm/dd/yyyy" class="datepicker" />
|
||||
<div><small>Enter as date, eg 02/19/2013 or 02-19-2013</small></div>
|
||||
</div>
|
||||
<fieldset class="option-group">
|
||||
<legend class="option-title">Due Date</legend>
|
||||
<input class="option-content datepicker" type="text" value="{:task.date}" data-date-format="mm/dd/yyyy">
|
||||
</fieldset>
|
||||
{/}
|
||||
{#if not(equal(:task.type, 'reward'))}
|
||||
|
||||
<!-- Advanced Options -->
|
||||
{#unless equal(:task.type, 'reward')}
|
||||
<p x-bind="click:tasksToggleAdvanced" class="option-title mega">Advanced Options</p>
|
||||
<fieldset class="option-group advanced-option visuallyhidden">
|
||||
<legend class="option-title"><a class='priority-multiplier-help' href="https://trello.com/card/priority-multiplier/50e5d3684fe3a7266b0036d6/17" target="_blank"><i class='icon-question-sign'></i></a> Difficulty</legend>
|
||||
<div class="task-controls tile-group priority-multiplier" data-id="{{:task.id}}">
|
||||
<button type="button" class="task-action-btn tile {#if equal(:task.priority,'!')}active{/}{#unless :task.priority}active{/}" data-priority='!' x-bind=click:tasksSetPriority>Easy</button>
|
||||
<button type="button" class="task-action-btn tile {#if equal(:task.priority,'!!')}active{/}" data-priority='!!' x-bind=click:tasksSetPriority>Medium</button>
|
||||
<button type="button" class="task-action-btn tile {#if equal(:task.priority,'!!!')}active{/}" data-priority='!!!' x-bind=click:tasksSetPriority>Hard</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
{/}
|
||||
|
||||
<div class="control-group">
|
||||
<div class="btn-group">
|
||||
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||
|
|
@ -227,8 +204,8 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{/}
|
||||
<button type=submit class="btn" x-bind="click:tasksSaveAndClose">Save & Close</button>
|
||||
|
||||
<button type=submit class="task-action-btn tile spacious" x-bind="click:tasksSaveAndClose">Save & Close</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,337 +0,0 @@
|
|||
<Title:>
|
||||
Privacy Policy
|
||||
|
||||
<Body:>
|
||||
<div class="well">
|
||||
<a href="/">« Back</a>
|
||||
<div class="page-header">
|
||||
<h1>Privacy Policy</h1>
|
||||
</div>
|
||||
<p class="pagemeta">
|
||||
Last updated September 02, 2012
|
||||
</p>
|
||||
<p>
|
||||
<strong>PLEASE READ THIS PRIVACY POLICY CAREFULLY.</strong>
|
||||
<br>
|
||||
By accessing
|
||||
or otherwise using habitrpg.com or any sub domains thereto ("the Sites"),
|
||||
or using a habitrpg.com or HabitRPG application on a mobile device ("the Applications"),
|
||||
you agree to be bound contractually by this Privacy Policy. Individually
|
||||
or collectively, the Applications and the Sites may be referred to as
|
||||
the "Services."
|
||||
</p>
|
||||
<p>
|
||||
To review material modifications and their effective dates scroll
|
||||
to the bottom of the page.
|
||||
</p>
|
||||
<p>
|
||||
<strong> 1. Privacy Statement; Collection of Personal
|
||||
Information. </strong>
|
||||
<br>
|
||||
1.1 OCDevel owns and operates this business. All
|
||||
references to "we", "us", shall be construed to mean OCDevel.
|
||||
</p>
|
||||
<p>
|
||||
1.2 We understand that visitors to this website are concerned
|
||||
about the privacy of information. The following describes our privacy
|
||||
policy regarding information, including personal information, that we
|
||||
collect through this website.
|
||||
</p>
|
||||
<p>
|
||||
<strong>2. Modification of Privacy Policy.</strong>
|
||||
<br>
|
||||
We reserve the right to modify this Privacy Policy at any time,
|
||||
and without prior notice, by posting an amended Privacy Policy that is
|
||||
always accessible by clicking on the "Privacy Policy" link on this
|
||||
site's home page. Your continued use of this site indicates your
|
||||
acceptance of the amended Privacy Policy. You should check the Privacy
|
||||
Policy through this link periodically for modifications by clicking on
|
||||
the link provided near the top of the Privacy Policy for a listing of
|
||||
material modifications and their effective dates. Regarding personal
|
||||
information, if any modifications are materially less restrictive on our
|
||||
use or disclosure of the personal information previously disclosed by
|
||||
you, we will obtain your consent before implementing such revisions with
|
||||
respect to such information.
|
||||
</p>
|
||||
<p>
|
||||
<strong>3. Collection of Anonymous, Passive Information.</strong>
|
||||
<br>
|
||||
We reserve the right to monitor your use of the services. As you
|
||||
navigate through the services, certain anonymous information may be
|
||||
passively collected (that is, gathered without your actively providing
|
||||
the information) using various technologies, such as cookies, Internet
|
||||
tags or web beacons, and navigational data collection (log files, server
|
||||
logs, clickstream). The following is a listing and a brief explanation
|
||||
of passive information collection methodologies which we may use from
|
||||
time to time to better understand how the Services are being used.
|
||||
</p>
|
||||
<p>
|
||||
3.1 A "cookie" is a text file that this site sends to your
|
||||
browser in the form of a text file The information generated by the
|
||||
cookie about your use of this site (including your IP address) will be
|
||||
transmitted to and stored. Most browsers automatically accept cookies,
|
||||
but they usually can be modified to decline cookies if you prefer;
|
||||
however, certain features of this site might not work without cookies.
|
||||
</p>
|
||||
<p>
|
||||
3.2 "Session" cookies are temporary bits of information that are
|
||||
used to improve navigation, block visitors from providing information
|
||||
where inappropriate (the Services "remembers" previous entries of age or
|
||||
country of origin that were outside the specified parameters and blocks
|
||||
subsequent changes), and collect aggregate statistical information on
|
||||
the Services. They are erased once you exit your Web browser or
|
||||
otherwise turn off your computer.
|
||||
</p>
|
||||
<p>
|
||||
3.3 "Persistent" cookies are more permanent bits of information
|
||||
that are placed on the hard drive of your computer and stay there unless
|
||||
you delete the cookie. Persistent cookies store information on your
|
||||
computer for a number of purposes, such as retrieving certain
|
||||
information you have previously provided, helping to determine what
|
||||
areas of the Services you may find most valuable, and customizing the
|
||||
Services based on your preferences on an ongoing basis. Persistent
|
||||
cookies placed by this site in your computer do not hold personal
|
||||
information.
|
||||
</p>
|
||||
<p>
|
||||
3.4 You can set your browser to accept all cookies, to reject all
|
||||
cookies, or to notify you whenever a cookie is offered so that you can
|
||||
decide each time whether to accept it. To learn more about cookies and
|
||||
how to specify your preferences, please search for "cookie" in the
|
||||
"Help" portion of your browser.
|
||||
</p>
|
||||
<p>
|
||||
3.5 An Internet Protocol (IP) address is a number assigned to
|
||||
your computer by your Internet service provider so you can access the
|
||||
Internet and is generally considered to be non-personally identifiable
|
||||
information, because in most cases an IP address is dynamic (changing
|
||||
each time you connect to the Internet), rather than static (unique to a
|
||||
particular user's computer). The IP address can be used to diagnose
|
||||
problems with a server, report aggregate information, determine the
|
||||
fastest route for your computer to use in connecting to a site, and
|
||||
administer and improve the Services.
|
||||
</p>
|
||||
<p>
|
||||
3.6 "Internet tags" (also known as Web Beacons, single-pixel
|
||||
GIFs, clear GIFs, invisible GIFs, and 1-by-1 GIFs) are smaller than
|
||||
cookies and tell the Web site server information such as the IP address
|
||||
and browser type related to the visitor's computer. Tags may be placed
|
||||
both on online advertisements that bring people to the Services and on
|
||||
different pages of the Services. Such tags indicate how many times a
|
||||
page is opened and which information is consulted.
|
||||
</p>
|
||||
<p>
|
||||
3.7 "Navigational data" (log files, server logs, and clickstream
|
||||
data) are used for system management, to improve the content of the
|
||||
Services, market research purposes, and to communicate information to
|
||||
visitors.
|
||||
</p>
|
||||
<p>
|
||||
<strong>4. Use and Sharing of Anonymous, Passive Information.</strong>
|
||||
<br>
|
||||
The Services may make full use of passively collected anonymous
|
||||
information, including without limitation the right to use such
|
||||
information to provide better service to Service users, customize the
|
||||
Services based on your preferences, compile and analyze statistics and
|
||||
trends, and otherwise administer and improve the Services for your use. We
|
||||
reserve the right to share this anonymous, passive information in
|
||||
aggregated form.
|
||||
</p>
|
||||
<p>
|
||||
<strong>5. 3rd Party Behavioral Ads; Google's AdSense Network.</strong>
|
||||
<br>
|
||||
5.1 We reserve the right to use anonymous, passive information
|
||||
about your visits to this and other websites (not including your name,
|
||||
address, email address or telephone number) for purposes of serving our
|
||||
ads and third party ads that are targeted to your interests ("3rd Party
|
||||
Behavioral Ads"). We reserve the right to share anonymous, passive
|
||||
information collected on the services with third parties for purposes of
|
||||
serving 3rd Party Behavioral Ads. These 3rd Party Behavioral Ads do not
|
||||
identify you personally. Instead, they associate your behavioral data on
|
||||
visited sites with your browser, so that the ads your computer sees on
|
||||
this site are more likely to be relevant to your interests. 3rd Party
|
||||
Behavioral Ads require that that you be served with a cookie containing
|
||||
a tracking code. You may refuse the use of cookies by selecting the
|
||||
appropriate settings on your browser; however, please note that if you
|
||||
do this you may not be able to use the full functionality of this site.
|
||||
</p>
|
||||
<p>
|
||||
5.2 We reserve the right to participate in Google's AdSense
|
||||
network for purposes of serving 3rd Party Behavioral Ads. Google uses
|
||||
DoubleClick's DART cookie for serving 3rd Party Behavioral Ads over the
|
||||
AdSense network. You may opt out of the use of the DART cookie. For
|
||||
information regarding how to opt out, go to
|
||||
http://www.google.com/privacy_ads.html.
|
||||
</p>
|
||||
<p>
|
||||
<strong>6. Use of 3rd Party Analytics.</strong>
|
||||
<br>
|
||||
We reserve the right to use analytics services provided by
|
||||
third parties. These services use 3rd party cookies to collect
|
||||
anonymous, passive information about your use of this site (see
|
||||
explanation of cookies in Collection of Anonymous, Passive Information
|
||||
above). We use this information for the purpose of evaluating your use
|
||||
of the Services, compiling reports on activity, and providing other
|
||||
services. These web analytics services may also transfer this
|
||||
information to third parties where required to do so by law, or where
|
||||
such third parties process the information on the service's behalf.
|
||||
</p>
|
||||
<p>
|
||||
<strong>7. Collection of Personal Information; Categories.</strong>
|
||||
<br>
|
||||
We will ask you for personal information when you sign up for any
|
||||
specific benefit or purpose that requires registration. Personal
|
||||
information that we collect may vary with the each registration, and it
|
||||
may include one or more of the following categories: name, physical
|
||||
address, an email address, phone number, and credit card information
|
||||
including credit card number, expiration date, and billing address,
|
||||
emergency contact information, current medications, allergies, medical
|
||||
insurance information.
|
||||
</p>
|
||||
<p>
|
||||
<strong> 8. Use And Sharing of Personal Information: General
|
||||
Policy And Exceptions. </strong>
|
||||
<br>
|
||||
Our general policy is that we will use your personal information,
|
||||
including combining your personal information with passive information
|
||||
collected from this site, only for: the performance of the services or
|
||||
transaction for which it was given, our private, internal reporting for
|
||||
this site, and security assessments for this site, and we will not
|
||||
share, sell, or rent your personal information to others. The only
|
||||
exceptions to this general policy: (i) are described in the subsections
|
||||
below, and (ii) if you explicitly approve through our site.
|
||||
</p>
|
||||
<p>
|
||||
8.1 Affiliates And Service Providers. We reserve the right to
|
||||
provide such information to our affiliates or subsidiaries, or trusted
|
||||
service providers for the purpose of hosting our servers or processing
|
||||
or archiving personal information for us. We require that these parties
|
||||
agree to privacy and security safeguards for this information that are
|
||||
consistent with this Privacy Policy.
|
||||
</p>
|
||||
<p>
|
||||
8.2 Acquisition; Bankruptcy. In the event that we are acquired by
|
||||
or merged with a third party entity, we reserve the right to transfer
|
||||
such information as part of such merger, acquisition, sale, or other
|
||||
change of control. In the unlikely event of our bankruptcy, insolvency,
|
||||
reorganization, receivership, or assignment for the benefit of
|
||||
creditors, or the application of laws or equitable principles affecting
|
||||
creditors' rights generally, we reserve the right to transfer such
|
||||
information to protect our rights or as required by law.
|
||||
</p>
|
||||
<p>
|
||||
8.3 Enforcement; Legal Process. We reserve the right to transfer
|
||||
such information if we have a good faith belief that access, use,
|
||||
preservation or disclosure of such information is reasonably necessary
|
||||
(i) to satisfy any applicable law, regulation, legal process or
|
||||
enforceable governmental request, or (ii) to investigate or enforce
|
||||
violations of our rights or the security of this site.
|
||||
</p>
|
||||
<p>
|
||||
8.4 Miscellaneous. We reserve the right to share personal
|
||||
information with the following additional parties: online organizers
|
||||
using our tools and resellers of our products and services from whose
|
||||
site the sale originated (even though the sale originates at site of the
|
||||
reseller, registration and collection of personal information occurs at
|
||||
this site).
|
||||
</p>
|
||||
<p>
|
||||
<strong> 9. Onward Transfer of Personal Information Outside Your
|
||||
Country of Residence. </strong>
|
||||
<br>
|
||||
Any personal information which we may collect on this site will
|
||||
be stored and processed in our servers located only in the United
|
||||
States. By using this site, if you reside outside the United States, you
|
||||
consent to the transfer of personal information outside your country of
|
||||
residence to the United States.
|
||||
</p>
|
||||
<p>
|
||||
<strong>10. Security of Personal Information.</strong>
|
||||
<br>
|
||||
We follow reasonable and appropriate industry standards to
|
||||
protect your personal information and data. Unfortunately, no data
|
||||
transmission over the Internet or method of data storage can be
|
||||
guaranteed 100% secure. Therefore, while we strive to protect your
|
||||
personal information by following generally accepted industry standards,
|
||||
we cannot ensure or warrant the absolute security of any information you
|
||||
transmit to us or archive at this site.
|
||||
</p>
|
||||
<p>
|
||||
<strong>11. Changing And Updating Personal Information.</strong>
|
||||
<br>
|
||||
Upon request, we will permit you to request or make changes or
|
||||
updates to your personal information for legitimate purposes. We request
|
||||
identification prior to approving such requests. We reserve the right to
|
||||
decline any requests that are unreasonably repetitive or systematic,
|
||||
require unreasonable time or effort of our technical or administrative
|
||||
personnel, or undermine the privacy rights of others. We reserve the
|
||||
right to permit you to access your personal information in any account
|
||||
you establish with this site for purposes of making your own changes or
|
||||
updates, and in such case, instructions for making such changes or
|
||||
updates will be provided where necessary.
|
||||
</p>
|
||||
<p>
|
||||
<strong>12. Email From This Site; Opt-Out Rights.</strong>
|
||||
<br>
|
||||
If you supply us with your e-mail address you may receive
|
||||
periodic messages from us with information specific to the Services and
|
||||
required for the normal functioning of the Services as well as for new
|
||||
products or services or upcoming events. If you prefer not to receive
|
||||
periodic email messages, you may opt-out by following the instructions
|
||||
on the email.
|
||||
</p>
|
||||
<p>
|
||||
<strong>13. Children's Online Policy.</strong>
|
||||
<br>
|
||||
We are committed to preserving online privacy for all of its
|
||||
website visitors, including children. This site is a general audience
|
||||
site. Consistent with the Children's Online Privacy Protection Act
|
||||
(COPPA), we will not knowingly collect any information from, or sell to,
|
||||
children under the age of 13. If you are a parent or guardian who has
|
||||
discovered that your child under the age of 13 has submitted his or her
|
||||
personally identifiable information without your permission or consent,
|
||||
we will remove the information from our active list, at your request. To
|
||||
request the removal of your child's information, please send contact our
|
||||
site as provided below under “Contact Us”, and be sure to include in
|
||||
your message the same login information that your child submitted.
|
||||
</p>
|
||||
<p>
|
||||
<strong> 14. Email And Other Messages Through This Site; ECPA
|
||||
Notice. </strong>
|
||||
<br>
|
||||
This site treats email messages and other electronic messages
|
||||
that are sent through this site and not viewable by others as
|
||||
confidential and private, except as required by law, including without
|
||||
limitation, the Electronic Communications Privacy Act of 1986, 18 U.S.C.
|
||||
Sections 2701-2711 (the "ECPA"). The ECPA permits this site's limited
|
||||
ability to intercept and/or disclose electronic messages, for example
|
||||
(i) as necessary to operate our system or to protect our rights or
|
||||
property, (ii) upon legal demand (court orders, warrants, subpoenas), or
|
||||
(iii) where we receive information inadvertently which appears to
|
||||
pertain to the commission of a crime. This site is not considered a
|
||||
"secure communications medium" under the ECPA.
|
||||
</p>
|
||||
<p>
|
||||
<strong>15. Contact Us.</strong>
|
||||
<br>
|
||||
If you have any questions regarding this Privacy Policy, please
|
||||
contact the owner and operator of this website business:
|
||||
</p>
|
||||
<address>
|
||||
<strong>OCDevel</strong>
|
||||
<br>
|
||||
98 Morrison Ave #2
|
||||
<br>
|
||||
Somerville, MA 02144
|
||||
<br>
|
||||
Email:
|
||||
<a href="mailto:tylerrenelle@gmail.com">tylerrenelle@gmail.com</a>
|
||||
<br>
|
||||
Telephone: 1-805-975-5236
|
||||
</address>
|
||||
<p></p>
|
||||
<div id="home_static_footer">
|
||||
© 2012 OCDevel
|
||||
</div>
|
||||
|
||||
</div>
|
||||
67
views/static/about.html
Normal file
67
views/static/about.html
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | About
|
||||
|
||||
<Head:>
|
||||
<app:head:head active="about"/>
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header />
|
||||
<div class='container'>
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>About</h1>
|
||||
</div>
|
||||
<p class="lead">HabitRPG is an <a href="https://github.com/lefnire/habitrpg">open source</a> habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.</p>
|
||||
</section>
|
||||
|
||||
<div class='marketing'>
|
||||
<p><iframe src="http://player.vimeo.com/video/57654086" width="960" height="539" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></p>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2>Habits</h2>
|
||||
</div>
|
||||
<p class="lead">Habits are situational goals that you constantly track. You can either try to break bad habits or reinforce good ones. You may encounter some habits multiple times a day (like "Floss After Eating"), and some you may not encounter often at all (like "Replace the Toilet Paper When it Runs Out"). For some habits, it only makes sense to gain points (like "Do One Hour of Productive Work"). For others, it only makes sense to lose points (like "Eat Junk Food"). For the rest, both gain and loss apply (like for "Take The Stairs", taking the stairs would be a gain while taking the elevator would be a loss).</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2>Dailies</h2>
|
||||
</div>
|
||||
<p class="lead">Dailies are goals that you want to complete once a day, building them into your routine (like "Workout for 30 Minutes"). Unlike Habits, Dailies may only be checked off once a day. Also unlike habits, at the end of each day non-completed Dailies will cause damage, making you lose Hit Points. If you are doing well and consistantly check off a Daily day after day, it will turn green and earn less gold and experience, though it will also cause you to lose fewer Hit Points if you skip it. This means you can ease up on it for a bit. Conversely, if you are doing poorly and fail to check something off every day, it will turn red. The worse you do, the more experience and gold that Daily is worth and the more hit points it takes away if left uncompleted. This encourages you to focus on your shortcomings, the reds. Oh, and don't worry. You can make a Daily active only for certain days of the week, so weekday tasks won't pester you on the weekends. Or you can make a Daily active only on Mondays, thus creating a weekly task.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2>Todos</h2>
|
||||
</div>
|
||||
<p class="lead">Todos are one-time goals which need to be completed eventually (like "Wash the car" or "Buy milk"). Non-completed Todos won’t hurt you, but they will become more valuable over time. This will encourage you to wrap up stale Todos.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2>Rewards</h2>
|
||||
</div>
|
||||
<p class='lead'>As you complete goals, you earn gold to buy rewards that you create (like "An Hour of Video Games"). Create plenty of rewards and buy them liberally! Rewarding good performance is the best way to reinforce good habits. If you really need a reward but don't have enough gold, you can still make a purchase. But be careful because it will cost you Hit Points!</p>
|
||||
<p class='lead'>After you’ve reached level 2, you unlock the <strong>Item Store</strong> under the rewards column. You can now buy weapons, armor, and potions. Armor keeps you protected by reducing how much damage you take from failed tasks. Weapons increase how much experience you gain for completing a task. Potions recover 15 HP, for when you've piled just a little too much onto your schedule.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2>License & Credits</h2>
|
||||
</div>
|
||||
<p class='lead'>Code is licensed under GNU GPL v3. Content is licensed under CC-BY-SA 3.0. See the LICENSE file for details.</p>
|
||||
|
||||
<p class='lead'>Content and assets comes from Mozilla’s <a href="http://browserquest.mozilla.org/">BrowserQuest</a> (<a href="http://mozilla.org">Mozilla</a>, <a href="http://www.littleworkshop.fr">Little Workshop</a>). They seriously rock.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
43
views/static/extensions.html
Normal file
43
views/static/extensions.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | Add-ons & Extensions
|
||||
|
||||
<Head:>
|
||||
<app:head:head active="about"/>
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header />
|
||||
<div class='container'>
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>Add-ons & Extensions</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h2 id="chrome-extension"><a href="https://chrome.google.com/webstore/detail/habitrpg/pidkmpibnnnhneohdgjclfdjpijggmjj">Chrome Extension</a></h2>
|
||||
<p>Monitors your browsing habits to divvy points to your HabitRPG character. Think Stayfocusd for HabitRPG (http://habitrpg.com).</p>
|
||||
<p>Lose HP for lingering on bad websites, gain Exp and GP for hanging out on good websites. Set the hours and days of the week you want to be productive and integrate your browsing habits with HabitRPG to improve your overall productivity.</p>
|
||||
<p>To use, enter your HabitRPG User ID and API Token in the options page (User -> Settings, see the screenshot). Hit me up with suggestions</p>
|
||||
<h2 id="pomodoro">Pomodoro</h2>
|
||||
<p>I use Ugo Landini's Pomodoro, which allows start / stop custom scripts: <code>shell do shell script "curl -X POST -H 'Content-Type:application/json' https://habitrpg.com/v1/users/.../tasks/productivity/up -d '{\\"apiToken\\":\\"...\\"}"</code></p>
|
||||
<h2 id="anki"><a href="https://github.com/Pherr/HabitRPG-Anki-Addon">Anki</a></h2>
|
||||
<p>A HabitRPG addon for Anki.</p>
|
||||
<h2 id="habitrpg-mobile"><a href="https://github.com/HabitRPG/habitrpg-mobile">HabitRPG Mobile</a></h2>
|
||||
<p>HabitRPG mobile application under development. Angular + PhoneGap</p>
|
||||
<h2 id="habitrpg-ncurses"><a href="https://github.com/arscan/habitrpg-ncurses">habitrpg-ncurses</a></h2>
|
||||
<p>node + ncurses interface for habitRPG.</p>
|
||||
<p>This is set up to meet my general task workflow and my preferences. Todo lists are simple enought that I feel like investing time into making something that works exactly how I want it to work is worthwhile. Plus I felt like learning ncurses. At this point, attempt to use at your own risk.</p>
|
||||
<p>Apparently the ncurses binding doesn't seem to work properly when loaded from npm. Grabbing the master branch from https://github.com/mscdex/node-ncurses works for me.</p>
|
||||
<h2 id="pyhabit"><a href="https://github.com/elssar/pyhabit">pyhabit</a></h2>
|
||||
<p>Python command line app for HabitRPG (http://habitrpg.com)</p>
|
||||
<h2 id="php-api"><a href="http://github.com/ruddfawcett/HabitRPG_PHP">PHP API</a></h2>
|
||||
<p>A PHP class for the HabitRPG API. Will be expanded when more features become available in the API.</p>
|
||||
<h2 id="suggest-an-add-on-extension">Suggest an Add-on / Extension</h2>
|
||||
<p><a href="mailto:tylerrenelle@gmail.com">Email me</a> to have something listed here</p>
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
107
views/static/faq.html
Normal file
107
views/static/faq.html
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | FAQ
|
||||
|
||||
<Head:>
|
||||
<app:head:head active="about"/>
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header />
|
||||
<div class='container'>
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>FAQ</h1>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="habit-tracking">
|
||||
<div class="page-header">
|
||||
<h2>What is habit tracking?</h2>
|
||||
</div>
|
||||
<p>Habit tracking is keeping tabs on all your habits to help you improve. It is to life what calorie-counting is to diets (and calorie-counting falls under the habit-tracking umbrella). See <a href="http://blog.beeminder.com/trackhack/">Trackers vs Lifehackers</a> for more information and website examples.</p>
|
||||
<p>There are many habit-tracking apps, but HabitRPG takes a different spin: treating your goals and habits like a Role Playing Game (RPG). If you've played RPGs growing up, this app will make sense to you; otherwise, it may be overwhelming at first. Be sure to follow the tutorial when you first load the website, and if you're still confused watch this <a href="/static/about">video tutorial</a></p>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="which-task-type">
|
||||
<div class="page-header">
|
||||
<h2>Should this task be a Habit, Daily, or Todo?</h2>
|
||||
</div>
|
||||
<p>See <a href="https://habitrpg.com/static/about">About</a> on task type definitions, but think of it like this.</p>
|
||||
<ul>
|
||||
<li>Habits you track many times per day. Taking the stairs, eating candy, biting your nails, etc. They're sort of micro-tasks. They're also the dumping ground for 3rd-party tasks, created via API calls - for example, when you visit vice websites (Reddit, 9gag) while using the <a href="https://chrome.google.com/webstore/detail/habitrpg/pidkmpibnnnhneohdgjclfdjpijggmjj">Chrome extension</a> - that's updated as a habit.</li>
|
||||
<li>Dailies are things you track once or twice a day. Exercising, 1h Reading, Anki Session, etc. They reset at the end of each day, and must be completed each day. The confusion is usually between Dailies and Habits, and it usually goes like this: "Should I have a Daily called <em>1h Reading</em> (I want to read 1 hour each day), or a Habit called <em>1h Reading</em> that I up-score for every hour I read in a day?". My answer - Daily. If it's a decently difficult task that you want to become a good habit, make it a Daily - you're more likely to do it when it loses points at the end of each day than if it sits.</li>
|
||||
<li>Todos are things you'll do once and only once. <em>Call Mom</em>, <em>Send eBay Package</em>, <em>Pay Doctor Bill</em>, etc. Pretty straight forward.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section id="what-are-tokens">
|
||||
<div class="page-header">
|
||||
<h2>What are tokens?</h2>
|
||||
</div>
|
||||
<p>In the days of yore, arcade machines ran on tokens or quarters, and were known to gobble such nuggets with avaricious glee. HabitRPG has no wish to drain your pocket change, but those who use and appreciate it can support the project by turning real-world quarters into tokens, and using the tokens in turn to reroll your tasks, spruce up their character, and tame fearsome pets & mounts to impress friend and foe alike!</p>
|
||||
</section>
|
||||
|
||||
<section id="how-do-i-reset">
|
||||
<div class="page-header">
|
||||
<h2>How do I reset?</h2>
|
||||
</div>
|
||||
<p>Many people have requested the ability to start completely from scratch. This is highly discouraged because you'll lose historical data, which is useful for graphing your progress over time. However, some people find it useful in the beginning after playing with the app for a while - once they've become accustomed to how things work, they want to start playing "for real". To do this, make sure you're logged in and click the profile drop-down in the top-right corner, then click "Reset".</p>
|
||||
</section>
|
||||
|
||||
<section id="how-do-i-customize-a-tasks-value">
|
||||
<div class="page-header">
|
||||
<h2>How do I customize a task's value?</h2>
|
||||
</div>
|
||||
<p>Some tasks are more valuable to you than others. Finishing your research paper, for example, should be worth more points than flossing your teeth. Instead of customizing value upon task-creation, HabitRPG calculates a task's value automatically over time. The longer something sits without being completed (Dailies and Todos), the more valuable it becomes; and the more frequently you complete Dailies, the less valuable they become. Trust in the system's machine-learning of task-values.</p>
|
||||
<p>If you <em>absolutely</em> must customize a task's value, there's a "difficulty" setting under the Advanced section of task-editing.</p>
|
||||
</section>
|
||||
|
||||
<section id="vacation">
|
||||
<div class="page-header">
|
||||
<h2>I'm going on vacation, how can I disable my Dailies?</h2>
|
||||
</div>
|
||||
<p>Dailies have a "Repeat" field, which specifies which week days this item is due. When you on go vacation, just set each Daily's repeats such that they aren't due on any week days at all. <a href="https://www.evernote.com/shard/s17/sh/e362a4ec-97aa-4c35-8a09-74dc5c16a0fe/a9e895c1f93f4398c56eedef0e58c6fe/res/a44859e5-726e-4e1f-817a-d4f37adbc6f9/skitch.png?resizeSmall&width=832">See this screenshot</a>. As for Todos, they will still gain value but they won't hurt you.</p>
|
||||
</section>
|
||||
|
||||
<section id="beta-access">
|
||||
<div class="page-header">
|
||||
<h2>Beta Access</h2>
|
||||
</div>
|
||||
<h3>Release Date</h3>
|
||||
<p>The mobile apps are slated to be released in April / May 2013. We hope sooner than that, but that's our current estimate. For the Kickstarter beta access, here's how we'll handle that:</p>
|
||||
<h3>Website</h3>
|
||||
<p>If you like to live dangerously, you can use <a href="https://beta.habitrpg.com">beta.habitrpg.com</a> instead of the production site, which will introduce new features faster. It tracks the production database, so yes your account will be synced.</p>
|
||||
<h3>Mobile App</h3>
|
||||
<p>We'll post updates on the Kickstarter when we have a beta version of the mobile app that y'all can play with (downloadable files, outside the various app stores), and will post each new beta version update on the KS update section as well.</p>
|
||||
</section>
|
||||
|
||||
<section id="browserquest-art">
|
||||
<div class="page-header">
|
||||
<h2>BrowserQuest Art</h2>
|
||||
</div>
|
||||
<p>We're currently using the pixel art from <a href="https://github.com/mozilla/BrowserQuest">Mozilla BrowserQuest</a>. Some have been opposed because they've played BQ themselves, and don't like design recycling, but here's my reasoning:</p>
|
||||
<p>It is licensed under CC-BY-SA 3.0 (so we can use it). Fortunately for us (but sadly for Mozilla), BQ is mothballed. That has two implications. First, only those in-the-know will recognize it - which is mostly developers & designers who have been keeping abreast HTML5's progress. Second, it's absolutely superb in design and nostalgia - it does an excellent job of bringing back SNES, and it would be a shame to see it go to waste. Also, it's free, and will get pretty far before we need to start customizing.</p>
|
||||
</section>
|
||||
|
||||
<section id="browser-support">
|
||||
<div class="page-header">
|
||||
<h2>Browser Support</h2>
|
||||
</div>
|
||||
<p>Everything except Internet Explorer. Derby doesn't currently support IE (see <a href="https://github.com/codeparty/derby/issues/126">this thread</a>), though support is <a href="https://github.com/codeparty/saddle">eventually planned</a>. Use Chrome for best results.</p>
|
||||
</section>
|
||||
|
||||
<section id="how-can-i-get-involved">
|
||||
<div class="page-header">
|
||||
<h2>How can I get involved?</h2>
|
||||
</div>
|
||||
<p>Check out <a href="https://github.com/lefnire/habitrpg/wiki/Business-Model#count-me-in">"Count Me In!"</a>, we need all the help we can get!</p>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
28
views/static/front.html
Normal file
28
views/static/front.html
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | Gamify Your Life
|
||||
|
||||
<Head:>
|
||||
<app:head:head />
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header active="front"/>
|
||||
|
||||
|
||||
<div class='jumbotron masthead'>
|
||||
<div class='container'>
|
||||
<h1><img src="/img/logo/habitrpg_pixel.png" alt="HabitRPG"/></h1>
|
||||
<p>A habit building program which treats your life like a Role Playing Game. Level up as you succeed, lose HP as you fail, earn money to buy weapons and armor.</p>
|
||||
<a href="/?play=1" class='btn btn-primary btn-small'>Play</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='container'>
|
||||
<p style="height:600;"><iframe src="http://player.vimeo.com/video/57639356" width="100%" height="539" frameborder="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen></iframe></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
17
views/static/head.html
Normal file
17
views/static/head.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<head:>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<!-- CDN -->
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||
|
||||
<link href="/vendor/bootstrap/docs/assets/css/docs.css" rel="stylesheet">
|
||||
<link href="/css/static-pages.css" rel="stylesheet">
|
||||
<link href="/css/footer.css" rel="stylesheet">
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$.getScript("//s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire");
|
||||
</script>
|
||||
33
views/static/header.html
Normal file
33
views/static/header.html
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
<header:>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand {{#if equal(@active,'front')}}active{{/}}" href="/static/front">HabitRPG</a>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
|
||||
<li class="{{#if equal(@active,'about')}}active{{/}}">
|
||||
<a href="/static/about">About</a>
|
||||
</li>
|
||||
|
||||
<li class="">
|
||||
<a target="_blank" href="http://habitrpg.tumblr.com/">Blog</a>
|
||||
</li>
|
||||
|
||||
<li class="{{#if equal(@active,'team')}}team{{/}}">
|
||||
<a href="/static/team">Team</a>
|
||||
</li>
|
||||
|
||||
<li class="{{#if equal(@active,'extensions')}}active{{/}}">
|
||||
<a href="/static/extensions">Extensions</a>
|
||||
</li>
|
||||
|
||||
<li class="{{#if equal(@active,'faq')}}active{{/}}">
|
||||
<a href="/static/faq">FAQ</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
347
views/static/privacy.html
Normal file
347
views/static/privacy.html
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | Privacy
|
||||
|
||||
<Head:>
|
||||
<app:head:head active="about"/>
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header />
|
||||
<div class='container'>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Privacy Policy</h1>
|
||||
</div>
|
||||
<p class="pagemeta">
|
||||
Last updated September 02, 2012
|
||||
</p>
|
||||
<p>
|
||||
<strong>PLEASE READ THIS PRIVACY POLICY CAREFULLY.</strong>
|
||||
<br>
|
||||
By accessing or otherwise using habitrpg.com or any sub domains thereto ("the Sites"),
|
||||
or using a habitrpg.com or HabitRPG application on a mobile device ("the Applications"),
|
||||
you agree to be bound contractually by this Privacy Policy. Individually
|
||||
or collectively, the Applications and the Sites may be referred to as
|
||||
the "Services."
|
||||
</p>
|
||||
<p>
|
||||
To review material modifications and their effective dates scroll
|
||||
to the bottom of the page.
|
||||
</p>
|
||||
<p>
|
||||
<strong> 1. Privacy Statement; Collection of Personal
|
||||
Information. </strong>
|
||||
<br>
|
||||
1.1 OCDevel owns and operates this business. All
|
||||
references to "we", "us", shall be construed to mean OCDevel.
|
||||
</p>
|
||||
<p>
|
||||
1.2 We understand that visitors to this website are concerned
|
||||
about the privacy of information. The following describes our privacy
|
||||
policy regarding information, including personal information, that we
|
||||
collect through this website.
|
||||
</p>
|
||||
<p>
|
||||
<strong>2. Modification of Privacy Policy.</strong>
|
||||
<br>
|
||||
We reserve the right to modify this Privacy Policy at any time,
|
||||
and without prior notice, by posting an amended Privacy Policy that is
|
||||
always accessible by clicking on the "Privacy Policy" link on this
|
||||
site's home page. Your continued use of this site indicates your
|
||||
acceptance of the amended Privacy Policy. You should check the Privacy
|
||||
Policy through this link periodically for modifications by clicking on
|
||||
the link provided near the top of the Privacy Policy for a listing of
|
||||
material modifications and their effective dates. Regarding personal
|
||||
information, if any modifications are materially less restrictive on our
|
||||
use or disclosure of the personal information previously disclosed by
|
||||
you, we will obtain your consent before implementing such revisions with
|
||||
respect to such information.
|
||||
</p>
|
||||
<p>
|
||||
<strong>3. Collection of Anonymous, Passive Information.</strong>
|
||||
<br>
|
||||
We reserve the right to monitor your use of the services. As you
|
||||
navigate through the services, certain anonymous information may be
|
||||
passively collected (that is, gathered without your actively providing
|
||||
the information) using various technologies, such as cookies, Internet
|
||||
tags or web beacons, and navigational data collection (log files, server
|
||||
logs, clickstream). The following is a listing and a brief explanation
|
||||
of passive information collection methodologies which we may use from
|
||||
time to time to better understand how the Services are being used.
|
||||
</p>
|
||||
<p>
|
||||
3.1 A "cookie" is a text file that this site sends to your
|
||||
browser in the form of a text file The information generated by the
|
||||
cookie about your use of this site (including your IP address) will be
|
||||
transmitted to and stored. Most browsers automatically accept cookies,
|
||||
but they usually can be modified to decline cookies if you prefer;
|
||||
however, certain features of this site might not work without cookies.
|
||||
</p>
|
||||
<p>
|
||||
3.2 "Session" cookies are temporary bits of information that are
|
||||
used to improve navigation, block visitors from providing information
|
||||
where inappropriate (the Services "remembers" previous entries of age or
|
||||
country of origin that were outside the specified parameters and blocks
|
||||
subsequent changes), and collect aggregate statistical information on
|
||||
the Services. They are erased once you exit your Web browser or
|
||||
otherwise turn off your computer.
|
||||
</p>
|
||||
<p>
|
||||
3.3 "Persistent" cookies are more permanent bits of information
|
||||
that are placed on the hard drive of your computer and stay there unless
|
||||
you delete the cookie. Persistent cookies store information on your
|
||||
computer for a number of purposes, such as retrieving certain
|
||||
information you have previously provided, helping to determine what
|
||||
areas of the Services you may find most valuable, and customizing the
|
||||
Services based on your preferences on an ongoing basis. Persistent
|
||||
cookies placed by this site in your computer do not hold personal
|
||||
information.
|
||||
</p>
|
||||
<p>
|
||||
3.4 You can set your browser to accept all cookies, to reject all
|
||||
cookies, or to notify you whenever a cookie is offered so that you can
|
||||
decide each time whether to accept it. To learn more about cookies and
|
||||
how to specify your preferences, please search for "cookie" in the
|
||||
"Help" portion of your browser.
|
||||
</p>
|
||||
<p>
|
||||
3.5 An Internet Protocol (IP) address is a number assigned to
|
||||
your computer by your Internet service provider so you can access the
|
||||
Internet and is generally considered to be non-personally identifiable
|
||||
information, because in most cases an IP address is dynamic (changing
|
||||
each time you connect to the Internet), rather than static (unique to a
|
||||
particular user's computer). The IP address can be used to diagnose
|
||||
problems with a server, report aggregate information, determine the
|
||||
fastest route for your computer to use in connecting to a site, and
|
||||
administer and improve the Services.
|
||||
</p>
|
||||
<p>
|
||||
3.6 "Internet tags" (also known as Web Beacons, single-pixel
|
||||
GIFs, clear GIFs, invisible GIFs, and 1-by-1 GIFs) are smaller than
|
||||
cookies and tell the Web site server information such as the IP address
|
||||
and browser type related to the visitor's computer. Tags may be placed
|
||||
both on online advertisements that bring people to the Services and on
|
||||
different pages of the Services. Such tags indicate how many times a
|
||||
page is opened and which information is consulted.
|
||||
</p>
|
||||
<p>
|
||||
3.7 "Navigational data" (log files, server logs, and clickstream
|
||||
data) are used for system management, to improve the content of the
|
||||
Services, market research purposes, and to communicate information to
|
||||
visitors.
|
||||
</p>
|
||||
<p>
|
||||
<strong>4. Use and Sharing of Anonymous, Passive Information.</strong>
|
||||
<br>
|
||||
The Services may make full use of passively collected anonymous
|
||||
information, including without limitation the right to use such
|
||||
information to provide better service to Service users, customize the
|
||||
Services based on your preferences, compile and analyze statistics and
|
||||
trends, and otherwise administer and improve the Services for your use. We
|
||||
reserve the right to share this anonymous, passive information in
|
||||
aggregated form.
|
||||
</p>
|
||||
<p>
|
||||
<strong>5. 3rd Party Behavioral Ads; Google's AdSense Network.</strong>
|
||||
<br>
|
||||
5.1 We reserve the right to use anonymous, passive information
|
||||
about your visits to this and other websites (not including your name,
|
||||
address, email address or telephone number) for purposes of serving our
|
||||
ads and third party ads that are targeted to your interests ("3rd Party
|
||||
Behavioral Ads"). We reserve the right to share anonymous, passive
|
||||
information collected on the services with third parties for purposes of
|
||||
serving 3rd Party Behavioral Ads. These 3rd Party Behavioral Ads do not
|
||||
identify you personally. Instead, they associate your behavioral data on
|
||||
visited sites with your browser, so that the ads your computer sees on
|
||||
this site are more likely to be relevant to your interests. 3rd Party
|
||||
Behavioral Ads require that that you be served with a cookie containing
|
||||
a tracking code. You may refuse the use of cookies by selecting the
|
||||
appropriate settings on your browser; however, please note that if you
|
||||
do this you may not be able to use the full functionality of this site.
|
||||
</p>
|
||||
<p>
|
||||
5.2 We reserve the right to participate in Google's AdSense
|
||||
network for purposes of serving 3rd Party Behavioral Ads. Google uses
|
||||
DoubleClick's DART cookie for serving 3rd Party Behavioral Ads over the
|
||||
AdSense network. You may opt out of the use of the DART cookie. For
|
||||
information regarding how to opt out, go to
|
||||
http://www.google.com/privacy_ads.html.
|
||||
</p>
|
||||
<p>
|
||||
<strong>6. Use of 3rd Party Analytics.</strong>
|
||||
<br>
|
||||
We reserve the right to use analytics services provided by
|
||||
third parties. These services use 3rd party cookies to collect
|
||||
anonymous, passive information about your use of this site (see
|
||||
explanation of cookies in Collection of Anonymous, Passive Information
|
||||
above). We use this information for the purpose of evaluating your use
|
||||
of the Services, compiling reports on activity, and providing other
|
||||
services. These web analytics services may also transfer this
|
||||
information to third parties where required to do so by law, or where
|
||||
such third parties process the information on the service's behalf.
|
||||
</p>
|
||||
<p>
|
||||
<strong>7. Collection of Personal Information; Categories.</strong>
|
||||
<br>
|
||||
We will ask you for personal information when you sign up for any
|
||||
specific benefit or purpose that requires registration. Personal
|
||||
information that we collect may vary with the each registration, and it
|
||||
may include one or more of the following categories: name, physical
|
||||
address, an email address, phone number, and credit card information
|
||||
including credit card number, expiration date, and billing address,
|
||||
emergency contact information, current medications, allergies, medical
|
||||
insurance information.
|
||||
</p>
|
||||
<p>
|
||||
<strong> 8. Use And Sharing of Personal Information: General
|
||||
Policy And Exceptions. </strong>
|
||||
<br>
|
||||
Our general policy is that we will use your personal information,
|
||||
including combining your personal information with passive information
|
||||
collected from this site, only for: the performance of the services or
|
||||
transaction for which it was given, our private, internal reporting for
|
||||
this site, and security assessments for this site, and we will not
|
||||
share, sell, or rent your personal information to others. The only
|
||||
exceptions to this general policy: (i) are described in the subsections
|
||||
below, and (ii) if you explicitly approve through our site.
|
||||
</p>
|
||||
<p>
|
||||
8.1 Affiliates And Service Providers. We reserve the right to
|
||||
provide such information to our affiliates or subsidiaries, or trusted
|
||||
service providers for the purpose of hosting our servers or processing
|
||||
or archiving personal information for us. We require that these parties
|
||||
agree to privacy and security safeguards for this information that are
|
||||
consistent with this Privacy Policy.
|
||||
</p>
|
||||
<p>
|
||||
8.2 Acquisition; Bankruptcy. In the event that we are acquired by
|
||||
or merged with a third party entity, we reserve the right to transfer
|
||||
such information as part of such merger, acquisition, sale, or other
|
||||
change of control. In the unlikely event of our bankruptcy, insolvency,
|
||||
reorganization, receivership, or assignment for the benefit of
|
||||
creditors, or the application of laws or equitable principles affecting
|
||||
creditors' rights generally, we reserve the right to transfer such
|
||||
information to protect our rights or as required by law.
|
||||
</p>
|
||||
<p>
|
||||
8.3 Enforcement; Legal Process. We reserve the right to transfer
|
||||
such information if we have a good faith belief that access, use,
|
||||
preservation or disclosure of such information is reasonably necessary
|
||||
(i) to satisfy any applicable law, regulation, legal process or
|
||||
enforceable governmental request, or (ii) to investigate or enforce
|
||||
violations of our rights or the security of this site.
|
||||
</p>
|
||||
<p>
|
||||
8.4 Miscellaneous. We reserve the right to share personal
|
||||
information with the following additional parties: online organizers
|
||||
using our tools and resellers of our products and services from whose
|
||||
site the sale originated (even though the sale originates at site of the
|
||||
reseller, registration and collection of personal information occurs at
|
||||
this site).
|
||||
</p>
|
||||
<p>
|
||||
<strong> 9. Onward Transfer of Personal Information Outside Your
|
||||
Country of Residence. </strong>
|
||||
<br>
|
||||
Any personal information which we may collect on this site will
|
||||
be stored and processed in our servers located only in the United
|
||||
States. By using this site, if you reside outside the United States, you
|
||||
consent to the transfer of personal information outside your country of
|
||||
residence to the United States.
|
||||
</p>
|
||||
<p>
|
||||
<strong>10. Security of Personal Information.</strong>
|
||||
<br>
|
||||
We follow reasonable and appropriate industry standards to
|
||||
protect your personal information and data. Unfortunately, no data
|
||||
transmission over the Internet or method of data storage can be
|
||||
guaranteed 100% secure. Therefore, while we strive to protect your
|
||||
personal information by following generally accepted industry standards,
|
||||
we cannot ensure or warrant the absolute security of any information you
|
||||
transmit to us or archive at this site.
|
||||
</p>
|
||||
<p>
|
||||
<strong>11. Changing And Updating Personal Information.</strong>
|
||||
<br>
|
||||
Upon request, we will permit you to request or make changes or
|
||||
updates to your personal information for legitimate purposes. We request
|
||||
identification prior to approving such requests. We reserve the right to
|
||||
decline any requests that are unreasonably repetitive or systematic,
|
||||
require unreasonable time or effort of our technical or administrative
|
||||
personnel, or undermine the privacy rights of others. We reserve the
|
||||
right to permit you to access your personal information in any account
|
||||
you establish with this site for purposes of making your own changes or
|
||||
updates, and in such case, instructions for making such changes or
|
||||
updates will be provided where necessary.
|
||||
</p>
|
||||
<p>
|
||||
<strong>12. Email From This Site; Opt-Out Rights.</strong>
|
||||
<br>
|
||||
If you supply us with your e-mail address you may receive
|
||||
periodic messages from us with information specific to the Services and
|
||||
required for the normal functioning of the Services as well as for new
|
||||
products or services or upcoming events. If you prefer not to receive
|
||||
periodic email messages, you may opt-out by following the instructions
|
||||
on the email.
|
||||
</p>
|
||||
<p>
|
||||
<strong>13. Children's Online Policy.</strong>
|
||||
<br>
|
||||
We are committed to preserving online privacy for all of its
|
||||
website visitors, including children. This site is a general audience
|
||||
site. Consistent with the Children's Online Privacy Protection Act
|
||||
(COPPA), we will not knowingly collect any information from, or sell to,
|
||||
children under the age of 13. If you are a parent or guardian who has
|
||||
discovered that your child under the age of 13 has submitted his or her
|
||||
personally identifiable information without your permission or consent,
|
||||
we will remove the information from our active list, at your request. To
|
||||
request the removal of your child's information, please send contact our
|
||||
site as provided below under “Contact Us”, and be sure to include in
|
||||
your message the same login information that your child submitted.
|
||||
</p>
|
||||
<p>
|
||||
<strong> 14. Email And Other Messages Through This Site; ECPA
|
||||
Notice. </strong>
|
||||
<br>
|
||||
This site treats email messages and other electronic messages
|
||||
that are sent through this site and not viewable by others as
|
||||
confidential and private, except as required by law, including without
|
||||
limitation, the Electronic Communications Privacy Act of 1986, 18 U.S.C.
|
||||
Sections 2701-2711 (the "ECPA"). The ECPA permits this site's limited
|
||||
ability to intercept and/or disclose electronic messages, for example
|
||||
(i) as necessary to operate our system or to protect our rights or
|
||||
property, (ii) upon legal demand (court orders, warrants, subpoenas), or
|
||||
(iii) where we receive information inadvertently which appears to
|
||||
pertain to the commission of a crime. This site is not considered a
|
||||
"secure communications medium" under the ECPA.
|
||||
</p>
|
||||
<p>
|
||||
<strong>15. Contact Us.</strong>
|
||||
<br>
|
||||
If you have any questions regarding this Privacy Policy, please
|
||||
contact the owner and operator of this website business:
|
||||
</p>
|
||||
<address>
|
||||
<strong>OCDevel</strong>
|
||||
<br>
|
||||
98 Morrison Ave #2
|
||||
<br>
|
||||
Somerville, MA 02144
|
||||
<br>
|
||||
Email:
|
||||
<a href="mailto:tylerrenelle@gmail.com">tylerrenelle@gmail.com</a>
|
||||
<br>
|
||||
Telephone: 1-805-975-5236
|
||||
</address>
|
||||
<p></p>
|
||||
<div id="home_static_footer">
|
||||
© 2012 OCDevel
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
74
views/static/team.html
Normal file
74
views/static/team.html
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | Team
|
||||
|
||||
<Head:>
|
||||
<app:head:head active="team" />
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header />
|
||||
<div class='container'>
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h1>Team</h1>
|
||||
<p class="lead">The HabitRPG team has developed organically in a meritocratic fashion. We're still figuring it all out, but the members we have listed here have contributed greatly. If you want to be part of the team, contribute to the repository, and if you're a big asset we'll likely adopt you :) See the <a href="https://github.com/lefnire/habitrpg/wiki/Business-Model">Count Me In</a></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2 id="developers">Developers</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/lefnire">Tyler Renelle</a></li>
|
||||
<li><a href="https://github.com/switz">Daniel Saewitz</a></li>
|
||||
<li><a href="https://github.com/SlappyBag">Stan Lindsey</a></li>
|
||||
<li><a href="https://github.com/MrConcept">Philip How</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2 id="pixel-artists">Pixel Artists</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/pandoro">Alex Hermans</a></li>
|
||||
<li><a href="https://github.com/Shaners">Shane Listers</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2 id="mobile-developers">Mobile Developers</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/litenull">Tomaz Korenika</a></li>
|
||||
<li><a href="https://github.com/lefnire">Tyler Renelle</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2 id="designers">Designers</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/zakkain">Zachary Kain</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div class="page-header">
|
||||
<h2 id="community-managers">Community Management</h2>
|
||||
<ul>
|
||||
<li><a href="https://github.com/wc8">@wc8</a></li>
|
||||
<li><a href="https://github.com/horusofoz">Jeff Smith</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
646
views/static/terms.html
Normal file
646
views/static/terms.html
Normal file
|
|
@ -0,0 +1,646 @@
|
|||
<import: src="head">
|
||||
<import: src="header">
|
||||
<import: src="../app/footer">
|
||||
|
||||
<Title:>
|
||||
HabitRPG | Terms
|
||||
|
||||
<Head:>
|
||||
<app:head:head active="about"/>
|
||||
|
||||
<Body:>
|
||||
<div id="wrap">
|
||||
<app:header:header />
|
||||
<div class='container'>
|
||||
<div class="page-header">
|
||||
<h1>Terms of Use</h1>
|
||||
</div>
|
||||
<p>
|
||||
Last updated September 2, 2012
|
||||
</p>
|
||||
<p>
|
||||
HabitRPG (or "we") provides services through our
|
||||
software applications for various devices and platforms ("HabitRPG
|
||||
Applications") and the HabitRPG.com domain, and any sub domains thereto
|
||||
(the "Sites"). Individually or collectively, HabitRPG Applications and
|
||||
Sites may be referred to as the "Services".
|
||||
</p>
|
||||
<p>
|
||||
Please read the following terms and conditions ("Terms of
|
||||
Service") carefully. These Terms of Service govern your access to and
|
||||
use of the Services and HabitRPG Content (defined below) and set forth
|
||||
the legally binding terms for your use of the Services and HabitRPG
|
||||
Content, whether or not you have registered as a Member.
|
||||
</p>
|
||||
<p>
|
||||
Certain areas of the Services (and your access to or use of
|
||||
HabitRPG Content) may have different terms and conditions posted or may
|
||||
require you to agree with and accept additional terms and conditions. If
|
||||
there is a conflict between these Terms of Service and terms and
|
||||
conditions posted for a specific area of the Services or HabitRPG
|
||||
Content, the latter terms and conditions will take precedence with
|
||||
respect to your use of or access to that area of the Services or HabitRPG
|
||||
Content.
|
||||
</p>
|
||||
<p>
|
||||
YOU ACKNOWLEDGE AND AGREE THAT, BY CLICKING ON THE "I AGREE" OR
|
||||
"I ACCEPT" BUTTON, OR BY ACCESSING OR USING THE SERVICES OR BY
|
||||
DOWNLOADING OR POSTING ANY CONTENT FROM OR ON THE SITES OR THROUGH THE
|
||||
SERVICES, YOU ARE INDICATING THAT YOU HAVE READ, UNDERSTAND AND AGREE TO
|
||||
BE BOUND BY THESE TERMS, WHETHER OR NOT YOU HAVE REGISTERED AS A MEMBER,
|
||||
AND AGREE TO OUR PRIVACY POLICY AS DESCRIBED BELOW. IF YOU DO NOT AGREE
|
||||
TO THESE TERMS, THEN YOU HAVE NO RIGHT TO ACCESS OR USE THE SERVICES OR
|
||||
HABITRPG CONTENT.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Modification</strong>
|
||||
<br>
|
||||
HabitRPG reserves the right, at its sole discretion, to modify,
|
||||
discontinue or terminate the Services, including any portion thereof on
|
||||
a global or individual basis, or to modify these Terms of Service, at
|
||||
any time and without prior notice. If we modify these Terms of Service,
|
||||
we will update the "Last Updated Date" above and post the modification
|
||||
on the Sites and perhaps elsewhere within the Services. By continuing to
|
||||
access or use the Services after we have posted a modification to these
|
||||
Terms of Service or have provided you with notice of a modification, you
|
||||
are indicating that you agree to be bound by the modified Terms of
|
||||
Service. If the modified Terms of Service are not acceptable to you,
|
||||
your only recourse is to cease using the Services.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Eligibility and HabitRPG Account Registration</strong>
|
||||
<br>
|
||||
In order to access certain features of the Sites and Services, and to
|
||||
post any Public User Content (defined below) on the Sites or through the
|
||||
Services, you must register to create an account ("HabitRPG Account") and
|
||||
become a "Member". In compliance with privacy laws, we do not allow
|
||||
people below the age of 14 to create accounts; please see our Privacy
|
||||
Policy for further information. During the registration process, you
|
||||
will be required to provide certain information and you will establish a
|
||||
username and a password. You agree to provide accurate, current and
|
||||
complete information during the registration process and to update such
|
||||
information to keep it accurate, current and complete. HabitRPG reserves
|
||||
the right to suspend or terminate your HabitRPG Account if any
|
||||
information provided during the registration process or thereafter
|
||||
proves to be inaccurate, not current or incomplete. If you are not a
|
||||
Member you may browse all areas of the Sites or use the parts of the
|
||||
Services that are not limited to Members only. You are responsible for
|
||||
safeguarding your password. You agree not to disclose your password to
|
||||
any third party and to take sole responsibility for any activities or
|
||||
actions under your HabitRPG Account, whether or not you have authorized
|
||||
such activities or actions. You agree to immediately notify HabitRPG of
|
||||
any unauthorized use of your HabitRPG Account. We are not liable for any
|
||||
damages or losses caused by someone using your account without your
|
||||
permission.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Privacy</strong>
|
||||
<br>
|
||||
See HabitRPG's Privacy Policy at http://www.HabitRPG.com/privacy for
|
||||
information and notices concerning HabitRPG's collection and use of your
|
||||
personal information. If you have any questions about the HabitRPG
|
||||
Privacy Policy, please contact HabitRPG at privacy AT HabitRPG.com. By
|
||||
accessing the Services you are agreeing to the terms of our Privacy
|
||||
Policy.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Content</strong>
|
||||
<br>
|
||||
Certain types of content are made available through the Services.
|
||||
"HabitRPG Content" means, collectively, the text, data, graphics, images,
|
||||
illustrations, forms and look and feel attributes, HabitRPG trademarks
|
||||
and logos and other content made available through the Services,
|
||||
including any technology or code making up the Services, excluding User
|
||||
Content. "Public User Content" means the text, data, graphics, images, photos,
|
||||
video or audiovisual content, hypertext links and any other content uploaded,
|
||||
transmitted or submitted by a Member via the Services with the intent to share
|
||||
with other users. "Private User Content" means data created through the services
|
||||
exclusively for personal use or private sharing.
|
||||
This includes tasks and related data created in HabitRPG Tasks that have not
|
||||
been explicitly shared publicly.
|
||||
You understand that by using any of the Services, you may encounter content
|
||||
that may be deemed offensive, indecent, or objectionable, which content
|
||||
may or may not be identified as having explicit language, and that the
|
||||
results of any search or entering of a particular URL may automatically
|
||||
and unintentionally generate links or references to objectionable
|
||||
material. Nevertheless, you agree to use the Services at your sole risk
|
||||
and that we shall not have any liability to you for content that may be
|
||||
found to be offensive, indecent, or objectionable.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Ownership</strong>
|
||||
<br>
|
||||
The Services and HabitRPG Content are protected by copyright, trademark,
|
||||
and other laws of the United States and foreign countries. Except as
|
||||
expressly provided in these Terms of Service, HabitRPG and its licensors
|
||||
exclusively own all right, title and interest in and to the Services and
|
||||
HabitRPG Content, including all associated intellectual property rights.
|
||||
You will not remove, alter or obscure any copyright, trademark, service
|
||||
mark or other proprietary rights notices incorporated in or accompanying
|
||||
the Services or HabitRPG Content.
|
||||
</p>
|
||||
<p>
|
||||
<strong>HabitRPG License</strong>
|
||||
<br>
|
||||
Subject to your compliance with the terms and conditions of these Terms
|
||||
of Service, HabitRPG grants you a limited, non-exclusive,
|
||||
non-transferable license, without the right to sublicense, to access,
|
||||
use, view, download and print, where applicable, the Services and any
|
||||
HabitRPG Content solely for your personal and non-commercial purposes.
|
||||
You will not use, copy, adapt, modify, prepare derivative works based
|
||||
upon, distribute, license, sell, transfer, publicly display, publicly
|
||||
perform, transmit, stream, broadcast or otherwise exploit the Services
|
||||
or HabitRPG Content, except as expressly permitted in these Terms of
|
||||
Service. No licenses or rights are granted to you by implication or
|
||||
otherwise under any intellectual property rights owned or controlled by
|
||||
HabitRPG or its licensors, except for the licenses and rights expressly
|
||||
granted in these Terms of Service. With respect to HabitRPG Applications,
|
||||
your license is limited to use of such applications on platforms and
|
||||
devices that you own or control, and you may not distribute or make the
|
||||
HabitRPG Applications available over a network where it could be used by
|
||||
multiple devices at the same time.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Public User Content</strong>
|
||||
<br>
|
||||
By making available any Public User Content through the Services, you hereby
|
||||
grant to HabitRPG a worldwide, irrevocable, perpetual, non-exclusive,
|
||||
transferable, royalty-free license, with the right to sublicense, to
|
||||
use, copy, adapt, modify, distribute, license, sell, transfer, publicly
|
||||
display, publicly perform, transmit, stream, broadcast and otherwise
|
||||
exploit such Public User Content only on, through or by means of the Services.
|
||||
HabitRPG does not claim any ownership rights in any such Public User Content and
|
||||
nothing in these Terms of Service will be deemed to restrict any rights
|
||||
that you may have to use and exploit any such Public User Content.
|
||||
</p>
|
||||
<p>
|
||||
You acknowledge and agree that you are solely responsible for all
|
||||
Public User Content that you make available through the Services. Accordingly,
|
||||
you represent and warrant that: (i) you either are the sole and
|
||||
exclusive owner of all Public User Content that you make available through the
|
||||
Services or you have all rights, licenses, consents and releases that
|
||||
are necessary to grant to HabitRPG the rights in such Public User Content, as
|
||||
contemplated under these Terms of Service; and (ii) neither the User
|
||||
Content nor your posting, uploading, publication, submission or
|
||||
transmittal of the Public User Content or HabitRPG's use of the Public User Content (or
|
||||
any portion thereof) on, through or by means of the Services will
|
||||
infringe, misappropriate or violate a third party's patent, copyright,
|
||||
trademark, trade secret, moral rights or other intellectual property
|
||||
rights, or rights of publicity or privacy, or result in the violation of
|
||||
any applicable law or regulation.
|
||||
</p>
|
||||
<p>
|
||||
Copyrighted Materials: No Infringing Use. You will not use the
|
||||
Services to offer, display, distribute, transmit, route, provide
|
||||
connections to or store any material that infringes copyrighted works or
|
||||
otherwise violates or promotes the violation of the intellectual
|
||||
property rights of any third party. HabitRPG has adopted and implemented
|
||||
a policy that provides for the termination in appropriate circumstances
|
||||
of the accounts of users who repeatedly infringe or are believed to be
|
||||
or are charged with repeatedly infringing the rights of copyright
|
||||
holders.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Notify Us of Infringers</strong>
|
||||
<br>
|
||||
If you believe that something on the Services violates your copyright,
|
||||
notify our copyright agent in writing. The contact information for our
|
||||
copyright agent is at the bottom of this section.
|
||||
</p>
|
||||
<p>
|
||||
In order for us to take action, you must do the following in your
|
||||
notice:
|
||||
</p>
|
||||
<p>
|
||||
(1) provide your physical or electronic signature; (2) identify
|
||||
the copyrighted work that you believe is being infringed; (3) identify
|
||||
the item on the Services that you think is infringing your work and
|
||||
include sufficient information about where the material is located on
|
||||
the Services (including which website and URL) so that we can find it;
|
||||
(4) provide us with a way to contact you, such as your address,
|
||||
telephone number, or e-mail; (5) provide a statement that you believe in
|
||||
good faith that the item you have identified as infringing is not
|
||||
authorized by the copyright owner, its agent, or the law to be used on
|
||||
the Services; and (6) provide a statement that the information you
|
||||
provide in your notice is accurate, and that (under penalty of perjury),
|
||||
you are authorized to act on behalf of the copyright owner whose work is
|
||||
being infringed.
|
||||
</p>
|
||||
<p>
|
||||
Here is the contact information for our copyright agent:
|
||||
</p>
|
||||
<p>
|
||||
Copyright Enforcement
|
||||
<br>
|
||||
OCDevel
|
||||
<br>
|
||||
98 Morrison Ave #2
|
||||
<br>
|
||||
Somerville, MA 02144
|
||||
<br>
|
||||
Phone: (805) 975-5236
|
||||
<br>
|
||||
E-Mail: tylerrenelle at gmail dot com
|
||||
</p>
|
||||
<p>
|
||||
Again, we cannot take action unless you give us all the required
|
||||
information.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Ratings and Comments & Feedback.</strong>
|
||||
<br>
|
||||
You can rate and make comments about content made available through the
|
||||
Services ("Comments"). HabitRPG advises you to exercise caution and good
|
||||
judgment when leaving such Comments. Once you complete and submit your
|
||||
Comments to the Services you will not be able to go back and edit your
|
||||
Comments. You should also be aware that you could be held legally
|
||||
responsible for damages to someone's reputation if your Comments are
|
||||
deemed to be defamatory. Without limiting any other terms of this Terms
|
||||
of Service, HabitRPG may, but is under no obligation to, monitor or
|
||||
censor Comments and disclaims any and all liability relating thereto.
|
||||
Notwithstanding the foregoing, HabitRPG does reserve the right, in its
|
||||
sole discretion, to remove any Comments that it deems to be improper,
|
||||
inappropriate or inconsistent with the online activities that are
|
||||
permitted under these Terms of Service. We welcome and encourage you to
|
||||
provide feedback, comments and suggestions for improvements to the
|
||||
Services ("Feedback"). You may submit Feedback by emailing us at support
|
||||
AT HabitRPG.com. You acknowledge and agree that all Comments and Feedback
|
||||
will be the sole and exclusive property of HabitRPG and you hereby
|
||||
irrevocably assign to HabitRPG and agree to irrevocably assign to HabitRPG
|
||||
all of your right, title, and interest in and to all Comments and
|
||||
Feedback, including without limitation all worldwide patent rights,
|
||||
copyright rights, trade secret rights, and other proprietary or
|
||||
intellectual property rights therein. At HabitRPG's request and expense,
|
||||
you will execute documents and take such further acts as HabitRPG may
|
||||
reasonably request to assist HabitRPG to acquire, perfect, and maintain
|
||||
its intellectual property rights and other legal protections for the
|
||||
Comments and Feedback.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Interactions between Users</strong>
|
||||
<br>
|
||||
You are solely responsible for your interactions (including any
|
||||
disputes) with other users. You understand that HabitRPG does not in any
|
||||
way screen HabitRPG users, except to only allow people aged 14 and over
|
||||
to create accounts. You are solely responsible for, and will exercise
|
||||
caution, discretion, common sense and judgment in, using the Services
|
||||
and disclosing personal information to other HabitRPG users. You agree to
|
||||
take reasonable precautions in all interactions with other HabitRPG
|
||||
users, particularly if you decide to meet a HabitRPG user offline, or in
|
||||
person. Your use of the Services, HabitRPG Content and any other content
|
||||
made available through the Services is at your sole risk and discretion
|
||||
and HabitRPG hereby disclaims any and all liability to you or any third
|
||||
party relating thereto. HabitRPG reserves the right to contact Members,
|
||||
in compliance with applicable law, in order to evaluate compliance with
|
||||
the rules and policies in these Terms of Service. You will cooperate
|
||||
fully with HabitRPG to investigate any suspected unlawful, fraudulent or
|
||||
improper activity, including, without limitation, granting authorized
|
||||
HabitRPG representatives access to any password-protected portions of
|
||||
your HabitRPG Account.
|
||||
</p>
|
||||
<p>
|
||||
<strong>General Prohibitions</strong>
|
||||
<br>
|
||||
You agree not to do any of the following while using the Services or
|
||||
HabitRPG Content:
|
||||
<br>
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Post, upload, publish, submit or transmit any text, graphics,
|
||||
images, software, music, audio, video, information or other material
|
||||
that: (i) infringes, misappropriates or violates a third party's
|
||||
patent, copyright, trademark, trade secret, moral rights or other
|
||||
intellectual property rights, or rights of publicity or privacy; (ii)
|
||||
violates, or encourages any conduct that would violate, any applicable
|
||||
law or regulation or would give rise to civil liability; (iii) is
|
||||
fraudulent, false, misleading or deceptive; (iv) is defamatory,
|
||||
obscene, pornographic, vulgar or offensive; (v) promotes
|
||||
discrimination, bigotry, racism, hatred, harassment or harm against any
|
||||
individual or group; (vi) is violent or threatening or promotes
|
||||
violence or actions that are threatening to any other person; or (vii)
|
||||
promotes illegal or harmful activities or substances (including but not
|
||||
limited to activities that promote or provide instructional information
|
||||
regarding the manufacture or purchase of illegal weapons or illegal
|
||||
substances).
|
||||
</li>
|
||||
<li>
|
||||
Use, display, mirror, frame or utilize framing techniques to
|
||||
enclose the Services, or any individual element or materials within the
|
||||
Services, HabitRPG's name, any HabitRPG trademark, logo or other
|
||||
proprietary information, the content of any text or the layout and
|
||||
design of any page or form contained on a page, without HabitRPG's
|
||||
express written consent;
|
||||
</li>
|
||||
<li>
|
||||
Access, tamper with, or use non-public areas of the Services,
|
||||
HabitRPG's computer systems, or the technical delivery systems of
|
||||
HabitRPG's providers;
|
||||
</li>
|
||||
<li>
|
||||
Attempt to probe, scan, or test the vulnerability of any
|
||||
HabitRPG system or network or breach any security or authentication
|
||||
measures;
|
||||
</li>
|
||||
<li>
|
||||
Avoid, bypass, remove, deactivate, impair, descramble or
|
||||
otherwise circumvent any technological measure implemented by HabitRPG
|
||||
or any of HabitRPG's providers or any other third party (including
|
||||
another user) to protect the Services or HabitRPG Content;
|
||||
</li>
|
||||
<li>
|
||||
Attempt to access or search the Services or HabitRPG Content or
|
||||
download HabitRPG Content from the Services through the use of any
|
||||
engine, software, tool, agent, device or mechanism (including spiders,
|
||||
robots, crawlers, data mining tools or the like) other than the
|
||||
software and/or search agents provided by HabitRPG or other generally
|
||||
available third party web browsers (such as Google Chrome, Microsoft
|
||||
Internet Explorer, Mozilla Firefox, Safari or Opera);
|
||||
</li>
|
||||
<li>
|
||||
Send any unsolicited or unauthorized advertising, promotional
|
||||
materials, email, junk mail, spam, chain letters or other form of
|
||||
solicitation;
|
||||
</li>
|
||||
<li>
|
||||
Use any meta tags or other hidden text or metadata utilizing a
|
||||
HabitRPG trademark, logo URL or product name without HabitRPG's express
|
||||
written consent;
|
||||
</li>
|
||||
<li>
|
||||
Use the Services or HabitRPG Content for any commercial purpose
|
||||
or the benefit of any third party or in any manner not permitted by
|
||||
these Terms of Service;
|
||||
</li>
|
||||
<li>
|
||||
Forge any TCP/IP packet header or any part of the header
|
||||
information in any email or newsgroup posting, or in any way use the
|
||||
Services or HabitRPG Content to send altered, deceptive or false
|
||||
source-identifying information;
|
||||
</li>
|
||||
<li>
|
||||
Attempt to decipher, decompile, disassemble or reverse
|
||||
engineer any of the software used to provide the Services or HabitRPG
|
||||
Content;
|
||||
</li>
|
||||
<li>
|
||||
Interfere with, or attempt to interfere with, the access of
|
||||
any user, host or network, including, without limitation, sending a
|
||||
virus, overloading, flooding, spamming, or mail-bombing the Services;
|
||||
</li>
|
||||
<li>
|
||||
Collect or store any personally identifiable information from
|
||||
the Services from other users of the Services without their express
|
||||
permission;
|
||||
</li>
|
||||
<li>
|
||||
Impersonate or misrepresent your affiliation with any person
|
||||
or entity; Violate any applicable law or regulation; or
|
||||
</li>
|
||||
<li>
|
||||
Encourage or enable any other individual to do any of the
|
||||
foregoing.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
HabitRPG will have the right to investigate and prosecute
|
||||
violations of any of the above, including intellectual property rights
|
||||
infringement and Services security issues, to the fullest extent of the
|
||||
law. HabitRPG may involve and cooperate with law enforcement authorities
|
||||
in prosecuting users who violate these Terms of Service. You acknowledge
|
||||
that HabitRPG has no obligation to monitor your access to or use of the
|
||||
Services or HabitRPG Content or to review or edit any Public User Content, but
|
||||
has the right to do so for the purpose of operating the Services, to
|
||||
ensure your compliance with these Terms of Service, or to comply with
|
||||
applicable law or the order or requirement of a court, administrative
|
||||
agency or other governmental body. HabitRPG reserves the right, at any
|
||||
time and without prior notice, to remove or disable access to any
|
||||
HabitRPG Content, including, any Public User Content, that HabitRPG, in its sole
|
||||
discretion, considers to be in violation of these Terms of Service or
|
||||
otherwise harmful to the Services.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Links</strong>
|
||||
<br>
|
||||
The Services may contain links to third-party websites or resources. You
|
||||
acknowledge and agree that HabitRPG is not responsible or liable for: (i)
|
||||
the availability or accuracy of such websites or resources; or (ii) the
|
||||
content, products, or services on or available from such websites or
|
||||
resources. Links to such websites or resources do not imply any
|
||||
endorsement by HabitRPG of such websites or resources or the content,
|
||||
products, or services available from such websites or resources. You
|
||||
acknowledge sole responsibility for and assume all risk arising from
|
||||
your use of any such websites or resources.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Termination and HabitRPG Account; Cancellation</strong>
|
||||
<br>
|
||||
Without limiting other remedies, HabitRPG may at any time suspend or
|
||||
terminate your HabitRPG Account and refuse to provide access to the
|
||||
Services. In addition, HabitRPG may notify authorities or take any
|
||||
actions it deems appropriate, without notice to you, if HabitRPG suspects
|
||||
or determines, in its own discretion, that you may have or there is a
|
||||
significant risk that you have (i) failed to comply with any provision
|
||||
of these Terms of Service or any policies or rules established by
|
||||
HabitRPG; or (ii) engaged in actions relating to or in the course of
|
||||
using the Services that may be illegal or cause liability, harm,
|
||||
embarrassment, harassment, abuse or disruption for you, HabitRPG Users,
|
||||
HabitRPG or any other third parties or the Services.
|
||||
</p>
|
||||
<p>
|
||||
You may terminate your HabitRPG Account at any time and for any
|
||||
reason by sending email to support AT HabitRPG.com. Upon any termination
|
||||
by a Member, the related account will no longer be accessible.
|
||||
</p>
|
||||
<p>
|
||||
After any termination, you understand and acknowledge that we
|
||||
will have no further obligation to provide the Services and all licenses
|
||||
and other rights granted to you by these Terms of Service will
|
||||
immediately cease. HabitRPG will not be liable to you or any third party
|
||||
for termination of the Services or termination of your use of either.
|
||||
UPON ANY TERMINATION OR SUSPENSION, ANY CONTENT, MATERIALS OR
|
||||
INFORMATION (INCLUDING PUBLIC USER CONTENT) THAT YOU HAVE SUBMITTED ON THE
|
||||
SERVICES OR THAT WHICH IS RELATED TO YOUR ACCOUNT MAY NO LONGER BE
|
||||
ACCESSED BY YOU. Furthermore, HabitRPG will have no obligation to
|
||||
maintain any information stored in our database related to your account
|
||||
or to forward any information to you or any third party.
|
||||
</p>
|
||||
<p>
|
||||
Any suspension, termination or cancellation will not affect your
|
||||
obligations to HabitRPG under these Terms of Service (including, without
|
||||
limitation, proprietary rights and ownership, indemnification and
|
||||
limitation of liability), which by their sense and context are intended
|
||||
to survive such suspension, termination or cancellation.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Disclaimers</strong>
|
||||
<br>
|
||||
THE SERVICES, HABITRPG CONTENT AND PUBLIC USER CONTENT ARE PROVIDED "AS IS",
|
||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED. WITHOUT
|
||||
LIMITING THE FOREGOING, HABITRPG EXPLICITLY DISCLAIMS ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
||||
NON-INFRINGEMENT, AND ANY WARRANTIES ARISING OUT OF COURSE OF DEALING OR
|
||||
USAGE OF TRADE.
|
||||
</p>
|
||||
<p>
|
||||
HABITRPG MAKES NO WARRANTY THAT THE SERVICES, HABITRPG CONTENT OR
|
||||
PUBLIC USER CONTENT WILL MEET YOUR REQUIREMENTS OR BE AVAILABLE ON AN
|
||||
UNINTERRUPTED, SECURE, OR ERROR-FREE BASIS. HABITRPG MAKES NO WARRANTY
|
||||
REGARDING THE QUALITY OF ANY PRODUCTS, SERVICES OR CONTENT PURCHASED OR
|
||||
OBTAINED THROUGH THE SERVICES OR THE ACCURACY, TIMELINESS, TRUTHFULNESS,
|
||||
COMPLETENESS OR RELIABILITY OF ANY CONTENT OBTAINED THROUGH THE
|
||||
SERVICES.
|
||||
</p>
|
||||
<p>
|
||||
NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED FROM
|
||||
HABITRPG OR THROUGH THE SERVICES, HABITRPG CONTENT OR PUBLIC USER CONTENT, WILL
|
||||
CREATE ANY WARRANTY NOT EXPRESSLY MADE HEREIN.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Indemnity</strong>
|
||||
<br>
|
||||
You agree to defend, indemnify, and hold HabitRPG, its officers,
|
||||
directors, employees and agents, harmless from and against any claims,
|
||||
liabilities, damages, losses, and expenses, including, without
|
||||
limitation, reasonable legal and accounting fees, arising out of or in
|
||||
any way connected with Public User Content you submit to HabitRPG, your access
|
||||
to or use of the Services or HabitRPG Content, or your violation of these
|
||||
Terms of Service.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Limitation of Liability</strong>
|
||||
<br>
|
||||
YOU ACKNOWLEDGE AND AGREE THAT, TO THE MAXIMUM EXTENT PERMITTED BY LAW,
|
||||
THE ENTIRE RISK ARISING OUT OF YOUR ACCESS TO AND USE OF THE SERVICES
|
||||
AND CONTENT THEREIN REMAINS WITH YOU. NEITHER HABITRPG NOR ANY OTHER
|
||||
PARTY INVOLVED IN CREATING, PRODUCING, OR DELIVERING THE SERVICES OR
|
||||
HABITRPG CONTENT WILL BE LIABLE FOR ANY INCIDENTAL, SPECIAL, EXEMPLARY OR
|
||||
CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, LOSS OF DATA OR LOSS OF
|
||||
GOODWILL, SERVICE INTERRUPTION, COMPUTER DAMAGE OR SYSTEM FAILURE OR THE
|
||||
COST OF SUBSTITUTE PRODUCTS OR SERVICES, ARISING OUT OF OR IN CONNECTION
|
||||
WITH THESE TERMS OR FROM THE USE OF OR INABILITY TO USE THE SERVICES OR
|
||||
CONTENT THEREIN, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING
|
||||
NEGLIGENCE), PRODUCT LIABILITY OR ANY OTHER LEGAL THEORY, AND WHETHER OR
|
||||
NOT HABITRPG HAS BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGE, EVEN IF
|
||||
A LIMITED REMEDY SET FORTH HEREIN IS FOUND TO HAVE FAILED OF ITS
|
||||
ESSENTIAL PURPOSE. YOU SPECIFICALLY ACKNOWLEDGE THAT HABITRPG IS NOT
|
||||
LIABLE FOR THE DEFAMATORY, OFFENSIVE OR ILLEGAL CONDUCT OF OTHER USERS
|
||||
OR THIRD PARTIES AND THAT THE RISK OF INJURY FROM THE FOREGOING RESTS
|
||||
ENTIRELY WITH YOU. FURTHER, HABITRPG WILL HAVE NO LIABILITY TO YOU OR TO
|
||||
ANY THIRD PARTY FOR ANY PUBLIC USER CONTENT OR THIRD-PARTY CONTENT UPLOADED
|
||||
ONTO OR DOWNLOADED FROM THE SITES OR THROUGH THE SERVICES.
|
||||
</p>
|
||||
<p>
|
||||
IN NO EVENT WILL HABITRPG'S AGGREGATE LIABILITY ARISING OUT OF OR
|
||||
IN CONNECTION WITH THESE TERMS OF SERVICE OR FROM THE USE OF OR
|
||||
INABILITY TO USE THE SITE, SERVICES OR CONTENT THEREIN EXCEED ONE
|
||||
HUNDRED U.S. DOLLARS ($100). THE LIMITATIONS OF DAMAGES SET FORTH ABOVE
|
||||
ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN HABITRPG AND
|
||||
YOU. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF
|
||||
LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE
|
||||
LIMITATION MAY NOT APPLY TO YOU.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Proprietary Rights Notices</strong>
|
||||
<br>
|
||||
All trademarks, service marks, logos, trade names and any other
|
||||
proprietary designations of HabitRPG used herein are trademarks or
|
||||
registered trademarks of HabitRPG. Any other trademarks, service marks,
|
||||
logos, trade names and any other proprietary designations are the
|
||||
trademarks or registered trademarks of their respective parties.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Controlling Law and Jurisdiction</strong>
|
||||
<br>
|
||||
These Terms of Service and any action related thereto will be governed
|
||||
by the laws of the State of California without regard to its conflict of
|
||||
laws provisions. The exclusive jurisdiction and venue of any action with
|
||||
respect to the subject matter of these Terms of Service will be the
|
||||
courts having jurisdiction over disputes arising in Santa Clara County,
|
||||
California, and each of the parties hereto waives any objection to
|
||||
jurisdiction and venue in such courts.
|
||||
</p>
|
||||
<p>
|
||||
YOU AGREE THAT IF YOU WANT TO SUE US, YOU MUST FILE YOUR LAWSUIT
|
||||
WITHIN ONE YEAR AFTER THE EVENT THAT GAVE RISE TO YOUR LAWSUIT.
|
||||
OTHERWISE, YOUR LAWSUIT WILL BE PERMANENTLY BARRED.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Export Control</strong>
|
||||
<br>
|
||||
You may not use or otherwise export or re-export the Services except as
|
||||
authorized by United States law and the laws of the jurisdiction in
|
||||
which the Services were obtained. In particular, but without limitation,
|
||||
the Services may not be exported or re-exported (a) into any U.S.
|
||||
embargoed countries or (b) to anyone on the U.S. Treasury Department's
|
||||
list of Specially Designated Nationals or the U.S. Department of
|
||||
Commerce Denied Person's List or Entity List. By using the Services, you
|
||||
represent and warrant that you are not located in any such country or on
|
||||
any such list. You also agree that you will not use these products for
|
||||
any purposes prohibited by United States law, including, without
|
||||
limitation, the development, design, manufacture or production of
|
||||
nuclear, missiles, or chemical or biological weapons.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Entire Agreement</strong>
|
||||
<br>
|
||||
These Terms of Service constitute the entire and exclusive understanding
|
||||
and agreement between HabitRPG and you regarding the Services and HabitRPG
|
||||
Content, and these Terms of Service supersede and replace any and all
|
||||
prior oral or written understandings or agreements between HabitRPG and
|
||||
you regarding the Services and HabitRPG Content.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Assignment</strong>
|
||||
<br>
|
||||
You may not assign or transfer these Terms of Service, by operation of
|
||||
law or otherwise, without HabitRPG's prior written consent. Any attempt
|
||||
by you to assign or transfer these Terms of Service, without such
|
||||
consent, will be null and of no effect. HabitRPG may freely assign these
|
||||
Terms of Service. Subject to the foregoing, these Terms of Service will
|
||||
bind and inure to the benefit of the parties, their successors and
|
||||
permitted assigns.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Notices</strong>
|
||||
<br>
|
||||
You consent to the use of: (i) electronic means to complete these Terms
|
||||
of Service and to deliver any notices or other communications permitted
|
||||
or required hereunder; and (ii) electronic records to store information
|
||||
related to these Terms of Service or your use of the Services. Any
|
||||
notices or other communications permitted to required hereunder,
|
||||
including those regarding modifications to these Terms of Service, will
|
||||
be in writing and given: (x) by HabitRPG via email (in each case to the
|
||||
address that you provide) or (y) by posting to the Sites or Services.
|
||||
For notices made by e-mail, the date of receipt will be deemed the date
|
||||
on which such notice is transmitted.
|
||||
</p>
|
||||
<p>
|
||||
<strong>General</strong>
|
||||
<br>
|
||||
The failure of HabitRPG to enforce any right or provision of these Terms
|
||||
of Service will not constitute a waiver of future enforcement of that
|
||||
right or provision. The waiver of any such right or provision will be
|
||||
effective only if in writing and signed by a duly authorized
|
||||
representative of HabitRPG. Except as expressly set forth in these Terms
|
||||
of Service, the exercise by either party of any of its remedies under
|
||||
these Terms of Service will be without prejudice to its other remedies
|
||||
under these Terms of Service or otherwise. If for any reason a court of
|
||||
competent jurisdiction finds any provision of these Terms of Service
|
||||
invalid or unenforceable, that provision will be enforced to the maximum
|
||||
extent permissible and the other provisions of these Terms of Service
|
||||
will remain in full force and effect.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Contacting Us</strong>
|
||||
<br>
|
||||
If you have any questions about these Terms of Service, please contact
|
||||
us at tylerrenelle@gmail.com.
|
||||
</p>
|
||||
<div id="home_static_footer">
|
||||
© 2012 OCDevel
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<app:footer:footer />
|
||||
636
views/terms.html
636
views/terms.html
|
|
@ -1,636 +0,0 @@
|
|||
<Title:>
|
||||
Terms Of Use
|
||||
|
||||
<Body:>
|
||||
<div class="well">
|
||||
<a href="/">« Back</a>
|
||||
<div class="page-header">
|
||||
<h1>Terms Of Use</h1>
|
||||
</div>
|
||||
<p>
|
||||
Last updated September 2, 2012
|
||||
</p>
|
||||
<p>
|
||||
HabitRPG (or "we") provides services through our
|
||||
software applications for various devices and platforms ("HabitRPG
|
||||
Applications") and the HabitRPG.com domain, and any sub domains thereto
|
||||
(the "Sites"). Individually or collectively, HabitRPG Applications and
|
||||
Sites may be referred to as the "Services".
|
||||
</p>
|
||||
<p>
|
||||
Please read the following terms and conditions ("Terms of
|
||||
Service") carefully. These Terms of Service govern your access to and
|
||||
use of the Services and HabitRPG Content (defined below) and set forth
|
||||
the legally binding terms for your use of the Services and HabitRPG
|
||||
Content, whether or not you have registered as a Member.
|
||||
</p>
|
||||
<p>
|
||||
Certain areas of the Services (and your access to or use of
|
||||
HabitRPG Content) may have different terms and conditions posted or may
|
||||
require you to agree with and accept additional terms and conditions. If
|
||||
there is a conflict between these Terms of Service and terms and
|
||||
conditions posted for a specific area of the Services or HabitRPG
|
||||
Content, the latter terms and conditions will take precedence with
|
||||
respect to your use of or access to that area of the Services or HabitRPG
|
||||
Content.
|
||||
</p>
|
||||
<p>
|
||||
YOU ACKNOWLEDGE AND AGREE THAT, BY CLICKING ON THE "I AGREE" OR
|
||||
"I ACCEPT" BUTTON, OR BY ACCESSING OR USING THE SERVICES OR BY
|
||||
DOWNLOADING OR POSTING ANY CONTENT FROM OR ON THE SITES OR THROUGH THE
|
||||
SERVICES, YOU ARE INDICATING THAT YOU HAVE READ, UNDERSTAND AND AGREE TO
|
||||
BE BOUND BY THESE TERMS, WHETHER OR NOT YOU HAVE REGISTERED AS A MEMBER,
|
||||
AND AGREE TO OUR PRIVACY POLICY AS DESCRIBED BELOW. IF YOU DO NOT AGREE
|
||||
TO THESE TERMS, THEN YOU HAVE NO RIGHT TO ACCESS OR USE THE SERVICES OR
|
||||
HABITRPG CONTENT.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Modification</strong>
|
||||
<br>
|
||||
HabitRPG reserves the right, at its sole discretion, to modify,
|
||||
discontinue or terminate the Services, including any portion thereof on
|
||||
a global or individual basis, or to modify these Terms of Service, at
|
||||
any time and without prior notice. If we modify these Terms of Service,
|
||||
we will update the "Last Updated Date" above and post the modification
|
||||
on the Sites and perhaps elsewhere within the Services. By continuing to
|
||||
access or use the Services after we have posted a modification to these
|
||||
Terms of Service or have provided you with notice of a modification, you
|
||||
are indicating that you agree to be bound by the modified Terms of
|
||||
Service. If the modified Terms of Service are not acceptable to you,
|
||||
your only recourse is to cease using the Services.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Eligibility and HabitRPG Account Registration</strong>
|
||||
<br>
|
||||
In order to access certain features of the Sites and Services, and to
|
||||
post any Public User Content (defined below) on the Sites or through the
|
||||
Services, you must register to create an account ("HabitRPG Account") and
|
||||
become a "Member". In compliance with privacy laws, we do not allow
|
||||
people below the age of 14 to create accounts; please see our Privacy
|
||||
Policy for further information. During the registration process, you
|
||||
will be required to provide certain information and you will establish a
|
||||
username and a password. You agree to provide accurate, current and
|
||||
complete information during the registration process and to update such
|
||||
information to keep it accurate, current and complete. HabitRPG reserves
|
||||
the right to suspend or terminate your HabitRPG Account if any
|
||||
information provided during the registration process or thereafter
|
||||
proves to be inaccurate, not current or incomplete. If you are not a
|
||||
Member you may browse all areas of the Sites or use the parts of the
|
||||
Services that are not limited to Members only. You are responsible for
|
||||
safeguarding your password. You agree not to disclose your password to
|
||||
any third party and to take sole responsibility for any activities or
|
||||
actions under your HabitRPG Account, whether or not you have authorized
|
||||
such activities or actions. You agree to immediately notify HabitRPG of
|
||||
any unauthorized use of your HabitRPG Account. We are not liable for any
|
||||
damages or losses caused by someone using your account without your
|
||||
permission.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Privacy</strong>
|
||||
<br>
|
||||
See HabitRPG's Privacy Policy at http://www.HabitRPG.com/privacy for
|
||||
information and notices concerning HabitRPG's collection and use of your
|
||||
personal information. If you have any questions about the HabitRPG
|
||||
Privacy Policy, please contact HabitRPG at privacy AT HabitRPG.com. By
|
||||
accessing the Services you are agreeing to the terms of our Privacy
|
||||
Policy.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Content</strong>
|
||||
<br>
|
||||
Certain types of content are made available through the Services.
|
||||
"HabitRPG Content" means, collectively, the text, data, graphics, images,
|
||||
illustrations, forms and look and feel attributes, HabitRPG trademarks
|
||||
and logos and other content made available through the Services,
|
||||
including any technology or code making up the Services, excluding User
|
||||
Content. "Public User Content" means the text, data, graphics, images, photos,
|
||||
video or audiovisual content, hypertext links and any other content uploaded,
|
||||
transmitted or submitted by a Member via the Services with the intent to share
|
||||
with other users. "Private User Content" means data created through the services
|
||||
exclusively for personal use or private sharing.
|
||||
This includes tasks and related data created in HabitRPG Tasks that have not
|
||||
been explicitly shared publicly.
|
||||
You understand that by using any of the Services, you may encounter content
|
||||
that may be deemed offensive, indecent, or objectionable, which content
|
||||
may or may not be identified as having explicit language, and that the
|
||||
results of any search or entering of a particular URL may automatically
|
||||
and unintentionally generate links or references to objectionable
|
||||
material. Nevertheless, you agree to use the Services at your sole risk
|
||||
and that we shall not have any liability to you for content that may be
|
||||
found to be offensive, indecent, or objectionable.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Ownership</strong>
|
||||
<br>
|
||||
The Services and HabitRPG Content are protected by copyright, trademark,
|
||||
and other laws of the United States and foreign countries. Except as
|
||||
expressly provided in these Terms of Service, HabitRPG and its licensors
|
||||
exclusively own all right, title and interest in and to the Services and
|
||||
HabitRPG Content, including all associated intellectual property rights.
|
||||
You will not remove, alter or obscure any copyright, trademark, service
|
||||
mark or other proprietary rights notices incorporated in or accompanying
|
||||
the Services or HabitRPG Content.
|
||||
</p>
|
||||
<p>
|
||||
<strong>HabitRPG License</strong>
|
||||
<br>
|
||||
Subject to your compliance with the terms and conditions of these Terms
|
||||
of Service, HabitRPG grants you a limited, non-exclusive,
|
||||
non-transferable license, without the right to sublicense, to access,
|
||||
use, view, download and print, where applicable, the Services and any
|
||||
HabitRPG Content solely for your personal and non-commercial purposes.
|
||||
You will not use, copy, adapt, modify, prepare derivative works based
|
||||
upon, distribute, license, sell, transfer, publicly display, publicly
|
||||
perform, transmit, stream, broadcast or otherwise exploit the Services
|
||||
or HabitRPG Content, except as expressly permitted in these Terms of
|
||||
Service. No licenses or rights are granted to you by implication or
|
||||
otherwise under any intellectual property rights owned or controlled by
|
||||
HabitRPG or its licensors, except for the licenses and rights expressly
|
||||
granted in these Terms of Service. With respect to HabitRPG Applications,
|
||||
your license is limited to use of such applications on platforms and
|
||||
devices that you own or control, and you may not distribute or make the
|
||||
HabitRPG Applications available over a network where it could be used by
|
||||
multiple devices at the same time.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Public User Content</strong>
|
||||
<br>
|
||||
By making available any Public User Content through the Services, you hereby
|
||||
grant to HabitRPG a worldwide, irrevocable, perpetual, non-exclusive,
|
||||
transferable, royalty-free license, with the right to sublicense, to
|
||||
use, copy, adapt, modify, distribute, license, sell, transfer, publicly
|
||||
display, publicly perform, transmit, stream, broadcast and otherwise
|
||||
exploit such Public User Content only on, through or by means of the Services.
|
||||
HabitRPG does not claim any ownership rights in any such Public User Content and
|
||||
nothing in these Terms of Service will be deemed to restrict any rights
|
||||
that you may have to use and exploit any such Public User Content.
|
||||
</p>
|
||||
<p>
|
||||
You acknowledge and agree that you are solely responsible for all
|
||||
Public User Content that you make available through the Services. Accordingly,
|
||||
you represent and warrant that: (i) you either are the sole and
|
||||
exclusive owner of all Public User Content that you make available through the
|
||||
Services or you have all rights, licenses, consents and releases that
|
||||
are necessary to grant to HabitRPG the rights in such Public User Content, as
|
||||
contemplated under these Terms of Service; and (ii) neither the User
|
||||
Content nor your posting, uploading, publication, submission or
|
||||
transmittal of the Public User Content or HabitRPG's use of the Public User Content (or
|
||||
any portion thereof) on, through or by means of the Services will
|
||||
infringe, misappropriate or violate a third party's patent, copyright,
|
||||
trademark, trade secret, moral rights or other intellectual property
|
||||
rights, or rights of publicity or privacy, or result in the violation of
|
||||
any applicable law or regulation.
|
||||
</p>
|
||||
<p>
|
||||
Copyrighted Materials: No Infringing Use. You will not use the
|
||||
Services to offer, display, distribute, transmit, route, provide
|
||||
connections to or store any material that infringes copyrighted works or
|
||||
otherwise violates or promotes the violation of the intellectual
|
||||
property rights of any third party. HabitRPG has adopted and implemented
|
||||
a policy that provides for the termination in appropriate circumstances
|
||||
of the accounts of users who repeatedly infringe or are believed to be
|
||||
or are charged with repeatedly infringing the rights of copyright
|
||||
holders.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Notify Us of Infringers</strong>
|
||||
<br>
|
||||
If you believe that something on the Services violates your copyright,
|
||||
notify our copyright agent in writing. The contact information for our
|
||||
copyright agent is at the bottom of this section.
|
||||
</p>
|
||||
<p>
|
||||
In order for us to take action, you must do the following in your
|
||||
notice:
|
||||
</p>
|
||||
<p>
|
||||
(1) provide your physical or electronic signature; (2) identify
|
||||
the copyrighted work that you believe is being infringed; (3) identify
|
||||
the item on the Services that you think is infringing your work and
|
||||
include sufficient information about where the material is located on
|
||||
the Services (including which website and URL) so that we can find it;
|
||||
(4) provide us with a way to contact you, such as your address,
|
||||
telephone number, or e-mail; (5) provide a statement that you believe in
|
||||
good faith that the item you have identified as infringing is not
|
||||
authorized by the copyright owner, its agent, or the law to be used on
|
||||
the Services; and (6) provide a statement that the information you
|
||||
provide in your notice is accurate, and that (under penalty of perjury),
|
||||
you are authorized to act on behalf of the copyright owner whose work is
|
||||
being infringed.
|
||||
</p>
|
||||
<p>
|
||||
Here is the contact information for our copyright agent:
|
||||
</p>
|
||||
<p>
|
||||
Copyright Enforcement
|
||||
<br>
|
||||
OCDevel
|
||||
<br>
|
||||
98 Morrison Ave #2
|
||||
<br>
|
||||
Somerville, MA 02144
|
||||
<br>
|
||||
Phone: (805) 975-5236
|
||||
<br>
|
||||
E-Mail: tylerrenelle at gmail dot com
|
||||
</p>
|
||||
<p>
|
||||
Again, we cannot take action unless you give us all the required
|
||||
information.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Ratings and Comments & Feedback.</strong>
|
||||
<br>
|
||||
You can rate and make comments about content made available through the
|
||||
Services ("Comments"). HabitRPG advises you to exercise caution and good
|
||||
judgment when leaving such Comments. Once you complete and submit your
|
||||
Comments to the Services you will not be able to go back and edit your
|
||||
Comments. You should also be aware that you could be held legally
|
||||
responsible for damages to someone's reputation if your Comments are
|
||||
deemed to be defamatory. Without limiting any other terms of this Terms
|
||||
of Service, HabitRPG may, but is under no obligation to, monitor or
|
||||
censor Comments and disclaims any and all liability relating thereto.
|
||||
Notwithstanding the foregoing, HabitRPG does reserve the right, in its
|
||||
sole discretion, to remove any Comments that it deems to be improper,
|
||||
inappropriate or inconsistent with the online activities that are
|
||||
permitted under these Terms of Service. We welcome and encourage you to
|
||||
provide feedback, comments and suggestions for improvements to the
|
||||
Services ("Feedback"). You may submit Feedback by emailing us at support
|
||||
AT HabitRPG.com. You acknowledge and agree that all Comments and Feedback
|
||||
will be the sole and exclusive property of HabitRPG and you hereby
|
||||
irrevocably assign to HabitRPG and agree to irrevocably assign to HabitRPG
|
||||
all of your right, title, and interest in and to all Comments and
|
||||
Feedback, including without limitation all worldwide patent rights,
|
||||
copyright rights, trade secret rights, and other proprietary or
|
||||
intellectual property rights therein. At HabitRPG's request and expense,
|
||||
you will execute documents and take such further acts as HabitRPG may
|
||||
reasonably request to assist HabitRPG to acquire, perfect, and maintain
|
||||
its intellectual property rights and other legal protections for the
|
||||
Comments and Feedback.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Interactions between Users</strong>
|
||||
<br>
|
||||
You are solely responsible for your interactions (including any
|
||||
disputes) with other users. You understand that HabitRPG does not in any
|
||||
way screen HabitRPG users, except to only allow people aged 14 and over
|
||||
to create accounts. You are solely responsible for, and will exercise
|
||||
caution, discretion, common sense and judgment in, using the Services
|
||||
and disclosing personal information to other HabitRPG users. You agree to
|
||||
take reasonable precautions in all interactions with other HabitRPG
|
||||
users, particularly if you decide to meet a HabitRPG user offline, or in
|
||||
person. Your use of the Services, HabitRPG Content and any other content
|
||||
made available through the Services is at your sole risk and discretion
|
||||
and HabitRPG hereby disclaims any and all liability to you or any third
|
||||
party relating thereto. HabitRPG reserves the right to contact Members,
|
||||
in compliance with applicable law, in order to evaluate compliance with
|
||||
the rules and policies in these Terms of Service. You will cooperate
|
||||
fully with HabitRPG to investigate any suspected unlawful, fraudulent or
|
||||
improper activity, including, without limitation, granting authorized
|
||||
HabitRPG representatives access to any password-protected portions of
|
||||
your HabitRPG Account.
|
||||
</p>
|
||||
<p>
|
||||
<strong>General Prohibitions</strong>
|
||||
<br>
|
||||
You agree not to do any of the following while using the Services or
|
||||
HabitRPG Content:
|
||||
<br>
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
Post, upload, publish, submit or transmit any text, graphics,
|
||||
images, software, music, audio, video, information or other material
|
||||
that: (i) infringes, misappropriates or violates a third party's
|
||||
patent, copyright, trademark, trade secret, moral rights or other
|
||||
intellectual property rights, or rights of publicity or privacy; (ii)
|
||||
violates, or encourages any conduct that would violate, any applicable
|
||||
law or regulation or would give rise to civil liability; (iii) is
|
||||
fraudulent, false, misleading or deceptive; (iv) is defamatory,
|
||||
obscene, pornographic, vulgar or offensive; (v) promotes
|
||||
discrimination, bigotry, racism, hatred, harassment or harm against any
|
||||
individual or group; (vi) is violent or threatening or promotes
|
||||
violence or actions that are threatening to any other person; or (vii)
|
||||
promotes illegal or harmful activities or substances (including but not
|
||||
limited to activities that promote or provide instructional information
|
||||
regarding the manufacture or purchase of illegal weapons or illegal
|
||||
substances).
|
||||
</li>
|
||||
<li>
|
||||
Use, display, mirror, frame or utilize framing techniques to
|
||||
enclose the Services, or any individual element or materials within the
|
||||
Services, HabitRPG's name, any HabitRPG trademark, logo or other
|
||||
proprietary information, the content of any text or the layout and
|
||||
design of any page or form contained on a page, without HabitRPG's
|
||||
express written consent;
|
||||
</li>
|
||||
<li>
|
||||
Access, tamper with, or use non-public areas of the Services,
|
||||
HabitRPG's computer systems, or the technical delivery systems of
|
||||
HabitRPG's providers;
|
||||
</li>
|
||||
<li>
|
||||
Attempt to probe, scan, or test the vulnerability of any
|
||||
HabitRPG system or network or breach any security or authentication
|
||||
measures;
|
||||
</li>
|
||||
<li>
|
||||
Avoid, bypass, remove, deactivate, impair, descramble or
|
||||
otherwise circumvent any technological measure implemented by HabitRPG
|
||||
or any of HabitRPG's providers or any other third party (including
|
||||
another user) to protect the Services or HabitRPG Content;
|
||||
</li>
|
||||
<li>
|
||||
Attempt to access or search the Services or HabitRPG Content or
|
||||
download HabitRPG Content from the Services through the use of any
|
||||
engine, software, tool, agent, device or mechanism (including spiders,
|
||||
robots, crawlers, data mining tools or the like) other than the
|
||||
software and/or search agents provided by HabitRPG or other generally
|
||||
available third party web browsers (such as Google Chrome, Microsoft
|
||||
Internet Explorer, Mozilla Firefox, Safari or Opera);
|
||||
</li>
|
||||
<li>
|
||||
Send any unsolicited or unauthorized advertising, promotional
|
||||
materials, email, junk mail, spam, chain letters or other form of
|
||||
solicitation;
|
||||
</li>
|
||||
<li>
|
||||
Use any meta tags or other hidden text or metadata utilizing a
|
||||
HabitRPG trademark, logo URL or product name without HabitRPG's express
|
||||
written consent;
|
||||
</li>
|
||||
<li>
|
||||
Use the Services or HabitRPG Content for any commercial purpose
|
||||
or the benefit of any third party or in any manner not permitted by
|
||||
these Terms of Service;
|
||||
</li>
|
||||
<li>
|
||||
Forge any TCP/IP packet header or any part of the header
|
||||
information in any email or newsgroup posting, or in any way use the
|
||||
Services or HabitRPG Content to send altered, deceptive or false
|
||||
source-identifying information;
|
||||
</li>
|
||||
<li>
|
||||
Attempt to decipher, decompile, disassemble or reverse
|
||||
engineer any of the software used to provide the Services or HabitRPG
|
||||
Content;
|
||||
</li>
|
||||
<li>
|
||||
Interfere with, or attempt to interfere with, the access of
|
||||
any user, host or network, including, without limitation, sending a
|
||||
virus, overloading, flooding, spamming, or mail-bombing the Services;
|
||||
</li>
|
||||
<li>
|
||||
Collect or store any personally identifiable information from
|
||||
the Services from other users of the Services without their express
|
||||
permission;
|
||||
</li>
|
||||
<li>
|
||||
Impersonate or misrepresent your affiliation with any person
|
||||
or entity; Violate any applicable law or regulation; or
|
||||
</li>
|
||||
<li>
|
||||
Encourage or enable any other individual to do any of the
|
||||
foregoing.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
HabitRPG will have the right to investigate and prosecute
|
||||
violations of any of the above, including intellectual property rights
|
||||
infringement and Services security issues, to the fullest extent of the
|
||||
law. HabitRPG may involve and cooperate with law enforcement authorities
|
||||
in prosecuting users who violate these Terms of Service. You acknowledge
|
||||
that HabitRPG has no obligation to monitor your access to or use of the
|
||||
Services or HabitRPG Content or to review or edit any Public User Content, but
|
||||
has the right to do so for the purpose of operating the Services, to
|
||||
ensure your compliance with these Terms of Service, or to comply with
|
||||
applicable law or the order or requirement of a court, administrative
|
||||
agency or other governmental body. HabitRPG reserves the right, at any
|
||||
time and without prior notice, to remove or disable access to any
|
||||
HabitRPG Content, including, any Public User Content, that HabitRPG, in its sole
|
||||
discretion, considers to be in violation of these Terms of Service or
|
||||
otherwise harmful to the Services.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Links</strong>
|
||||
<br>
|
||||
The Services may contain links to third-party websites or resources. You
|
||||
acknowledge and agree that HabitRPG is not responsible or liable for: (i)
|
||||
the availability or accuracy of such websites or resources; or (ii) the
|
||||
content, products, or services on or available from such websites or
|
||||
resources. Links to such websites or resources do not imply any
|
||||
endorsement by HabitRPG of such websites or resources or the content,
|
||||
products, or services available from such websites or resources. You
|
||||
acknowledge sole responsibility for and assume all risk arising from
|
||||
your use of any such websites or resources.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Termination and HabitRPG Account; Cancellation</strong>
|
||||
<br>
|
||||
Without limiting other remedies, HabitRPG may at any time suspend or
|
||||
terminate your HabitRPG Account and refuse to provide access to the
|
||||
Services. In addition, HabitRPG may notify authorities or take any
|
||||
actions it deems appropriate, without notice to you, if HabitRPG suspects
|
||||
or determines, in its own discretion, that you may have or there is a
|
||||
significant risk that you have (i) failed to comply with any provision
|
||||
of these Terms of Service or any policies or rules established by
|
||||
HabitRPG; or (ii) engaged in actions relating to or in the course of
|
||||
using the Services that may be illegal or cause liability, harm,
|
||||
embarrassment, harassment, abuse or disruption for you, HabitRPG Users,
|
||||
HabitRPG or any other third parties or the Services.
|
||||
</p>
|
||||
<p>
|
||||
You may terminate your HabitRPG Account at any time and for any
|
||||
reason by sending email to support AT HabitRPG.com. Upon any termination
|
||||
by a Member, the related account will no longer be accessible.
|
||||
</p>
|
||||
<p>
|
||||
After any termination, you understand and acknowledge that we
|
||||
will have no further obligation to provide the Services and all licenses
|
||||
and other rights granted to you by these Terms of Service will
|
||||
immediately cease. HabitRPG will not be liable to you or any third party
|
||||
for termination of the Services or termination of your use of either.
|
||||
UPON ANY TERMINATION OR SUSPENSION, ANY CONTENT, MATERIALS OR
|
||||
INFORMATION (INCLUDING PUBLIC USER CONTENT) THAT YOU HAVE SUBMITTED ON THE
|
||||
SERVICES OR THAT WHICH IS RELATED TO YOUR ACCOUNT MAY NO LONGER BE
|
||||
ACCESSED BY YOU. Furthermore, HabitRPG will have no obligation to
|
||||
maintain any information stored in our database related to your account
|
||||
or to forward any information to you or any third party.
|
||||
</p>
|
||||
<p>
|
||||
Any suspension, termination or cancellation will not affect your
|
||||
obligations to HabitRPG under these Terms of Service (including, without
|
||||
limitation, proprietary rights and ownership, indemnification and
|
||||
limitation of liability), which by their sense and context are intended
|
||||
to survive such suspension, termination or cancellation.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Disclaimers</strong>
|
||||
<br>
|
||||
THE SERVICES, HABITRPG CONTENT AND PUBLIC USER CONTENT ARE PROVIDED "AS IS",
|
||||
WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED. WITHOUT
|
||||
LIMITING THE FOREGOING, HABITRPG EXPLICITLY DISCLAIMS ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR
|
||||
NON-INFRINGEMENT, AND ANY WARRANTIES ARISING OUT OF COURSE OF DEALING OR
|
||||
USAGE OF TRADE.
|
||||
</p>
|
||||
<p>
|
||||
HABITRPG MAKES NO WARRANTY THAT THE SERVICES, HABITRPG CONTENT OR
|
||||
PUBLIC USER CONTENT WILL MEET YOUR REQUIREMENTS OR BE AVAILABLE ON AN
|
||||
UNINTERRUPTED, SECURE, OR ERROR-FREE BASIS. HABITRPG MAKES NO WARRANTY
|
||||
REGARDING THE QUALITY OF ANY PRODUCTS, SERVICES OR CONTENT PURCHASED OR
|
||||
OBTAINED THROUGH THE SERVICES OR THE ACCURACY, TIMELINESS, TRUTHFULNESS,
|
||||
COMPLETENESS OR RELIABILITY OF ANY CONTENT OBTAINED THROUGH THE
|
||||
SERVICES.
|
||||
</p>
|
||||
<p>
|
||||
NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED FROM
|
||||
HABITRPG OR THROUGH THE SERVICES, HABITRPG CONTENT OR PUBLIC USER CONTENT, WILL
|
||||
CREATE ANY WARRANTY NOT EXPRESSLY MADE HEREIN.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Indemnity</strong>
|
||||
<br>
|
||||
You agree to defend, indemnify, and hold HabitRPG, its officers,
|
||||
directors, employees and agents, harmless from and against any claims,
|
||||
liabilities, damages, losses, and expenses, including, without
|
||||
limitation, reasonable legal and accounting fees, arising out of or in
|
||||
any way connected with Public User Content you submit to HabitRPG, your access
|
||||
to or use of the Services or HabitRPG Content, or your violation of these
|
||||
Terms of Service.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Limitation of Liability</strong>
|
||||
<br>
|
||||
YOU ACKNOWLEDGE AND AGREE THAT, TO THE MAXIMUM EXTENT PERMITTED BY LAW,
|
||||
THE ENTIRE RISK ARISING OUT OF YOUR ACCESS TO AND USE OF THE SERVICES
|
||||
AND CONTENT THEREIN REMAINS WITH YOU. NEITHER HABITRPG NOR ANY OTHER
|
||||
PARTY INVOLVED IN CREATING, PRODUCING, OR DELIVERING THE SERVICES OR
|
||||
HABITRPG CONTENT WILL BE LIABLE FOR ANY INCIDENTAL, SPECIAL, EXEMPLARY OR
|
||||
CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, LOSS OF DATA OR LOSS OF
|
||||
GOODWILL, SERVICE INTERRUPTION, COMPUTER DAMAGE OR SYSTEM FAILURE OR THE
|
||||
COST OF SUBSTITUTE PRODUCTS OR SERVICES, ARISING OUT OF OR IN CONNECTION
|
||||
WITH THESE TERMS OR FROM THE USE OF OR INABILITY TO USE THE SERVICES OR
|
||||
CONTENT THEREIN, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING
|
||||
NEGLIGENCE), PRODUCT LIABILITY OR ANY OTHER LEGAL THEORY, AND WHETHER OR
|
||||
NOT HABITRPG HAS BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGE, EVEN IF
|
||||
A LIMITED REMEDY SET FORTH HEREIN IS FOUND TO HAVE FAILED OF ITS
|
||||
ESSENTIAL PURPOSE. YOU SPECIFICALLY ACKNOWLEDGE THAT HABITRPG IS NOT
|
||||
LIABLE FOR THE DEFAMATORY, OFFENSIVE OR ILLEGAL CONDUCT OF OTHER USERS
|
||||
OR THIRD PARTIES AND THAT THE RISK OF INJURY FROM THE FOREGOING RESTS
|
||||
ENTIRELY WITH YOU. FURTHER, HABITRPG WILL HAVE NO LIABILITY TO YOU OR TO
|
||||
ANY THIRD PARTY FOR ANY PUBLIC USER CONTENT OR THIRD-PARTY CONTENT UPLOADED
|
||||
ONTO OR DOWNLOADED FROM THE SITES OR THROUGH THE SERVICES.
|
||||
</p>
|
||||
<p>
|
||||
IN NO EVENT WILL HABITRPG'S AGGREGATE LIABILITY ARISING OUT OF OR
|
||||
IN CONNECTION WITH THESE TERMS OF SERVICE OR FROM THE USE OF OR
|
||||
INABILITY TO USE THE SITE, SERVICES OR CONTENT THEREIN EXCEED ONE
|
||||
HUNDRED U.S. DOLLARS ($100). THE LIMITATIONS OF DAMAGES SET FORTH ABOVE
|
||||
ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN HABITRPG AND
|
||||
YOU. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF
|
||||
LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE
|
||||
LIMITATION MAY NOT APPLY TO YOU.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Proprietary Rights Notices</strong>
|
||||
<br>
|
||||
All trademarks, service marks, logos, trade names and any other
|
||||
proprietary designations of HabitRPG used herein are trademarks or
|
||||
registered trademarks of HabitRPG. Any other trademarks, service marks,
|
||||
logos, trade names and any other proprietary designations are the
|
||||
trademarks or registered trademarks of their respective parties.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Controlling Law and Jurisdiction</strong>
|
||||
<br>
|
||||
These Terms of Service and any action related thereto will be governed
|
||||
by the laws of the State of California without regard to its conflict of
|
||||
laws provisions. The exclusive jurisdiction and venue of any action with
|
||||
respect to the subject matter of these Terms of Service will be the
|
||||
courts having jurisdiction over disputes arising in Santa Clara County,
|
||||
California, and each of the parties hereto waives any objection to
|
||||
jurisdiction and venue in such courts.
|
||||
</p>
|
||||
<p>
|
||||
YOU AGREE THAT IF YOU WANT TO SUE US, YOU MUST FILE YOUR LAWSUIT
|
||||
WITHIN ONE YEAR AFTER THE EVENT THAT GAVE RISE TO YOUR LAWSUIT.
|
||||
OTHERWISE, YOUR LAWSUIT WILL BE PERMANENTLY BARRED.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Export Control</strong>
|
||||
<br>
|
||||
You may not use or otherwise export or re-export the Services except as
|
||||
authorized by United States law and the laws of the jurisdiction in
|
||||
which the Services were obtained. In particular, but without limitation,
|
||||
the Services may not be exported or re-exported (a) into any U.S.
|
||||
embargoed countries or (b) to anyone on the U.S. Treasury Department's
|
||||
list of Specially Designated Nationals or the U.S. Department of
|
||||
Commerce Denied Person's List or Entity List. By using the Services, you
|
||||
represent and warrant that you are not located in any such country or on
|
||||
any such list. You also agree that you will not use these products for
|
||||
any purposes prohibited by United States law, including, without
|
||||
limitation, the development, design, manufacture or production of
|
||||
nuclear, missiles, or chemical or biological weapons.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Entire Agreement</strong>
|
||||
<br>
|
||||
These Terms of Service constitute the entire and exclusive understanding
|
||||
and agreement between HabitRPG and you regarding the Services and HabitRPG
|
||||
Content, and these Terms of Service supersede and replace any and all
|
||||
prior oral or written understandings or agreements between HabitRPG and
|
||||
you regarding the Services and HabitRPG Content.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Assignment</strong>
|
||||
<br>
|
||||
You may not assign or transfer these Terms of Service, by operation of
|
||||
law or otherwise, without HabitRPG's prior written consent. Any attempt
|
||||
by you to assign or transfer these Terms of Service, without such
|
||||
consent, will be null and of no effect. HabitRPG may freely assign these
|
||||
Terms of Service. Subject to the foregoing, these Terms of Service will
|
||||
bind and inure to the benefit of the parties, their successors and
|
||||
permitted assigns.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Notices</strong>
|
||||
<br>
|
||||
You consent to the use of: (i) electronic means to complete these Terms
|
||||
of Service and to deliver any notices or other communications permitted
|
||||
or required hereunder; and (ii) electronic records to store information
|
||||
related to these Terms of Service or your use of the Services. Any
|
||||
notices or other communications permitted to required hereunder,
|
||||
including those regarding modifications to these Terms of Service, will
|
||||
be in writing and given: (x) by HabitRPG via email (in each case to the
|
||||
address that you provide) or (y) by posting to the Sites or Services.
|
||||
For notices made by e-mail, the date of receipt will be deemed the date
|
||||
on which such notice is transmitted.
|
||||
</p>
|
||||
<p>
|
||||
<strong>General</strong>
|
||||
<br>
|
||||
The failure of HabitRPG to enforce any right or provision of these Terms
|
||||
of Service will not constitute a waiver of future enforcement of that
|
||||
right or provision. The waiver of any such right or provision will be
|
||||
effective only if in writing and signed by a duly authorized
|
||||
representative of HabitRPG. Except as expressly set forth in these Terms
|
||||
of Service, the exercise by either party of any of its remedies under
|
||||
these Terms of Service will be without prejudice to its other remedies
|
||||
under these Terms of Service or otherwise. If for any reason a court of
|
||||
competent jurisdiction finds any provision of these Terms of Service
|
||||
invalid or unenforceable, that provision will be enforced to the maximum
|
||||
extent permissible and the other provisions of these Terms of Service
|
||||
will remain in full force and effect.
|
||||
</p>
|
||||
<p>
|
||||
<strong>Contacting Us</strong>
|
||||
<br>
|
||||
If you have any questions about these Terms of Service, please contact
|
||||
us at tylerrenelle@gmail.com.
|
||||
</p>
|
||||
<div id="home_static_footer">
|
||||
© 2012 OCDevel
|
||||
</div>
|
||||
|
||||
</div>
|
||||
Loading…
Reference in a new issue