Merge branch 'develop' into card_tweaks
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Shared', 'Guide', 'Tasks', 'Analytics',
|
||||
function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Shared, Guide, Tasks, Analytics) {
|
||||
habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','Notification', '$http', 'ApiUrl', '$timeout', 'Content', 'Shared', 'Guide', 'Tasks', 'Analytics',
|
||||
function($scope, $rootScope, $location, User, Notification, $http, ApiUrl, $timeout, Content, Shared, Guide, Tasks, Analytics) {
|
||||
$scope.obj = User.user; // used for task-lists
|
||||
$scope.user = User.user;
|
||||
|
||||
@@ -212,10 +212,13 @@ habitrpg.controller("TasksCtrl", ['$scope', '$rootScope', '$location', 'User','N
|
||||
------------------------
|
||||
*/
|
||||
|
||||
$scope.$watchGroup(['user.items.gear.owned', 'user.flags.armoireEnabled'], function(){
|
||||
$scope.$watch('user.items.gear.owned', function(){
|
||||
$scope.itemStore = Shared.updateStore(User.user);
|
||||
},true);
|
||||
|
||||
$scope.healthPotion = Content.potion;
|
||||
$scope.armoire = Content.armoire;
|
||||
|
||||
$scope.buy = function(item) {
|
||||
User.user.ops.buy({params:{key:item.key}});
|
||||
$rootScope.playSound('Reward');
|
||||
|
||||
@@ -108,6 +108,12 @@
|
||||
properties.Mana = Math.floor(user.stats.mp);
|
||||
}
|
||||
properties.tutorialComplete = user.flags && user.flags.tour && user.flags.tour.intro === -2;
|
||||
properties["Number Of Tasks"] = {
|
||||
habits: user.habits.length,
|
||||
dailys: user.dailys.length,
|
||||
todos: user.todos.length,
|
||||
rewards: user.rewards.length
|
||||
};
|
||||
if (user.contributor && user.contributor.level) properties.contributorLevel = user.contributor.level;
|
||||
if (user.purchased && user.purchased.plan.planId) properties.subscription = user.purchased.plan.planId;
|
||||
}
|
||||
|
||||
@@ -153,6 +153,12 @@ function _formatUserData(user) {
|
||||
}
|
||||
|
||||
properties.tutorialComplete = user.flags && user.flags.tour && user.flags.tour.intro === -2;
|
||||
properties["Number Of Tasks"] = {
|
||||
habits: user.habits.length,
|
||||
dailys: user.dailys.length,
|
||||
todos: user.todos.length,
|
||||
rewards: user.rewards.length
|
||||
};
|
||||
|
||||
if (user.contributor && user.contributor.level) {
|
||||
properties.contributorLevel = user.contributor.level;
|
||||
|
||||
@@ -102,13 +102,13 @@ dataexport.avatarPage = function(req, res) {
|
||||
User.findById(req.params.uuid).select('stats profile items achievements preferences backer contributor').exec(function(err, user){
|
||||
res.render('avatar-static', {
|
||||
title: user.profile.name,
|
||||
env: _.defaults({user:user},res.locals.habitrpg)
|
||||
env: _.defaults({user:user}, res.locals.habitrpg)
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
dataexport.avatarImage = function(req, res, next) {
|
||||
var filename = 'avatar-'+req.params.uuid+'.png';
|
||||
var filename = 'avatars/'+req.params.uuid+'.png';
|
||||
request.head('https://'+bucket+'.s3.amazonaws.com/'+filename, function(err,response,body) {
|
||||
// cache images for 10 minutes on aws, else upload a new one
|
||||
if (response.statusCode==200 && moment().diff(response.headers['last-modified'], 'minutes') < 10)
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ module.exports = {
|
||||
|
||||
|
||||
// Export en strings only, temporary solution for mobile
|
||||
// This is copied from middleware.js#module.exports.locals#t()
|
||||
// This is copied from middlewares/locals#t()
|
||||
module.exports.enTranslations = function(){ // stringName and vars are the allowed parameters
|
||||
var language = _.find(avalaibleLanguages, {code: 'en'});
|
||||
//language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var manifestFiles = require("../../public/manifest.json");
|
||||
|
||||
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||
var buildFiles = [];
|
||||
|
||||
var walk = function(folder){
|
||||
var res = fs.readdirSync(folder);
|
||||
|
||||
res.forEach(function(fileName){
|
||||
var file = folder + '/' + fileName;
|
||||
if(fs.statSync(file).isDirectory()){
|
||||
walk(file);
|
||||
}else{
|
||||
var relFolder = path.relative(path.join(__dirname, "/../../build"), folder);
|
||||
var old = fileName.replace(/-.{8}(\.[\d\w]+)$/, '$1');
|
||||
|
||||
if(relFolder){
|
||||
old = relFolder + '/' + old;
|
||||
fileName = relFolder + '/' + fileName;
|
||||
}
|
||||
|
||||
buildFiles[old] = fileName;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
walk(path.join(__dirname, "/../../build"));
|
||||
|
||||
var getBuildUrl = module.exports.getBuildUrl = function(url){
|
||||
if(buildFiles[url]) return '/' + buildFiles[url];
|
||||
|
||||
return '/' + url;
|
||||
};
|
||||
|
||||
module.exports.getManifestFiles = function(page){
|
||||
var files = manifestFiles[page];
|
||||
|
||||
if(!files) throw new Error("Page not found!");
|
||||
|
||||
var code = '';
|
||||
|
||||
if(IS_PROD){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(page + '.css') + '">';
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(page + '.js') + '"></script>';
|
||||
}else{
|
||||
_.each(files.css, function(file){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(file) + '">';
|
||||
});
|
||||
_.each(files.js, function(file){
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(file) + '"></script>';
|
||||
});
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
@@ -1,249 +0,0 @@
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var User = require('./models/user').model
|
||||
var limiter = require('connect-ratelimit');
|
||||
var logging = require('./logging');
|
||||
var domainMiddleware = require('domain-middleware');
|
||||
var cluster = require('cluster');
|
||||
var i18n = require('./i18n.js');
|
||||
var shared = require('../../common');
|
||||
var request = require('request');
|
||||
var os = require('os');
|
||||
var moment = require('moment');
|
||||
var utils = require('./utils');
|
||||
|
||||
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||
var BASE_URL = nconf.get("BASE_URL");
|
||||
|
||||
module.exports.apiThrottle = function(app) {
|
||||
if (!IS_PROD) return;
|
||||
app.use(limiter({
|
||||
end:false,
|
||||
catagories:{
|
||||
normal: {
|
||||
// 2 req/s, but split as minutes
|
||||
totalRequests: 80,
|
||||
every: 60000
|
||||
}
|
||||
}
|
||||
})).use(function(req,res,next){
|
||||
//logging.info(res.ratelimit);
|
||||
if (res.ratelimit.exceeded) return res.json(429,{err:'Rate limit exceeded'});
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.domainMiddleware = function(server,mongoose) {
|
||||
if (IS_PROD) {
|
||||
var mins = 3, // how often to run this check
|
||||
useAvg = false, // use average over 3 minutes, or simply the last minute's report
|
||||
url = 'https://api.newrelic.com/v2/applications/'+nconf.get('NEW_RELIC_APPLICATION_ID')+'/metrics/data.json?names[]=Apdex&values[]=score';
|
||||
setInterval(function(){
|
||||
// see https://docs.newrelic.com/docs/apm/apis/api-v2-examples/average-response-time-examples-api-v2, https://rpm.newrelic.com/api/explore/applications/data
|
||||
request({
|
||||
url: useAvg ? url+'&from='+moment().subtract({minutes:mins}).utc().format()+'&to='+moment().utc().format()+'&summarize=true' : url,
|
||||
headers: {'X-Api-Key': nconf.get('NEW_RELIC_API_KEY')}
|
||||
}, function(err, response, body){
|
||||
var ts = JSON.parse(body).metric_data.metrics[0].timeslices,
|
||||
score = ts[ts.length-1].values.score,
|
||||
apdexBad = score < .75 || score == 1,
|
||||
memory = os.freemem() / os.totalmem(),
|
||||
memoryHigh = memory < 0.1;
|
||||
if (/*apdexBad || */memoryHigh) throw "[Memory Leak] Apdex="+score+" Memory="+parseFloat(memory).toFixed(3)+" Time="+moment().format();
|
||||
})
|
||||
}, mins*60*1000);
|
||||
}
|
||||
|
||||
return domainMiddleware({
|
||||
server: {
|
||||
close:function(){
|
||||
server.close();
|
||||
mongoose.connection.close();
|
||||
}
|
||||
},
|
||||
killTimeout: 10000
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.errorHandler = function(err, req, res, next) {
|
||||
//res.locals.domain.emit('error', err);
|
||||
// when we hit an error, send it to admin as an email. If no ADMIN_EMAIL is present, just send it to yourself (SMTP_USER)
|
||||
var stack = (err.stack ? err.stack : err.message ? err.message : err) +
|
||||
"\n ----------------------------\n" +
|
||||
"\n\noriginalUrl: " + req.originalUrl +
|
||||
"\n\nauth: " + req.headers['x-api-user'] + ' | ' + req.headers['x-api-key'] +
|
||||
"\n\nheaders: " + JSON.stringify(req.headers) +
|
||||
"\n\nbody: " + JSON.stringify(req.body) +
|
||||
(res.locals.ops ? "\n\ncompleted ops: " + JSON.stringify(res.locals.ops) : "");
|
||||
logging.error(stack);
|
||||
/*logging.loggly({
|
||||
error: "Uncaught error",
|
||||
stack: (err.stack || err.message || err),
|
||||
body: req.body, headers: req.header,
|
||||
auth: req.headers['x-api-user'],
|
||||
originalUrl: req.originalUrl
|
||||
});*/
|
||||
var message = err.message ? err.message : err;
|
||||
message = (message.length < 200) ? message : message.substring(0,100) + message.substring(message.length-100,message.length);
|
||||
res.json(500,{err:message}); //res.end(err.message);
|
||||
}
|
||||
|
||||
function isHTTP(req) {
|
||||
return (
|
||||
req.headers['x-forwarded-proto'] &&
|
||||
req.headers['x-forwarded-proto'] !== 'https' &&
|
||||
IS_PROD &&
|
||||
BASE_URL.indexOf('https') === 0
|
||||
);
|
||||
}
|
||||
|
||||
function isProxied(req) {
|
||||
return (
|
||||
req.headers['x-habitica-lb'] &&
|
||||
req.headers['x-habitica-lb'] === 'Yes'
|
||||
);
|
||||
}
|
||||
|
||||
module.exports.forceSSL = function(req, res, next){
|
||||
if(isHTTP(req) && !isProxied(req)) {
|
||||
return res.redirect(BASE_URL + req.url);
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
// Redirect to habitica for non-api urls
|
||||
// NOTE: Currently using a static 'habitica.com' string, rather than BASE_URL,
|
||||
// to make rollback easy. Eventually, BASE_URL should be migrated.
|
||||
|
||||
function nonApiUrl(req) {
|
||||
return req.url.search(/\/api\//) === -1;
|
||||
}
|
||||
|
||||
module.exports.forceHabitica = function(req, res, next) {
|
||||
var ignoreRedirect = nconf.get('IGNORE_REDIRECT');
|
||||
|
||||
if (IS_PROD && !ignoreRedirect && !isProxied(req) && nonApiUrl(req)) {
|
||||
return res.redirect('https://habitica.com' + req.url);
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports.cors = function(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,Accept,Content-Encoding,X-Requested-With,x-api-user,x-api-key");
|
||||
if (req.method === 'OPTIONS') return res.send(200);
|
||||
return next();
|
||||
};
|
||||
|
||||
var siteVersion = 1;
|
||||
|
||||
module.exports.forceRefresh = function(req, res, next){
|
||||
if(req.query.siteVersion && req.query.siteVersion != siteVersion){
|
||||
return res.json(400, {needRefresh: true});
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
|
||||
var buildFiles = [];
|
||||
|
||||
var walk = function(folder){
|
||||
var res = fs.readdirSync(folder);
|
||||
|
||||
res.forEach(function(fileName){
|
||||
file = folder + '/' + fileName;
|
||||
if(fs.statSync(file).isDirectory()){
|
||||
walk(file);
|
||||
}else{
|
||||
var relFolder = path.relative(path.join(__dirname, "/../build"), folder);
|
||||
var old = fileName.replace(/-.{8}(\.[\d\w]+)$/, '$1');
|
||||
|
||||
if(relFolder){
|
||||
old = relFolder + '/' + old;
|
||||
fileName = relFolder + '/' + fileName;
|
||||
}
|
||||
|
||||
buildFiles[old] = fileName
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
walk(path.join(__dirname, "/../build"));
|
||||
|
||||
var getBuildUrl = function(url){
|
||||
if(buildFiles[url]) return '/' + buildFiles[url];
|
||||
|
||||
return '/' + url;
|
||||
}
|
||||
|
||||
var manifestFiles = require("../public/manifest.json");
|
||||
|
||||
var getManifestFiles = function(page){
|
||||
var files = manifestFiles[page];
|
||||
|
||||
if(!files) throw new Error("Page not found!");
|
||||
|
||||
var code = '';
|
||||
|
||||
if(IS_PROD){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(page + '.css') + '">';
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(page + '.js') + '"></script>';
|
||||
}else{
|
||||
_.each(files.css, function(file){
|
||||
code += '<link rel="stylesheet" type="text/css" href="' + getBuildUrl(file) + '">';
|
||||
});
|
||||
_.each(files.js, function(file){
|
||||
code += '<script type="text/javascript" src="' + getBuildUrl(file) + '"></script>';
|
||||
});
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
module.exports.locals = function(req, res, next) {
|
||||
var language = _.find(i18n.avalaibleLanguages, {code: req.language});
|
||||
var isStaticPage = req.url.split('/')[1] === 'static'; // If url contains '/static/'
|
||||
|
||||
// Load moment.js language file only when not on static pages
|
||||
language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||
|
||||
var tavern = require('./models/group').tavern;
|
||||
var envVars = _.pick(nconf.get(), 'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY AMPLITUDE_KEY'.split(' '));
|
||||
res.locals.habitrpg = _.merge(envVars, {
|
||||
IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')),
|
||||
getManifestFiles: getManifestFiles,
|
||||
getBuildUrl: getBuildUrl,
|
||||
avalaibleLanguages: i18n.avalaibleLanguages,
|
||||
language: language,
|
||||
isStaticPage: isStaticPage,
|
||||
translations: i18n.translations[language.code],
|
||||
t: function(){ // stringName and vars are the allowed parameters
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args.push(language.code);
|
||||
return shared.i18n.t.apply(null, args);
|
||||
},
|
||||
siteVersion: siteVersion,
|
||||
Content: shared.content,
|
||||
mods: require('./models/user').mods,
|
||||
tavern: tavern, // for world boss
|
||||
worldDmg: (tavern && tavern.quest && tavern.quest.extra && tavern.quest.extra.worldDmg) || {},
|
||||
_: _,
|
||||
MP_ID: nconf.get('MP_ID'),
|
||||
AMAZON_PAYMENTS: {
|
||||
SELLER_ID: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
|
||||
CLIENT_ID: nconf.get('AMAZON_PAYMENTS:CLIENT_ID')
|
||||
}
|
||||
});
|
||||
|
||||
// Put query-string party (& guild but use partyInvite for backward compatibility)
|
||||
// invitations into session to be handled later
|
||||
try{
|
||||
req.session.partyInvite = JSON.parse(utils.decrypt(req.query.partyInvite))
|
||||
} catch(e){}
|
||||
|
||||
next();
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
var nconf = require('nconf');
|
||||
var limiter = require('connect-ratelimit');
|
||||
|
||||
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||
|
||||
module.exports = function(app) {
|
||||
if (!IS_PROD) return;
|
||||
app.use(limiter({
|
||||
end:false,
|
||||
categories:{
|
||||
normal: {
|
||||
// 2 req/s, but split as minutes
|
||||
totalRequests: 80,
|
||||
every: 60000
|
||||
}
|
||||
}
|
||||
})).use(function(req,res,next){
|
||||
//logging.info(res.ratelimit);
|
||||
if (res.ratelimit.exceeded) return res.json(429,{err:'Rate limit exceeded'});
|
||||
next();
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
module.exports = function(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,Accept,Content-Encoding,X-Requested-With,x-api-user,x-api-key");
|
||||
if (req.method === 'OPTIONS') return res.send(200);
|
||||
return next();
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
var nconf = require('nconf');
|
||||
var moment = require('moment');
|
||||
var domainMiddleware = require('domain-middleware');
|
||||
var os = require('os');
|
||||
var request = require('request');
|
||||
|
||||
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||
|
||||
module.exports = function(server,mongoose) {
|
||||
if (IS_PROD) {
|
||||
var mins = 3, // how often to run this check
|
||||
useAvg = false, // use average over 3 minutes, or simply the last minute's report
|
||||
url = 'https://api.newrelic.com/v2/applications/'+nconf.get('NEW_RELIC_APPLICATION_ID')+'/metrics/data.json?names[]=Apdex&values[]=score';
|
||||
setInterval(function(){
|
||||
// see https://docs.newrelic.com/docs/apm/apis/api-v2-examples/average-response-time-examples-api-v2, https://rpm.newrelic.com/api/explore/applications/data
|
||||
request({
|
||||
url: useAvg ? url+'&from='+moment().subtract({minutes:mins}).utc().format()+'&to='+moment().utc().format()+'&summarize=true' : url,
|
||||
headers: {'X-Api-Key': nconf.get('NEW_RELIC_API_KEY')}
|
||||
}, function(err, response, body){
|
||||
var ts = JSON.parse(body).metric_data.metrics[0].timeslices,
|
||||
score = ts[ts.length-1].values.score,
|
||||
apdexBad = score < .75 || score == 1,
|
||||
memory = os.freemem() / os.totalmem(),
|
||||
memoryHigh = memory < 0.1;
|
||||
if (/*apdexBad || */memoryHigh) throw "[Memory Leak] Apdex="+score+" Memory="+parseFloat(memory).toFixed(3)+" Time="+moment().format();
|
||||
});
|
||||
}, mins*60*1000);
|
||||
}
|
||||
|
||||
return domainMiddleware({
|
||||
server: {
|
||||
close:function(){
|
||||
server.close();
|
||||
mongoose.connection.close();
|
||||
}
|
||||
},
|
||||
killTimeout: 10000
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
var logging = require('../logging');
|
||||
|
||||
module.exports = function(err, req, res, next) {
|
||||
//res.locals.domain.emit('error', err);
|
||||
// when we hit an error, send it to admin as an email. If no ADMIN_EMAIL is present, just send it to yourself (SMTP_USER)
|
||||
var stack = (err.stack ? err.stack : err.message ? err.message : err) +
|
||||
"\n ----------------------------\n" +
|
||||
"\n\noriginalUrl: " + req.originalUrl +
|
||||
"\n\nauth: " + req.headers['x-api-user'] + ' | ' + req.headers['x-api-key'] +
|
||||
"\n\nheaders: " + JSON.stringify(req.headers) +
|
||||
"\n\nbody: " + JSON.stringify(req.body) +
|
||||
(res.locals.ops ? "\n\ncompleted ops: " + JSON.stringify(res.locals.ops) : "");
|
||||
logging.error(stack);
|
||||
/*logging.loggly({
|
||||
error: "Uncaught error",
|
||||
stack: (err.stack || err.message || err),
|
||||
body: req.body, headers: req.header,
|
||||
auth: req.headers['x-api-user'],
|
||||
originalUrl: req.originalUrl
|
||||
});*/
|
||||
var message = err.message ? err.message : err;
|
||||
message = (message.length < 200) ? message : message.substring(0,100) + message.substring(message.length-100,message.length);
|
||||
res.json(500,{err:message}); //res.end(err.message);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports.siteVersion = 1;
|
||||
|
||||
module.exports.middleware = function(req, res, next){
|
||||
if(req.query.siteVersion && req.query.siteVersion != module.exports.siteVersion){
|
||||
return res.json(400, {needRefresh: true});
|
||||
}
|
||||
|
||||
return next();
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var utils = require('../utils');
|
||||
var shared = require('../../../common');
|
||||
var i18n = require('../i18n.js');
|
||||
var buildManifest = require('../libs/buildManifest');
|
||||
var shared = require('../../../common');
|
||||
var forceRefresh = require('./forceRefresh');
|
||||
var tavern = require('../models/group').tavern;
|
||||
var mods = require('../models/user').mods;
|
||||
|
||||
// To avoid stringifying more data then we need,
|
||||
// items from `env` used on the client will have to be specified in this array
|
||||
var clientVars = ['language', 'isStaticPage', 'avalaibleLanguages', 'translations',
|
||||
'FACEBOOK_KEY', 'NODE_ENV', 'BASE_URL', 'GA_ID',
|
||||
'AMAZON_PAYMENTS', 'STRIPE_PUB_KEY', 'AMPLITUDE_KEY',
|
||||
'worldDmg', 'mods', 'IS_MOBILE'];
|
||||
|
||||
var env = {
|
||||
getManifestFiles: buildManifest.getManifestFiles,
|
||||
getBuildUrl: buildManifest.getBuildUrl,
|
||||
_: _,
|
||||
clientVars: clientVars,
|
||||
tavern: tavern, // for world boss
|
||||
mods: mods,
|
||||
Content: shared.content,
|
||||
siteVersion: forceRefresh.siteVersion,
|
||||
avalaibleLanguages: i18n.avalaibleLanguages,
|
||||
AMAZON_PAYMENTS: {
|
||||
SELLER_ID: nconf.get('AMAZON_PAYMENTS:SELLER_ID'),
|
||||
CLIENT_ID: nconf.get('AMAZON_PAYMENTS:CLIENT_ID')
|
||||
}
|
||||
};
|
||||
|
||||
'NODE_ENV BASE_URL GA_ID STRIPE_PUB_KEY FACEBOOK_KEY AMPLITUDE_KEY'.split(' ').forEach(function(key){
|
||||
env[key] = nconf.get(key);
|
||||
});
|
||||
|
||||
module.exports = function(req, res, next) {
|
||||
var language = _.find(i18n.avalaibleLanguages, {code: req.language});
|
||||
var isStaticPage = req.url.split('/')[1] === 'static'; // If url contains '/static/'
|
||||
|
||||
// Load moment.js language file only when not on static pages
|
||||
language.momentLang = ((!isStaticPage && i18n.momentLangs[language.code]) || undefined);
|
||||
|
||||
res.locals.habitrpg = _.assign(env, {
|
||||
IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')),
|
||||
language: language,
|
||||
isStaticPage: isStaticPage,
|
||||
translations: i18n.translations[language.code],
|
||||
t: function(){ // stringName and vars are the allowed parameters
|
||||
var args = Array.prototype.slice.call(arguments, 0);
|
||||
args.push(language.code);
|
||||
return shared.i18n.t.apply(null, args);
|
||||
},
|
||||
// Defined here and not outside of the middleware because tavern might be an
|
||||
// empty object until the query to fetch it finishes
|
||||
worldDmg: (tavern && tavern.quest && tavern.quest.extra && tavern.quest.extra.worldDmg) || {},
|
||||
});
|
||||
|
||||
// Put query-string party (& guild but use partyInvite for backward compatibility)
|
||||
// invitations into session to be handled later
|
||||
// TODO once we have proper logging in place, log errors here
|
||||
if(req.query.partyInvite){
|
||||
try{
|
||||
req.session.partyInvite = JSON.parse(utils.decrypt(req.query.partyInvite));
|
||||
} catch(e){}
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
@@ -0,0 +1,43 @@
|
||||
var nconf = require('nconf');
|
||||
var IS_PROD = nconf.get('NODE_ENV') === 'production';
|
||||
var ignoreRedirect = nconf.get('IGNORE_REDIRECT');
|
||||
var BASE_URL = nconf.get("BASE_URL");
|
||||
|
||||
function isHTTP(req) {
|
||||
return (
|
||||
req.headers['x-forwarded-proto'] &&
|
||||
req.headers['x-forwarded-proto'] !== 'https' &&
|
||||
IS_PROD &&
|
||||
BASE_URL.indexOf('https') === 0
|
||||
);
|
||||
}
|
||||
|
||||
function isProxied(req) {
|
||||
return (
|
||||
req.headers['x-habitica-lb'] &&
|
||||
req.headers['x-habitica-lb'] === 'Yes'
|
||||
);
|
||||
}
|
||||
|
||||
module.exports.forceSSL = function(req, res, next){
|
||||
if(isHTTP(req) && !isProxied(req)) {
|
||||
return res.redirect(BASE_URL + req.url);
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
// Redirect to habitica for non-api urls
|
||||
// NOTE: Currently using a static 'habitica.com' string, rather than BASE_URL,
|
||||
// to make rollback easy. Eventually, BASE_URL should be migrated.
|
||||
|
||||
function nonApiUrl(req) {
|
||||
return req.url.search(/\/api\//) === -1;
|
||||
}
|
||||
|
||||
module.exports.forceHabitica = function(req, res, next) {
|
||||
if (IS_PROD && !ignoreRedirect && !isProxied(req) && nonApiUrl(req)) {
|
||||
return res.redirect('https://habitica.com' + req.url);
|
||||
}
|
||||
next();
|
||||
};
|
||||
@@ -236,7 +236,8 @@ module.exports.tavern = {};
|
||||
var tavernQ = {_id:'habitrpg','quest.key':{$ne:null}};
|
||||
process.nextTick(function(){
|
||||
mongoose.model('Group').findOne(tavernQ,function(err,tavern){
|
||||
module.exports.tavern = tavern;
|
||||
// Using _assign so we don't lose the reference to the exported tavern
|
||||
_.assign(module.exports.tavern, tavern);
|
||||
});
|
||||
})
|
||||
GroupSchema.statics.tavernBoss = function(user,progress) {
|
||||
|
||||
@@ -577,11 +577,16 @@ UserSchema.methods.unlink = function(options, cb) {
|
||||
|
||||
module.exports.schema = UserSchema;
|
||||
module.exports.model = mongoose.model("User", UserSchema);
|
||||
// Initially export an empty object so external requires will get
|
||||
// the right object by reference when it's defined later
|
||||
// Otherwise it would remain undefined if requested before the query executes
|
||||
module.exports.mods = [];
|
||||
|
||||
mongoose.model("User")
|
||||
.find({'contributor.admin':true})
|
||||
.sort('-contributor.level -backer.npc profile.name')
|
||||
.select('profile contributor backer')
|
||||
.exec(function(err,mods){
|
||||
module.exports.mods = mods
|
||||
// Using push to maintain the reference to mods
|
||||
module.exports.mods.push.apply(module.exports.mods, mods);
|
||||
});
|
||||
|
||||
@@ -5,9 +5,9 @@ var async = require('async');
|
||||
var icalendar = require('icalendar');
|
||||
var api = require('./../controllers/user');
|
||||
var auth = require('./../controllers/auth');
|
||||
var middleware = require('../middleware');
|
||||
var logging = require('./../logging');
|
||||
var i18n = require('./../i18n');
|
||||
var forceRefresh = require('../middlewares/forceRefresh').middleware;
|
||||
|
||||
/* ---------- Deprecated API ------------*/
|
||||
|
||||
@@ -161,7 +161,7 @@ router.post('/user/task', auth.auth, i18n.getUserLanguage, cron, api.addTask);
|
||||
// User
|
||||
router.get('/user', auth.auth, i18n.getUserLanguage, cron, api.getUser);
|
||||
router.post('/user/revive', auth.auth, i18n.getUserLanguage, cron, api.revive);
|
||||
router.post('/user/batch-update', middleware.forceRefresh, auth.auth, i18n.getUserLanguage, cron, batchUpdate);
|
||||
router.post('/user/batch-update', forceRefresh, auth.auth, i18n.getUserLanguage, cron, batchUpdate);
|
||||
|
||||
function deprecated(req, res) {
|
||||
res.json(404, {err:'API v1 is no longer supported, please use API v2 instead (https://github.com/HabitRPG/habitrpg/blob/develop/API.md)'});
|
||||
|
||||
@@ -16,12 +16,11 @@ hall = require("../controllers/hall")
|
||||
challenges = require("../controllers/challenges")
|
||||
dataexport = require("../controllers/dataexport")
|
||||
nconf = require("nconf")
|
||||
middleware = require("../middleware")
|
||||
cron = user.cron
|
||||
_ = require('lodash')
|
||||
content = require('../../../common').content
|
||||
i18n = require('../i18n')
|
||||
|
||||
forceRefresh = require('../middlewares/forceRefresh').middleware
|
||||
|
||||
module.exports = (swagger, v2) ->
|
||||
[path,body,query] = [swagger.pathParam, swagger.bodyParam, swagger.queryParam]
|
||||
@@ -326,7 +325,7 @@ module.exports = (swagger, v2) ->
|
||||
parameters:[
|
||||
body '','The array of batch-operations to perform','object'
|
||||
]
|
||||
middleware: [middleware.forceRefresh, auth.auth, i18n.getUserLanguage, cron, user.sessionPartyInvite]
|
||||
middleware: [forceRefresh, auth.auth, i18n.getUserLanguage, cron, user.sessionPartyInvite]
|
||||
action: user.batchUpdate
|
||||
|
||||
# Tags
|
||||
|
||||
@@ -4,13 +4,13 @@ var dataexport = require('../controllers/dataexport');
|
||||
var auth = require('../controllers/auth');
|
||||
var nconf = require('nconf');
|
||||
var i18n = require('../i18n');
|
||||
var middleware = require('../middleware.js');
|
||||
var locals = require('../middlewares/locals');
|
||||
|
||||
/* Data export */
|
||||
router.get('/history.csv',auth.authWithSession,i18n.getUserLanguage,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes
|
||||
router.get('/userdata.xml',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.xml);
|
||||
router.get('/userdata.json',auth.authWithSession,i18n.getUserLanguage,dataexport.leanuser,dataexport.userdata.json);
|
||||
router.get('/avatar-:uuid.html', i18n.getUserLanguage, middleware.locals, dataexport.avatarPage);
|
||||
router.get('/avatar-:uuid.png', i18n.getUserLanguage, middleware.locals, dataexport.avatarImage);
|
||||
router.get('/avatar-:uuid.html', i18n.getUserLanguage, locals, dataexport.avatarPage);
|
||||
router.get('/avatar-:uuid.png', i18n.getUserLanguage, locals, dataexport.avatarImage);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
@@ -2,13 +2,13 @@ var nconf = require('nconf');
|
||||
var express = require('express');
|
||||
var router = new express.Router();
|
||||
var _ = require('lodash');
|
||||
var middleware = require('../middleware');
|
||||
var locals = require('../middlewares/locals');
|
||||
var user = require('../controllers/user');
|
||||
var auth = require('../controllers/auth');
|
||||
var i18n = require('../i18n');
|
||||
|
||||
// -------- App --------
|
||||
router.get('/', i18n.getUserLanguage, middleware.locals, function(req, res) {
|
||||
router.get('/', i18n.getUserLanguage, locals, function(req, res) {
|
||||
if (!req.headers['x-api-user'] && !req.headers['x-api-key'] && !(req.session && req.session.userId))
|
||||
return res.redirect('/static/front')
|
||||
|
||||
@@ -23,7 +23,7 @@ router.get('/', i18n.getUserLanguage, middleware.locals, function(req, res) {
|
||||
var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit'];
|
||||
|
||||
_.each(pages, function(name){
|
||||
router.get('/static/' + name, i18n.getUserLanguage, middleware.locals, function(req, res) {
|
||||
router.get('/static/' + name, i18n.getUserLanguage, locals, function(req, res) {
|
||||
res.render('static/' + name, {env: res.locals.habitrpg});
|
||||
});
|
||||
})
|
||||
|
||||
@@ -30,8 +30,6 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
||||
// Setup translations
|
||||
var i18n = require('./i18n');
|
||||
|
||||
var middleware = require('./middleware');
|
||||
|
||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||
var app = express();
|
||||
var server = http.createServer();
|
||||
@@ -89,16 +87,18 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
||||
var publicDir = path.join(__dirname, "/../public");
|
||||
|
||||
app.set("port", nconf.get('PORT'));
|
||||
middleware.apiThrottle(app);
|
||||
app.use(middleware.domainMiddleware(server,mongoose));
|
||||
require('./middlewares/apiThrottle')(app);
|
||||
app.use(require('./middlewares/domain')(server,mongoose));
|
||||
if (!isProd) app.use(express.logger("dev"));
|
||||
app.use(express.compress());
|
||||
app.set("views", __dirname + "/../views");
|
||||
app.set("view engine", "jade");
|
||||
app.use(express.favicon(publicDir + '/favicon.ico'));
|
||||
app.use(middleware.cors);
|
||||
app.use(middleware.forceHabitica);
|
||||
app.use(middleware.forceSSL);
|
||||
app.use(require('./middlewares/cors'));
|
||||
|
||||
var redirects = require('./middlewares/redirects');
|
||||
app.use(redirects.forceHabitica);
|
||||
app.use(redirects.forceSSL);
|
||||
app.use(express.urlencoded());
|
||||
app.use(express.json());
|
||||
app.use(require('method-override')());
|
||||
@@ -134,7 +134,7 @@ if (cores!==0 && cluster.isMaster && (isDev || isProd)) {
|
||||
app.use('/api/v1', require('./routes/apiv1').middleware);
|
||||
app.use('/export', require('./routes/dataexport').middleware);
|
||||
require('./routes/apiv2.coffee')(swagger, v2);
|
||||
app.use(middleware.errorHandler);
|
||||
app.use(require('./middlewares/errorHandler'));
|
||||
|
||||
server.on('request', app);
|
||||
server.listen(app.get("port"), function() {
|
||||
|
||||
@@ -8,8 +8,15 @@ html(ng-app="habitrpg")
|
||||
meta(name='viewport', content='width=device-width, initial-scale=1.0')
|
||||
meta(name='apple-mobile-web-app-capable', content='yes')
|
||||
|
||||
// .slice(0).push('user') is to clone the array,
|
||||
// to be surethat `user` is never avalaible to other requests' env
|
||||
// TODO does it need only `user` in clientVars, not the others?
|
||||
-
|
||||
clientVars = env.clientVars.slice(0);
|
||||
clientVars.push('user');
|
||||
|
||||
script(type='text/javascript').
|
||||
window.env = !{JSON.stringify(env)};
|
||||
window.env = !{JSON.stringify(env._.pick(env, clientVars))};
|
||||
|
||||
!= env.getManifestFiles("app")
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -144,6 +144,7 @@
|
||||
include ./chat-box
|
||||
.alert.alert-info.alert-sm
|
||||
!= ' ' + env.t('tavernAlert1') + ' <a href="https://github.com/HabitRPG/habitrpg/issues/2760" target="_blank">' + env.t('tavernAlert2') + '</a>.<br />' + env.t('moderatorIntro1')
|
||||
//- FIXME this could be rendered server-side?
|
||||
span(ng-repeat='mod in env.mods')
|
||||
|
|
||||
span(ng-if='::mod.contributor.admin',popover=env.t('gamemaster'),popover-trigger='mouseenter',popover-placement='right')
|
||||
|
||||
@@ -6,7 +6,7 @@ script(id='modals/cards.html', type='text/ng-template')
|
||||
.bg-info(style='padding:10px')
|
||||
p {{::env.t('toAndFromCard', { toName: user.profile.name, fromName: user.items.special[cardType + 'Received'][0]})}}
|
||||
hr
|
||||
markdown(text='{{::cardMessage}}')
|
||||
markdown(text='::cardMessage')
|
||||
.modal-footer
|
||||
small.pull-left {{::env.t(cardType + 'CardExplanation')}}
|
||||
button.btn.btn-default(ng-click='user.ops.readCard({params: {cardType: cardType}}); $close()')=env.t('ok')
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
ul.items.rewards(ng-if='main && list.type=="reward"')
|
||||
li.task.reward-item(ng-repeat='item in itemStore',popover-trigger='mouseenter', popover-placement='top', popover='{{item.key == "armoire" && !user.flags.armoireEmpty ? env.t("armoireNotesFull") + armoireCount(user.items.gear.owned) : item.notes()}}')
|
||||
mixin reward(item)
|
||||
li.task.reward-item(popover-trigger='mouseenter', popover-placement='top', popover='{{::#{item}.notes()}}')&attributes(attributes)
|
||||
// right-hand side control buttons
|
||||
.task-meta-controls
|
||||
span.task-notes
|
||||
span.glyphicon.glyphicon-comment
|
||||
//left-hand size commands
|
||||
.task-controls.task-primary
|
||||
input.visuallyhidden(
|
||||
class="reward",
|
||||
input.reward.visuallyhidden(
|
||||
type='checkbox',
|
||||
ui-keypress='{13:"buy(item)"}')
|
||||
a.money.btn-buy.item-btn(ng-class='{highValue: item.value >= 1000}', ng-click='buy(item)')
|
||||
ui-keypress='{13:"buy(#{item})"}')
|
||||
a.money.btn-buy.item-btn(ng-class='::{highValue: #{item}.value >= 1000}', ng-click='::buy(#{item})')
|
||||
span.shop_gold
|
||||
span.reward-cost {{item.value}}
|
||||
span.reward-cost {{::#{item}.value}}
|
||||
// main content
|
||||
span(ng-class='::{"shop_{{item.key}} shop-sprite item-img": true}').reward-img
|
||||
p.task-text {{item.text()}}
|
||||
span(ng-class='::{"shop_{{#{item}.key}} shop-sprite item-img": true}').reward-img
|
||||
p.task-text {{::#{item}.text()}}
|
||||
|
||||
ul.items.rewards(ng-if='main && list.type=="reward"')
|
||||
+reward('item')(ng-repeat='item in itemStore')
|
||||
+reward('healthPotion')
|
||||
+reward('armoire')(ng-if='user.flags.armoireEnabled',
|
||||
popover='{{armoire.notes(user, armoireCount(user.items.gear.owned))}}')
|
||||
|
||||
@@ -26,7 +26,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
link(rel='stylesheet', type='text/css', href='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.min.css')
|
||||
|
||||
script(type='text/javascript').
|
||||
window.env = !{JSON.stringify(env)};
|
||||
window.env = !{JSON.stringify(env._.pick(env, env.clientVars))};
|
||||
!= env.getManifestFiles("tmp_static_front")
|
||||
|
||||
script(type='text/javascript', src='https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/js/bootstrap.min.js')
|
||||
@@ -51,7 +51,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
span.icon-bar
|
||||
span.icon-bar
|
||||
a.navbar-brand(href='#')
|
||||
img(src='https://s3.amazonaws.com/habitica-assets/assets/habitica_lockup2_desat.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/assets/habitica_lockup2_desat.png')
|
||||
// Collect the nav links, forms, and other content for toggling
|
||||
#bs-example-navbar-collapse-1.collapse.navbar-collapse
|
||||
ul.nav.navbar-nav.navbar-right
|
||||
@@ -69,7 +69,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
button#header-play-button.btn.btn-primary.navbar-btn.navbar-right(ng-click='playButtonClick()')= env.t('playButtonFull')
|
||||
#intro
|
||||
h1=env.t('motivate1')
|
||||
img.center-block.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/intro.png')
|
||||
img.center-block.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/intro.png')
|
||||
// insert intro images
|
||||
.introcall.bg-success
|
||||
h4= env.t('joinOthers')
|
||||
@@ -78,13 +78,13 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.presslogos.text-center
|
||||
= env.t('featuredIn')
|
||||
br
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/lifehacker.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/nyt-logo.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/makeuseof.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/Forbes_logo.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/Cnetlogo.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/Fast-Company-logo.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/kickstarter-logo.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/lifehacker.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/nyt-logo.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/makeuseof.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/Forbes_logo.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/Cnetlogo.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/Fast-Company-logo.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/kickstarter-logo.png')
|
||||
#intro-text
|
||||
.container-fluid
|
||||
.row
|
||||
@@ -96,20 +96,20 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.row
|
||||
.col-md-3.col-md-offset-1
|
||||
h2= env.t('testimonialHeading')
|
||||
img.img-say.img-responsive.visible-lg-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/publicSpaces.png')
|
||||
img.img-say.img-responsive.visible-lg-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/publicSpaces.png')
|
||||
// Testimonials
|
||||
.col-md-8.col-sm-10.col-sx-10
|
||||
#quote-carousel.carousel.slide(data-interval='10000', data-ride='carousel')
|
||||
// Bottom Carousel Indicators
|
||||
ol.carousel-indicators
|
||||
li.active(data-target='#quote-carousel', data-slide-to='0')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/Drag0nsilver.png', alt='')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/Drag0nsilver.png', alt='')
|
||||
li(data-target='#quote-carousel', data-slide-to='1')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/frabjabulous.png', alt='')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/frabjabulous.png', alt='')
|
||||
li(data-target='#quote-carousel', data-slide-to='2')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/AndeeLiao.png', alt='')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/AndeeLiao.png', alt='')
|
||||
li(data-target='#quote-carousel', data-slide-to='3')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/AlexandraSo.png', alt='')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/AlexandraSo.png', alt='')
|
||||
// Carousel Slides / Quotes
|
||||
.carousel-inner.text-center
|
||||
// Quote 1
|
||||
@@ -201,16 +201,16 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; left:0px; position:relative; background-color: rgb(220, 93, 93); border-right: 1px solid rgba(0, 0, 0, 0.25);') +
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; background-color: rgb(220, 93, 93); border-right: 0px none;') -
|
||||
.task-desc(style='display: inline-block; font-family: Lato,sans-serif; padding: 0.6em 0px 0em 1em; letter-spacing: normal; float:left; word-wrap: break-word;')= env.t('businessSample5')
|
||||
img.sample-img.visible-lg-inline-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/coding.png')
|
||||
img.sample-img.visible-lg-inline-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/coding.png')
|
||||
.usetweet-groups.col-lg-5.col-md-6.visible-lg-inline-block.visible-md-inline-block
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='frabjabulous', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/frabjabulous.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='frabjabulous', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/frabjabulous.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
= env.t('frabjabulousQuote')
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='_AlexandraSo_', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/AlexandraSo.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='_AlexandraSo_', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/AlexandraSo.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -245,16 +245,16 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; left:0px; position:relative; background-color: rgb(255, 207, 66); border-right: 1px solid rgba(0, 0, 0, 0.25);') +
|
||||
|
|
||||
.task-desc(style='display: inline-block; font-family: Lato,sans-serif; padding: 0.6em 0px 0em 1em; letter-spacing: normal; float:left; word-wrap: break-word;')= env.t('healthSample5')
|
||||
img.sample-img.visible-lg-inline-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/clipart-rosemonkeyct-meditation.png')
|
||||
img.sample-img.visible-lg-inline-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/clipart-rosemonkeyct-meditation.png')
|
||||
.usetweet-groups.col-lg-5.col-md-6.visible-lg-inline-block.visible-md-inline-block
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='EvaGantz', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/EvaGantz.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='EvaGantz', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/EvaGantz.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
= env.t('evagantzQuote')
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='supermouse35', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/supermouse35.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='supermouse35', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/supermouse35.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -283,10 +283,10 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
|
|
||||
.task-desc(style='display: inline-block; font-family: Lato,sans-serif; padding: 0.6em 0px 0em 1em; letter-spacing: normal; float:left; word-wrap: break-word;')= env.t('teamSample3')
|
||||
br
|
||||
img.sample-img.visible-lg-inline-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/publicSpaces.png')
|
||||
img.sample-img.visible-lg-inline-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/publicSpaces.png')
|
||||
.usetweet-groups.col-lg-5.col-md-6.visible-lg-inline-block.visible-md-inline-block
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='Althaire', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/Althaire.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='Althaire', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/Althaire.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -321,15 +321,15 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; left:0px; position:relative; background-color: rgb(90, 98, 223); border-right: 1px solid rgba(0, 0, 0, 0.25);') +
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; background-color: rgb(90, 98, 223); border-right: 0px none;') -
|
||||
.task-desc(style='display: inline-block; font-family: Lato,sans-serif; padding: 0.6em 0px 0em 1em; letter-spacing: normal; float:left; word-wrap: break-word;')= env.t('schoolSample5')
|
||||
img.sample-img.visible-lg-inline-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/reading.png')
|
||||
img.sample-img.visible-lg-inline-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/reading.png')
|
||||
.usetweet-groups.col-lg-5.col-md-6.visible-lg-inline-block.visible-md-inline-block
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='InfH', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/InfH.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='InfH', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/InfH.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content= env.t('infhQuote')
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='Drei-M', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/Drei-M.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='Drei-M', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/Drei-M.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -364,10 +364,10 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; left:0px; position:relative; background-color: rgb(90, 98, 223); border-right: 1px solid rgba(0, 0, 0, 0.25);') +
|
||||
|
|
||||
.task-desc(style='display: inline-block; font-family: Lato,sans-serif; padding: 0.6em 0px 0em 1em; letter-spacing: normal; float:left; word-wrap: break-word;')= env.t('goalSample5')
|
||||
img.sample-img.visible-lg-inline-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/gaining_an_achievement_by_cosmic_caterpillar-d7uyv5z.png')
|
||||
img.sample-img.visible-lg-inline-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/gaining_an_achievement_by_cosmic_caterpillar-d7uyv5z.png')
|
||||
.usetweet-groups.col-lg-5.col-md-6.visible-lg-inline-block.visible-md-inline-block
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='Kazui', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/Kazui.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='Kazui', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/Kazui.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -402,15 +402,15 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.task-btn(style='display: inline-block; width: 1.62765em; height: 1.62765em; padding: 0px; margin:0px; line-height: 1.62765; text-align: center; color: rgb(34, 34, 34); float:left; cursor: pointer; vertical-align: top; left:0px; position:relative; background-color: rgb(90, 98, 223); border-right: 1px solid rgba(0, 0, 0, 0.25);') +
|
||||
|
|
||||
.task-desc(style='display: inline-block; font-family: Lato,sans-serif; padding: 0.6em 0px 0em 1em; letter-spacing: normal; float:left; word-wrap: break-word;')= env.t('choreSample5')
|
||||
img.sample-img.visible-lg-inline-block(src='https://s3.amazonaws.com/habitrpg-assets/front/images/uses/dusting_by_leephon.png')
|
||||
img.sample-img.visible-lg-inline-block(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/uses/dusting_by_leephon.png')
|
||||
.usetweet-groups.col-lg-5.col-md-6.visible-lg-inline-block.visible-md-inline-block
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='autumnesquirrel', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/autumnesquirrel.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='autumnesquirrel', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/autumnesquirrel.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content= env.t('autumnesquirrelQuote')
|
||||
.usetweet-group
|
||||
img(data-toggle='tooltip', data-placement='top', title='irishfeet123', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/irishfeet123.png')
|
||||
img(data-toggle='tooltip', data-placement='top', title='irishfeet123', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/irishfeet123.png')
|
||||
.usetweet.tweet.popover.right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -430,7 +430,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
br
|
||||
= env.t('rewardByline2')
|
||||
.scrolltweet.hidden-xs.hidden-sm
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='Elmi', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/Elmi.png')
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='Elmi', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/Elmi.png')
|
||||
.tweet.popover.right.pull-right
|
||||
.arrow
|
||||
.popover-content= env.t('elmiQuote')
|
||||
@@ -440,12 +440,12 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
span.glyphicon.glyphicon-check
|
||||
h2
|
||||
span.glyphicon.glyphicon-arrow-down
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/misc/shop_gold.png')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/misc/shop_gold.png')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/misc/shop_gold.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/misc/shop_gold.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/misc/shop_gold.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/misc/shop_gold.png')
|
||||
h2
|
||||
span.glyphicon.glyphicon-arrow-down
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/TVreward.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/TVreward.png')
|
||||
button.btn.btn-primary.btn-lg.fixedcta.gamifybutton(ng-click='playButtonClick()')= env.t('gamifyButton')
|
||||
section#levels.container-fluid
|
||||
.row
|
||||
@@ -459,17 +459,17 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
br
|
||||
= env.t('unlockByline2')
|
||||
.scrolltweet.hidden-xs.hidden-sm
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='16bitFil', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/16bitFil.png')
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='16bitFil', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/16bitFil.png')
|
||||
.tweet.popover.right.pull-right
|
||||
.arrow
|
||||
.popover-content
|
||||
= env.t('sixteenBitFilQuote')
|
||||
.col-lg-4.col-md-4.col-lg-offset-1.col-sm-8.well.col-xs-8.col-xs-offset-2.col-sm-offset-2
|
||||
.avatarscroll
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/avatar/avatarstatic.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/avatar/avatarstatic.png')
|
||||
h2
|
||||
span.glyphicon.glyphicon-arrow-down
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/SAMPLEadventurers.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/SAMPLEadventurers.png')
|
||||
button.btn.btn-primary.btn-lg.fixedcta.gamifybutton(ng-click='playButtonClick()')= env.t('gamifyButton')
|
||||
section#health.bg-danger.container-fluid
|
||||
.row
|
||||
@@ -480,7 +480,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
h2= env.t('punishHeading2')
|
||||
p.sectioninfo= env.t('punishByline')
|
||||
.scrolltweet.hidden-xs.hidden-sm
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='Zelah Meyer', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/Zelah_Meyer.png')
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='Zelah Meyer', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/Zelah_Meyer.png')
|
||||
.tweet.popover.right.pull-right
|
||||
.arrow
|
||||
.popover-content
|
||||
@@ -515,13 +515,13 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
br
|
||||
= env.t('questByline2')
|
||||
.scrolltweet.hidden-xs.hidden-sm
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='skysailor', src='https://s3.amazonaws.com/habitrpg-assets/front/images/testimonials/skysailor.png')
|
||||
img.scrolltweet-image(data-toggle='tooltip', data-placement='top', title='skysailor', src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/testimonials/skysailor.png')
|
||||
.tweet.popover.right.pull-right
|
||||
.arrow
|
||||
.popover-content
|
||||
= env.t('skysailorQuote')
|
||||
.col-lg-4.col-md-6.col-md-offset-1.col-lg-offset-1.col-sm-10.well.col-xs-10.col-xs-offset-1.col-sm-offset-1.well-lg
|
||||
img.clearfix.img-responsive.center-block(style='padding-bottom:30px;', src="https://s3.amazonaws.com/habitrpg-assets/front/images/Quest_dilatory_drag'onSmall.png")
|
||||
img.clearfix.img-responsive.center-block(style='padding-bottom:30px;', src="https://d2afqr2xdmyzvu.cloudfront.net/front/images/Quest_dilatory_dragonSmall.png")
|
||||
.row
|
||||
.col-lg-5.col-md-5.col-sm-5.col-sm-offset-0.col-xs-5.col-xs-offset-0
|
||||
.health-progress
|
||||
@@ -531,7 +531,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.bar
|
||||
div(style='width:20%;')
|
||||
.quest-friend-img
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/party/AnnaCosplay.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/party/AnnaCosplay.png')
|
||||
.col-md-1.col-sm-1.col-xs-1
|
||||
h2
|
||||
span.glyphicon.glyphicon-arrow-right
|
||||
@@ -543,7 +543,7 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.bar
|
||||
div(style='width:0%;')
|
||||
.quest-friend-img
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/party/GrimReaper.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/party/GrimReaper.png')
|
||||
button.btn.btn-primary.btn-lg.fixedcta.gamifybutton(ng-click='playButtonClick()')= env.t('gamifyButton')
|
||||
section#features.bg-info.container-fluid
|
||||
.row
|
||||
@@ -551,29 +551,29 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.row
|
||||
.col-md-4.col-md-offset-2.col-sm-6
|
||||
.feature-img
|
||||
img.center-block.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/Mount.png')
|
||||
img.center-block.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/Mount.png')
|
||||
.featuretext
|
||||
h4= env.t('featurePetHeading')
|
||||
p
|
||||
= env.t('featurePetByline')
|
||||
.col-md-4.col-sm-6
|
||||
.feature-img.center-block
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/achievement-triadbingo.png')
|
||||
img(src='https://s3.amazonaws.com/habitrpg-assets/front/images/achievement-perfect.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/achievement-triadbingo.png')
|
||||
img(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/achievement-perfect.png')
|
||||
.featuretext
|
||||
h4= env.t('featureAchievementHeading')
|
||||
p= env.t('featureAchievementByline')
|
||||
.row
|
||||
.col-md-4.col-md-offset-2.col-sm-6
|
||||
.feature-img
|
||||
img.center-block.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/Promo_springclasses2015.png')
|
||||
img.center-block.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/Promo_springclasses2015.png')
|
||||
.featuretext
|
||||
h4= env.t('featureEquipHeading')
|
||||
p
|
||||
= env.t('featureEquipByline')
|
||||
.col-md-4.col-sm-6
|
||||
.feature-img
|
||||
img.center-block.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/Party-Header.png')
|
||||
img.center-block.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/Party-Header.png')
|
||||
.featuretext
|
||||
h4= env.t('featureSocialHeading')
|
||||
p
|
||||
@@ -593,19 +593,19 @@ html(ng-app='habitrpg', ng-controller='RootCtrl')
|
||||
.row
|
||||
.col-lg-2.col-md-2.col-md-offset-1.col-sm-4.col-sm-offset-1.col-xs-6.col-xs-offset-1
|
||||
a(href='http://ionicframework.com/')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/ionic-logo-horizontal-transparent.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/ionic-logo-horizontal-transparent.png')
|
||||
.col-lg-2.col-lg-offset-0.col-md-2.col-md-offset-0.col-sm-4.col-sm-offset-1.col-xs-6.col-xs-offset-1
|
||||
a(href='https://www.jetbrains.com/webstorm/')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/logo_webstorm.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/logo_webstorm.png')
|
||||
.col-lg-2.col-lg-offset-0.col-md-2.col-md-offset-0.col-sm-4.col-sm-offset-1.col-xs-6.col-xs-offset-1
|
||||
a(href='http://github.com/')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/GitHub_Logo.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/GitHub_Logo.png')
|
||||
.col-lg-2.col-lg-offset-0.col-md-2.col-md-offset-0.col-sm-4.col-sm-offset-1.col-xs-6.col-xs-offset-1
|
||||
a(href='https://trello.com/')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/trello-logo-blue.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/trello-logo-blue.png')
|
||||
.col-lg-2.col-lg-offset-0.col-md-2.col-md-offset-0.col-sm-4.col-sm-offset-1.col-xs-6.col-xs-offset-1
|
||||
a(href='https://slack.com/')
|
||||
img.img-responsive(src='https://s3.amazonaws.com/habitrpg-assets/front/images/presslogos/landing_slack_hash_wordmark_logo.png')
|
||||
img.img-responsive(src='https://d2afqr2xdmyzvu.cloudfront.net/front/images/presslogos/landing_slack_hash_wordmark_logo.png')
|
||||
|
||||
.row.footer-content
|
||||
include ../shared/footer
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -7,7 +7,7 @@ html(ng-app='habitrpg')
|
||||
title=env.t('newStuff')
|
||||
|
||||
script(type='text/javascript').
|
||||
window.env = !{JSON.stringify(env)};
|
||||
window.env = !{JSON.stringify(env._.pick(env, env.clientVars))};
|
||||
|
||||
link(rel='shortcut icon', href='#{env.getBuildUrl("favicon.ico")}?v=4')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user