Merge branch 'develop' of https://github.com/lefnire/habitrpg into develop

This commit is contained in:
Matteo Pagliazzi
2013-10-23 16:38:37 +02:00
16 changed files with 222 additions and 182 deletions

View File

@@ -0,0 +1,5 @@
db.users.find().forEach(function(user){
if (!user.purchased) user.purchased = {hair: {}, skin: {}};
user.purchased.ads = user.flags && !!user.flags.ads;
db.users.update({_id:user._id}, {$set:{'purchased': user.purchased, 'flags.newStuff': true}, $unset: {'flags.ads':1}});
});

View File

@@ -0,0 +1,12 @@
// node .migrations/20131022_restore_ads.js
var mongo = require('mongoskin');
var _ = require('lodash');
var dbBackup = mongo.db('localhost:27017/habitrpg?auto_reconnect');
var dbLive = mongo.db('localhost:27017/habitrpg2?auto_reconnect');
var count = 89474;
dbBackup.collection('users').findEach({$or: [{'flags.ads':'show'}, {'flags.ads': null}]}, {batchSize:10}, function(err, item) {
if (err) return console.error({err:err});
if (!item || !item._id) return console.error('blank user');
dbLive.collection('users').update({_id:item._id}, {$set:{'purchased.ads':false}, $unset: {'flags.ads': 1}});
if (--count <= 0) console.log("DONE!");
});

View File

@@ -1 +1 @@
db.users.update({},{$set:{'flags.newStuff':'show'}},{multi:true})
db.users.update({},{$set:{'flags.newStuff':true}},{multi:true})

View File

@@ -52,83 +52,6 @@ menu
}
}
// narrower spriting for customize modal
.customize-menu
.m_hair_blond,.m_hair_black,.m_hair_brown,.m_hair_white,
.f_hair_blond,.f_hair_black,.f_hair_brown,.f_hair_white,
.m_skin_dead,.m_skin_orc,.m_skin_asian,.m_skin_black,.m_skin_white,.m_skin_ea8349,.m_skin_c06534,.m_skin_98461a,
.f_skin_dead,.f_skin_orc,.f_skin_asian,.f_skin_black,.f_skin_white,.f_skin_ea8349,.f_skin_c06534,.f_skin_98461a,
.m_head_0,.f_head_0,
.f_armor_0_v1,.f_armor_0_v2
width: 60px
height: 60px
// head
.m_head_0
background-position: -1557px -9px;
// hair
.m_hair_blond
background-position: -1647px -4px;
.m_hair_black
background-position: -1737px -4px;
.m_hair_brown
background-position: -1827px -4px;
.m_hair_white
background-position: -1917px -4px;
// skin
.m_skin_dead
background-position: -2547px -20px;
.m_skin_orc
background-position: -2637px -20px;
.m_skin_asian
background-position: -2727px -20px;
.m_skin_black
background-position: -2817px -20px;
.m_skin_white
background-position: -2907px -20px;
.m_skin_ea8349
background-position: -2997px -20px;
.m_skin_c06534
background-position: -3087px -20px;
.m_skin_98461a
background-position: -3177px -20px;
// head
.f_head_0
background-position: -1917px -9px;
// hair
.f_hair_white
background-position: -2009px -8px;
.f_hair_brown
background-position: -2099px -8px;
.f_hair_black
background-position: -2189px -8px;
.f_hair_blond
background-position: -2279px -8px;
// skin
.f_skin_dead
background-position: -2997px -20px;
.f_skin_orc
background-position: -3087px -20px;
.f_skin_asian
background-position: -3177px -20px;
.f_skin_black
background-position: -3267px -20px;
.f_skin_white
background-position: -3357px -20px;
.f_skin_ea8349
background-position: -3447px -20px;
.f_skin_c06534
background-position: -3537px -20px;
.f_skin_98461a
background-position: -3627px -20px;
// starting armor
.f_armor_0_v2
background-position: -2819px -38px;
.f_armor_0_v1
background-position: -2909px -38px;
.well.limited-edition
padding: 5px
margin: 0px

View File

@@ -170,6 +170,6 @@ a
.btn
margin-right: 5px
.buy-gems-list
.modal-indented-list
margin-left: 10px;
padding-left: 10px;

View File

@@ -1,4 +1,4 @@
.NPC-Alex, .NPC-Bailey, .NPC-Bailey-Head, .NPC-Daniel, .NPC-Justin, .NPC-Justin-Head, .NPC-Matt {background: url("/bower_components/habitrpg-shared/img/npcs/NPC-SpriteSheet.png") no-repeat}
.NPC-Alex, .NPC-Bailey, .NPC-Bailey-Head, .NPC-Daniel, .NPC-Justin, .NPC-Justin-Head, .NPC-Matt {background: url("/bower_components/habitrpg-shared/img/npcs/halloween/NPC-SpriteSheet.png") no-repeat}
.NPC-Alex {background-position: 0 0; width: 162px; height: 138px;}
.NPC-Alex-container{margin-bottom: 20px;}

View File

@@ -14,7 +14,7 @@ habitrpg.controller("FooterCtrl", ['$scope', '$rootScope', 'User', '$http',
$.getScript('//checkout.stripe.com/v2/checkout.js');
// Amazon Affiliate
// if ($rootScope.authenticated() && User.user.flags.ads !== 'hide') {
// if ($rootScope.authenticated() && !User.user.purchased.ads) {
// $.getScript('//wms.assoc-amazon.com/20070822/US/js/link-enhancer-common.js?tag=ha0d2-20').fail(function() {
// $('body').append('<img src="//wms.assoc-amazon.com/20070822/US/img/noscript.gif?tag=ha0d2-20" alt="" />');
// });

View File

@@ -1,7 +1,7 @@
"use strict";
habitrpg.controller("UserCtrl", ['$scope', '$location', 'User',
function($scope, $location, User) {
habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$http',
function($rootScope, $scope, $location, User, $http) {
$scope.profile = User.user;
$scope.hideUserAvatar = function() {
$(".userAvatar").hide();
@@ -32,4 +32,8 @@ habitrpg.controller("UserCtrl", ['$scope', '$location', 'User',
$scope.removeWebsite = function($index){
$scope.editingProfile.websites.splice($index,1);
}
}]);
$scope.unlock = User.unlock;
}
]);

View File

@@ -164,6 +164,38 @@ angular.module('userServices', []).
userServices.log(log);
},
/**
* For gem-unlockable preferences, (a) if owned, select preference (b) else, purchase
* @param path: User.preferences <-> User.purchased maps like User.preferences.skin=abc <-> User.purchased.skin.abc.
* Pass in this paramater as "skin.abc". Alternatively, pass as an array ["skin.abc", "skin.xyz"] to unlock sets
*/
unlock: function(path){
var self = userServices; //this; // why isn't this working?
if (_.isArray(path)) {
if (confirm("Purchase for 5 Gems?") !== true) return;
if (user.balance < 1.25) return $rootScope.modals.buyGems = true;
path = path.join(',');
} else {
if (window.habitrpgShared.helpers.dotGet('purchased.' + path, user)) {
var pref = path.split('.')[0],
val = path.split('.')[1];
return self.set('preferences.' + pref, val);
} else {
if (confirm("Purchase for 2 Gems?") !== true) return;
if (user.balance < 0.5) return $rootScope.modals.buyGems = true;
}
}
$http.post(API_URL + '/api/v1/user/unlock?path=' + path)
.success(function(data, status, headers, config){
self.log({}); // sync new unlocked & preferences
}).error(function(data, status, headers, config){
alert(status + ': ' + data);
//FIXME use method used elsewhere for handling this error, this is temp while developing
})
},
save: save,
settings: settings

View File

@@ -564,6 +564,49 @@ api['delete'] = function(req, res) {
})
}
/*
------------------------------------------------------------------------
Unlock Preferences
------------------------------------------------------------------------
*/
api.unlock = function(req, res) {
var user = res.locals.user;
var path = req.query.path;
var fullSet = ~path.indexOf(',');
// 5G per set, 2G per individual
cost = fullSet ? 1.25 : 0.5;
if (user.balance < cost)
return res.json(401, {err: 'Not enough gems'});
if (fullSet) {
var paths = path.split(',');
_.each(paths, function(p){
helpers.dotSet('purchased.' + p, true, user);
});
} else {
if (helpers.dotGet('purchased.' + path, user) === true)
return res.json(401, {err: 'User already purchased that'});
helpers.dotSet('purchased.' + path, true, user);
}
user.balance -= cost;
user.__v++;
user.markModified('purchased');
user.save(function(err, saved){
if (err) res.json(500, {err:err});
res.send(200);
})
}
/*
------------------------------------------------------------------------
Buy Gems
------------------------------------------------------------------------
*/
/*
Setup Stripe response when posting payment
@@ -584,7 +627,7 @@ api.buyGems = function(req, res) {
},
function(response, cb) {
res.locals.user.balance += 5;
res.locals.user.flags.ads = 'hide';
res.locals.user.purchased.ads = true;
res.locals.user.save(cb);
}
], function(err, saved){
@@ -609,7 +652,7 @@ api.buyGemsPaypalIPN = function(req, res) {
if (err) throw err;
if (_.isEmpty(user)) throw "user not found with uuid " + uuid + " when completing paypal transaction"
user.balance += 5;
user.flags.ads = 'hide';
user.purchased.ads = true;
user.save();
console.log('PayPal transaction completed and user updated');
});

View File

@@ -69,13 +69,18 @@ var UserSchema = new Schema({
rewardIds: Array,
filters: {type: Schema.Types.Mixed, 'default': {}},
purchased: {
ads: {type: Boolean, 'default': false},
skin: {type: Schema.Types.Mixed, 'default': {}}, // eg, {skeleton: true, pumpkin: true, eb052b: true}
hair: {type: Schema.Types.Mixed, 'default': {}}
},
flags: {
customizationsNotification: {type: Boolean, 'default': false},
showTour: {type: Boolean, 'default': true},
ads: {type: String, 'default': 'show'}, // FIXME make this a boolean, run migration
dropsEnabled: {type: Boolean, 'default': false},
itemsEnabled: {type: Boolean, 'default': false},
newStuff: {type: String, 'default': 'hide'}, //FIXME to boolean (currently show/hide)
newStuff: {type: Boolean, 'default': false},
rewrite: {type: Boolean, 'default': true},
partyEnabled: Boolean, // FIXME do we need this?
petsEnabled: {type: Boolean, 'default': false},

View File

@@ -48,6 +48,7 @@ router.post('/user/batch-update', auth.auth, cron, user.batchUpdate);
router.post('/user/reroll', auth.auth, cron, user.reroll);
router.post('/user/buy-gems', auth.auth, user.buyGems);
router.post('/user/buy-gems/paypal-ipn', user.buyGemsPaypalIPN);
router.post('/user/unlock', auth.auth, cron, user.unlock);
router.post('/user/reset', auth.auth, user.reset);
router['delete']('/user', auth.auth, user['delete']);

View File

@@ -6,11 +6,14 @@
// gender
li.customize-menu
menu(label='Head')
menu
button.m_head_0.customize-option(type='button', ng-click='set("preferences.gender","m")')
button.f_head_0.customize-option(type='button', ng-click='set("preferences.gender","f")')
label.checkbox
input(type='checkbox', ng-model='user.preferences.showHelm', ng-change='toggleHelm(user.preferences.showHelm)')
| Show Helm
hr
// hair
li.customize-menu
menu(label='Hair')
@@ -18,17 +21,53 @@
button(class='{{user.preferences.gender}}_hair_black customize-option', type='button', ng-click='set("preferences.hair","black")')
button(class='{{user.preferences.gender}}_hair_brown customize-option', type='button', ng-click='set("preferences.hair","brown")')
button(class='{{user.preferences.gender}}_hair_white customize-option', type='button', ng-click='set("preferences.hair","white")')
hr
// skin
li.customize-menu
menu(label='Skin')
button.customize-option(class='{{user.preferences.gender}}_skin_asian', type='button', ng-click='set("preferences.skin","asian")')
button.customize-option(class='{{user.preferences.gender}}_skin_white', type='button', ng-click='set("preferences.skin","white")')
button.customize-option(class='{{user.preferences.gender}}_skin_ea8349', type='button', ng-click='set("preferences.skin","ea8349")')
button.customize-option(class='{{user.preferences.gender}}_skin_c06534', type='button', ng-click='set("preferences.skin","c06534")')
button.customize-option(class='{{user.preferences.gender}}_skin_98461a', type='button', ng-click='set("preferences.skin","98461a")')
button.customize-option(class='{{user.preferences.gender}}_skin_black', type='button', ng-click='set("preferences.skin","black")')
button.customize-option(class='{{user.preferences.gender}}_skin_dead', type='button', ng-click='set("preferences.skin","dead")')
button.customize-option(class='{{user.preferences.gender}}_skin_orc', type='button', ng-click='set("preferences.skin","orc")')
menu(label='Basic Skins')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_asian', ng-click='set("preferences.skin","asian")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_white', ng-click='set("preferences.skin","white")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_ea8349', ng-click='set("preferences.skin","ea8349")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_c06534', ng-click='set("preferences.skin","c06534")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_98461a', ng-click='set("preferences.skin","98461a")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_black', ng-click='set("preferences.skin","black")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_dead', ng-click='set("preferences.skin","dead")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_orc', ng-click='set("preferences.skin","orc")')
// Rainbow Skin
h5.
Rainbow Skins - 2<span class="Pet_Currency_Gem1x inline-gems"/>/skin
//menu(label='Rainbow Skins (2G / skin)')
menu
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_eb052b', ng-class='{locked: !user.purchased.skin.eb052b}', ng-click='unlock("skin.eb052b")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_f69922', ng-class='{locked: !user.purchased.skin.f69922}', ng-click='unlock("skin.f69922")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_f5d70f', ng-class='{locked: !user.purchased.skin.f5d70f}', ng-click='unlock("skin.f5d70f")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_0ff591', ng-class='{locked: !user.purchased.skin.0ff591}', ng-click='unlock("skin.0ff591")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_2b43f6', ng-class='{locked: !user.purchased.skin.2b43f6}', ng-click='unlock("skin.2b43f6")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_d7a9f7', ng-class='{locked: !user.purchased.skin.d7a9f7}', ng-click='unlock("skin.d7a9f7")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_800ed0', ng-class='{locked: !user.purchased.skin.800ed0}', ng-click='unlock("skin.800ed0")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_rainbow', ng-class='{locked: !user.purchased.skin.rainbow}', ng-click='unlock("skin.rainbow")')
button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.eb052b && user.purchased.skin.f69922 && user.purchased.skin.f5d70f && user.purchased.skin.0ff591 && user.purchased.skin.2b43f6 && user.purchased.skin.d7a9f7 && user.purchased.skin.800ed0 && user.purchased.skin.rainbow', ng-click='unlock(["skin.eb052b", "skin.f69922", "skin.f5d70f", "skin.0ff591", "skin.2b43f6", "skin.d7a9f7", "skin.800ed0", "skin.rainbow"])') Unlock Set - 5<span class="Pet_Currency_Gem1x inline-gems"/>
// Special Events
div.well.limited-edition
.label.label-info.pull-right(popover='Available for purchase until November 10th (but permanently in your options if purchased).', popover-title='Limited Edition', popover-placement='right', popover-trigger='mouseenter')
| Limited Edition&nbsp;
i.icon.icon-question-sign
h5.
Spooky Skins - 2<span class="Pet_Currency_Gem1x inline-gems"/>/skin
//menu(label='Spooky Skins (2 Gems / skin)')
menu
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_monster', ng-class='{locked: !user.purchased.skin.monster}', ng-click='unlock("skin.monster")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_pumpkin', ng-class='{locked: !user.purchased.skin.pumpkin}', ng-click='unlock("skin.pumpkin")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_skeleton', ng-class='{locked: !user.purchased.skin.skeleton}', ng-click='unlock("skin.skeleton")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_zombie', ng-class='{locked: !user.purchased.skin.zombie}', ng-click='unlock("skin.zombie")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_ghost', ng-class='{locked: !user.purchased.skin.ghost}', ng-click='unlock("skin.ghost")')
button.customize-option(type='button', class='{{user.preferences.gender}}_skin_shadow', ng-class='{locked: !user.purchased.skin.shadow}', ng-click='unlock("skin.shadow")')
button.btn.btn-small.btn-primary(ng-hide='user.purchased.skin.monster && user.purchased.skin.pumpkin && user.purchased.skin.skeleton && user.purchased.skin.zombie && user.purchased.skin.ghost && user.purchased.skin.shadow', ng-click='unlock(["skin.monster", "skin.pumpkin", "skin.skeleton", "skin.zombie", "skin.ghost", "skin.shadow"])') Unlock Set - 5<span class="Pet_Currency_Gem1x inline-gems"/>
menu(ng-show='user.preferences.gender=="f"', type='list')
li.customize-menu
menu(label='Clothing')

View File

@@ -15,10 +15,10 @@ div(modal='modals.buyGems')
a(target='_blank', href='https://twitter.com/bowenNstuff') Justin
.popover-content
p <span class='label label-info'>$5 USD</span> will:
ul.buy-gems-list
ul.modal-indented-list
li Add 20 gems to your account, which are used to buy special items.
li Disable ads.
li Donate to the developers (as an open source project, we can us all the help we can get).
li Donate to the developers (as an open source project, we can use all the help we can get).
br
.row-fluid
.span6.well

View File

@@ -1,49 +1,3 @@
div(modal='user.flags.rewrite !== false')
.modal-header
h3 Mega Ultra Update!
.modal-body
table
tr
td
.NPC-Bailey
td
.popover.static-popover.fade.right.in.wide-popover
.arrow
h3.popover-title
a(target='_blank', href='https://twitter.com/Mihakuu') Bailey
.popover-content
p.
Hello my Habiteers! I have some amazing news to share with you, it's huge!
Has Habit ever crashed for you? (Joke). Well we <u><a target='_blank' href="http://habitrpg.tumblr.com/post/59104876969/website-issues-what-were-doing">re-wrote the website</a></u> from the ground up
to conquor those critical bugs once and for all (more from Tyler in a bit). If you haven't seen me for a while (due to a bug in the old site), be sure to catch up with me on the right side of the screen for any missed news. Importantly:
.alert.alert-success(style='margin-bottom:5px').
<a target='_blank' href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg'>Android</a> & <a target='_blank' href='https://itunes.apple.com/us/app/habitrpg/id689569235?mt=8'>iOS</a> Apps are out!</u>
p.
They're open source, so help us make them awesome. As for the rewrite: not all features are yet ported, but don't worry - you're still getting drops and streak-bonuses in the background, even if you can't see them yet.
We'll be working hard to bring in all the missing features. And if you're not already, be sure to follow our updates on <a href="http://habitrpg.tumblr.com/" target="_blank">Tumblr</a> (there are some fun member highlights recently). One more thing: if you are a Veteran of the old site, I have granted you a Veteran Wolf! Check your inventory :)
table(style='clear:both;')
tr
td
.popover.static-popover.fade.left.in
.arrow
h3.popover-title
a(target='_blank', href='https://twitter.com/lefnire') Tyler
.popover-content
p.
JavaScript developers! To me! We must finish vanquishing the old site, as not all features have been ported.
We rewrote Habit on <a target='_blank' href='http://angularjs.org/'>AngularJS</a> + <a target='_blank' href='http://expressjs.com/'>Express</a>.
We desparately need your help porting <a href='http://goo.gl/jYWTwl' target='_blank'>the rest of the features</a>, and polishing off the bugs. <a target='_blank' href='https://github.com/lefnire/habitrpg/wiki/Contributing#website'>Read this guide</a> to getting started.
Thanks everyone for all your support and patience!
td
img.pull-right(src='/bower_components/habitrpg-shared/img/unprocessed/efbd21c4-82a1-11e2-8190-fbc609b5c58b.png', style='height:72px')
.modal-footer
button.btn.btn-default.cancel(ng-click='user.flags.rewrite = false') Read Later
button.btn.btn-warning.cancel(ng-click='set("flags.rewrite", false)') Dismiss
div(modal='modals.newStuff')
.modal-header
h3 New Stuff!
@@ -58,24 +12,46 @@ div(modal='modals.newStuff')
h3.popover-title
a(target='_blank', href='https://twitter.com/Mihakuu') Bailey
.popover-content
strong 10/19/2013
strong 10/22/2013
p
ul.modal-indented-list
li TRICK OR TREAT! It's Habit Halloween! Some of the NPCs have decorated for the occasion. Can you spot us?
li Two gem-purchasable skin tones are now available! The Rainbow Skin Set is here to stay, but in honor of Halloween, we also have the LIMITED EDITION SPOOKY SKIN SET. You will only be able to purchase the Spooky Skin Set until November 10th, so if you want a monstrous avatar, now's the time to act!
li Do note, skins won't work on mobile until the app is updated. We'll update Android ASAP, iPhone usually takes ~1wk to approve.
hr
h3 Archive
p
h4 10/19/2013
ul
li.
New custom skin colors are now available! Go check them out in the Profile section. Also, the new mobile update, 0.0.10, is now available to download! It includes the new skin tones and the ability to hide or show your helm, among other things.
li.
You can now sell un-wanted drops to Alex the Merchant. Trade those troves of eggs for gold!
hr
h3 Archive
p
h4 09/01/2013
ul
li.
We <a target='_blank' href="http://habitrpg.tumblr.com/post/59104876969/website-issues-what-were-doing">re-wrote the website from the ground up</a>
And in case you missed it, <a target='_blank' href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg'>Android</a> & <a target='_blank' href='https://itunes.apple.com/us/app/habitrpg/id689569235?mt=8'>iOS</a> Apps are out!
Both apps and the website are open source, and we desparately need your help porting the rest of the features, and polishing off the bugs. <a target='_blank' href='https://github.com/lefnire/habitrpg/wiki/Contributing#website'>Read this guide</a> to getting started.
Each of your pull requests shall grant you a special <a href='https://github.com/lefnire/habitrpg/issues/946#issuecomment-18654292' target='_blank'>Contributor Gear piece</a>.
We're working on a system of Contributor Gear to reward the awesome people who help out, so stay tuned!
h4 The Rewrite! (Mid August)
ul
li.
Hello my Habiteers! I have some amazing news to share with you, it's huge!
Has Habit ever crashed for you? (Joke). Well we <u><a target='_blank' href="http://habitrpg.tumblr.com/post/59104876969/website-issues-what-were-doing">re-wrote the website</a></u> from the ground up
to conquor those critical bugs once and for all (more from Tyler in a bit). If you haven't seen me for a while (due to a bug in the old site), be sure to catch up with me on the right side of the screen for any missed news. Importantly:
.alert.alert-success(style='margin-bottom:5px').
<a target='_blank' href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg'>Android</a> & <a target='_blank' href='https://itunes.apple.com/us/app/habitrpg/id689569235?mt=8'>iOS</a> Apps are out!</u>
li.
They're open source, so help us make them awesome. As for the rewrite: not all features are yet ported, but don't worry - you're still getting drops and streak-bonuses in the background, even if you can't see them yet.
We'll be working hard to bring in all the missing features. And if you're not already, be sure to follow our updates on <a href="http://habitrpg.tumblr.com/" target="_blank">Tumblr</a> (there are some fun member highlights recently). One more thing: if you are a Veteran of the old site, I have granted you a Veteran Wolf! Check your inventory :)
li.
JavaScript developers! To me! We must finish vanquishing the old site, as not all features have been ported.
We rewrote Habit on <a target='_blank' href='http://angularjs.org/'>AngularJS</a> + <a target='_blank' href='http://expressjs.com/'>Express</a>.
We desparately need your help porting <a href='http://goo.gl/jYWTwl' target='_blank'>the rest of the features</a>, and polishing off the bugs. <a target='_blank' href='https://github.com/lefnire/habitrpg/wiki/Contributing#website'>Read this guide</a> to getting started.
Thanks everyone for all your support and patience!
h4 8/20/2013
ul

View File

@@ -1,4 +1,4 @@
div(ng-if='authenticated() && user.flags.ads!="hide"')
div(ng-if='authenticated() && !user.purchased.ads')
span.pull-right(ng-if='list.type!="reward"')
a(ng-click='modals.buyGems=true', tooltip='Remove Ads')
i.icon-remove