diff --git a/.bowerrc b/.bowerrc index 20772d5d24..9c9ea40600 100644 --- a/.bowerrc +++ b/.bowerrc @@ -1,8 +1,3 @@ { - "directory": "public/bower_components", - "storage": { - "packages": ".bower-cache", - "registry": ".bower-registry" - }, - "tmp": ".bower-tmp" + "directory": "public/bower_components" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index bb5ec66ebf..4a6eb3925d 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ newrelic_agent.log .bower-tmp .bower-registry .bower-cache + +*.log src/*/*.map src/*/*/*.map test/*.js diff --git a/.nodemonignore b/.nodemonignore index f4e0b53926..5cfae9cb6a 100644 --- a/.nodemonignore +++ b/.nodemonignore @@ -6,4 +6,6 @@ Gruntfile.js CHANGELOG.md .idea* .git* +*.log newrelic_agent.log +.bower-cache* diff --git a/Gruntfile.js b/Gruntfile.js index e5fa1ed3ed..d778230751 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,6 +4,8 @@ module.exports = function(grunt) { // Project configuration. grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + git_changelog: { minimal: { options: { @@ -56,7 +58,10 @@ module.exports = function(grunt) { build: { files: [ {expand: true, cwd: 'public/', src: 'favicon.ico', dest: 'build/'}, - {expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/dist/spritesmith-*.png', dest: 'build/'} + {expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/dist/spritesmith.png', dest: 'build/'}, + {expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/img/sprites/backer-only/*.gif', dest: 'build/'}, + {expand: true, cwd: 'public/', src: 'bower_components/habitrpg-shared/img/sprites/npc_ian.gif', dest: 'build/'}, + {expand: true, cwd: 'public/', src: 'bower_components/bootstrap/dist/fonts/*', dest: 'build/'} ] } }, @@ -69,16 +74,18 @@ module.exports = function(grunt) { }, src: [ 'build/*.js', 'build/*.css', 'build/favicon.ico', - 'build/bower_components/bootstrap/docs/assets/css/*.css', - 'build/bower_components/habitrpg-shared/dist/*.css' + 'build/bower_components/habitrpg-shared/dist/*.png', + 'build/bower_components/habitrpg-shared/img/sprites/backer-only/*.gif', + 'build/bower_components/habitrpg-shared/img/sprites/npc_ian.gif', + 'build/bower_components/bootstrap/dist/fonts/*' ], - dest: 'make-sure-i-do-not-exist' + dest: 'build/*.css' } }, - nodemon: { + nodemon: { dev: { - ignoredFiles: ['public/*', 'Gruntfile.js', 'CHANGELOG.md', 'views/*', 'build/*', '.idea*', '.git*'] + script: '<%= pkg.main %>' } }, @@ -106,27 +113,35 @@ module.exports = function(grunt) { var files = grunt.file.readJSON('./public/manifest.json'); var uglify = {}; var cssmin = {}; + _.each(files, function(val, key){ + var js = uglify['build/' + key + '.js'] = []; + _.each(files[key]['js'], function(val){ js.push('public/' + val); }); + + var css = cssmin['build/' + key + '.css'] = []; + _.each(files[key]['css'], function(val){ - if(val == 'app.css' || val == 'static.css'){ - cssmin['build/' + val] = ['build/' + val] - }else{ - cssmin['build/' + val] = ['public/' + val] - } + var path = (val == 'app.css' || val == 'static.css') ? 'build/' : 'public/'; + css.push(path + val) }); + }); + grunt.config.set('uglify.build.files', uglify); - grunt.config.set('uglify.build.options', {compress: false}) + grunt.config.set('uglify.build.options', {compress: false}); + grunt.config.set('cssmin.build.files', cssmin); + // Rewrite urls to relative path + grunt.config.set('cssmin.build.options', {'target': 'public/css/whatever-css.css'}); }); // Register tasks. grunt.registerTask('build:prod', ['loadManifestFiles', 'clean:build', 'uglify', 'stylus', 'cssmin', 'copy:build', 'hashres']); - grunt.registerTask('build:dev', ['loadManifestFiles', 'clean:build', 'stylus', 'cssmin', 'copy:build']); + grunt.registerTask('build:dev', ['stylus']); grunt.registerTask('run:dev', [ 'build:dev', 'concurrent' ]); diff --git a/bower.json b/bower.json index 74fea1a96d..a6de245fda 100644 --- a/bower.json +++ b/bower.json @@ -14,20 +14,21 @@ "tests" ], "dependencies": { - "jquery": "~2.0.3", + "jquery": "~2.1.0", "jquery.cookie": "~1.4.0", "jquery-ui": "~1.10.3", - "angular": "~1.2.1", - "angular-sanitize": "~1.2.1", - "angular-resource": "~1.2.1", - "angular-ui": "~0.4.0", - "angular-bootstrap": "~0.5.0", + "angular": "~1.2.12", + "angular-sanitize": "~1.2.12", + "angular-resource": "~1.2.12", + "angular-ui-utils": "~0.1.0", + "angular-ui-select2": "https://github.com/angular-ui/ui-select2.git#4469fd7a32eba2e28d5e3dd319b49e1ad090b167", + "angular-bootstrap": "~0.10.0", "angular-ui-router": "ca3b4777a603df8f86cfd653c8f6c38b2ae05d89", - "angular-loading-bar": "~0.0.5", + "angular-loading-bar": "~0.3.0", "angular-bindonce": "~0.2.1", - "bootstrap": "v2.3.2", - "bootstrap-datepicker": "~1.2.0", - "bootstrap-growl": "~1.1.0", + "bootstrap": "~3.1.0", + "bootstrap-growl": "git://github.com/ifightcrime/bootstrap-growl.git#master", + "bootstrap-tour": "~0.8.1", "habitrpg-shared": "git://github.com/HabitRPG/habitrpg-shared.git#develop", "BrowserQuest": "https://github.com/mozilla/BrowserQuest.git", "github-buttons": "git://github.com/mdo/github-buttons.git", @@ -35,15 +36,12 @@ "JavaScriptButtons": "git://github.com/paypal/JavaScriptButtons.git#master", "Angular-At-Directive": "git://github.com/snicker/Angular-At-Directive#master", "js-emoji": "git://github.com/snicker/js-emoji#master", - "gemoji": "git://github.com/github/gemoji", "sticky": "*", - "bootstrap-tour": "~0.8.0", - "angular-ui-utils": "~0.1.0", "swagger-ui": "https://github.com/wordnik/swagger-ui.git#105c516f5f055b140e935e9cfe0c36f841921dff", "ngInfiniteScroll": "~1.0.0", - "angular-ui-select2": "https://github.com/angular-ui/ui-select2.git#4469fd7a32eba2e28d5e3dd319b49e1ad090b167" + "jquery-colorbox": "~1.4.36" }, "devDependencies": { - "angular-mocks": "~1.2.1" + "angular-mocks": "~1.2.12" } } diff --git a/config.json.example b/config.json.example index 634d22c7a7..da9b3bb238 100644 --- a/config.json.example +++ b/config.json.example @@ -11,8 +11,12 @@ "SMTP_USER":"user@domain.com", "SMTP_PASS":"password", "SMTP_SERVICE":"Gmail", + "SMTP_HOST":"smtp.gmail.com", + "SMTP_PORT": 587, + "SMTP_TLS": true, "STRIPE_API_KEY":"aaaabbbbccccddddeeeeffff00001111", "STRIPE_PUB_KEY":"22223333444455556666777788889999", "PAYPAL_MERCHANT":"paypal-merchant@gmail.com", - "NEW_RELIC_LICENSE_KEY":"NEW_RELIC_LICENSE_KEY" + "NEW_RELIC_LICENSE_KEY":"NEW_RELIC_LICENSE_KEY", + "GA_ID": "GA_ID" } diff --git a/migrations/contribs_plan.js b/migrations/contribs_plan.js new file mode 100644 index 0000000000..a08bd93f76 --- /dev/null +++ b/migrations/contribs_plan.js @@ -0,0 +1,23 @@ +// Give contrib.level 7+ free subscription for life +db.users.update( + + { + 'contributor.level':{$gte:7}, + 'purchased.plan.customerId':null + }, + + { + $set: { + 'purchased.plan':{ + planId: 'basic', + customerId: 'habitrpg', + dateCreated: new Date, + dateUpdated: new Date, + gemsBought: 0 + } + } + }, + + {multi:true} + +) \ No newline at end of file diff --git a/migrations/missing_gems.js b/migrations/missing_gems.js index ca9f674b9a..78ff37ed1d 100644 --- a/migrations/missing_gems.js +++ b/migrations/missing_gems.js @@ -1 +1 @@ -db.users.update({_id:''},{$set:{'purchased.ads':true},$inc:{balance:5}}); \ No newline at end of file +db.users.update({_id:''},{$inc:{balance:5}}); \ No newline at end of file diff --git a/package.json b/package.json index 338485de34..b3a788169d 100644 --- a/package.json +++ b/package.json @@ -40,12 +40,16 @@ "pretty-data": "git://github.com/vkiryukhin/pretty-data#master", "js2xmlparser": "~0.1.2", "mongoose": "~3.8.1", - "domain-middleware": "~0.1.0", "swagger-node-express": "git://github.com/lefnire/swagger-node-express#habitrpg", "passport": "~0.1.18", "passport-facebook": "~1.0.2", "newrelic": "~1.3.0", - "connect-ratelimit": "0.0.6" + "connect-ratelimit": "0.0.6", + "winston": "~0.7.2", + "winston-mail": "~0.2.7", + "winston-newrelic": "~0.1.4", + "domain-middleware": "~0.1.0", + "universal-analytics": "~0.3.2" }, "private": true, "subdomain": "habitrpg", diff --git a/public/css/alerts.styl b/public/css/alerts.styl index 4b765329b1..40a7b8910b 100644 --- a/public/css/alerts.styl +++ b/public/css/alerts.styl @@ -16,6 +16,27 @@ .wide-popover max-width: 400px +// Small alerts +alert-sm-mixin() + padding-top: 8px + padding-bottom: 8px + +.alert-sm + alert-sm-mixin() + +#notification-area + position: fixed + top: 0 + right: 0px + z-index: 1061 + + .alert + alert-sm-mixin() + + .notification-icon + transform: scale(0.8) + + // variables gpColor = #FBEFC0 @@ -78,30 +99,6 @@ borderDarken = 20% border-color: darken(dropColor,borderDarken) color: dropText -// alert icons - -.icon-gold - background: url("/bower_components/habitrpg-shared/img/coin_single_gold.png") no-repeat - background-position: center center - background-size: 18px - width: 14px - height: 14px - -.icon-silver - background: url("/bower_components/habitrpg-shared/img/coin_single_silver.png") no-repeat - background-position: center center - background-size: 18px - width: 14px - height: 14px - -.icon-death - background: url("/bower_components/habitrpg-shared/img/sprites/dead.png") no-repeat - background-position: center center - background-size: 14px - width: 14px - height: 14px - - .undo-button z-index: 3000 position:absolute diff --git a/public/css/avatar.styl b/public/css/avatar.styl index 3a774d4f65..5afe496a29 100644 --- a/public/css/avatar.styl +++ b/public/css/avatar.styl @@ -9,6 +9,16 @@ Seeing as the sprites might change drastically in the future re: pets and whatnot, this is just temporary. ---------------------------------------------------- */ +.profile-modal-header + width:560px + margin: 0px auto + +.avatar-level + position absolute + bottom 0px + right 0px + padding 2px 3px 2px 3px + // basic herobox styles .herobox height: 10.5em // higher to acct for the name area @@ -40,7 +50,7 @@ future re: pets and whatnot, this is just temporary. // always visible on the user's own avatar .herobox:after opacity: 0 -.herobox.isUser:after +.herobox.isUser:not(.hasMount):after opacity: 1 // make it a bit special background darken($better, 15%) @@ -52,21 +62,17 @@ future re: pets and whatnot, this is just temporary. .active border 1px solid black -// vertically center avatar sprite -// depending on if they have a pet -.herobox.hasPet - padding-top: 2em -.herobox:not(.hasPet) - padding-top: 1.75em - -// if it is the user's avatar, check if -// they have a pet and pad accordingly -.herobox.hasPet.isUser - padding-top: 3.25em - // if not user's avatar, remove lines for party unity .herobox:not(.isUser) - outline: 0 + outline: 0 + +// vertically center avatar sprite depending on if they have a pet +.herobox.hasPet + padding-top: 1.75em +.herobox:not(.hasPet) + padding-top: 2em +.herobox.hasMount + padding-top:0em // expose hero info on hover, taking // into account the extra pet space @@ -74,13 +80,6 @@ future re: pets and whatnot, this is just temporary. background: desaturate(lighten($better, 30%), 10%) &:after opacity: 1 -/* .herobox.hasPet - &:hover, &:focus - padding-top: 3.25em -.herobox:not(.hasPet) - &:hover, &:focus - padding-top: 2.5em */ - // positioning the sprites, etc .herobox @@ -93,4 +92,4 @@ future re: pets and whatnot, this is just temporary. position: absolute .herobox.isLeader - background: $better + background: $better \ No newline at end of file diff --git a/public/css/challenges.styl b/public/css/challenges.styl index 98d12edb17..d3a39eaca1 100644 --- a/public/css/challenges.styl +++ b/public/css/challenges.styl @@ -10,12 +10,4 @@ ul.challenge-accordion-header-specs margin-bottom: 10px #challenges-filters h3 - margin-top: 0px; - -// for things like challenges, member profiles, etc -@media (min-width: 767px) - .wide-modal - /* new custom width */ - width: 1020px; - /* must be half of the width, minus scrollbar on the left (30px) */ - margin-left: -480px; \ No newline at end of file + margin-top: 0px; \ No newline at end of file diff --git a/public/css/customizer.styl b/public/css/customizer.styl index fd3ad1add1..f4317afe2e 100644 --- a/public/css/customizer.styl +++ b/public/css/customizer.styl @@ -22,15 +22,18 @@ menu line-height: 2 .customize-option - border: 0px solid grey; - background-color: hsl(0, 0%, 93%); + border: 0px solid grey + background-color: hsl(0, 0%, 93%) margin-bottom: 10px - cursor: pointer; -webkit-transition: background-color 0.5s ease-out -moz-transition: background-color 0.5s ease-out transition: background-color 0.5s ease-out + &.hair + width: 40px !important + height: 40px !important + .customize-option:active background-color: rgb(255, 242, 204); -webkit-transition: none diff --git a/public/css/filters.styl b/public/css/filters.styl index 1c68eb6470..6512c3e0b3 100644 --- a/public/css/filters.styl +++ b/public/css/filters.styl @@ -1,36 +1,25 @@ -/* Filters */ .filters - padding-left: 19px - padding-right: 19px -.filters .nav - margin-bottom: 0 - -.filter-description - margin-top: 7px - margin-right: 7px - -/* Filters applied to tasks */ -.task.filtered-out - display: none - -.tag-label:hover - background-color: #3a87ad - cursor: pointer - - .tag-editing - padding 2px 0 0 2px - - .tag-editing .tag-editing-pill - height:auto + margin-top: 5px + margin-bottom: 5px -.tag-list - overflow-y: auto - white-space: nowrap - - .nav-pills - height: 43px - - li - display: inline-block - float: none \ No newline at end of file + .filter-description + margin-top: 7px + margin-right: 7px + padding-left: 7px + + markdown + display: inline-block + + .glyphicon-bullhorn + margin-right: 5px + + .input-group + float: left + width: 1px // Trick for proper alignment + margin-left: 3px + margin-right: 3px + margin-top: 5px + + .form-control + width: auto \ No newline at end of file diff --git a/public/css/footer.styl b/public/css/footer.styl index 0f39a09f1b..ad03be3f23 100644 --- a/public/css/footer.styl +++ b/public/css/footer.styl @@ -1,83 +1,9 @@ -/* 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; -} - -.frontpage-play-button - //box-shadow: 0 0 40px #494141; - display:inline - font-size: 20px - width:100px - height:50px \ No newline at end of file +.footer + padding-top: 20px + margin-top: 40px + padding-bottom: 20px + border-top: 1px solid #e5e5e5 + background-color: #f5f5f5 \ No newline at end of file diff --git a/public/css/game-pane.styl b/public/css/game-pane.styl index 346e86f44c..a3aaf0151e 100644 --- a/public/css/game-pane.styl +++ b/public/css/game-pane.styl @@ -1,6 +1,3 @@ -.full-width - width 100% - .border-right border-right 1px solid #ddd @@ -8,21 +5,27 @@ color #fff background-color #51a351 -.tavern-pane - position relative - height 250px - .chat-form - position: relative - z-index: 10 + margin-bottom: 10px -.chat-message .chat-plus-one - opacity: 0 -.chat-message:hover .chat-plus-one - opacity: 1 + textarea + margin-bottom: 5px + +.chat-message + markdown, span.time + padding: 0 3px + + .chat-plus-one + opacity: 0 + background-color: #eee + padding: 3px 3px 0px 3px + + &:hover .chat-plus-one + opacity: 1 + color: #999 .tavern-chat, .party-chat - + li padding-top:15px padding-bottom:15px @@ -78,6 +81,11 @@ padding-top:0px padding-bottom:0px +.panel-tiers + div + display: none + margin-top: 10px + // Name tags .label-contributor-1, .label-contributor-2 background-color: #333; @@ -108,33 +116,33 @@ padding: 0px 5px // Autocomplete [TODO] make this nicer -.list-at-user { - width: 100%; - max-width: 120px; - position: absolute; - background: white; - border: 1px solid #a4a4a4; - z-index: 10; -} +.list-at-user + width: 100% + max-width: 120px + position: absolute + background: white + border: 1px solid #a4a4a4 + z-index: 10 -.list-at-user li { - line-height:14px; - border-bottom: 1px solid #a4a4a4; - list-style-type: none; -} -.list-at-user li:hover { - cursor: pointer; - background: #b9dff4; -} +.list-at-user li + line-height:14px + border-bottom: 1px solid #a4a4a4 + list-style-type: none -.list-at-user li span { - margin: 2px; - font-size: 11.844px; - margin-left: 5px; - display: inline-block; -} -.list-cur { - background: #b9dff4; -} +.list-at-user li:hover + cursor: pointer + background: #b9dff4 + + +.list-at-user li span + margin: 2px + font-size: 11.844px + margin-left: 5px + display: inline-block + + +.list-cur + background: #b9dff4 + diff --git a/public/css/index.styl b/public/css/index.styl index fb2d74a615..7ecb15ca2b 100644 --- a/public/css/index.styl +++ b/public/css/index.styl @@ -3,9 +3,6 @@ // Vendor Includes - include first so we can override // Import only styles that do not have urls to images! Include them directly in the page! -@import "../bower_components/bootstrap-datepicker/css/datepicker.css" -@import "../bower_components/angular-ui/build/angular-ui.min.css" -@import "../bower_components/bootstrap/docs/assets/css/bootstrap-responsive.css" @import "../bower_components/bootstrap-tour/build/css/bootstrap-tour.min.css" @import "../bower_components/angular-loading-bar/build/loading-bar.css" @import "../bower_components/js-emoji/emoji.css" @@ -19,7 +16,6 @@ @import "./inventory.styl" @import "./alerts.styl" @import "./helpers.styl" -@import "./responsive.styl" @import "./header.styl" @import "./filters.styl" @import "./scrollbars.styl" @@ -29,10 +25,7 @@ @import "./classes.styl" @import "./quests.styl" @import "./shared.styl" - -// fix exploding to very wide for some reason -.datepicker - width: 210px +@import "./footer.styl" html,body,p,h1,ul,li,table,tr,th,td margin: 0 @@ -42,54 +35,27 @@ html,body,p,h1,ul,li,table,tr,th,td -webkit-box-sizing: border-box -moz-box-sizing: border-box box-sizing: border-box - -* { font-family: "Lato", sans-serif } + font-family: "Lato", sans-serif hr border-top: 0 border-bottom: 1px solid #ddd border-color: rgba(0,0,0,0.1) - -/* Footer --------------------------------------------------- */ - -/* Bootstrap website boilerplate */ -.footer { - padding: 70px 0; - border-top: 1px solid #e5e5e5; - background-color: #f5f5f5; -} -.footer p { - margin-bottom: 0; - color: #777; -} -.footer-links { - margin: 10px 0; -} -.footer-links li { - display: inline; - margin-right: 10px; -} - -/* Bootstrap overrides */ -div.tooltip-inner { - max-width: 300px; -} - /* Customizations to make footer sticky */ -/*html, body {height: 100%;}*/ -#wrap - min-height: 100% - z-index:-1 -#main - padding-bottom: 250px; /* don't know why this works, sticky footers are weird */ -@media (max-width: 600px) { - #wrap { +html, body + height: 100% + +#wrap + z-index:-1 + padding: 0 15px // Required by Bootstrap 3 when using .container-fluid + min-height: 100% + height: auto !important + +@media (max-width: 600px) + #wrap margin-top: 0 - } -} /* Gems -------------------------------------------------- */ @@ -108,17 +74,17 @@ div.tooltip-inner { position: relative; .gem-cost::before - content: ""; - display: block; - width: 0; - height: 0; - border: 6px solid #D4D4D4; - border-color: transparent #D4D4D4 transparent transparent; - position: absolute; - right: 100%; - margin-right: 0; - top: 50%; - margin-top: -6px; + content: "" + display: block + width: 0 + height: 0 + border: 6px solid #D4D4D4 + border-color: transparent #D4D4D4 transparent transparent + position: absolute + right: 100% + margin-right: 0 + top: 50% + margin-top: -6px .gem-cost::after content: ""; @@ -135,68 +101,81 @@ div.tooltip-inner { /* Misc -------------------------------------------------- */ +//move -.modal - whitespace: normal +.new-stuff-modal // a .modal-body + h5 + font-weight: 700 -.notification-character - float:left +.modal-fixed-height + overflow-y: auto + max-height: 400px // Taken from Bootstrap 2.3.2 css, may need to change -.notification-message - display: inline-block; - padding-right: 1em - line-height: 3 - -.notification-action - vertical-align: baseline -#reset-modal - z-index:1500 -#notification-area - position: fixed; - top: 0; - right: 0px; - z-index: 1061; +// notification-character used only by death modal's figure, move .modal-body - margin: 10px + figure + float: left + padding: 15px - -// TODO this is supposed to go in avatar.styl, but stylus is breaking if I even touch that file -.profile-modal-header - width:560px - margin: 0px auto - -.avatar-level - position absolute - bottom 0px - right 0px - padding 1px 3px 1px 3px +.death-modal // a .modal-body + margin: 10px a cursor: pointer -#modalMember p - word-wrap: break-word +a span.glyphicon + color: #333 // Takes base color from bootstrap + +a.label + color: #fff + +.label .glyphicon + color: #fff + +.muted + color: #999 .btn margin-right: 5px +.vertical-align + vertical-align: middle +.inline-block + display: inline-block + +// Buttons in groups should not have a 5px margin between them; suppress margin for buttons in groups. +.btn-group .btn + margin-right: 0px + .modal-indented-list margin-left: 10px; padding-left: 10px; -.inline-modal - position: relative - top: auto; - left: auto - right: auto - margin: 0 auto 20px - z-index: 1 - max-width: 100% - .buy-gems .nav-tabs margin-top: 0.5em .gem-wallet padding-top: 2px +// Add badges' classes +.badge-info + background-color: #428bca +.badge-success + background-color: #47a447 + +.white, .white a + color: #fff !important + +.collapse-toolbar + position:absolute + right:0px + top:0px + width:30px + height:25px + background-color:#222 + z-index:999 + .glyphicon + color:white + padding-left:7px + padding-top:5px diff --git a/public/css/inventory.styl b/public/css/inventory.styl index 0dbf947dec..44c4819343 100644 --- a/public/css/inventory.styl +++ b/public/css/inventory.styl @@ -15,29 +15,12 @@ .modal-header .gem-wallet padding-top: 0px +.equipment-title + display: inline-block + vertical-align: middle -// 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%) - > div - margin:auto - margin-bottom:.5em - p - text-align:center - width:6.5em - height:2.5em +.stable .static-popover + max-width: 550px menu.pets div display: inline-block @@ -57,12 +40,13 @@ menu.pets div // display:block .food-tray - position:fixed - right:0px - bottom:0px - width:30% + position: fixed + right: 15px + bottom: 0px + width: 30% height: 50% - overflow-y: scroll + overflow-y: auto + z-index: 1 .inventory-list p diff --git a/public/css/npcs.styl b/public/css/npcs.styl index 48493536b3..4f55489d5c 100644 --- a/public/css/npcs.styl +++ b/public/css/npcs.styl @@ -4,15 +4,15 @@ .npc_matt margin-bottom: 20px +.toolbar-bailey-container + width:55px + position:relative .npc_bailey float:left .npc_bailey_head - //height: 30px !important - height: 50px !important position: absolute - //top: 117px // 147 (header-height) - 30 (bailey height) - top: 97px - right: 50px + top: 4px + height:45px !important cursor: pointer // Tour (Justin) diff --git a/public/css/responsive.styl b/public/css/responsive.styl deleted file mode 100644 index c7200751f8..0000000000 --- a/public/css/responsive.styl +++ /dev/null @@ -1,61 +0,0 @@ -// widths -.full - width: 100%; -.half - width: 50%; -.third - width: 33.3333%; -.two-thirds - width: 66.6666%; -.quarter - width: 25%; -.three-quarters - width: 75%; -.fifth - width: 20%; -.two-fifths - width: 40%; -.three-fifths - width: 60%; -.four-fifths - width: 80%; -.sixth - width: 16.6667%; -.eighth - width: 12.5%; -.tenth - width: 10%; -.twelfth - width: 8.3333%; - -.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 \ No newline at end of file diff --git a/public/css/shared.styl b/public/css/shared.styl index 1766422a59..1d4b71b780 100644 --- a/public/css/shared.styl +++ b/public/css/shared.styl @@ -13,4 +13,14 @@ .dashed-underline border-bottom:1px dashed #000 text-decoration:none - cursor pointer \ No newline at end of file + cursor pointer + +// Required by angular-ui-bootstrap +.nav, .pagination, .carousel, .panel-title a + cursor: pointer + +.nav-tabs + margin-bottom: 15px + +img + max-width: 100% \ No newline at end of file diff --git a/public/css/static-pages.css b/public/css/static-pages.css deleted file mode 100644 index f617e486e2..0000000000 --- a/public/css/static-pages.css +++ /dev/null @@ -1,21 +0,0 @@ -.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; -} - -a { - cursor: pointer; -} \ No newline at end of file diff --git a/public/css/static.styl b/public/css/static.styl index fe576834e7..ff01b7f125 100644 --- a/public/css/static.styl +++ b/public/css/static.styl @@ -3,10 +3,38 @@ // Vendor Includes - include first so we can override // Import only styles that do not have urls to images! Include them directly in the page! @import "../bower_components/angular-loading-bar/build/loading-bar.css" - -@import "./static-pages.css" @import "./shared.styl" @import "./footer" +body + margin-top: 60px + +#aboutPage + iframe + margin-bottom: 20px + .muted i, i.muted - opacity:0.5 \ No newline at end of file + opacity:0.5 + +#header-play-button + margin-right: 15px + +#tagline + margin:10px 0 20px 0 + font-size:40px + +.marketing + text-align: center + +#frontpage-play-button + //box-shadow: 0 0 40px #494141; + display:inline + font-size: 20px + width:100px + height:50px + +#about-page + img + box-shadow 0px 0px 10px 5px #888 + margin 10px + max-width 500px \ No newline at end of file diff --git a/public/css/tasks.styl b/public/css/tasks.styl index 3e59280ac9..1391af1ecf 100644 --- a/public/css/tasks.styl +++ b/public/css/tasks.styl @@ -23,6 +23,8 @@ for $stage in $stages .completed background-color: $completed color: #999 + .glyphicon + color: #333 .task-checker label background-color: darken($completed, 30%) .task-action-btn @@ -31,11 +33,20 @@ for $stage in $stages .reward background-color: white - - // 2. Columns & Tasks // =================== +.tasks-lists + + .row + margin-left: -5px + margin-right: -5px + + .col-md-3 + padding-left: 10px // Override Bootstrap 15px padding + padding-right: 10px // Override Bootstrap 15px padding + margin-bottom: 15px + // main columns // ------------ .task-column @@ -47,7 +58,13 @@ for $stage in $stages .task-column_title margin: 0 0 0.5em padding: 0 + font-weight: 300 +// 50% width columns with scrollbars for tablets +@media (min-width: 768px) and (max-width: 970px) + .task-column + max-height: 18.6em + overflow-y: scroll // add new task form // ----------------- @@ -112,6 +129,9 @@ for $stage in $stages &:last-child margin-bottom: 0 + label + font-weight: 400 + // task content .task-text display: block @@ -124,19 +144,14 @@ for $stage in $stages height:1.5em background-size:1.5em -// task due date (for to-dos) -.task-date - font-size: 70% - .habit-wide .task-text padding-left: 7em // when a task is being dragged -.task.ui-sortable-helper { +.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) -} // primary task commands // ---------------------- @@ -270,13 +285,23 @@ for $stage in $stages // ----------------------- .task-meta-controls float: right - margin: 0.75em 0.5em 0 0.25em + margin: 0.75em 0.5em 0 0.5em height: 1em opacity: 0.25 - a > i - margin-left: 0.125em - .task-notes - margin-left: 0.125em + + a + text-decoration: none + + // improve alignment of task icons + .glyphicon-signal + margin-left: 0.25em + .glyphicon-remove, .glyphicon-ok, a.badge + margin-right: 0.25em + .glyphicon-tags + margin-left: 0.1em + margin-right: 0.4em + a.badge + position: relative; top:-2px .task:hover .task-meta-controls opacity: 1 @@ -296,7 +321,6 @@ form [class$="-options"] padding: 1em 1em 0 color: #333 - z-index: 0 position: relative //background-color: @@ -360,6 +384,9 @@ form text-align: center margin: 0 + &.task-attributes + text-align: left + .task-action-btn.tile border: 0 font-size: 1.15em @@ -393,6 +420,9 @@ form &:first-child border-right: 0 +.task-options + .task-action-btn.tile + margin: 3px // todos ui // -------- diff --git a/public/js/app.js b/public/js/app.js index 914f0437f0..ef55bb10d4 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -1,23 +1,5 @@ "use strict"; -window.env = window.env || {}; //FIX tests - -if(window.env.language && window.env.language.momentLang && window.env.language.momentLangCode){ - var head = document.getElementsByTagName('head')[0]; - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.text = window.env.language.momentLang; - head.appendChild(script); - moment.lang(window.env.language.momentLangCode); -} - -window.env.t = function(stringName, vars){ - var string = window.env.translations[stringName]; - if(!string) return window._.template(window.env.translations.stringNotFound, {string: stringName}); - - return vars === undefined ? string : window._.template(string, vars); -} - window.habitrpg = angular.module('habitrpg', ['ngResource', 'ngSanitize', 'userServices', 'groupServices', 'memberServices', 'challengeServices', 'authServices', 'notificationServices', 'guideServices', 'authCtrl', @@ -132,8 +114,12 @@ window.habitrpg = angular.module('habitrpg', .state('options.social.guilds.detail', { url: '/:gid', templateUrl: 'partials/options.social.guilds.detail.html', - controller: ['$scope', 'Groups', '$stateParams', function($scope, Groups, $stateParams){ - $scope.group = Groups.Group.get({gid:$stateParams.gid}); + controller: ['$scope', 'Groups', '$stateParams', + function($scope, Groups, $stateParams){ + Groups.Group.get({gid:$stateParams.gid}, function(group){ + $scope.group = group; + Groups.seenMessage(group._id); + }); }] }) @@ -218,24 +204,40 @@ window.habitrpg = angular.module('habitrpg', } // Handle errors - var interceptor = ['$rootScope', '$q', function ($rootScope, $q) { + $httpProvider.responseInterceptors.push(['$rootScope', '$q', function ($rootScope, $q) { function success(response) { return response; } function error(response) { - //var status = response.status; - response.data = (response.data.err) ? response.data.err : response.data; - if (response.status == 0) response.data = 'Server currently unreachable.'; - if (response.status == 500) response.data += ' (see Chrome console for more details).'; + debugger; + // Offline + if (response.status == 0 || + // don't know why we're getting 404 here, should be 0 + (response.status == 404 && _.isEmpty(response.data))) { + $rootScope.$broadcast('responseText', window.env.t('serverUnreach')); - var error = response.status == 0 ? response.data : ('Error ' + response.status + ': ' + response.data); - $rootScope.$broadcast('responseError', error); - console.log(arguments); - return $q.reject(response); + // Needs refresh + } else if (response.needRefresh) { + $rootScope.$broadcast('responseError', "The site has been updated and the page needs to refresh. The last action has not been recorded, please refresh and try again."); + + // 400 range? + } else if (response < 500) { + $rootScope.$broadcast('responseText', response.data.err || response.data); + + // Error + } else { + var error = 'Please reload, ' + + '"'+window.env.t('error')+' '+(response.data.err || response.data || 'something went wrong')+'" ' + + window.env.t('seeConsole'); + $rootScope.$broadcast('responseError', error); + console.error(response); + } + + //return $q.reject(response); // this completely halts the chain, meaning we can't queue offline actions + return response; } return function (promise) { return promise.then(success, error); } - }]; - $httpProvider.responseInterceptors.push(interceptor); + }]); }]) diff --git a/public/js/controllers/authCtrl.js b/public/js/controllers/authCtrl.js index 1c50d56be9..ab4b89eece 100644 --- a/public/js/controllers/authCtrl.js +++ b/public/js/controllers/authCtrl.js @@ -5,15 +5,15 @@ */ angular.module('authCtrl', []) - .controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','API_URL', - function($scope, $rootScope, User, $http, $location, $window, API_URL) { + .controller("AuthCtrl", ['$scope', '$rootScope', 'User', '$http', '$location', '$window','API_URL', '$modal', + function($scope, $rootScope, User, $http, $location, $window, API_URL, $modal) { var runAuth; var showedFacebookMessage; $scope.useUUID = false; $scope.toggleUUID = function() { if (showedFacebookMessage === false) { - alert("Until we add Facebook, use your UUID and API Token to log in (found at https://habitrpg.com > Options > Settings)."); + alert(window.env.t('untilNoFace')); showedFacebookMessage = true; } $scope.useUUID = !$scope.useUUID; @@ -27,10 +27,19 @@ angular.module('authCtrl', []) runAuth = function(id, token) { User.authenticate(id, token, function(err) { $window.location.href = '/'; - //$rootScope.modals.login = false; }); }; + function errorAlert(data, status, headers, config) { + if (status === 0) { + $window.alert(window.env.t('noReachServer')); + } else if (!!data && !!data.err) { + $window.alert(data.err); + } else { + $window.alert(window.env.t('errorUpCase') + ' ' + status); + } + }; + $scope.register = function() { /*TODO highlight invalid inputs we have this as a workaround for https://github.com/HabitRPG/habitrpg-mobile/issues/64 @@ -40,27 +49,9 @@ angular.module('authCtrl', []) } $http.post(API_URL + "/api/v2/register", $scope.registerVals).success(function(data, status, headers, config) { runAuth(data.id, data.apiToken); - }).error(function(data, status, headers, config) { - if (status === 0) { - $window.alert("Server not currently reachable, try again later"); - } else if (!!data && !!data.err) { - $window.alert(data.err); - } else { - $window.alert("ERROR: " + status); - } - }); + }).error(errorAlert); }; - function errorAlert(data, status, headers, config) { - if (status === 0) { - $window.alert("Server not currently reachable, try again later"); - } else if (!!data && !!data.err) { - $window.alert(data.err); - } else { - $window.alert("ERROR: " + status); - } - } - $scope.auth = function() { var data = { username: $scope.loginUsername || $('#login-tab input[name="username"]').val(), @@ -77,17 +68,21 @@ angular.module('authCtrl', []) }; $scope.playButtonClick = function(){ + window.ga && ga('send', 'event', 'button', 'click', 'Play'); if (User.authenticated()) { window.location.href = '/#/tasks'; } else { - $('#login-modal').modal('show'); + $modal.open({ + templateUrl: 'modals/login.html' + // Using controller: 'AuthCtrl' it causes problems + }); } } $scope.passwordReset = function(email){ $http.post(API_URL + '/api/v2/user/reset-password', {email:email}) .success(function(){ - alert('New password sent.'); + alert(window.env.t('newPassSent')); }) .error(function(data){ alert(data.err); diff --git a/public/js/controllers/challengesCtrl.js b/public/js/controllers/challengesCtrl.js index 8c1cd05a57..979fa0e3a6 100644 --- a/public/js/controllers/challengesCtrl.js +++ b/public/js/controllers/challengesCtrl.js @@ -44,11 +44,11 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', * Save */ $scope.save = function(challenge) { - if (!challenge.group) return alert('Please select group'); + if (!challenge.group) return alert(window.env.t('selectGroup')); var isNew = !challenge._id; challenge.$save(function(_challenge){ if (isNew) { - Notification.text('Challenge Created'); + Notification.text(window.env.t('challengeCreated')); $state.go('options.social.challenges.detail', {cid: _challenge._id}); $scope.discard(); $scope.challenges = Challenges.Challenge.query(); @@ -85,14 +85,14 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', $scope.closingChal = undefined; } $scope["delete"] = function(challenge) { - if (!confirm("Delete challenge, are you sure?")) return; + if (!confirm(window.env.t('sureDelCha'))) return; challenge.$delete(function(){ $scope.popoverEl.popover('destroy'); backToChallenges(); }); }; $scope.selectWinner = function(challenge) { - if (!confirm("Are you sure?")) return; + if (!confirm(window.env.t('youSure'))) return; challenge.$close({uid:challenge.winner}, function(){ $scope.popoverEl.popover('destroy'); backToChallenges(); @@ -106,7 +106,7 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', html: true, placement: 'right', trigger: 'manual', - title: 'Close challenge and...', + title: window.env.t('closeCha'), content: html }).popover('show'); @@ -140,7 +140,7 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', }; $scope.removeTask = function(list, $index) { - if (!confirm("Are you sure you want to delete this task?")) return; + if (!confirm(window.env.t('sureDelete'))) return; //TODO persist // User.log({op: "delTask", data: task}); list.splice($index, 1); @@ -185,13 +185,13 @@ habitrpg.controller("ChallengesCtrl", ['$rootScope','$scope', 'Shared', 'User', $scope.selectedChal = chal; $scope.popoverEl = $($event.target); var html = $compile( - 'Remove Tasks
\nKeep Tasks
\nCancel
' + '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' )($scope); $scope.popoverEl.popover('destroy').popover({ html: true, placement: 'top', trigger: 'manual', - title: 'Leave challenge and...', + title: window.env.t('leaveCha'), content: html }).popover('show'); } diff --git a/public/js/controllers/footerCtrl.js b/public/js/controllers/footerCtrl.js index 6541f6f70f..5d2fa238e6 100644 --- a/public/js/controllers/footerCtrl.js +++ b/public/js/controllers/footerCtrl.js @@ -1,6 +1,7 @@ "use strict"; -habitrpg.controller("FooterCtrl", ['$scope', '$rootScope', 'User', '$http', 'Notification', 'API_URL', +(typeof habitrpg !== 'undefined' ? habitrpg : habitrpgStatic) + .controller("FooterCtrl", ['$scope', '$rootScope', 'User', '$http', 'Notification', 'API_URL', function($scope, $rootScope, User, $http, Notification, API_URL) { /** @@ -13,17 +14,14 @@ habitrpg.controller("FooterCtrl", ['$scope', '$rootScope', 'User', '$http', 'Not // Stripe $.getScript('//checkout.stripe.com/v2/checkout.js'); - // Amazon Affiliate -// 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(''); -// }); -// } - // Google Analytics, only in production if (window.env.NODE_ENV === 'production') { - window._gaq = [["_setAccount", "UA-33510635-1"], ["_setDomainName", "habitrpg.com"], ["_trackPageview"]]; - $.getScript(("https:" === document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js"); + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); + ga('create', window.env.GA_ID, 'habitrpg.com'); + ga('send', 'pageview'); } // Scripts only for desktop @@ -44,27 +42,30 @@ habitrpg.controller("FooterCtrl", ['$scope', '$rootScope', 'User', '$http', 'Not /** * Debug functions. Note that the server route for gems is only available if process.env.DEBUG=true */ - $scope.addMissedDay = function(){ - if (!confirm("Are you sure you want to reset the day?")) return; - var dayBefore = moment(User.user.lastCron).subtract('days', 1).toDate(); - User.set({'lastCron': dayBefore}); - Notification.text('-1 day, remember to refresh'); - } - $scope.addTenGems = function(){ - $http.post(API_URL + '/api/v2/user/addTenGems').success(function(){ - User.log({}); - }) - } - $scope.addLevelsAndGold = function(){ - User.set({ - 'stats.exp': User.user.stats.exp + 10000, - 'stats.gp': User.user.stats.gp + 10000, - 'stats.mp': User.user.stats.mp + 10000 - }); - } - $scope.addOneLevel = function(){ - User.set({ - 'stats.exp': User.user.stats.exp + (Math.round(((Math.pow(User.user.stats.lvl, 2) * 0.25) + (10 * User.user.stats.lvl) + 139.75) / 10) * 10) - }); + if (window.env.NODE_ENV === 'development') { + $scope.addMissedDay = function(){ + if (!confirm("Are you sure you want to reset the day?")) return; + var dayBefore = moment(User.user.lastCron).subtract('days', 1).toDate(); + User.set({'lastCron': dayBefore}); + Notification.text('-1 day, remember to refresh'); + } + $scope.addTenGems = function(){ + $http.post(API_URL + '/api/v2/user/addTenGems').success(function(){ + User.log({}); + }) + } + $scope.addLevelsAndGold = function(){ + User.set({ + 'stats.exp': User.user.stats.exp + 10000, + 'stats.gp': User.user.stats.gp + 10000, + 'stats.mp': User.user.stats.mp + 10000 + }); + } + $scope.addOneLevel = function(){ + User.set({ + 'stats.exp': User.user.stats.exp + (Math.round(((Math.pow(User.user.stats.lvl, 2) * 0.25) + (10 * User.user.stats.lvl) + 139.75) / 10) * 10) + }); + } } + }]) \ No newline at end of file diff --git a/public/js/controllers/groupsCtrl.js b/public/js/controllers/groupsCtrl.js index ddb36c824f..c0ba02c5a1 100644 --- a/public/js/controllers/groupsCtrl.js +++ b/public/js/controllers/groupsCtrl.js @@ -29,12 +29,12 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' // We need the member information up top here, but then we pass it down to the modal controller // down below. Better way of handling this? Members.selectMember(uid); - $rootScope.modals.member = true; + $rootScope.openModal('member', {controller:'MemberModalCtrl'}); } } $scope.removeMember = function(group, member, isMember){ - var yes = confirm("Do you really want to remove this member from the party?") + var yes = confirm(window.env.t('sureKick')) if(yes){ Groups.Group.removeMember({gid: group._id, uuid: member._id }, undefined, function(){ if(isMember){ @@ -99,8 +99,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.$watch('group.chat',$scope.chatChanged); $scope.caretChanged = function(newCaretPos) { - var relativeelement = $('.-options'); - var textarea = $('.chat-textarea'); + var relativeelement = $('.chat-form div:first'); + var textarea = $('.chat-form textarea'); var userlist = $('.list-at-user'); var offset = { x: textarea.offset().left - relativeelement.offset().left, @@ -180,7 +180,7 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.likeChatMessage = function(group,message) { if (message.uuid == User.user._id) - return Notification.text("Can't like your own message. Don't be that person."); + return Notification.text(window.env.t('foreverAlone')); if (!message.likes) message.likes = {}; if (message.likes[User.user._id]) { delete message.likes[User.user._id]; @@ -198,10 +198,10 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' // List of Ordering options for the party members list $scope.partyOrderChoices = { - 'level': 'Sort by Level', - 'random': 'Sort randomly', - 'pets': 'Sort by number of pets', - 'party_date_joined': 'Sort by Party date joined', + 'level': window.env.t('sortLevel'), + 'random': window.env.t('sortRandom'), + 'pets': window.env.t('sortPets'), + 'party_date_joined': window.env.t('sortJoined'), }; }]) @@ -219,9 +219,9 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } $scope.newGroup = newGroup() $scope.create = function(group){ - if (User.user.balance < 1) return $rootScope.modals.buyGems = true; + if (User.user.balance < 1) return $rootScope.openModal('buyGems', {track:"Gems > Create Group"}); - if (confirm("Create Guild for 4 Gems?")) { + if (confirm(window.env.t('confirmGuild'))) { group.$save(function(saved){ User.user.balance--; $scope.groups.guilds.push(saved); @@ -284,14 +284,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }), '_id'); if (_.intersection(challenges, User.user.challenges).length > 0) { html = $compile( - 'Remove Tasks
\nKeep Tasks
\nCancel
' + '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' )($scope); - title = "Leave group challenges and..."; + title = window.env.t('leaveGroupCha'); } else { html = $compile( - 'Confirm
\nCancel
' + '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
' )($scope); - title = "Leave group?" + title = window.env.t('leaveGroup') } $scope.popoverEl.popover('destroy').popover({ html: true, @@ -319,6 +319,9 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' $scope.text = 'Party'; $scope.group = $rootScope.party = Groups.party(); $scope.newGroup = new Groups.Group({type:'party'}); + + Groups.seenMessage($scope.group._id); + $scope.create = function(group){ group.$save(function(newGroup){ $scope.group = newGroup; @@ -358,14 +361,14 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' }), '_id'); if (_.intersection(challenges, User.user.challenges).length > 0) { html = $compile( - 'Remove Tasks
\nKeep Tasks
\nCancel
' + '' + window.env.t('removeTasks') + '
\n' + window.env.t('keepTasks') + '
\n' + window.env.t('cancel') + '
' )($scope); - title = "Leave party challenges and..."; + title = window.env.t('leavePartyCha'); } else { html = $compile( - 'Confirm
\nCancel
' + '' + window.env.t('confirm') + '
\n' + window.env.t('cancel') + '
' )($scope); - title = "Leave party?"; + title = window.env.t('leaveParty'); } $scope.popoverEl.popover('destroy').popover({ html: true, @@ -383,8 +386,8 @@ habitrpg.controller("GroupsCtrl", ['$scope', '$rootScope', 'Shared', 'Groups', ' } $scope.questAbort = function(){ - if (!confirm("Are you sure you want to abort this mission? It will abort it for everyone in your party, and you'll lose your quest scroll.")) return; - if (!confirm("Are you double sure? Make sure they won't hate you forever!")) return; + if (!confirm(window.env.t('sureAbort'))) return; + if (!confirm(window.env.t('doubleSureAbort'))) return; $rootScope.party.$questAbort(); } } diff --git a/public/js/controllers/inventoryCtrl.js b/public/js/controllers/inventoryCtrl.js index 6d769ca47f..8b3b084903 100644 --- a/public/js/controllers/inventoryCtrl.js +++ b/public/js/controllers/inventoryCtrl.js @@ -7,7 +7,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', $scope.selectedEgg = null; // {index: 1, name: "Tiger", value: 5} $scope.selectedPotion = null; // {index: 5, name: "Red", value: 3} - $scope.totalPets = _.size(Content.eggs) * _.size(Content.hatchingPotions); + $scope.totalPets = _.size(Content.dropEggs) * _.size(Content.hatchingPotions); + $scope.totalMounts = _.size(Content.eggs) * _.size(Content.hatchingPotions); // count egg, food, hatchingPotion stack totals var countStacks = function(items) { return _.reduce(items,function(m,v){return m+v;},0);} @@ -79,7 +80,7 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', } $scope.hatch = function(egg, potion){ - if (!$window.confirm('Hatch a ' + potion.key + ' ' + egg.key + '?')) return; + if (!$window.confirm(window.env.t('hatchAPot', {potion: potion.key, egg: egg.key}))) return; user.ops.hatch({params:{egg:egg.key, hatchingPotion:potion.key}}); $scope.selectedEgg = null; $scope.selectedPotion = null; @@ -87,9 +88,11 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', $scope.purchase = function(type, item){ var gems = User.user.balance * 4; - if(gems < item.value) return $rootScope.modals.buyGems = true; - var string = (type == 'hatchingPotion') ? 'hatching potion' : type; // give hatchingPotion a space - var message = "Buy this " + string + " with " + item.value + " of your " + gems + " Gems?" + + if(gems < item.value) return $rootScope.openModal('buyGems'); + var string = (type == 'hatchingPotions') ? 'hatching potion' : (type == 'eggs') ? 'egg' : (type == 'quests') ? 'quest' : (item.key == 'Saddle') ? 'saddle' : (type == 'special') ? item.key : type; // this is ugly but temporary, once the purchase modal is done this will be removed + var message = window.env.t('buyThis', {text: string, price: item.value, gems: gems}) + if($window.confirm(message)) User.user.ops.purchase({params:{type:type,key:item.key}}); } @@ -102,8 +105,8 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', if ($scope.selectedFood) { var food = $scope.selectedFood if (food.key == 'Saddle') { - if (!$window.confirm('Saddle ' + pet + '?')) return; - } else if (!$window.confirm('Feed ' + petDisplayName + ' '+ food.article + food.text + '?')) { + if (!$window.confirm(window.env.t('useSaddle', {pet: pet}))) return; + } else if (!$window.confirm(window.env.t('feedPet', {name: petDisplayName, article: food.article, text: food.text}))) { return; } User.user.ops.feed({params:{pet: pet, food: food.key}}); @@ -123,16 +126,14 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', var item = Content.quests[quest]; var completedPrevious = !item.previous || (User.user.achievements.quests && User.user.achievements.quests[item.previous]); if (!completedPrevious) - return alert("You must first complete " + $rootScope.Content.quests[item.previous].text + '.'); + return alert(window.env.t('mustComplete', {quest: $rootScope.Content.quests[item.previous].text})); if (item.lvl && item.lvl > user.stats.lvl) - return alert("You must be level " + item.lvl + '.'); + return alert(window.env.t('mustLevel', {level: item.lvl})); $rootScope.selectedQuest = item; - $rootScope.modals.showQuest = true; + $rootScope.openModal('showQuest', {controller:'InventoryCtrl'}); } $scope.closeQuest = function(){ $rootScope.selectedQuest = undefined; - $rootScope.modals.showQuest = false; - $rootScope.modals.buyQuest = false; } $scope.questInit = function(){ $rootScope.party.$questAccept({key:$scope.selectedQuest.key}, function(){ @@ -142,11 +143,13 @@ habitrpg.controller("InventoryCtrl", ['$rootScope', '$scope', '$window', 'User', } $scope.buyQuest = function(quest) { var item = Content.quests[quest]; + if (item.lvl && item.lvl > user.stats.lvl) + return alert(window.env.t('mustLvlQuest', {level: item.lvl})); var completedPrevious = !item.previous || (User.user.achievements.quests && User.user.achievements.quests[item.previous]); if (!completedPrevious) return $scope.purchase("quests", item); $rootScope.selectedQuest = item; - $rootScope.modals.buyQuest = true; + $rootScope.openModal('buyQuest', {controller:'InventoryCtrl'}); } } ]); diff --git a/public/js/controllers/notificationCtrl.js b/public/js/controllers/notificationCtrl.js index a93deb9b1a..82c42af8a7 100644 --- a/public/js/controllers/notificationCtrl.js +++ b/public/js/controllers/notificationCtrl.js @@ -1,9 +1,13 @@ 'use strict'; habitrpg.controller('NotificationCtrl', - ['$scope', '$rootScope', 'Shared', 'User', 'Guide', 'Notification', function ($scope, $rootScope, Shared, User, Guide, Notification) { + ['$scope', '$rootScope', 'Shared', 'User', 'Guide', 'Notification', + function ($scope, $rootScope, Shared, User, Guide, Notification) { $rootScope.$watch('user.stats.hp', function(after, before) { + if (after <= 0){ + $rootScope.openModal('death', {keyboard:false, backdrop:'static'}); + } if (after == before) return; if (User.user.stats.lvl == 0) return; Notification.hp(after - before, 'hp'); @@ -26,7 +30,7 @@ habitrpg.controller('NotificationCtrl', if ((money > 0) && !!bonus) { if (bonus < 0.01) bonus = 0.01; - Notification.text("+ " + Notification.coins(bonus) + " Streak Bonus!"); + Notification.text("+ " + Notification.coins(bonus) + ' ' + window.env.t('streakCoins')); delete User.user._tmp.streakBonus; } }); @@ -61,31 +65,35 @@ habitrpg.controller('NotificationCtrl', $rootScope.$watch('user.achievements.streak', function(after, before){ if(before == undefined || after == before || after < before) return; - $rootScope.modals.achievements.streak = true; + $rootScope.openModal('achievements/streak'); }); $rootScope.$watch('user.achievements.ultimateGear', function(after, before){ if (after === before || after !== true) return; - $rootScope.modals.achievements.ultimateGear = true; + $rootScope.openModal('achievements/ultimateGear'); }); $rootScope.$watch('user.items.pets', function(after, before){ if(_.size(after) === _.size(before) || Shared.countPets(null, after) < 90) return; User.user.achievements.beastMaster = true; - $rootScope.modals.achievements.beastMaster = true; + $rootScope.openModal('achievements/beastMaster'); }, true); $rootScope.$watch('user.achievements.rebirths', function(after, before){ if(after === before) return; - $rootScope.modals.achievements.rebirth = true; + $rootScope.openModal('achievements/rebirth'); + }); + + $rootScope.$watch('user.flags.contributor', function(after, before){ + if (after === before || after !== true) return; + $rootScope.openModal('achievements/contributor'); }); /*_.each(['weapon', 'head', 'chest', 'shield'], function(watched){ $rootScope.$watch('user.items.' + watched, function(before, after){ if (after == before) return; if (+after < +before) { - Notification.death(); //don't want to day "lost a head" if (watched === 'head') watched = 'helm'; Notification.text('Lost GP, 1 LVL, ' + watched); @@ -93,6 +101,13 @@ habitrpg.controller('NotificationCtrl', }) });*/ + // Classes modal + $rootScope.$watch('!user.flags.classSelected && user.stats.lvl >= 10', function(after, before){ + if(after){ + $rootScope.openModal('chooseClass', {controller:'UserCtrl', keyboard:false, backdrop:'static'}); + } + }); + $rootScope.$watch('user.stats.lvl', function(after, before) { if (after == before) return; if (after > before) { @@ -100,8 +115,23 @@ habitrpg.controller('NotificationCtrl', } }); + // Completed quest modal + $rootScope.$watch('user.party.quest.completed', function(after, before){ + if (after == before || after != true) return; + $rootScope.openModal('questCompleted', {controller:'InventoryCtrl'}); + }); + + // Quest invitation modal + $rootScope.$watch('party.quest.key && !party.quest.active && party.quest.members[user._id] == undefined', function(after, before){ + if (after == before || after != true) return; + $rootScope.openModal('questInvitation'); + }); + $rootScope.$on('responseError', function(ev, error){ Notification.error(error); }); + $rootScope.$on('responseText', function(ev, error){ + Notification.text(error); + }); } ]); diff --git a/public/js/controllers/rootCtrl.js b/public/js/controllers/rootCtrl.js index e37a4a9662..508afbdf33 100644 --- a/public/js/controllers/rootCtrl.js +++ b/public/js/controllers/rootCtrl.js @@ -3,8 +3,8 @@ /* Make user and settings available for everyone through root scope. */ -habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', 'Shared', 'Content', - function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups, Shared, Content) { +habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$http', '$state', '$stateParams', 'Notification', 'Groups', 'Shared', 'Content', '$modal', '$timeout', + function($scope, $rootScope, $location, User, $http, $state, $stateParams, Notification, Groups, Shared, Content, $modal, $timeout) { var user = User.user; var initSticky = _.once(function(){ @@ -13,8 +13,11 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ }) $rootScope.$on('userUpdated',initSticky); - $rootScope.modals = {}; - $rootScope.modals.achievements = {}; + $rootScope.$on('$stateChangeSuccess', + function(event, toState, toParams, fromState, fromParams){ + if (!!fromState.name) window.ga && ga('send', 'pageview', {page: '/#/'+toState.name}); + }); + $rootScope.User = User; $rootScope.user = user; $rootScope.moment = window.moment; @@ -23,6 +26,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $rootScope.Shared = Shared; $rootScope.Content = Content; $rootScope.env = window.env; + $rootScope.Math = Math; // Angular UI Router $rootScope.$state = $state; @@ -66,29 +70,60 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $rootScope.set = User.set; $rootScope.authenticated = User.authenticated; + // Open a modal from a template expression (like ng-click,...) + // Otherwise use the proper $modal.open + $rootScope.openModal = function(template, options){//controller, scope, keyboard, backdrop){ + if (!options) options = {}; + if (options.track) window.ga && ga('send', 'event', 'button', 'click', options.track); + return $modal.open({ + templateUrl: 'modals/' + template + '.html', + controller: options.controller, // optional + scope: options.scope, // optional + keyboard: (options.keyboard === undefined ? true : options.keyboard), // optional + backdrop: (options.backdrop === undefined ? true : options.backdrop) // optional + }); + } + $rootScope.dismissAlert = function() { - $rootScope.modals.newStuff = false; $rootScope.set({'flags.newStuff':false}); } $rootScope.notPorted = function(){ - alert("This feature is not yet ported from the original site."); + alert(window.env.t('notPorted')); } $rootScope.dismissErrorOrWarning = function(type, $index){ $rootScope.flash[type].splice($index, 1); } + $rootScope.initPayPalButton = function($event){ + //debugger + var data = { + name:env.t('paypalText'), + //env:window.env.NODE_ENV == 'production' ? '' : 'sandbox', + quantity:1, + amount:5, + currency:'USD', + tax:0, + callback:window.env.BASE_URL + '/api/v2/user/buy-gems/paypal-ipn', + custom:'?uid='+User.user._id + '&apiToken=' + User.user.apiToken, + 'return':window.env.BASE_URL, + rm:1, + no_shipping:1 + }; + PAYPAL.apps.ButtonFactory.create(window.env.PAYPAL_MERCHANT, data, 'buynow', document.getElementById('custom-paypal-button')); + } + $rootScope.showStripe = function(subscription) { StripeCheckout.open({ key: window.env.STRIPE_PUB_KEY, address: false, amount: 500, - name: subscription ? "Subscribe" : "Checkout", + name: subscription ? window.env.t('subscribe') : window.env.t('checkout'), description: subscription ? - "Buy gems with Gold, No Ads, Support the Devs" : - "20 Gems, No Ads, Support the Devs", - panelLabel: subscription ? "Subscribe" : "Checkout", + window.env.t('buySubsText') : + window.env.t('buyCheckText'), + panelLabel: subscription ? window.env.t('subscribe') : window.env.t('checkout'), token: function(data) { var url = '/api/v2/user/buy-gems'; if (subscription) url += '?plan=basic_earned'; @@ -105,7 +140,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ } $scope.cancelSubscription = function(){ - if (!confirm("Are you sure you want to cancel your subscription?")) return; + if (!confirm(window.env.t('sureCancelSub'))) return; //TODO use Stripe API to keep subscription till end of their month $http.post('/api/v2/user/cancel-subscription').success(function(){ window.location.reload(true); @@ -117,7 +152,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ if (backer && backer.npc) return backer.npc; var l = contrib && contrib.level; if (l && l > 0) { - var level = (l < 3) ? 'Friend' : (l < 5) ? 'Elite' : (l < 7) ? 'Champion' : (l < 8) ? 'Legendary' : 'Heroic'; + var level = (l < 3) ? window.env.t('friend') : (l < 5) ? window.env.t('elite') : (l < 7) ? window.env.t('champion') : (l < 8) ? window.env.t('legendary') : window.env.t('heroic'); return level + ' ' + contrib.text; } } @@ -145,7 +180,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ }); data = google.visualization.arrayToDataTable(matrix); options = { - title: 'History', + title: window.env.t('history'), backgroundColor: { fill: 'transparent' }, @@ -163,7 +198,12 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ ------------------------ */ $scope.castStart = function(spell) { - if (User.user.stats.mp < spell.mana) return Notification.text("Not enough mana."); + if (User.user.stats.mp < spell.mana) return Notification.text(window.env.t('notEnoughMana')); + + // Temporary for valentine's day, remove after event + if (spell.key == 'valentine' && User.user.stats.gp < spell.value) + return Notification.text('Not enough gold.'); + $rootScope.applyingAction = true; $scope.spell = spell; if (spell.target == 'self') { @@ -178,7 +218,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $scope.castEnd = function(target, type, $event){ if (!$rootScope.applyingAction) return; $event && ($event.stopPropagation(),$event.preventDefault()); - if ($scope.spell.target != type) return Notification.text("Invalid target"); + if ($scope.spell.target != type) return Notification.text(window.env.t('invalidTarget')); $scope.spell.cast(User.user, target); User.save(); @@ -189,11 +229,11 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $http.post('/api/v2/user/class/cast/'+spell.key+'?targetType='+type+'&targetId='+targetId) .success(function(){ - var msg = "You cast " + spell.text; + var msg = window.env.t('youCast', {spell: spell.text}); switch (type) { - case 'task': msg += ' on ' + target.text + '.';break; - case 'user': msg += ' on ' + target.profile.name + '.';break; - case 'party': msg += ' for the Party.';break; + case 'task': msg = window.env.t('youCastTarget', {spell: spell.text, target: target.text});break; + case 'user': msg = window.env.t('youCastTarget', {spell: spell.text, target: target.profile.name});break; + case 'party': msg = window.env.t('youCastParty', {spell: spell.text});break; } Notification.text(msg); }); diff --git a/public/js/controllers/settingsCtrl.js b/public/js/controllers/settingsCtrl.js index 8ad6419a14..98752e50be 100644 --- a/public/js/controllers/settingsCtrl.js +++ b/public/js/controllers/settingsCtrl.js @@ -38,7 +38,7 @@ habitrpg.controller('SettingsCtrl', var dayStart = +User.user.preferences.dayStart; if (_.isNaN(dayStart) || dayStart < 0 || dayStart > 24) { dayStart = 0; - return alert('Please enter a number between 0 and 24'); + return alert(window.env.t('enterNumber')); } User.set({'preferences.dayStart': dayStart}); } @@ -55,23 +55,21 @@ habitrpg.controller('SettingsCtrl', $scope.reroll = function(){ User.user.ops.reroll({}); - $rootScope.modals.reroll = false; $rootScope.$state.go('tasks'); } $scope.rebirth = function(){ User.user.ops.rebirth({}); - $rootScope.modals.rebirth = false; $rootScope.$state.go('tasks'); } $scope.changePassword = function(changePass){ if (!changePass.oldPassword || !changePass.newPassword || !changePass.confirmNewPassword) { - return alert("Please fill out all fields"); + return alert(window.env.t('fillAll')); } $http.post(API_URL + '/api/v2/user/change-password', changePass) .success(function(){ - alert("Password successfully changed"); + alert(window.env.t('passSuccess')); $scope.changePass = {}; }) .error(function(data){ @@ -80,12 +78,11 @@ habitrpg.controller('SettingsCtrl', } $scope.restoreValues = {}; - $rootScope.$watch('modals.restore', function(value){ - if(value === true){ - $scope.restoreValues.stats = angular.copy(User.user.stats); - $scope.restoreValues.achievements = {streak: User.user.achievements.streak || 0}; - } - }) + $rootScope.openRestoreModal = function(){ + $scope.restoreValues.stats = angular.copy(User.user.stats); + $scope.restoreValues.achievements = {streak: User.user.achievements.streak || 0}; + $rootScope.openModal('restore', {scope:$scope}); + }; $scope.restore = function(){ var stats = $scope.restoreValues.stats, @@ -98,12 +95,10 @@ habitrpg.controller('SettingsCtrl', "stats.mp": stats.mp, "achievements.streak": achievements.streak }); - $rootScope.modals.restore = false; } $scope.reset = function(){ User.user.ops.reset({}); - $rootScope.modals.reset = false; $rootScope.$state.go('tasks'); } diff --git a/public/js/controllers/tasksCtrl.js b/public/js/controllers/tasksCtrl.js index 5dd8fc9369..1bde5c0bed 100644 --- a/public/js/controllers/tasksCtrl.js +++ b/public/js/controllers/tasksCtrl.js @@ -38,7 +38,7 @@ habitrpg.controller("TasksCtrl", ['$scope', '$location', 'User','Notification', }; $scope.removeTask = function(list, $index) { - if (!confirm("Are you sure you want to delete this task?")) return; + if (!confirm(window.env.t('sureDelete'))) return; User.user.ops.deleteTask({params:{id:list[$index].id}}) }; diff --git a/public/js/controllers/userCtrl.js b/public/js/controllers/userCtrl.js index ce3c629d82..43c1996296 100644 --- a/public/js/controllers/userCtrl.js +++ b/public/js/controllers/userCtrl.js @@ -18,7 +18,7 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$ $scope.changeClass = function(klass){ if (!klass) { - if (!confirm("Are you sure? This will reset your character's class and allocated points (you'll get them all back to re-allocate), and costs 3 gems")) + if (!confirm(window.env.t('sureReset'))) return; return User.user.ops.changeClass({}); } @@ -52,11 +52,11 @@ habitrpg.controller("UserCtrl", ['$rootScope', '$scope', '$location', 'User', '$ var cost = fullSet ? 1.25 : 0.5; // 5G per set, 2G per individual if (fullSet) { - if (confirm("Purchase for 5 Gems?") !== true) return; - if (User.user.balance < cost) return $rootScope.modals.buyGems = true; + if (confirm(window.env.t('purchaseFor5')) !== true) return; + if (User.user.balance < cost) return $rootScope.openModal('buyGems'); } else if (!User.user.fns.dotGet('purchased.' + path)) { - if (confirm("Purchase for 2 Gems?") !== true) return; - if (User.user.balance < cost) return $rootScope.modals.buyGems = true; + if (confirm(window.env.t('purchaseFor2')) !== true) return; + if (User.user.balance < cost) return $rootScope.openModal('buyGems'); } User.user.ops.unlock({query:{path:path}}) } diff --git a/public/js/directives/directives.js b/public/js/directives/directives.js index 2d36fea375..7a8e9d71fd 100644 --- a/public/js/directives/directives.js +++ b/public/js/directives/directives.js @@ -26,7 +26,7 @@ habitrpg.directive('habitrpgAdsense', function() { template: '
', link: function ($scope, element, attrs) {} } -}) +}); habitrpg.directive('whenScrolled', function() { return function(scope, elm, attr) { @@ -85,21 +85,21 @@ habitrpg scope.main = attrs.main; $rootScope.lists = [ { - header: env.t('habits'), + header: window.env.t('habits'), type: 'habit', - placeHolder: env.t('newHabit') + placeHolder: window.env.t('newHabit') }, { - header: env.t('dailies'), + header: window.env.t('dailies'), type: 'daily', - placeHolder: env.t('newDaily') + placeHolder: window.env.t('newDaily') }, { - header: env.t('todos'), + header: window.env.t('todos'), type: 'todo', - placeHolder: env.t('newTodo') + placeHolder: window.env.t('newTodo') }, { - header: env.t('rewards'), + header: window.env.t('rewards'), type: 'reward', - placeHolder: env.t('newReward') + placeHolder: window.env.t('newReward') } ]; diff --git a/public/js/env.js b/public/js/env.js new file mode 100644 index 0000000000..8a6611cd90 --- /dev/null +++ b/public/js/env.js @@ -0,0 +1,20 @@ +"use strict"; + +window.env = window.env || {}; //FIX tests + +// If Moment.js is loaded, +if(window.moment && window.env.language && window.env.language.momentLang && window.env.language.momentLangCode){ + var head = document.getElementsByTagName('head')[0]; + var script = document.createElement('script'); + script.type = 'text/javascript'; + script.text = window.env.language.momentLang; + head.appendChild(script); + window.moment.lang(window.env.language.momentLangCode); +} + +window.env.t = function(stringName, vars){ + var string = window.env.translations[stringName]; + if(!string) return window._.template(window.env.translations.stringNotFound, {string: stringName}); + + return vars === undefined ? string : window._.template(string, vars); +} \ No newline at end of file diff --git a/public/js/services/authServices.js b/public/js/services/authServices.js index d1667c959a..9e55d7af32 100644 --- a/public/js/services/authServices.js +++ b/public/js/services/authServices.js @@ -32,7 +32,7 @@ factory('Facebook', $http.post(API_URL + '/api/v2/user/auth/facebook', data).success(function(data, status, headers, config) { User.authenticate(data.id, data.token, function(err) { if (!err) { - alert('Login successful!'); + alert(window.env.t('loginSuccess')); $location.path("/habit"); } }); diff --git a/public/js/services/groupServices.js b/public/js/services/groupServices.js index 1f72b4a648..db33d73de9 100644 --- a/public/js/services/groupServices.js +++ b/public/js/services/groupServices.js @@ -5,8 +5,8 @@ */ angular.module('groupServices', ['ngResource']). - factory('Groups', ['API_URL', '$resource', '$q', - function(API_URL, $resource, $q) { + factory('Groups', ['API_URL', '$resource', '$q', '$http', 'User', + function(API_URL, $resource, $q, $http, User) { var Group = $resource(API_URL + '/api/v2/groups/:gid', {gid:'@_id', messageId: '@_messageId'}, { @@ -44,6 +44,12 @@ angular.module('groupServices', ['ngResource']). return tavern; }, + // On enter, set chat message to "seen" + seenMessage: function(gid){ + $http.post('/api/v2/groups/'+gid+'/chat/seen'); + if (User.user.newMessages) User.user.newMessages[gid] = false; + }, + Group: Group } } diff --git a/public/js/services/guideServices.js b/public/js/services/guideServices.js index 32df74a401..98879e040b 100644 --- a/public/js/services/guideServices.js +++ b/public/js/services/guideServices.js @@ -18,58 +18,58 @@ angular.module('guideServices', []). var tourSteps = [ { element: ".main-herobox", - title: "Welcome to HabitRPG", - content: "Welcome to HabitRPG, a habit-tracker which treats your goals like a Role Playing Game. I'm Justin, your guide!", + title: window.env.t('welcomeHabit'), + content: window.env.t('welcomeHabitT1') + " Justin, " + window.env.t('welcomeHabitT2'), }, { element: ".main-herobox", - title: "Your Avatar", - content: "This is your avatar. It represents you in the world of Habitica. As you accomplish goals, your avatar will gain levels, earn gold, and equip itself for further challenges ahead.", + title: window.env.t('yourAvatar'), + content: window.env.t('yourAvatarText'), }, { element: ".main-herobox", - title: "Avatar Customization", - content: "You can customize your avatar by clicking anywhere in this box. Change your body type, hair color, skin color, and more from this menu. You can also find a number of HabitRPG's exciting social features by clicking through tabs on the customization page.", + title: window.env.t('avatarCustom'), + content: window.env.t('avatarCustomText'), }, { element: "#bars", - title: "Hit Points", - content: "The red bar tracks your avatar's health points. Whenever you fail to meet a goal, you take damage and lose health. If your health bar reaches zero, you die. Dying results in the loss of one level, all your gold, and a piece of equipment.", + title: window.env.t('hitPoints'), + content: window.env.t('hitPointsText'), }, { element: "#bars", - title: "Experience Points", - content: "The yellow bar tracks your avatar's experience points. Whenever you succeed in achieving a goal, you gain both gold and experience. When your experience bar maxes out, you gain a level. Gaining levels is how you unlock new and exciting features on HabitRPG.", + title: window.env.t('expPoints'), + content: window.env.t('expPointsText'), }, { element: "ul.habits", - title: "Types of Goals", - content: "HabitRPG allows you to track your goals in three different ways. These goals are categorized in columns as Habits, Dailies, or To-Dos.", + title: window.env.t('typeGoals'), + content: window.env.t('typeGoalsText'), placement: "top" }, { element: "ul.habits", - title: "Habits", - content: "Habits are goals that you constantly track. They can be given plus or minus values, allowing you to gain experience and gold for good habits or lose health for bad ones.", + title: window.env.t('habits'), + content: window.env.t('tourHabits'), placement: "top" }, { element: "ul.dailys", - title: "Dailies", - content: "Dailies are goals that you want to complete once a day. Checking off a daily reaps experience and gold. Failing to check off your daily before the day resets results in a loss of health. You can change your day start settings from the options menu.", + title: window.env.t('dailies'), + content: window.env.t('tourDailies'), placement: "top" }, { element: "ul.todos", - title: "To-Dos", - content: "To-Dos are one-off goals that you can get to eventually. While it is possible to set a deadline on a to-do, they are not required. To-Dos make for a quick and easy way to gain experience.", + title: window.env.t('todos'), + content: window.env.t('tourTodos'), placement: "top" }, { element: "ul.main-list.rewards", - title: "Rewards", - content: "All that gold you earned will allow you to reward yourself with either custom or in-game prizes. Buy them liberally – rewarding yourself is integral in forming good habits.", + title: window.env.t('rewards'), + content: window.env.t('tourRewards'), placement: "top" }, { element: "ul.habits li:first-child", - title: "Hover over comments", - content: "You can add comments to your tasks by clicking the edit icon. Hover over each task's comment for more details about how HabitRPG works. When you're ready to get started, you can delete the existing tasks and add your own.", + title: window.env.t('hoverOver'), + content: window.env.t('hoverOverText'), placement: "right" }, { element: "ul.habits li:first-child", - title: "Unlock New Features", - content: "That's all you need to know for now, but I'll be back as you level up to let you know about new features you've unlocked. Each new feature will give you more incentives to accomplish your goals. Find out more at the HabitRPG Wiki or let yourself be surprised. Good luck!", + title: window.env.t('unlockFeatures'), + content: window.env.t('unlockFeaturesT1') + " " + window.env.t('habitWiki') + " " + window.env.t('unlockFeaturesT2'), placement: "right" } ]; @@ -96,7 +96,7 @@ angular.module('guideServices', []). var showPopover = function(selector, title, html, placement) { if (!placement) placement = 'bottom'; $(selector).popover('destroy'); - var button = ""; + var button = ""; html = "
" + html + '
' + button + '
'; $(selector).popover({ title: title, @@ -109,19 +109,19 @@ angular.module('guideServices', []). $rootScope.$watch('user.flags.customizationsNotification', function(after, before) { if (alreadyShown(before, after)) return; - showPopover('.main-herobox', 'Customize Your Avatar', "Click your avatar to customize your appearance.", 'bottom'); + showPopover('.main-herobox', window.env.t('customAvatar'), window.env.t('customAvatarText'), 'bottom'); }); $rootScope.$watch('user.flags.itemsEnabled', function(after, before) { if (alreadyShown(before, after)) return; - var html = "Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information."; - showPopover('div.rewards', 'Item Store Unlocked', html, 'left'); + var html = window.env.t('storeUnlockedText'); + showPopover('div.rewards', window.env.t('storeUnlocked'), html, 'left'); }); $rootScope.$watch('user.flags.partyEnabled', function(after, before) { if (alreadyShown(before, after)) return; - var html = "Be social, join a party and play Habit with your friends! You'll be better at your habits with accountability partners. Click User -> Options -> Party, and follow the instructions. LFG anyone?"; - showPopover('.user-menu', 'Party System', html, 'bottom'); + var html = window.env.t('partySysText'); + showPopover('.user-menu', window.env.t('partySys'), html, 'bottom'); }); $rootScope.$watch('user.flags.dropsEnabled', function(after, before) { @@ -130,20 +130,12 @@ angular.module('guideServices', []). if (!eggs) { eggs['Wolf'] = 1; // This is also set on the server } - $rootScope.modals.dropsEnabled = true; - }); - - $rootScope.$watch('user.items.pets', function(after, before) { - if (User.user.achievements && User.user.achievements.beastMaster) return; - if (before >= 90) { - User.set({'achievements.beastMaster': true}); - $('#beastmaster-achievement-modal').modal('show'); // FIXME - } + $rootScope.openModal('dropsEnabled'); }); $rootScope.$watch('user.flags.rebirthEnabled', function(after, before) { if (alreadyShown(before, after)) return; - $rootScope.modals.rebirthEnabled = true; + $rootScope.openModal('rebirthEnabled'); }); @@ -160,8 +152,8 @@ angular.module('guideServices', []). $timeout(function(){tour.goTo(0)}); }), element: '.equipment-tab', - title: "Class Gear", - content: "First: don't panic! Your old gear is in your inventory, and you're now wearing your apprentice " + User.user.stats.class + " equipment. Wearing your class's gear grants you a 50% bonus to stats. However, feel free to switch back to your old gear." + title: window.env.t('classGear'), + content: window.env.t('classGearText', {klass: User.user.stats.class}) }, { path: '/#/options/profile/stats', @@ -169,21 +161,21 @@ angular.module('guideServices', []). $timeout(function(){tour.goTo(1)}); }), element: ".allocate-stats", - title: "Stats", - content: "These are your class's stats, they affect the game-play. Each time you level up, you get one point to allocate to particular stat. Hover over each stat for more information.", + title: window.env.t('stats'), + content: window.env.t('classStats'), }, { element: ".auto-allocate", - title: "Auto Allocate", + title: window.env.t('autoAllocate'), placement: 'left', - content: "If 'automatic allocation' is checked, your avatar gains stats automatically based on your tasks' attributes, which you can find in TASK > Edit > Advanced > Attributes. Eg, if you hit the gym often, and your 'Gym' Daily is set to 'Physical', you'll gain STR automatically.", + content: window.env.t('autoAllocateText'), }, { element: ".meter.mana", - title: "Spells", - content: "You can now unlock class-specific spells. You'll see your first at level 11. Your mana replenishes 10 points per day, plus 1 point per completed To-Do." + title: window.env.t('spells'), + content: window.env.t('spellsText') + " " + window.env.t('toDo') + "." }, { orphan: true, - title: "Read More", - content: "For more information on the class-system, see Wikia." + title: window.env.t('readMore'), + content: window.env.t('moreClass') + " Wikia." } ]; _.each(tourSteps, function(step){ diff --git a/public/js/services/notificationServices.js b/public/js/services/notificationServices.js index 7b4a48c817..c67770e8f0 100644 --- a/public/js/services/notificationServices.js +++ b/public/js/services/notificationServices.js @@ -6,11 +6,11 @@ angular.module("notificationServices", []) function growl(html, type) { $.bootstrapGrowl(html, { ele: '#notification-area', - type: type, //(null, 'text', 'error', 'success', 'gp', 'xp', 'hp', 'lvl', 'death', 'mp', 'crit') + type: type || 'warning', //('info', 'text', 'warning', 'success', 'gp', 'xp', 'hp', 'lvl', 'death', 'mp', 'crit') top_offset: 20, align: 'right', //('left', 'right', or 'center') width: 250, //(integer, or 'auto') - delay: 7000, + delay: (type=='error') ? 0 : 7000, allow_dismiss: true, stackup_spacing: 10 // spacing between consecutive stacecked growls. }); @@ -25,11 +25,11 @@ angular.module("notificationServices", []) gold = Math.floor(absolute); silver = Math.floor((absolute - gold) * 100); if (gold && silver > 0) { - return "" + gold + " " + silver + " "; + return "" + gold + " " + silver + " "; } else if (gold > 0) { - return "" + gold + " "; + return "" + gold + " "; } else if (silver > 0) { - return "" + silver + " "; + return "" + silver + " "; } }; @@ -45,11 +45,11 @@ angular.module("notificationServices", []) coins: coins, hp: function(val) { // don't show notifications if user dead - growl(" " + sign(val) + " " + round(val) + " HP", 'hp'); + growl("  " + sign(val) + " " + round(val) + " " + window.env.t('hp'), 'hp'); }, exp: function(val) { if (val < -50) return; // don't show when they level up (resetting their exp) - growl(" " + sign(val) + " " + round(val) + " XP", 'xp'); + growl("  " + sign(val) + " " + round(val) + " " + window.env.t('xp'), 'xp'); }, gp: function(val, bonus) { growl(sign(val) + " " + coins(val - bonus), 'gp'); @@ -58,22 +58,19 @@ angular.module("notificationServices", []) growl(val); }, lvl: function(){ - growl(' Level Up!', 'lvl'); - }, - death: function(){ - growl(" Respawn!", "death"); + growl(' ' + window.env.t('levelUp'), 'lvl'); }, error: function(error){ - growl(" " + error, "error"); + growl("  " + error, "danger"); }, mp: function(val) { - growl(" " + sign(val) + " " + round(val) + " MP", 'mp'); + growl("  " + sign(val) + " " + round(val) + " " + window.env.t('mp'), 'mp'); }, crit: function(val) { - growl(" Critical Hit! Bonus: " + Math.round(val) + "%", 'crit'); + growl(" " + window.env.t('critBonus') + Math.round(val) + "%", 'crit'); }, drop: function(val) { - growl(" " + val, 'drop'); + growl("  " + val, 'drop'); } }; } diff --git a/public/js/static.js b/public/js/static.js index d0b5683b76..84894ec5ab 100644 --- a/public/js/static.js +++ b/public/js/static.js @@ -5,3 +5,22 @@ window.habitrpgStatic = angular.module('habitrpgStatic', ['notificationServices' .constant("STORAGE_USER_ID", 'habitrpg-user') .constant("STORAGE_SETTINGS_ID", 'habit-mobile-settings') .constant("MOBILE_APP", false) + +habitrpgStatic.controller("PlansCtrl", ['$rootScope', + function($rootScope) { + $rootScope.clickContact = function(){ + window.ga && ga('send', 'event', 'button', 'click', 'Contact Us (Plans)'); + } + } +]) +.controller('AboutCtrl',[function(){ + $(document).ready(function(){ + $('a.gallery').colorbox({ + maxWidth: '90%', + maxHeight: '80%', + transition: 'none', + scalePhotos:true + //maxHeight: '70%' + }); + }); +}]) diff --git a/public/manifest.json b/public/manifest.json index 2d616d2a19..85e39cd970 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,7 +1,7 @@ { "app": { "js": [ - "bower_components/jquery/jquery.js", + "bower_components/jquery/dist/jquery.min.js", "bower_components/jquery.cookie/jquery.cookie.js", "bower_components/bootstrap-growl/jquery.bootstrap-growl.js", "bower_components/bootstrap-tour/build/js/bootstrap-tour.js", @@ -10,7 +10,6 @@ "bower_components/marked/lib/marked.js", "bower_components/angular-ui-router/release/angular-ui-router.js", "bower_components/angular-resource/angular-resource.min.js", - "bower_components/angular-ui/build/angular-ui.js", "bower_components/angular-ui-utils/ui-utils.min.js", "bower_components/angular-loading-bar/build/loading-bar.js", "bower_components/Angular-At-Directive/src/at.js", @@ -21,10 +20,11 @@ "bower_components/ngInfiniteScroll/ng-infinite-scroll.js", "bower_components/select2/select2.js", "bower_components/angular-ui-select2/src/select2.js", + "bower_components/JavaScriptButtons/dist/paypal-button.min.js", - "bower_components/bootstrap/docs/assets/js/bootstrap.js", "bower_components/angular-bootstrap/ui-bootstrap.js", "bower_components/angular-bootstrap/ui-bootstrap-tpls.js", + "bower_components/bootstrap/dist/js/bootstrap.js", "bower_components/jquery-ui/ui/minified/jquery.ui.core.min.js", "bower_components/jquery-ui/ui/minified/jquery.ui.widget.min.js", @@ -33,6 +33,8 @@ "bower_components/habitrpg-shared/dist/habitrpg-shared.js", + "js/env.js", + "js/app.js", "js/services/sharedServices.js", "js/services/authServices.js", @@ -63,34 +65,33 @@ "js/controllers/hallCtrl.js" ], "css": [ - "bower_components/bootstrap/docs/assets/css/bootstrap.css", + "bower_components/bootstrap/dist/css/bootstrap.css", "app.css", "bower_components/habitrpg-shared/dist/habitrpg-shared.css" ] }, "static": { "js": [ - "bower_components/jquery/jquery.js", + "bower_components/jquery/dist/jquery.min.js", "bower_components/habitrpg-shared/dist/habitrpg-shared.js", "bower_components/angular/angular.js", - - "bower_components/bootstrap/docs/assets/js/bootstrap.js", - "bower_components/angular-ui/build/angular-ui.js", "bower_components/angular-bootstrap/ui-bootstrap.js", "bower_components/angular-bootstrap/ui-bootstrap-tpls.js", + "bower_components/bootstrap/dist/js/bootstrap.js", + "bower_components/jquery-colorbox/jquery.colorbox-min.js", "bower_components/angular-loading-bar/build/loading-bar.js", + "js/env.js", "js/static.js", "js/services/notificationServices.js", "bower_components/habitrpg-shared/script/userServices.js", - "js/controllers/authCtrl.js" + "js/controllers/authCtrl.js", + "js/controllers/footerCtrl.js" ], "css": [ - "bower_components/bootstrap/docs/assets/css/bootstrap.css", - "bower_components/bootstrap/docs/assets/css/bootstrap-responsive.css", - "bower_components/bootstrap/docs/assets/css/docs.css", - + "bower_components/bootstrap/dist/css/bootstrap.css", + "bower_components/jquery-colorbox/example1/colorbox.css", "static.css" ] } diff --git a/public/marketing/android_iphone.png b/public/marketing/android_iphone.png new file mode 100644 index 0000000000..3256b4a668 Binary files /dev/null and b/public/marketing/android_iphone.png differ diff --git a/public/marketing/animals.png b/public/marketing/animals.png new file mode 100644 index 0000000000..ecae556c6e Binary files /dev/null and b/public/marketing/animals.png differ diff --git a/public/marketing/challenge.png b/public/marketing/challenge.png new file mode 100644 index 0000000000..1b83b16b0f Binary files /dev/null and b/public/marketing/challenge.png differ diff --git a/public/marketing/drops.png b/public/marketing/drops.png new file mode 100644 index 0000000000..2324e26eac Binary files /dev/null and b/public/marketing/drops.png differ diff --git a/public/marketing/education.png b/public/marketing/education.png new file mode 100644 index 0000000000..b8c330419f Binary files /dev/null and b/public/marketing/education.png differ diff --git a/public/marketing/gear.png b/public/marketing/gear.png new file mode 100644 index 0000000000..ef6bad6beb Binary files /dev/null and b/public/marketing/gear.png differ diff --git a/public/marketing/guild.png b/public/marketing/guild.png new file mode 100644 index 0000000000..bbd66b3020 Binary files /dev/null and b/public/marketing/guild.png differ diff --git a/public/marketing/guild_small.png b/public/marketing/guild_small.png new file mode 100644 index 0000000000..28ab8fbea3 Binary files /dev/null and b/public/marketing/guild_small.png differ diff --git a/public/marketing/integration.png b/public/marketing/integration.png new file mode 100644 index 0000000000..290c5dc302 Binary files /dev/null and b/public/marketing/integration.png differ diff --git a/public/marketing/lefnire.png b/public/marketing/lefnire.png new file mode 100644 index 0000000000..657dc1eaf5 Binary files /dev/null and b/public/marketing/lefnire.png differ diff --git a/public/marketing/social_competitve.png b/public/marketing/social_competitve.png new file mode 100644 index 0000000000..3243a9d762 Binary files /dev/null and b/public/marketing/social_competitve.png differ diff --git a/public/marketing/wellness.png b/public/marketing/wellness.png new file mode 100644 index 0000000000..c91d295fc2 Binary files /dev/null and b/public/marketing/wellness.png differ diff --git a/src/controllers/auth.js b/src/controllers/auth.js index ab141c268e..c4d1541eaa 100644 --- a/src/controllers/auth.js +++ b/src/controllers/auth.js @@ -8,6 +8,7 @@ var async = require('async'); var utils = require('../utils'); var nconf = require('nconf'); var User = require('../models/user').model; +var ga = require('./../utils').ga; var api = module.exports; @@ -39,7 +40,7 @@ api.authWithSession = function(req, res, next) { //[todo] there is probably a mo if (!(req.session && req.session.userId)) return res.json(401, NO_SESSION_FOUND); User.findOne({_id: uid}, function(err, user) { - if (err) return res.json(500, {err: err}); + if (err) return next(err); if (_.isEmpty(user)) return res.json(401, NO_USER_FOUND); res.locals.user = user; next(); @@ -88,6 +89,7 @@ api.registerUser = function(req, res, next) { }; user = new User(newUser); user.save(cb); + ga.event('register', 'Local').send() } ], function(err, saved) { if (err) { @@ -107,15 +109,15 @@ api.loginLocal = function(req, res, next) { var password = req.body.password; if (!(username && password)) return res.json(401, {err:'Missing :username or :password in request body, please provide both'}); User.findOne({'auth.local.username': username}, function(err, user){ - if (err) return res.json(500,{err:err}); - if (!user) return res.json(401, {err:"Username '" + username + "' not found. Usernames are case-sensitive, click 'Forgot Password' if you can't remember the capitalization."}); + if (err) return next(err); + if (!user) return res.json(401, {err:"Username or password incorrect. Click 'Forgot Password' for help with either. (Note: usernames are case-sensitive)"}); // We needed the whole user object first so we can get his salt to encrypt password comparison User.findOne({ 'auth.local.username': username, 'auth.local.hashed_password': utils.encryptPassword(password, user.auth.local.salt) }, function(err, user){ - if (err) return res.json(500,{err:err}); - if (!user) return res.json(401,{err:'Incorrect password'}); + if (err) return next(err); + if (!user) return res.json(401,{err:"Username or password incorrect. Click 'Forgot Password' for help with either. (Note: usernames are case-sensitive)"}); res.json({id: user._id,token: user.apiToken}); }); }); @@ -164,7 +166,7 @@ api.resetPassword = function(req, res, next){ hashed_password = utils.encryptPassword(newPassword, salt); User.findOne({'auth.local.email':email}, function(err, user){ - if (err) return res.json(500,{err:err}); + if (err) return next(err); if (!user) return res.send(500, {err:"Couldn't find a user registered for email " + email}); user.auth.local.salt = salt; user.auth.local.hashed_password = hashed_password; @@ -187,18 +189,18 @@ api.changePassword = function(req, res, next) { confirmNewPassword = req.body.confirmNewPassword; if (newPassword != confirmNewPassword) - return res.json(500, {err: "Password & Confirm don't match"}); + return res.json(401, {err: "Password & Confirm don't match"}); var salt = user.auth.local.salt, hashed_old_password = utils.encryptPassword(oldPassword, salt), hashed_new_password = utils.encryptPassword(newPassword, salt); if (hashed_old_password !== user.auth.local.hashed_password) - return res.json(500, {err:"Old password doesn't match"}); + return res.json(401, {err:"Old password doesn't match"}); user.auth.local.hashed_password = hashed_new_password; user.save(function(err, saved){ - if (err) res.json(500,{err:err}); + if (err) next(err); res.send(200); }) } @@ -250,8 +252,7 @@ api.setupPassport = function(router) { } }); user.save(cb); - - + ga.event('register', 'Facebook').send() } ], function(err, saved){ if (err) return res.redirect('/static/front?err=' + err); diff --git a/src/controllers/challenges.js b/src/controllers/challenges.js index c0be3cc8e6..81f4e4e2c7 100644 --- a/src/controllers/challenges.js +++ b/src/controllers/challenges.js @@ -7,6 +7,7 @@ var shared = require('habitrpg-shared'); var User = require('./../models/user').model; var Group = require('./../models/group').model; var Challenge = require('./../models/challenge').model; +var logging = require('./../logging'); var csv = require('express-csv'); var api = module.exports; @@ -214,7 +215,7 @@ api.update = function(req, res){ // Compare whether any changes have been made to tasks. If so, we'll want to sync those changes to subscribers if (before.isOutdated(req.body)) { User.find({_id: {$in: saved.members}}, function(err, users){ - console.log('Challenge updated, sync to subscribers'); + logging.info('Challenge updated, sync to subscribers'); if (err) throw err; _.each(users, function(user){ saved.syncToUser(user); diff --git a/src/controllers/groups.js b/src/controllers/groups.js index cfdaa2d179..4bdde2b83a 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -249,6 +249,14 @@ api.deleteChatMessage = function(req, res){ }); } +api.seenMessage = function(req,res,next){ + // Skip the auth step, we want this to be fast. If !found with uuid/token, then it just doesn't save + var update = {$set:{}}; + update['$set']['newMessages.'+req.params.gid+'.value'] = false; + User.update({_id:req.headers['x-api-user'], apiToken:req.headers['x-api-key']},update).exec(); + res.send(200); +} + api.likeChatMessage = function(req, res, next) { var user = res.locals.user; var group = res.locals.group; diff --git a/src/controllers/hall.js b/src/controllers/hall.js index 068efdfc3e..0edbd9e867 100644 --- a/src/controllers/hall.js +++ b/src/controllers/hall.js @@ -13,7 +13,7 @@ api.ensureAdmin = function(req, res, next) { } api.getHeroes = function(req,res,next) { - User.find({'contributor.level':{$ne:null}})// {$exists:true} causes terrible performance http://goo.gl/GCxzC9 + User.find({'contributor.level':{$gt:0}}) .select('contributor backer balance profile.name') .sort('-contributor.level') .exec(function(err, users){ diff --git a/src/controllers/user.js b/src/controllers/user.js index f0f1f72cbe..b8979639c4 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -10,8 +10,10 @@ var validator = require('validator'); var check = validator.check; var sanitize = validator.sanitize; var User = require('./../models/user').model; +var ga = require('./../utils').ga; var Group = require('./../models/group').model; var Challenge = require('./../models/challenge').model; +var logging = require('./../logging'); var acceptablePUTPaths; var api = module.exports; @@ -33,7 +35,7 @@ api.getContent = function(req, res, next) { --------------- */ -findTask = function(req, res) { +findTask = function(req, res, next) { return task = res.locals.user.tasks[req.params.id]; }; @@ -80,17 +82,32 @@ api.score = function(req, res, next) { var delta = user.ops.score({params:{id:task.id, direction:direction}}); user.save(function(err,saved){ - if (err) return res.json(500, {err: err}); + if (err) return next(err); // TODO this should be return {_v,task,stats,_tmp}, instead of merging everything togther at top-level response // However, this is the most commonly used API route, and changing it will mess with all 3rd party consumers. Bad idea :( res.json(200, _.extend({ delta: delta, _tmp: user._tmp }, saved.toJSON().stats)); - }); - // if it's a challenge task, sync the score - user.syncScoreToChallenge(task, delta); + // If it's a challenge task, sync the score. Do it in the background, we've already sent down a response + // and the user doesn't care what happens back there + if (!task.challenge || !task.challenge.id || task.challenge.broken) return; + if (task.type == 'reward') return; // we don't want to update the reward GP cost + Challenge.findById(task.challenge.id, 'habits dailys todos rewards', function(err, chal){ + if (err) return next(err); + if (!chal) { + task.challenge.broken = 'CHALLENGE_DELETED'; + return user.save(); + } + var t = chal.tasks[task.id]; + if (!t) return chal.syncToUser(user); // this task was removed from the challenge, notify user + t.value += delta; + if (t.type == 'habit' || t.type == 'daily') + t.history.push({value: t.value, date: +new Date}); + chal.save(); + }); + }); }; /** @@ -192,7 +209,7 @@ api.update = function(req, res, next) { }); user.save(function(err) { if (!_.isEmpty(errors)) return res.json(401, {err: errors}); - if (err) return res.json(500, {err: err}); + if (err) return next(err); res.json(200, user); }); }; @@ -232,9 +249,9 @@ api.cron = function(req, res, next) { // api.reroll // Shared.ops // api.reset // Shared.ops -api['delete'] = function(req, res) { +api['delete'] = function(req, res, next) { res.locals.user.remove(function(err){ - if (err) return res.json(500,{err:err}); + if (err) return next(err); res.send(200); }) } @@ -253,11 +270,11 @@ api['delete'] = function(req, res) { ------------------------------------------------------------------------ */ -api.addTenGems = function(req, res) { +api.addTenGems = function(req, res, next) { var user = res.locals.user; user.balance += 2.5; user.save(function(err){ - if (err) return res.json(500,{err:err}); + if (err) return next(err); res.send(204); }) } @@ -267,7 +284,7 @@ api.addTenGems = function(req, res) { /* Setup Stripe response when posting payment */ -api.buyGems = function(req, res) { +api.buyGems = function(req, res, next) { var api_key = nconf.get('STRIPE_API_KEY'); var stripe = require("stripe")(api_key); var token = req.body.id; @@ -300,8 +317,12 @@ api.buyGems = function(req, res) { dateUpdated: new Date, gemsBought: 0 }; + ga.event('subscribe', 'Stripe').send() + ga.transaction(response.id, 5).item(5, 1, "stripe-subscription", "Subscription > Stripe").send() } else { user.balance += 5; + ga.event('checkout', 'Stripe').send() + ga.transaction(response.id, 5).item(5, 1, "stripe-checkout", "Gems > Stripe").send() } user.save(cb); } @@ -311,7 +332,7 @@ api.buyGems = function(req, res) { }); }; -api.cancelSubscription = function(req, res) { +api.cancelSubscription = function(req, res, next) { var api_key = nconf.get('STRIPE_API_KEY'); var stripe = require("stripe")(api_key); var user = res.locals.user; @@ -330,6 +351,7 @@ api.cancelSubscription = function(req, res) { ], function(err, saved){ if (err) return res.send(500, err.toString()); // don't json this, let toString() handle errors res.send(200, saved); + ga.event('unsubscribe', 'Stripe').send() }); } @@ -349,7 +371,9 @@ api.buyGemsPaypalIPN = function(req, res, next) { user.balance += 5; //user.purchased.ads = true; user.save(); - console.log('PayPal transaction completed and user updated'); + logging.info('PayPal transaction completed and user updated'); + ga.event('checkout', 'PayPal').send() + ga.transaction(req.body.txn_id, 5).item(5, 1, "paypal-checkout", "Gems > PayPal").send() }); } }); @@ -369,7 +393,7 @@ api.buyGemsPaypalIPN = function(req, res, next) { Spells ------------------------------------------------------------------------ */ -api.cast = function(req, res) { +api.cast = function(req, res, next) { var user = res.locals.user; var targetType = req.query.targetType; var targetId = req.query.targetId; @@ -380,7 +404,7 @@ api.cast = function(req, res) { var done = function(){ var err = arguments[0]; var saved = _.size(arguments == 3) ? arguments[2] : arguments[1]; - if (err) return res.json(500, {err:err}); + if (err) return next(err); res.json(saved); } @@ -400,7 +424,7 @@ api.cast = function(req, res) { case 'user': async.waterfall([ function(cb){ - Group.findOne({type: 'party', members: {'$in': [user._id]}}).populate('members', 'profile.name stats achievements').exec(cb); + Group.findOne({type: 'party', members: {'$in': [user._id]}}).populate('members', 'profile.name stats achievements items.special').exec(cb); }, function(group, cb) { // Solo player? let's just create a faux group for simpler code @@ -444,15 +468,15 @@ _.each(shared.wrap({}).ops, function(op,k){ res.locals.user.ops[k](req,function(err, response){ // If we want to send something other than 500, pass err as {code: 200, message: "Not enough GP"} if (err) { - if (!err.code) return res.json(500,{err:err}); + if (!err.code) return next(err); if (err.code >= 400) return res.json(err.code,{err:err.message}); // In the case of 200s, they're friendly alert messages like "You're pet has hatched!" - still send the op } res.locals.user.save(function(err){ - if (err) return res.json(500,{err:err}); + if (err) return next(err); res.json(200,response); }) - }) + }, ga); } } }) @@ -472,28 +496,33 @@ api.batchUpdate = function(req, res, next) { var oldSend = res.send; var oldJson = res.json; - var callOp = function(_req, cb) { - res.send = res.json = function(code, data) { - if (_.isNumber(code) && code >= 500) - return cb(code+": "+ (data.message ? data.message : data.err ? data.err : JSON.stringify(data))); - return cb(); - }; - api[_req.op](_req, res); - }; + // Stash user.save, we'll queue the save op till the end (so we don't overload the server) + var oldSave = user.save; + user.save = function(cb){cb(null,user)} - res.locals.ops = []; // Setup the array of functions we're going to call in parallel with async - var ops = _.transform(req.body, function(result, _req) { - if (!_.isEmpty(_req)) { - result.push(function(cb) { - res.locals.ops.push(_req); - callOp(_req, cb); - }); - } + res.locals.ops = []; + var ops = _.transform(req.body, function(m,_req){ + if (_.isEmpty(_req)) return; + m.push(function() { + var cb = arguments[arguments.length-1]; + res.locals.ops.push(_req); + res.send = res.json = function(code, data) { + if (_.isNumber(code) && code >= 500) + return cb(code+": "+ (data.message ? data.message : data.err ? data.err : JSON.stringify(data))); + return cb(); + }; + api[_req.op](_req, res, cb); + }); + }) + // Finally, save user at the end + .concat(function(){ + user.save = oldSave; + user.save(arguments[arguments.length-1]); }); // call all the operations, then return the user object to the requester - async.series(ops, function(err) { + async.waterfall(ops, function(err,user) { res.json = oldJson; res.send = oldSend; if (err) return next(err); @@ -518,4 +547,4 @@ api.batchUpdate = function(req, res, next) { res.json(200, {_v: response._v}); } }); -}; \ No newline at end of file +}; diff --git a/src/logging.js b/src/logging.js new file mode 100644 index 0000000000..f8e56da7cf --- /dev/null +++ b/src/logging.js @@ -0,0 +1,49 @@ +var nconf = require('nconf'); +var winston = require('winston'); +require('winston-mail').Mail; +require('winston-newrelic'); + +var logger; + +if (logger == null) { + logger = new (winston.Logger)({}); + if (nconf.get('NODE_ENV') == 'production') { + logger.add(winston.transports.newrelic, {}); + logger.add(winston.transports.Mail, { + to: nconf.get('ADMIN_EMAIL') || nconf.get('SMTP_USER'), + from: "HabitRPG <" + nconf.get('SMTP_USER') + ">", + subject: "HabitRPG Error", + host: nconf.get('SMTP_HOST'), + port: nconf.get('SMTP_PORT'), + tls: nconf.get('SMTP_TLS'), + username: nconf.get('SMTP_USER'), + password: nconf.get('SMTP_PASS'), + level: 'error' + }); + } else { + logger.add(winston.transports.Console, {colorize:true}); + logger.add(winston.transports.File, {filename: 'habitrpg.log'}); + } +} + +// A custom log function that wraps Winston. Makes it easy to instrument code +// and still possible to replace Winston in the future. +module.exports.log = function(/* variable args */) { + if (logger) + logger.log.apply(logger, arguments); +}; + +module.exports.info = function(/* variable args */) { + if (logger) + logger.info.apply(logger, arguments); +}; + +module.exports.warn = function(/* variable args */) { + if (logger) + logger.warn.apply(logger, arguments); +}; + +module.exports.error = function(/* variable args */) { + if (logger) + logger.error.apply(logger, arguments); +}; diff --git a/src/middleware.js b/src/middleware.js index 19ada70aaf..4894b586d1 100644 --- a/src/middleware.js +++ b/src/middleware.js @@ -4,24 +4,57 @@ 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'); module.exports.apiThrottle = function(app) { + if (nconf.get('NODE_ENV') !== 'production') return; app.use(limiter({ end:false, catagories:{ normal: { // 2 req/s, but split as minutes - totalRequests: 120, + totalRequests: 80, every: 60000 } } })).use(function(req,res,next){ - //console.log(res.ratelimit); + //logging.info(res.ratelimit); if (res.ratelimit.exceeded) return res.json(429,{err:'Rate limit exceeded'}); next(); }); } +module.exports.domainMiddleware = function(server,mongoose) { + 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); + 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); +} + + module.exports.forceSSL = function(req, res, next){ var baseUrl = nconf.get("BASE_URL"); // Note x-forwarded-proto is used by Heroku & nginx, you'll have to do something different if you're not using those @@ -89,22 +122,21 @@ var getManifestFiles = function(page){ if(!files) throw new Error("Page not found!"); - var css = ''; - - _.each(files.css, function(file){ - css += ''; - }); + var code = ''; if(nconf.get('NODE_ENV') === 'production'){ - return css + ''; + code += ''; + code += ''; }else{ - var results = css; - _.each(files.js, function(file){ - results += ''; + _.each(files.css, function(file){ + code += ''; + }); + _.each(files.js, function(file){ + code += ''; }); - return results; } - + + return code; } // Translations @@ -112,12 +144,12 @@ var getManifestFiles = function(page){ var translations = {}; var loadTranslations = function(locale){ - var files = require(path.join(__dirname, "/../node_modules/habitrpg-shared/locales/", locale, 'app.json')).files; + var files = fs.readdirSync(path.join(__dirname, "/../node_modules/habitrpg-shared/locales/", locale)); translations[locale] = {}; _.each(files, function(file){ _.merge(translations[locale], require(path.join(__dirname, "/../node_modules/habitrpg-shared/locales/", locale, file))); }); -} +}; // First fetch english so we can merge with missing strings in other languages loadTranslations('en'); @@ -164,7 +196,6 @@ var getUserLanguage = function(req, callback){ var acceptable = _(req.acceptedLanguages).map(function(lang){ return lang.slice(0, 2); }).uniq().value(); - var matches = _.intersection(acceptable, langCodes); return matches.length > 0 ? matches[0] : 'en'; }; @@ -176,44 +207,52 @@ var getUserLanguage = function(req, callback){ return callback(null, _.find(avalaibleLanguages, {code: user.preferences.language})); }else{ var langCode = getFromBrowser(); - if(user && translations[langCode]){ - user.preferences.language = langCode; - user.save(); //callback? - } + // Because english is usually always avalaible as an acceptable language for the browser, + // if the user visit the page when his own language is not avalaible yet + // he'll have english set in his preferences, which is not good. + //if(user && translations[langCode]){ + //user.preferences.language = langCode; + //user.save(); //callback? + //} return callback(null, _.find(avalaibleLanguages, {code: langCode})) } }); }else{ - return callback(null, _.find(avalaibleLanguages, {code: getFromBrowser()})); + return callback(null, _.find(avalaibleLanguages, {code: getFromBrowser()})); } } module.exports.locals = function(req, res, next) { getUserLanguage(req, function(err, language){ - if(err) return res.json(500, {err: err}); + if(err) return res.json(500, {err: err}); + + 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 && momentLangs[language.code])|| undefined); - language.momentLang = (momentLangs[language.code] || undefined); - res.locals.habitrpg = { NODE_ENV: nconf.get('NODE_ENV'), BASE_URL: nconf.get('BASE_URL'), PAYPAL_MERCHANT: nconf.get('PAYPAL_MERCHANT'), + GA_ID: nconf.get("GA_ID"), IS_MOBILE: /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(req.header('User-Agent')), STRIPE_PUB_KEY: nconf.get('STRIPE_PUB_KEY'), getManifestFiles: getManifestFiles, getBuildUrl: getBuildUrl, avalaibleLanguages: avalaibleLanguages, language: language, + isStaticPage: isStaticPage, translations: translations[language.code], t: function(stringName, vars){ var string = translations[language.code][stringName]; if(!string) return _.template(translations[language.code].stringNotFound, {string: stringName}); - return vars === undefined ? string : _.template(string, vars); + return vars === undefined ? string : _.template(string, vars); }, siteVersion: siteVersion } - next(); + next(); }); -} \ No newline at end of file +} diff --git a/src/models/challenge.js b/src/models/challenge.js index 2656bebe1b..a01cea9575 100644 --- a/src/models/challenge.js +++ b/src/models/challenge.js @@ -3,7 +3,6 @@ var Schema = mongoose.Schema; var shared = require('habitrpg-shared'); var _ = require('lodash'); var TaskSchemas = require('./task'); -var Group = require('./group').model; var ChallengeSchema = new Schema({ _id: {type: String, 'default': shared.uuid}, diff --git a/src/models/group.js b/src/models/group.js index fa901dad8f..0ecc16221e 100644 --- a/src/models/group.js +++ b/src/models/group.js @@ -3,7 +3,6 @@ var Schema = mongoose.Schema; var shared = require('habitrpg-shared'); var _ = require('lodash'); var async = require('async'); -var User = require('./user').model; var GroupSchema = new Schema({ _id: {type: String, 'default': shared.uuid}, @@ -107,6 +106,17 @@ GroupSchema.methods.sendChat = function(message, user){ } group.chat.unshift(message); group.chat.splice(200); + + // Kick off chat notifications in the background. + var lastSeenUpdate = {$set:{}, $inc:{_v:1}}; + lastSeenUpdate['$set']['newMessages.'+group._id] = {name:group.name,value:true}; + if (group._id == 'habitrpg') { + // TODO For Tavern, only notify them if their name was mentioned + // var profileNames = [] // get usernames from regex of @xyz. how to handle space-delimited profile names? + // User.update({'profile.name':{$in:profileNames}},lastSeenUpdate,{multi:true}).exec(); + } else { + mongoose.model('User').update({_id:{$in:group.members, $ne: user ? user._id : ''}},lastSeenUpdate,{multi:true}).exec(); + } } var cleanQuestProgress = function(merge){ @@ -161,8 +171,6 @@ GroupSchema.methods.finishQuest = function(quest, cb) { }) var members = _.keys(group.quest.members); group.quest = {};group.markModified('quest'); - // FIXME this is TERRIBLE practice. Looks like there are circular dependencies in the models, such that `var User` at - // this point is undefined. So we get around that by loading from mongoose only once we get to this point mongoose.models.User.update({_id:{$in:members}}, updates, {multi:true}, cb); } diff --git a/src/models/user.js b/src/models/user.js index b442980d0b..9c1c5ba0cc 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -42,7 +42,8 @@ var UserSchema = new Schema({ rebirths: Number, rebirthLevel: Number, perfect: Number, - habitBirthday: Boolean + habitBirthday: Boolean, + valentine: Number }, auth: { facebook: Schema.Types.Mixed, @@ -68,7 +69,8 @@ var UserSchema = new Schema({ level: Number, // 1-7, see https://trello.com/c/wkFzONhE/277-contributor-gear admin: Boolean, text: String, // Artisan, Friend, Blacksmith, etc - contributions: String // a markdown textarea to list their contributions + links + contributions: String, // a markdown textarea to list their contributions + links + critical: String }, balance: {type: Number, 'default':0}, @@ -135,10 +137,12 @@ var UserSchema = new Schema({ }, special:{ - snowball: {type: Number, 'default': 0} + snowball: {type: Number, 'default': 0}, + valentine: Number, + valentineReceived: Array // array of strings, by sender name }, - // -------------- Animals ------------------- + // -------------- Animals ------------------- // Complex bit here. The result looks like: // pets: { // 'Wolf-Desert': 0, // 0 means does not own @@ -149,6 +153,8 @@ var UserSchema = new Schema({ _.defaults( // First transform to a 1D eggs/potions mapping _.transform(shared.content.pets, function(m,v,k){ m[k] = Number; }), + // Then add quest pets + _.transform(shared.content.questPets, function(m,v,k){ m[k] = Number; }), // Then add additional pets (backer, contributor) _.transform(shared.content.specialPets, function(m,v,k){ m[k] = Number; }) ), @@ -180,6 +186,8 @@ var UserSchema = new Schema({ mounts: _.defaults( // First transform to a 1D eggs/potions mapping _.transform(shared.content.pets, function(m,v,k){ m[k] = Boolean; }), + // Then add quest pets + _.transform(shared.content.questPets, function(m,v,k){ m[k] = Boolean; }), // Then add additional pets (backer, contributor) { 'LionCub-Ethereal': Boolean, @@ -202,9 +210,11 @@ var UserSchema = new Schema({ lastCron: {type: Date, 'default': Date.now}, + // {GROUP_ID: Boolean}, represents whether they have unseen chat messages + newMessages: {type: Schema.Types.Mixed, 'default': {}}, + party: { // id // FIXME can we use a populated doc instead of fetching party separate from user? - lastMessageSeen: String, order: {type:String, 'default':'level'}, quest: { key: String, @@ -240,7 +250,8 @@ var UserSchema = new Schema({ disableClasses: {type: Boolean, 'default': false}, newTaskEdit: {type: Boolean, 'default': false}, tagsCollapsed: {type: Boolean, 'default': false}, - advancedCollapsed: {type: Boolean, 'default': false} + advancedCollapsed: {type: Boolean, 'default': false}, + toolbarCollapsed: {type:Boolean, 'default':false} }, profile: { blurb: String, @@ -362,22 +373,6 @@ UserSchema.pre('save', function(next) { next(); }); -UserSchema.methods.syncScoreToChallenge = function(task, delta){ - if (!task.challenge || !task.challenge.id || task.challenge.broken) return; - if (task.type == 'reward') return; // we don't want to update the reward GP cost - var self = this; - Challenge.findById(task.challenge.id, function(err, chal){ - if (err) throw err; - var t = chal.tasks[task.id]; - if (!t) return chal.syncToUser(self); // this task was removed from the challenge, notify user - t.value += delta; - if (t.type == 'habit' || t.type == 'daily') { - t.history.push({value: t.value, date: +new Date}); - } - chal.save(); - }); -} - UserSchema.methods.unlink = function(options, cb) { var cid = options.cid, keep = options.keep, tid = options.tid; var self = this; diff --git a/src/routes/apiv1.js b/src/routes/apiv1.js index 53207631d2..ce09e8a24a 100644 --- a/src/routes/apiv1.js +++ b/src/routes/apiv1.js @@ -6,6 +6,7 @@ var icalendar = require('icalendar'); var api = require('./../controllers/user'); var auth = require('./../controllers/auth'); var middleware = require('../middleware'); +var logging = require('./../logging'); /* ---------- Deprecated API ------------*/ @@ -78,7 +79,7 @@ var batchUpdate = function(req, res, next) { req.body = action.data; res.send = res.json = function(code, data) { if (_.isNumber(code) && code >= 400) { - console.error({ + logging.error({ code: code, data: data }); @@ -168,4 +169,4 @@ router.get('*', deprecated); router.post('*', deprecated); router.put('*', deprecated); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index 513fb7e312..db5a0010f0 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -488,6 +488,17 @@ module.exports = (swagger, v2) -> middleware: [auth.auth, groups.attachGroup] action: groups.postChat + # placing before route below, so that if !=='seen' it goes to next() + "/groups/{gid}/chat/seen": + spec: + method: 'POST' + description: "Flag chat messages for a particular group as seen" + parameters: [ + path 'gid','Group id','string' + ] + middleware: [] + action: groups.seenMessage + "/groups/{gid}/chat/{messageId}": spec: method: 'DELETE' diff --git a/src/routes/pages.js b/src/routes/pages.js index a5ef570e2c..20902b09a3 100644 --- a/src/routes/pages.js +++ b/src/routes/pages.js @@ -18,9 +18,7 @@ router.get('/', middleware.locals, function(req, res) { // -------- Marketing -------- router.get('/static/front', middleware.locals, function(req, res) { - var env = res.locals.habitrpg; - env.isFrontPage = true; - res.render('static/front', {env: env}); + res.render('static/front', {env: res.locals.habitrpg}); }); router.get('/static/privacy', middleware.locals, function(req, res) { @@ -39,6 +37,10 @@ router.get('/static/about', middleware.locals, function(req, res) { res.render('static/about', {env: res.locals.habitrpg}); }); +router.get('/static/videos', middleware.locals, function(req, res) { + res.render('static/videos', {env: res.locals.habitrpg}); +}); + router.get('/static/contact', middleware.locals, function(req, res) { res.render('static/contact', {env: res.locals.habitrpg}); }); diff --git a/src/seed.js b/src/seed.js index becd3cf94b..111dfee1df 100644 --- a/src/seed.js +++ b/src/seed.js @@ -1,6 +1,7 @@ require('coffee-script') // for habitrpg-shared var nconf = require('nconf'); var utils = require('./utils'); +var logging = require('./logging'); utils.setupConfig(); var async = require('async'); var mongoose = require('mongoose'); @@ -15,7 +16,7 @@ async.waterfall([ Group.findById('habitrpg', cb); }, function(tavern, cb){ - console.log({tavern:tavern,cb:cb}); + logging.info({tavern:tavern,cb:cb}); if (!tavern) { tavern = new Group({ _id: 'habitrpg', @@ -31,6 +32,6 @@ async.waterfall([ } ],function(err){ if (err) throw err; - console.log("Done initializing database"); + logging.info("Done initializing database"); mongoose.disconnect(); }) diff --git a/src/server.js b/src/server.js index 8d95852ad0..bef7642207 100644 --- a/src/server.js +++ b/src/server.js @@ -4,19 +4,19 @@ var _ = require('lodash'); var nconf = require('nconf'); var utils = require('./utils'); utils.setupConfig(); - +var logging = require('./logging'); var isProd = nconf.get('NODE_ENV') === 'production'; var isDev = nconf.get('NODE_ENV') === 'development'; if (cluster.isMaster && (isDev || isProd)) { // Fork workers. - _.times(require('os').cpus().length, function(){ + _.times(_.min([require('os').cpus().length,2]), function(){ cluster.fork(); - }) + }); - cluster.on('exit', function(worker, code, signal) { + cluster.on('disconnect', function(worker, code, signal) { var w = cluster.fork(); // replace the dead worker - console.error('[%s] [master:%s] worker:%s disconnect! new worker:%s fork', new Date(), process.pid, worker.process.pid, w.process.pid); + logging.info('[%s] [master:%s] worker:%s disconnect! new worker:%s fork', new Date(), process.pid, worker.process.pid, w.process.pid); }); } else { @@ -24,7 +24,6 @@ if (cluster.isMaster && (isDev || isProd)) { var express = require("express"); var http = require("http"); var path = require("path"); - var domainMiddleware = require('domain-middleware'); var swagger = require("swagger-node-express"); var middleware = require('./middleware'); @@ -35,14 +34,18 @@ if (cluster.isMaster && (isDev || isProd)) { // ------------ MongoDB Configuration ------------ mongoose = require('mongoose'); - require('./models/user'); //load up the user schema - TODO is this necessary? - require('./models/group'); - require('./models/challenge'); - mongoose.connect(nconf.get('NODE_DB_URI'), {auto_reconnect:true}, function(err) { - if (err) throw err; - console.info('Connected with Mongoose'); + var mongooseOptions = !isProd ? {} : { + replset: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } }, + server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } } + }; + mongoose.connect(nconf.get('NODE_DB_URI'), mongooseOptions, function(err) { + if (err) throw err; + logging.info('Connected with Mongoose'); }); - + // load schemas & models + require('./models/challenge'); + require('./models/group'); + require('./models/user'); // ------------ Passport Configuration ------------ var passport = require('passport') @@ -63,7 +66,6 @@ if (cluster.isMaster && (isDev || isProd)) { done(null, obj); }); - // Use the FacebookStrategy within Passport. // Strategies in Passport require a `verify` function, which accept // credentials (in this case, an accessToken, refreshToken, and Facebook @@ -88,14 +90,9 @@ if (cluster.isMaster && (isDev || isProd)) { // ------------ Server Configuration ------------ - domainMiddleware({ - server: server, - killTimeout: 3000 - }), - app.set("port", nconf.get('PORT')); - middleware.apiThrottle(app); + app.use(middleware.domainMiddleware(server,mongoose)); if (!isProd) app.use(express.logger("dev")); app.use(express.compress()); app.set("views", __dirname + "/../views"); @@ -119,14 +116,11 @@ if (cluster.isMaster && (isDev || isProd)) { app.use(app.router); var maxAge = isProd ? 31536000000 : 0; + // Cache emojis without copying them to build, they are too many app.use(express['static'](path.join(__dirname, "/../build"), { maxAge: maxAge })); + app.use('/bower_components/habitrpg-shared/img/emoji/unicode', express['static'](path.join(__dirname, "/../public/bower_components/habitrpg-shared/img/emoji/unicode"), { maxAge: maxAge })); app.use(express['static'](path.join(__dirname, "/../public"))); - // development only - //if ("development" === app.get("env")) { - // app.use(express.errorHandler()); - //} - // Custom Directives app.use(require('./routes/pages').middleware); app.use(require('./routes/auth').middleware); @@ -134,15 +128,13 @@ if (cluster.isMaster && (isDev || isProd)) { app.use('/api/v2', v2); app.use('/api/v1', require('./routes/apiv1').middleware); app.use('/export', require('./routes/dataexport').middleware); - - app.use(utils.errorHandler); - require('./routes/apiv2.coffee')(swagger, v2); + app.use(middleware.errorHandler); server.on('request', app); server.listen(app.get("port"), function() { - return console.log("Express server listening on port " + app.get("port")); + return logging.info("Express server listening on port " + app.get("port")); }); module.exports = server; -} \ No newline at end of file +} diff --git a/src/utils.js b/src/utils.js index eacb9e7c59..b5f51ccb91 100644 --- a/src/utils.js +++ b/src/utils.js @@ -3,6 +3,8 @@ var nconf = require('nconf'); var crypto = require('crypto'); var path = require("path"); +module.exports.ga = undefined; // set Google Analytics on nconf init + module.exports.sendEmail = function(mailData) { var smtpTransport = nodemailer.createTransport("SMTP",{ service: nconf.get('SMTP_SERVICE'), @@ -12,8 +14,9 @@ module.exports.sendEmail = function(mailData) { } }); smtpTransport.sendMail(mailData, function(error, response){ - if(error) console.log(error); - else console.log("Message sent: " + response.message); + var logging = require('./logging'); + if(error) logging.error(error); + else logging.info("Message sent: " + response.message); smtpTransport.close(); // shut down the connection pool, no more messages }); } @@ -31,8 +34,6 @@ module.exports.makeSalt = function() { return crypto.randomBytes(Math.ceil(len / 2)).toString('hex').substring(0, len); } - - /** * Load nconf and define default configuration values if config.json or ENV vars are not found */ @@ -42,42 +43,10 @@ module.exports.setupConfig = function(){ //.file('defaults', path.join(path.resolve(__dirname, '../config.json.example'))) .file('user', path.join(path.resolve(__dirname, '../config.json'))); -// var agent; -// if (process.env.NODE_ENV === 'development') { -// // Follow these instructions for profiling / debugging leaks -// // * https://developers.google.com/chrome-developer-tools/docs/heap-profiling -// // * https://developers.google.com/chrome-developer-tools/docs/memory-analysis-101 -// agent = require('webkit-devtools-agent'); -// console.log("To debug memory leaks:" + -// "\n\t(1) Run `kill -SIGUSR2 " + process.pid + "`" + -// "\n\t(2) open http://c4milo.github.com/node-webkit-agent/21.0.1180.57/inspector.html?host=localhost:1337&page=0"); -// } - - if (nconf.get('NODE_ENV') === "development") { + if (nconf.get('NODE_ENV') === "development") Error.stackTraceLimit = Infinity; - } - if (nconf.get('NODE_ENV') === 'production') require('newrelic'); -}; + if (nconf.get('NODE_ENV') === 'production') + require('newrelic'); - -module.exports.errorHandler = function(err, req, res, next) { - // 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) : ""); - module.exports.sendEmail({ - from: "HabitRPG <" + nconf.get('SMTP_USER') + ">", - to: nconf.get('ADMIN_EMAIL') || nconf.get('SMTP_USER'), - subject: "HabitRPG Error", - text: stack - }); - console.error(stack); - 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); - process.exit(0); -} \ No newline at end of file + module.exports.ga = require('universal-analytics')(nconf.get('GA_ID')); +}; \ No newline at end of file diff --git a/test/api.mocha.js b/test/api.mocha.js index 9b69fd461e..3f4f20d6f2 100644 --- a/test/api.mocha.js +++ b/test/api.mocha.js @@ -253,6 +253,33 @@ describe('API', function () { }); }); + it('Challenge deleted, breaks task link', function (done) { + var itThis = this; + request.del(baseURL + "/challenges/" + challenge._id).end(function (res) { + User.findById(user._id, function(err,user){ + var len = user.dailys.length- 1, + daily = user.dailys[user.dailys.length-1]; + expect(daily.challenge.broken).to.be('CHALLENGE_DELETED'); + + // Now let's handle if challenge was deleted, but didn't get to update all the users (an error) + var unset = {$unset:{}}; + unset['$unset']['dailys.' + len + '.challenge.broken'] = 1; + User.findByIdAndUpdate(user._id,unset,function(err,user){ + expect(err).to.not.be.ok(); + expect(user.dailys[len].challenge.broken).to.not.be.ok(); + request.post(baseURL + "/user/tasks/" + daily.id + "/up").end(function (res) { + setTimeout(function(){ + User.findById(user._id,function(err,user){ + expect(user.dailys[len].challenge.broken).to.be('CHALLENGE_DELETED'); + done() + }) + }, 100); // we need to wait for challenge to update user, it's a background job for perf reasons + }) + }) + }) + }) + }); + it('Admin creates a challenge', function (done) { User.findByIdAndUpdate(_id, {$set:{'contributor.admin':true}}, function (err,_user) { expect(err).to.not.be.ok(); diff --git a/views/index.jade b/views/index.jade index 48fcae33aa..85f6c5d642 100644 --- a/views/index.jade +++ b/views/index.jade @@ -3,43 +3,34 @@ doctype 5 html(ng-app="habitrpg", ng-controller="RootCtrl", ng-class='{"applying-action":applyingAction}', ui-keyup="{27:'castCancel()'}") head title=env.t('titleIndex') - // ?v=1 needed to force refresh - link(rel='shortcut icon', href='#{env.getBuildUrl("favicon.ico")}?v=2') + link(rel='shortcut icon', href='#{env.getBuildUrl("favicon.ico")}?v=3') + + meta(charset='utf-8') + meta(name='viewport', content='width=device-width, initial-scale=1.0') + meta(name='apple-mobile-web-app-capable', content='yes') script(type='text/javascript'). window.env = !{JSON.stringify(env)}; - style. - [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { - display: none; - } - != env.getManifestFiles("app") //webfonts link(href='//fonts.googleapis.com/css?family=Lato:300,400,700,400italic,700italic', rel='stylesheet', type='text/css') - meta(name='viewport', content='width=device-width') - meta(name='apple-mobile-web-app-capable', content='yes') - body(ng-cloak) - div(ng-controller='GroupsCtrl') - include ./shared/modals/index - include ./shared/header/header - include ./shared/tasks/lists - include ./main/index - include ./options/index - - #notification-area(ng-controller='NotificationCtrl') - #wrap - - //if they hide the header, we still need user-menu visible - div(ng-if='user.preferences.hideHeader') - include ./shared/header/menu - - .exp-chart(ng-show='charts.exp') + body(ng-cloak, ng-controller='GroupsCtrl') + include ./shared/header/menu + include ./shared/modals/index + include ./shared/header/header + include ./shared/tasks/lists + include ./main/index + include ./options/index + #notification-area(ng-controller='NotificationCtrl') + #wrap.container-fluid + .row + .col-md-12.exp-chart(ng-show='charts.exp') #main(ui-view) - include ./shared/footer \ No newline at end of file + include ./shared/footer \ No newline at end of file diff --git a/views/main/filters.jade b/views/main/filters.jade index 0a5de7e1db..e356e552e5 100644 --- a/views/main/filters.jade +++ b/views/main/filters.jade @@ -1,26 +1,29 @@ -.filters.tag-list(ng-controller='FiltersCtrl') - .pull-left.filter-description - =env.t('tags') - |: - ul.nav.nav-pills - li - a(rel='tooltip', title=env.t('editTags'), ng-click='saveOrEdit()') - i(ng-class='{"icon-ok": _editing, "icon-pencil": !_editing}') - li(bindonce='user.tags', ng-class='{active: user.filters[tag.id]}', ng-repeat='tag in user.tags', style='position: relative;') - .input-append.option-group.tag-editing(ng-show='_editing') - input.input.input-small.option-content.tag-editing-pill(type='text', ng-model='tag.name') - span.add-on.tag-editing-pill - a.pull-right(ng-click='user.ops.deleteTag({params:{id:tag.id}})') - i.icon-trash - a(ng-hide='_editing', ng-click='toggleFilter(tag)') - i.icon-bullhorn(bo-if="tag.challenge") - markdown(ng-model='tag.name',style='display: inline-block') - li - form.form-inline(ng-show='_editing', ng-submit='createTag(_newTag)') - .input-append.tag-editing - input.span2.tag-editing-pill(type='text', ng-model='_newTag', placeholder=env.t('newTag')) - button.add-on.tag-editing-pill(type='submit')=env.t('add') - //
  • - li - a(rel='tooltip', title=env.t('clearFilters'), ng-click='user.filters = {}') - i.icon-remove-sign +.container-fluid + .row + .filters.col-md-12(ng-controller='FiltersCtrl') + .pull-left.filter-description + =env.t('tags') + |: + ul.nav.nav-pills + li + a(rel='tooltip', title=env.t('editTags'), ng-click='saveOrEdit()') + span.glyphicon(ng-class='{"glyphicon-ok": _editing, "glyphicon-pencil": !_editing}') + li(bindonce='user.tags', ng-class='{active: user.filters[tag.id]}', ng-repeat='tag in user.tags') + .input-group(ng-show='_editing') + input.form-control.input-sm(type='text', ng-model='tag.name') + span.input-group-addon + a(ng-click='user.ops.deleteTag({params:{id:tag.id}})') + span.glyphicon.glyphicon-trash + a(ng-hide='_editing', ng-click='toggleFilter(tag)') + span.glyphicon.glyphicon-bullhorn(bo-if="tag.challenge") + markdown(ng-model='tag.name') + li + form.form-inline(ng-show='_editing', ng-submit='createTag(_newTag)') + .input-group + input.form-control.input-sm(type='text', ng-model='_newTag', placeholder=env.t('newTag')) + span.input-group-addon(ng-click='createTag(_newTag)') + =env.t('add') + //
  • + li + a(rel='tooltip', title=env.t('clearFilters'), ng-click='user.filters = {}') + span.glyphicon.glyphicon-remove-sign diff --git a/views/options/index.jade b/views/options/index.jade index aea6dedb26..421f0adfe9 100644 --- a/views/options/index.jade +++ b/views/options/index.jade @@ -4,29 +4,34 @@ include ./inventory/index include ./settings script(id='partials/options.html', type="text/ng-template") - .grid - .module.full-width - span.option-box.pull-right.wallet - include ../shared/gems + .container-fluid + .row + .col-md-12 + span.option-box.pull-right.wallet + include ../shared/gems - ul.nav.nav-tabs - li(ng-class="{ active: $state.includes('options.profile') }") - a(ui-sref='options.profile') - i.icon-user - =env.t('profile') - li(ng-class="{ active: $state.includes('options.social') }") - a(ui-sref='options.social') - i.icon-heart - =env.t('social') - li(ng-class="{ active: $state.includes('options.inventory') }", ng-if='user.flags.dropsEnabled') - a(ui-sref='options.inventory') - i.icon-gift - =env.t('inventory') - li(ng-class="{ active: $state.includes('options.settings') }") - a(ui-sref='options.settings') - i.icon-wrench - =env.t('settings') + ul.nav.nav-tabs + li(ng-class="{ active: $state.includes('options.profile') }") + a(ui-sref='options.profile') + span.glyphicon.glyphicon-user + |   + =env.t('profile') + li(ng-class="{ active: $state.includes('options.social') }") + a(ui-sref='options.social') + span.glyphicon.glyphicon-heart + |   + =env.t('social') + li(ng-class="{ active: $state.includes('options.inventory') }", ng-if='user.flags.dropsEnabled') + a(ui-sref='options.inventory') + span.glyphicon.glyphicon-gift + |   + =env.t('inventory') + li(ng-class="{ active: $state.includes('options.settings') }") + a(ui-sref='options.settings') + span.glyphicon.glyphicon-wrench + |   + =env.t('settings') - .tab-content - .tab-pane.active - div(ui-view) + .tab-content + .tab-pane.active + div(ui-view) diff --git a/views/options/inventory/inventory.jade b/views/options/inventory/inventory.jade index 3fab6be8c2..94264834df 100644 --- a/views/options/inventory/inventory.jade +++ b/views/options/inventory/inventory.jade @@ -1,145 +1,161 @@ script(type='text/ng-template', id='partials/options.inventory.equipment.html') - .row-fluid - .span6.border-right - h3=env.t('battleGear') - small=env.t('battleGearText') - li.customize-menu.inventory-gear - menu.pets-menu(label='{{label}}', ng-repeat='(klass,label) in {base:"Base", warrior:"Warrior", wizard:"Mage", rogue:"Rogue", healer:"Healer", special:"Special"}', ng-show='gear[klass]') - div(ng-repeat='item in gear[klass]') - button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({params:{key:item.key}})', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.equipped[item.type] == item.key}') - .span6 - h3=env.t('costume') - small=env.t('costumeText') - br - label.checkbox.inline - input(type="checkbox", ng-model="user.preferences.costume", ng-click='set({"preferences.costume":!user.preferences.costume})') - =env.t('useCostume') - li.customize-menu(ng-if='user.preferences.costume') - menu.pets-menu(label='{{label}}', ng-repeat='(klass,label) in {base:"Base", warrior:"Warrior", wizard:"Mage", rogue:"Rogue", healer:"Healer", special:"Special"}', ng-show='gear[klass]') - div(ng-repeat='item in gear[klass]') - button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({params:{type:"costume", key:item.key}})', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') + .container-fluid + .row + .col-md-6.border-right + h3.equipment-title=env.t('battleGear') +   + .glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('battleGearText')) + li.customize-menu.inventory-gear + menu.pets-menu(label='{{label}}', ng-repeat='(klass,label) in {base:"Base", warrior:"Warrior", wizard:"Mage", rogue:"Rogue", healer:"Healer", special:"Special"}', ng-show='gear[klass]') + div(ng-repeat='item in gear[klass]') + button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({params:{key:item.key}})', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.equipped[item.type] == item.key}') + .col-md-6 + h3.equipment-title=env.t('costume') +   + i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('costumeText')) + .checkbox + label + input(type="checkbox", ng-model="user.preferences.costume", ng-click='set({"preferences.costume":!user.preferences.costume})') + |  + =env.t('useCostume') + li.customize-menu(ng-if='user.preferences.costume') + menu.pets-menu(label='{{label}}', ng-repeat='(klass,label) in {base:"Base", warrior:"Warrior", wizard:"Mage", rogue:"Rogue", healer:"Healer", special:"Special"}', ng-show='gear[klass]') + div(ng-repeat='item in gear[klass]') + button.customize-option(popover='{{item.notes}}', popover-title='{{item.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='user.ops.equip({params:{type:"costume", key:item.key}})', class='shop_{{item.key}}', ng-class='{selectableInventory: user.items.gear.costume[item.type] == item.key}') script(type='text/ng-template', id='partials/options.inventory.drops.html') - .row-fluid - .span6.border-right - h2=env.t('inventory') - p.well=env.t('inventoryText') - menu.inventory-list(type='list') - - li.customize-menu - menu.pets-menu(label=(env.t('eggs') + ' ({{eggCount}})')) - p.muted(ng-show='eggCount < 1')=env.t('noEggs') - div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)') - //TODO move positioning this styling to css - button.customize-option(popover='{{Content.eggs[egg].notes}}', popover-title='{{Content.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !(user.items.pets[egg+"-"+selectedPotion.key]>0)}') - .badge.badge-info.stack-count {{points}} - //-p {{Content.eggs[egg].text}} - - li.customize-menu - menu.hatchingPotion-menu(label=(env.t('hatchingPotions') + ' ({{potCount}})')) - p.muted(ng-show='potCount < 1')=env.t('noHatchingPotions') - div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)') - button.customize-option(popover='{{Content.hatchingPotions[pot].notes}}', popover-title='{{Content.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !(user.items.pets[selectedEgg.key+"-"+pot]>0)}') - .badge.badge-info.stack-count {{points}} - - li.customize-menu - menu.pets-menu(label=(env.t('scrolls') + ' ({{questCount}})')) - p.muted(ng-show='questCount < 1')=env.t('noScrolls') - p.muted!=env.t('scrollsText1') + ' ' + env.t('scrollsText2') + '' - div(ng-repeat='(quest_key,points) in ownedItems(user.items.quests)', ng-init='quest = Content.quests[quest_key]') - button.customize-option(popover="{{quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes | htmlDecode}}", popover-title='{{quest.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='showQuest(quest_key)', class='inventory_quest_scroll', ng-class='{locked: quest.previous && !user.achievements.quests[quest.previous]}') - .badge.badge-info.stack-count {{points}} - - li.customize-menu - menu.pets-menu(label=env.t('food') + ' ({{foodCount}})') - p(ng-show='foodCount < 1')=env.t('noFood') - div(ng-repeat='(food,points) in ownedItems(user.items.food)') - button.customize-option(popover='{{Content.food[food].notes}}', popover-title='{{Content.food[food].text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') - .badge.badge-info.stack-count {{points}} - - li.customize-menu(ng-if='user.items.special.snowball') - menu.pets-menu(label=env.t('special')) - div - button.customize-option(popover='{{Content.special.snowball.notes}}', popover-title='{{Content.special.snowball.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='castStart(Content.special.snowball)', class='inventory_special_snowball') - .badge.badge-info.stack-count {{user.items.special.snowball}} - - .span6 - h2=env.t('market') - .row-fluid - table.npc_alex_container - tr - td - .npc_alex.pull-left - td - .popover.static-popover.fade.right.in - .arrow - h3.popover-title - a(target='_blank', href='http://www.kickstarter.com/profile/523661924')=env.t('alexander') - .popover-content - p=env.t('welcomeMarket') - p - button.btn.btn-primary(ng-show='selectedEgg', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedEgg.text}}", gold: "{{selectedEgg.value}}"}) - button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedPotion.text}}", gold: "{{selectedPotion.value}}"}) - button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text}}", gold: "{{selectedFood.value}}"}) + .container-fluid + .row + .col-md-6.border-right + h2=env.t('inventory') + p.well=env.t('inventoryText') menu.inventory-list(type='list') - li.customize-menu - menu.pets-menu(label=env.t('eggs')) - div(ng-repeat='egg in Content.eggs', ng-if='egg.canBuy') - button.customize-option(popover='{{egg.notes}}', popover-title='{{egg.text}} Egg', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("eggs", egg)', class='Pet_Egg_{{egg.key}}') - p - | {{egg.value}} - span.Pet_Currency_Gem1x.inline-gems li.customize-menu - menu.pets-menu(label=env.t('hatchingPotions')) - div(ng-repeat='pot in Content.hatchingPotions') - button.customize-option(popover='{{pot.notes}}', popover-title='{{pot.text}} Potion', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("hatchingPotions", pot)', class='Pet_HatchingPotion_{{pot.key}}') - p - | {{pot.value}} - span.Pet_Currency_Gem1x.inline-gems + menu.pets-menu(label=(env.t('eggs') + ' ({{eggCount}})')) + p.muted(ng-show='eggCount < 1')=env.t('noEggs') + div(ng-repeat='(egg,points) in ownedItems(user.items.eggs)') + //TODO move positioning this styling to css + button.customize-option(popover='{{Content.eggs[egg].notes}}', popover-title='{{Content.eggs[egg].text}} Egg', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseEgg(egg)', class='Pet_Egg_{{egg}}', ng-class='{selectableInventory: selectedPotion && !(user.items.pets[egg+"-"+selectedPotion.key]>0)}') + .badge.badge-info.stack-count {{points}} + //-p {{Content.eggs[egg].text}} li.customize-menu - menu.pets-menu(label=env.t('food')) - div(ng-repeat='food in Content.food', ng-if='food.key !== "Saddle" && food.canBuy') - button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", food)', class='Pet_Food_{{food.key}}') - p - | {{food.value}} - span.Pet_Currency_Gem1x.inline-gems + menu.hatchingPotion-menu(label=(env.t('hatchingPotions') + ' ({{potCount}})')) + p.muted(ng-show='potCount < 1')=env.t('noHatchingPotions') + div(ng-repeat='(pot,points) in ownedItems(user.items.hatchingPotions)') + button.customize-option(popover='{{Content.hatchingPotions[pot].notes}}', popover-title='{{Content.hatchingPotions[pot].text}} Potion', popover-trigger='mouseenter', popover-placement='right', ng-click='choosePotion(pot)', class='Pet_HatchingPotion_{{pot}}', ng-class='{selectableInventory: selectedEgg && !(user.items.pets[selectedEgg.key+"-"+pot]>0)}') + .badge.badge-info.stack-count {{points}} li.customize-menu - menu.pets-menu(label=env.t('quests')) + menu.pets-menu(label=(env.t('scrolls') + ' ({{questCount}})')) + p.muted(ng-show='questCount < 1')=env.t('noScrolls') p.muted!=env.t('scrollsText1') + ' ' + env.t('scrollsText2') + '' - div(ng-repeat='quest in Content.quests', ng-if='quest.canBuy') - button.customize-option(popover="{{quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes | htmlDecode}}", popover-title='{{quest.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='{locked: quest.previous && !user.achievements.quests[quest.previous]}') - p - | {{quest.value}} - span.Pet_Currency_Gem1x.inline-gems + div(ng-repeat='(quest_key,points) in ownedItems(user.items.quests)', ng-init='quest = Content.quests[quest_key]') + button.customize-option(popover="{{quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes | htmlDecode}}", popover-title='{{quest.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='showQuest(quest_key)', class='inventory_quest_scroll', ng-class='{locked: quest.previous && !user.achievements.quests[quest.previous]}') + .badge.badge-info.stack-count {{points}} li.customize-menu + menu.pets-menu(label=env.t('food') + ' ({{foodCount}})') + p(ng-show='foodCount < 1')=env.t('noFood') + div(ng-repeat='(food,points) in ownedItems(user.items.food)') + button.customize-option(popover='{{Content.food[food].notes}}', popover-title='{{Content.food[food].text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') + .badge.badge-info.stack-count {{points}} + + li.customize-menu(ng-if='user.items.special.snowball') menu.pets-menu(label=env.t('special')) div - button.customize-option(popover='{{Content.food.Saddle.notes}}', popover-title='{{Content.food.Saddle.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", Content.food.Saddle)', class='Pet_Food_{{Content.food.Saddle.key}}') - p - | {{Content.food.Saddle.value}} - span.Pet_Currency_Gem1x.inline-gems + button.customize-option(popover='{{Content.special.snowball.notes}}', popover-title='{{Content.special.snowball.text}}', popover-trigger='mouseenter', popover-placement='right', ng-click='castStart(Content.special.snowball)', class='inventory_special_snowball') + .badge.badge-info.stack-count {{user.items.special.snowball}} + + li.customize-menu(ng-if='user.items.special.valentineReceived[0]') + menu.pets-menu(label=env.t('valentineCard')) div - button.customize-option(popover=env.t('fortifyPop'), popover-title=env.t('fortifyName'), popover-trigger='mouseenter', popover-placement='left', ng-click='modals.reroll = true', class='inventory_special_fortify') - p - | 4 - span.Pet_Currency_Gem1x.inline-gems - //-div - button.customize-option(popover='{{Content.spells.special.snowball.notes}}', popover-title='{{Content.spells.special.snowball.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("special", Content.spells.special.snowball)', class='inventory_special_snowball') - p - | {{Content.spells.special.snowball.value}} - span.Pet_Currency_Gem1x.inline-gems - div(ng-show='user.flags.rebirthEnabled') - button.customize-option(popover=env.t('rebirthPop'), popover-title=env.t('rebirthName'), popover-trigger='mouseenter', popover-placement='left', ng-click='modals.rebirth = true', class='rebirth_orb') - p - | 8 - span.Pet_Currency_Gem1x.inline-gems - div(ng-if='user.purchased.plan.planId') - button.customize-option(ng-click='user.ops.purchase({params:{type:"gems",key:"gem"}})') - span.Pet_Currency_Gem.inline-gems - p - | 20 - span.shop_gold + button.customize-option(popover="Valentine's Day Card from {{User.user.items.special.valentineReceived[0]}}", popover-trigger='mouseenter', popover-placement='right', ng-click='openModal("valentine")', class='inventory_special_valentine') + .badge.badge-info.stack-count {{user.items.special.valentineReceived.length}} + + li.customize-menu(ng-if='user.purchased.plan.customerId') + menu.pets-menu(label=env.t('subscriberItem')) + div + button.customize-option(popover=env.t('subscriberItemText'), popover-trigger='mouseenter', popover-placement='right', class='inventory_present') + + .col-md-6 + h2=env.t('market') + .row-fluid + table.npc_alex_container + tr + td + .npc_alex.pull-left + td + .popover.static-popover.fade.right.in + .arrow + h3.popover-title + a(target='_blank', href='http://www.kickstarter.com/profile/523661924')=env.t('alexander') + .popover-content + p=env.t('welcomeMarket') + p + button.btn.btn-primary(ng-show='selectedEgg', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedEgg.text}}", gold: "{{selectedEgg.value}}"}) + button.btn.btn-primary(ng-show='selectedPotion', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedPotion.text}}", gold: "{{selectedPotion.value}}"}) + button.btn.btn-primary(ng-show='selectedFood', ng-click='sellInventory()')=env.t('sellForGold', {item: "{{selectedFood.text}}", gold: "{{selectedFood.value}}"}) + menu.inventory-list(type='list') + li.customize-menu + menu.pets-menu(label=env.t('eggs')) + div(ng-repeat='egg in Content.eggs', ng-if='egg.canBuy') + button.customize-option(popover='{{egg.notes}}', popover-title='{{egg.text}} Egg', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("eggs", egg)', class='Pet_Egg_{{egg.key}}') + p + | {{egg.value}} + span.Pet_Currency_Gem1x.inline-gems + + li.customize-menu + menu.pets-menu(label=env.t('hatchingPotions')) + div(ng-repeat='pot in Content.hatchingPotions') + button.customize-option(popover='{{pot.notes}}', popover-title='{{pot.text}} Potion', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("hatchingPotions", pot)', class='Pet_HatchingPotion_{{pot.key}}') + p + | {{pot.value}} + span.Pet_Currency_Gem1x.inline-gems + + li.customize-menu + menu.pets-menu(label=env.t('food')) + div(ng-repeat='food in Content.food', ng-if='food.key !== "Saddle" && food.canBuy') + button.customize-option(popover='{{food.notes}}', popover-title='{{food.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", food)', class='Pet_Food_{{food.key}}') + p + | {{food.value}} + span.Pet_Currency_Gem1x.inline-gems + + li.customize-menu + menu.pets-menu(label=env.t('quests')) + p.muted!=env.t('scrollsText1') + ' ' + env.t('scrollsText2') + '' + div(ng-repeat='quest in Content.quests', ng-if='quest.canBuy') + button.customize-option(popover="{{quest.previous && !user.achievements.quests[quest.previous] ? env.t('scrollsPre') : quest.notes | htmlDecode}}", popover-title='{{quest.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='buyQuest(quest.key)', class='inventory_quest_scroll', ng-class='{locked: quest.previous && !user.achievements.quests[quest.previous]}') + p + | {{quest.value}} + span.Pet_Currency_Gem1x.inline-gems + + li.customize-menu + menu.pets-menu(label=env.t('special')) + div + button.customize-option(popover='{{Content.food.Saddle.notes}}', popover-title='{{Content.food.Saddle.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("food", Content.food.Saddle)', class='Pet_Food_{{Content.food.Saddle.key}}') + p + | {{Content.food.Saddle.value}} + span.Pet_Currency_Gem1x.inline-gems + div + button.customize-option(popover=env.t('fortifyPop'), popover-title=env.t('fortifyName'), popover-trigger='mouseenter', popover-placement='left', ng-click='openModal("reroll")', class='inventory_special_fortify') + p + | 4 + span.Pet_Currency_Gem1x.inline-gems + //-div + button.customize-option(popover='{{Content.spells.special.snowball.notes}}', popover-title='{{Content.spells.special.snowball.text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='purchase("special", Content.spells.special.snowball)', class='inventory_special_snowball') + p + | {{Content.spells.special.snowball.value}} + span.Pet_Currency_Gem1x.inline-gems + div(ng-show='user.flags.rebirthEnabled') + button.customize-option(popover=env.t('rebirthPop'), popover-title=env.t('rebirthName'), popover-trigger='mouseenter', popover-placement='left', ng-click='openModal("rebirth")', class='rebirth_orb') + p + | 8 + span.Pet_Currency_Gem1x.inline-gems + div(ng-if='user.purchased.plan.planId') + button.customize-option(ng-click='user.ops.purchase({params:{type:"gems",key:"gem"}})') + span.Pet_Currency_Gem.inline-gems + p + | 20 + span.shop_gold diff --git a/views/options/inventory/stable.jade b/views/options/inventory/stable.jade index 328be90ebb..d228ece0f2 100644 --- a/views/options/inventory/stable.jade +++ b/views/options/inventory/stable.jade @@ -1,85 +1,99 @@ script(type='text/ng-template', id='partials/options.inventory.mounts.html') - .stable - table(style='width:100%') - tr - td(style='width:144px') - .npc_matt - td - .popover.static-popover.fade.right.in(style='max-width: 550px; margin-left: 10px;') - .arrow - h3.popover-title - a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch') - .popover-content - p=env.t('mattShall', {name: "{{user.profile.name}}"}) - h4= '{{mountCount}} / {{totalPets}} ' + env.t('mountsTamed') - menu.pets(type='list') - li.customize-menu(ng-repeat='egg in Content.eggs') + .container-fluid + .stable.row + .col-md-2 + .npc_matt + .col-md-10 + .popover.static-popover.fade.right.in + .arrow + h3.popover-title + a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch') + .popover-content + p=env.t('mattShall', {name: "{{user.profile.name}}"}) + h4= '{{Shared.countMounts(null,User.user.items.mounts) || 0}} / {{totalMounts}} ' + env.t('mountsTamed') + .col-md-12 + menu.pets(type='list') + li.customize-menu(ng-repeat='egg in Content.eggs') + menu + div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', popover-placement='bottom', ng-init='mount = egg.key+"-"+potion.key') + button(class="pet-button Mount_Head_{{mount}}", ng-show='user.items.mounts[mount]', ng-class='{active: user.items.currentMount == mount}', ng-click='chooseMount(egg.key, potion.key)') + //div(class='Mount_Head_{{mount}}') + button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]') + .PixelPaw + .col-md-12 + h4=env.t('rareMounts') menu - div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.mountText}}', popover-placement='bottom', ng-init='mount = egg.key+"-"+potion.key') - button(class="pet-button Mount_Head_{{mount}}", ng-show='user.items.mounts[mount]', ng-class='{active: user.items.currentMount == mount}', ng-click='chooseMount(egg.key, potion.key)') - //div(class='Mount_Head_{{mount}}') - button(class="pet-button pet-not-owned", ng-hide='user.items.mounts[mount]') - .PixelPaw - - h4=env.t('rareMounts') - menu - div - button(ng-if='user.items.mounts["BearCub-Polar"]', class="pet-button Mount_Head_BearCub-Polar", ng-class='{active: user.items.currentMount == "BearCub-Polar"}', ng-click='chooseMount("BearCub", "Polar")', popover=env.t('polarBear'), popover-trigger='mouseenter', popover-placement='bottom') - //.Mount_Head_BearCub-Polar - button(ng-if='user.items.mounts["LionCub-Ethereal"]', class="pet-button Mount_Head_LionCub-Ethereal", ng-class='{active: user.items.currentMount == "LionCub-Ethereal"}', ng-click='chooseMount("LionCub", "Ethereal")', popover=env.t('etherealLion'), popover-trigger='mouseenter', popover-placement='bottom') - //.Mount_Head_LionCub-Ethereal + div + button(ng-if='user.items.mounts["BearCub-Polar"]', class="pet-button Mount_Head_BearCub-Polar", ng-class='{active: user.items.currentMount == "BearCub-Polar"}', ng-click='chooseMount("BearCub", "Polar")', popover=env.t('polarBear'), popover-trigger='mouseenter', popover-placement='bottom') + //.Mount_Head_BearCub-Polar + button(ng-if='user.items.mounts["LionCub-Ethereal"]', class="pet-button Mount_Head_LionCub-Ethereal", ng-class='{active: user.items.currentMount == "LionCub-Ethereal"}', ng-click='chooseMount("LionCub", "Ethereal")', popover=env.t('etherealLion'), popover-trigger='mouseenter', popover-placement='bottom') + //.Mount_Head_LionCub-Ethereal script(type='text/ng-template', id='partials/options.inventory.pets.html') - .stable - table(style='width:100%') - tr - td(style='width:144px') - .npc_matt - td - .popover.static-popover.fade.right.in(style='max-width: 550px; margin-left: 10px;') - .arrow - h3.popover-title - a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch') - .popover-content - p - =env.t('mattBochText1') + ' ' - | - =env.t('mattBochText2') - | - = ' ' + env.t('mattBochText3') - h4= '{{petCount}} / {{totalPets}} ' + env.t('petsFound') + .container-fluid + .stable.row + .col-md-2 + .npc_matt + .col-md-10 + .popover.static-popover.fade.right.in + .arrow + h3.popover-title + a(target='_blank', href='http://www.kickstarter.com/profile/mattboch')=env.t('mattBoch') + .popover-content + p + =env.t('mattBochText1') + ' ' + | + =env.t('mattBochText2') + | + = ' ' + env.t('mattBochText3') + h4= env.t('beastmasterProgress') + ': {{petCount}} / {{totalPets}} ' + env.t('petsFound') - menu.pets(type='list') - li.customize-menu(ng-repeat='egg in Content.eggs') + .col-md-12 + menu.pets(type='list') + li.customize-menu(ng-repeat='egg in Content.dropEggs') + menu + div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.key+"-"+potion.key') + button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.key, potion.key)') + .progress(ng-show='!user.items.mounts[pet]') + .progress-bar.progress-bar-success(style="width:{{user.items.pets[pet]/.5}}%") + button(class="pet-button pet-not-owned", ng-if='!user.items.pets[pet]') + .PixelPaw + // How to apply this style in stylus files? + button(class="pet-button Pet-{{pet}}" ng-if='user.items.pets[pet]<0',style='opacity:0.1;filter:alpha(opacity=10)') + //if this could be changed to the opacity talked about? can't figure it out + .col-md-12 + h4=env.t('questPets') + menu.pets(type='list') + li.customize-menu(ng-repeat='egg in Content.questEggs') + menu + div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.key+"-"+potion.key') + button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.key, potion.key)') + .progress(ng-show='!user.items.mounts[pet]') + .progress-bar.progress-bar-success(style="width:{{user.items.pets[pet]/.5}}%") + button(class="pet-button pet-not-owned", ng-if='!user.items.pets[pet]') + .PixelPaw + button(class="pet-button Pet-{{pet}}" ng-if='user.items.pets[pet]<0',style='opacity:0.1;filter:alpha(opacity=10)') + + .col-md-12 + h4=env.t('rarePets') menu - div(ng-repeat='potion in Content.hatchingPotions', popover-trigger='mouseenter', popover='{{potion.text}} {{egg.text}}', popover-placement='bottom', ng-init='pet = egg.key+"-"+potion.key') - button(class="pet-button Pet-{{pet}}", ng-if='user.items.pets[pet]>0', ng-class='{active: user.items.currentPet == pet, selectableInventory: selectedFood}', ng-click='choosePet(egg.key, potion.key)') - .progress(ng-class='{"progress-success": user.items.pets[pet]<50}') - .bar(style="width: {{user.items.pets[pet]/.5}}%;") - button(class="pet-button pet-not-owned", ng-if='!user.items.pets[pet]') - .PixelPaw - button(class="pet-button Pet-{{pet}}" ng-if='user.items.pets[pet]<0',style='opacity:0.1;filter:alpha(opacity=10)') - //if this could be changed to the opacity talked about? can't figure it out + div + button(ng-if='user.items.pets["Wolf-Veteran"]', class="pet-button Pet-Wolf-Veteran", ng-class='{active: user.items.currentPet == "Wolf-Veteran"}', ng-click='choosePet("Wolf", "Veteran")', popover=env.t('veteranWolf'), popover-trigger='mouseenter', popover-placement='bottom') + button(ng-if='user.items.pets["Wolf-Cerberus"]', class="pet-button Pet-Wolf-Cerberus", ng-class='{active: user.items.currentPet == "Wolf-Cerberus"}', ng-click='choosePet("Wolf", "Cerberus")', popover=env.t('cerberusPup'), popover-trigger='mouseenter', popover-placement='bottom') + button(ng-if='user.items.pets["Turkey-Base"]', class="pet-button Pet-Turkey-Base", ng-class='{active: user.items.currentPet == "Turkey-Base"}', ng-click='choosePet("Turkey", "Base")', popover=env.t('turkey'), popover-trigger='mouseenter', popover-placement='bottom') + button(ng-if='user.items.pets["BearCub-Polar"]', class="pet-button Pet-BearCub-Polar", ng-class='{active: user.items.currentPet == "BearCub-Polar"}', ng-click='choosePet("BearCub", "Polar")', popover=env.t('polarBearPup'), popover-trigger='mouseenter', popover-placement='bottom') + button(ng-if='user.items.pets["Dragon-Hydra"]', class="pet-button Pet-Dragon-Hydra", ng-class='{active: user.items.currentPet == "Dragon-Hydra"}', ng-click='choosePet("Dragon", "Hydra")', popover=env.t('hydra'), popover-trigger='mouseenter', popover-placement='bottom') + a(target='_blank', href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG') + button(ng-if='!user.items.pets["Dragon-Hydra"]', class="pet-button pet-not-owned", popover-trigger='mouseenter', popover-placement='right', popover=env.t('rarePetPop1'), popover-title=env.t('rarePetPop2')) + .PixelPaw-Gold - h4=env.t('rarePets') - menu - div - button(ng-if='user.items.pets["Wolf-Veteran"]', class="pet-button Pet-Wolf-Veteran", ng-class='{active: user.items.currentPet == "Wolf-Veteran"}', ng-click='choosePet("Wolf", "Veteran")', popover=env.t('veteranWolf'), popover-trigger='mouseenter', popover-placement='bottom') - button(ng-if='user.items.pets["Wolf-Cerberus"]', class="pet-button Pet-Wolf-Cerberus", ng-class='{active: user.items.currentPet == "Wolf-Cerberus"}', ng-click='choosePet("Wolf", "Cerberus")', popover=env.t('cerberusPup'), popover-trigger='mouseenter', popover-placement='bottom') - button(ng-if='user.items.pets["Turkey-Base"]', class="pet-button Pet-Turkey-Base", ng-class='{active: user.items.currentPet == "Turkey-Base"}', ng-click='choosePet("Turkey", "Base")', popover=env.t('turkey'), popover-trigger='mouseenter', popover-placement='bottom') - button(ng-if='user.items.pets["BearCub-Polar"]', class="pet-button Pet-BearCub-Polar", ng-class='{active: user.items.currentPet == "BearCub-Polar"}', ng-click='choosePet("BearCub", "Polar")', popover=env.t('polarBearPup'), popover-trigger='mouseenter', popover-placement='bottom') - button(ng-if='user.items.pets["Dragon-Hydra"]', class="pet-button Pet-Dragon-Hydra", ng-class='{active: user.items.currentPet == "Dragon-Hydra"}', ng-click='choosePet("Dragon", "Hydra")', popover=env.t('hydra'), popover-trigger='mouseenter', popover-placement='bottom') - a(target='_blank', href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG') - button(ng-if='!user.items.pets["Dragon-Hydra"]', class="pet-button pet-not-owned", popover-trigger='mouseenter', popover-placement='right', popover=env.t('rarePetPop1'), popover-title=env.t('rarePetPop2')) - .PixelPaw-Gold - - .well.food-tray - p(ng-show='foodCount < 1')=env.t('noFood') - menu.inventory-list(type='list', ng-if='foodCount > 0') - li.customize-menu - menu.pets-menu(label=env.t('food')) - div(ng-repeat='(food,points) in ownedItems(user.items.food)') - button.customize-option(popover-append-to-body='true', popover='{{Content.food[food].notes}}', popover-title='{{Content.food[food].text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') - .badge.badge-info.stack-count {{points}} - // Remove this once we have images in - p {{Content.food[food].text}} + .well.food-tray + p(ng-show='foodCount < 1')=env.t('noFood') + menu.inventory-list(type='list', ng-if='foodCount > 0') + li.customize-menu + menu.pets-menu(label=env.t('food')) + div(ng-repeat='(food,points) in ownedItems(user.items.food)') + button.customize-option(popover-append-to-body='true', popover='{{Content.food[food].notes}}', popover-title='{{Content.food[food].text}}', popover-trigger='mouseenter', popover-placement='left', ng-click='chooseFood(food)', class='Pet_Food_{{food}}') + .badge.badge-info.stack-count {{points}} + // Remove this once we have images in + p {{Content.food[food].text}} diff --git a/views/options/profile.jade b/views/options/profile.jade index fb0ae74794..18c0d72ef8 100644 --- a/views/options/profile.jade +++ b/views/options/profile.jade @@ -1,15 +1,15 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') - .row-fluid - .span4 + .row + .col-md-4 h3=env.t('bodyBody') small - | 2 / + | 2 / = ' ' + env.t('locked') h5=env.t('bodySize') .btn-group - button.btn.btn-small(ng-class='{active: user.preferences.size=="slim"}', ng-click='set({"preferences.size":"slim"})')=env.t('bodySlim') - button.btn.btn-small(ng-class='{active: user.preferences.size=="broad"}', ng-click='set({"preferences.size":"broad"})')=env.t('bodyBroad') + button.btn.btn-sm.btn-default(ng-class='{active: user.preferences.size=="slim"}', ng-click='set({"preferences.size":"slim"})')=env.t('bodySlim') + button.btn.btn-sm.btn-default(ng-class='{active: user.preferences.size=="broad"}', ng-click='set({"preferences.size":"broad"})')=env.t('bodyBroad') menu(type='list') li.customize-menu @@ -21,31 +21,30 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') each shirt in ['convict', 'cross', 'fire', 'horizon', 'ocean', 'purple', 'rainbow', 'redblue', 'thunder', 'tropical', 'zombie'] button.customize-option(type='button', class='{{user.preferences.size}}_shirt_'+shirt, ng-class='{locked: !user.purchased.shirt.'+shirt+'}', ng-click='unlock("shirt.'+shirt+'")') menu - button.btn.btn-small.btn-primary(ng-hide="user.purchased.shirt.convict && user.purchased.shirt.cross && user.purchased.shirt.fire && user.purchased.shirt.horizon && user.purchased.shirt.ocean && user.purchased.shirt.purple && user.purchased.shirt.rainbow && user.purchased.shirt.redblue && user.purchased.shirt.thunder && user.purchased.shirt.tropical && user.purchased.shirt.zombie", ng-click='unlock("shirt.convict,shirt.cross,shirt.fire,shirt.horizon,shirt.ocean,shirt.purple,shirt.rainbow,shirt.redblue,shirt.thunder,shirt.tropical,shirt.zombie")')!= env.t('unlockSet5') + ' ' + button.btn.btn-sm.btn-primary(ng-hide="user.purchased.shirt.convict && user.purchased.shirt.cross && user.purchased.shirt.fire && user.purchased.shirt.horizon && user.purchased.shirt.ocean && user.purchased.shirt.purple && user.purchased.shirt.rainbow && user.purchased.shirt.redblue && user.purchased.shirt.thunder && user.purchased.shirt.tropical && user.purchased.shirt.zombie", ng-click='unlock("shirt.convict,shirt.cross,shirt.fire,shirt.horizon,shirt.ocean,shirt.purple,shirt.rainbow,shirt.redblue,shirt.thunder,shirt.tropical,shirt.zombie")')!= env.t('unlockSet5') + ' ' - .span4 + .col-md-4 h3=env.t('bodyHead') small - | 2 / + | 2 / = ' ' + env.t('locked') menu(type='list') + // For special events code, see commit dfa27b3 + // Color li.customize-menu menu(label=env.t('color')) each v,k in {'c8c8c8':'white','903a00':'brown','cfb853':'blond','ec720f':'red','2e2e2e':'black'} - button(type='button', class='customize-option', style='width: 40px; height: 40px; background-color:##{k};', ng-click='set({"preferences.hair.color": "#{v}"})') - - // Special Events - li.customize-menu.well.limited-edition - .label.label-info.pull-right(popover=env.t('limited31Jan'), popover-title=env.t('limitedEdition'), popover-placement='right', popover-trigger='mouseenter') - =env.t('limitedEdition') - | - i.icon.icon-question-sign - menu(label=env.t('winterColors')) + button(type='button', class='customize-option hair', style='background-color:##{k};', ng-click='set({"preferences.hair.color": "#{v}"})') each color in ['candycane','frost','winternight','holly'] - button(type='button', ng-class='{locked: !user.purchased.hair.color.#{color}}', class='customize-option hair_bangs_1_#{color}', style='width: 40px; height: 40px;', ng-click='unlock("hair.color.#{color}")') - button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.color.candycane && user.purchased.hair.color.frost && user.purchased.hair.color.winternight && user.purchased.hair.color.holly', ng-click='unlock("hair.color.candycane,hair.color.frost,hair.color.winternight,hair.color.holly")')!= env.t('unlockSet5') + ' ' + button(type='button', ng-if='user.purchased.hair.color.#{color}', class='customize-option hair hair_bangs_1_#{color}', style='width: 40px; height: 40px;', ng-click='unlock("hair.color.#{color}")') + + li.customize-menu + menu(label=env.t('rainbowColors')) + each color in ['rainbow','yellow','green','purple','blue','TRUred'] + button(type='button', ng-class='{locked: !user.purchased.hair.color.#{color}}', class='customize-option hair hair_bangs_1_#{color}', ng-click='unlock("hair.color.#{color}")') + button.btn.btn-sm.btn-primary(ng-hide='user.purchased.hair.color.rainbow && user.purchased.hair.color.yellow && user.purchased.hair.color.green && user.purchased.hair.color.purple && user.purchased.hair.color.blue && user.purchased.hair.color.TRUred', ng-click='unlock("hair.color.rainbow,hair.color.yellow,hair.color.green,hair.color.purple,hair.color.blue,hair.color.TRUred")')!= env.t('unlockSet5') + ' ' h5=env.t('bodyHair') // Bangs @@ -64,7 +63,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') when true: button(class='hair_base_#{k}_{{user.preferences.hair.color}} customize-option', type='button', ng-click='set({"preferences.hair.base":#{k}})') when false: button(class='hair_base_#{k}_{{user.preferences.hair.color}} customize-option', type='button', ng-class='{locked: !user.purchased.hair.base.#{k}}', ng-click='unlock("hair.base.#{k}")') - button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.base.2 && user.purchased.hair.base.4 && user.purchased.hair.base.5 && user.purchased.hair.base.6 && user.purchased.hair.base.7 && user.purchased.hair.base.8', ng-click='unlock("hair.base.2,hair.base.4,hair.base.5,hair.base.6,hair.base.7,hair.base.8")')!= env.t('unlockSet5') + ' ' + button.btn.btn-sm.btn-primary(ng-hide='user.purchased.hair.base.2 && user.purchased.hair.base.4 && user.purchased.hair.base.5 && user.purchased.hair.base.6 && user.purchased.hair.base.7 && user.purchased.hair.base.8', ng-click='unlock("hair.base.2,hair.base.4,hair.base.5,hair.base.6,hair.base.7,hair.base.8")')!= env.t('unlockSet5') + ' ' h5=env.t('bodyFacialHair') @@ -82,12 +81,12 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') each num in [1,2] button(class='hair_mustache_#{num}_{{user.preferences.hair.color}} customize-option', type='button', ng-class='{locked: !user.purchased.hair.mustache.#{num}}', ng-click='unlock("hair.mustache.#{num}")') - button.btn.btn-small.btn-primary(ng-hide='user.purchased.hair.mustache.1 && user.purchased.hair.mustache.2 && user.purchased.hair.beard.1 && user.purchased.hair.beard.2 && user.purchased.hair.beard.3', ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockSet5') + ' ' + button.btn.btn-sm.btn-primary(ng-hide='user.purchased.hair.mustache.1 && user.purchased.hair.mustache.2 && user.purchased.hair.beard.1 && user.purchased.hair.beard.2 && user.purchased.hair.beard.3', ng-click='unlock("hair.mustache.1,hair.mustache.2,hair.beard.1,hair.beard.2,hair.beard.3")')!= env.t('unlockSet5') + ' ' - .span4 + .col-md-4 h3=env.t('bodySkin') small - | 2 / + | 2 / = ' ' + env.t('locked') // skin li.customize-menu @@ -101,7 +100,7 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') menu each color in ['eb052b','f69922','f5d70f','0ff591','2b43f6','d7a9f7','800ed0','rainbow'] button.customize-option(type='button', class='skin_#{color}', ng-class='{locked: !user.purchased.skin.#{color}}', ng-click='unlock("skin.#{color}")') - 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")')!= env.t('unlockSet5') + ' ' + button.btn.btn-sm.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")')!= env.t('unlockSet5') + ' ' // Special Events // restore to d4df481 to see purchasing + "limited edition" code @@ -112,108 +111,117 @@ script(id='partials/options.profile.avatar.html', type='text/ng-template') button.customize-option(type='button', class='skin_#{color}', ng-if='user.purchased.skin.#{color}', ng-click='unlock("skin.#{color}")') script(id='partials/options.profile.stats.html', type='text/ng-template') - .row-fluid - .border-right(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "span4" : "span6"') - include ../shared/profiles/stats - .span4.border-right.allocate-stats(ng-if='user.flags.classSelected && !user.preferences.disableClasses') - h3=env.t('characterBuild') - h4 - =env.t('class') + ': ' - span {{ {warrior:'Warrior',wizard:'Mage',healer:'Healer',rogue:'Rogue'}[user.stats.class] }}  - a.btn.btn-danger.btn-mini(ng-click='changeClass(null)')=env.t('changeClass') - small 3 - table.table.table-striped - tr - td - strong - {{user.stats.points}}  - =env.t('unallocated') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('levelPopover')) - td - tr - td(colspan=2) - fieldset.auto-allocate - label.checkbox - input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set({"preferences.automaticAllocation": user.preferences.automaticAllocation?true: false})', ng-click='set({"preferences.allocationMode":"taskbased"})') - =env.t('autoAllocation') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('autoAllocationPop')) - form(ng-show='user.preferences.automaticAllocation',style='margin-left:1em') - label.radio - input(type='radio', name='allocationMode', value='flat', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "flat"})') - =env.t('evenAllocation') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('evenAllocationPop')) - label.radio - input(type='radio', name='allocationMode', value='classbased', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "classbased"})') - =env.t('classAllocation') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('classAllocationPop')) - label.radio - input(type='radio', name='allocationMode', value='taskbased', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "taskbased"})') - =env.t('taskAllocation') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('taskAllocationPop')) - div(ng-show='user.preferences.automaticAllocation && !(user.preferences.allocationMode === "taskbased") && (user.stats.points > 0)') - a.btn.btn-primary.btn-mini(ng-click='user.ops.allocateNow({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('distributePointsPop')) - i.icon-download.icon-white -   - =env.t('distributePoints') - tr - td= env.t('allocateStr') + ' {{user.stats.str}}' - td - a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("str")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateStrPop')) + - tr - td= env.t('allocateCon') + ' {{user.stats.con}}' - td - a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("con")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateConPop')) + - tr - td= env.t('allocatePer') + ' {{user.stats.per}}' - td - a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("per")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocatePerPop')) + - tr - td= env.t('allocateInt') + ' {{user.stats.int}}' - td - a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("int")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateIntPop')) + - div(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "span4" : "span6"') - include ../shared/profiles/achievements + .container-fluid + .row + .border-right(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "col-md-4" : "col-md-6"') + include ../shared/profiles/stats + .col-md-4.border-right.allocate-stats(ng-if='user.flags.classSelected && !user.preferences.disableClasses') + h3=env.t('characterBuild') + h4 + =env.t('class') + ': ' + span {{ {warrior:'Warrior',wizard:'Mage',healer:'Healer',rogue:'Rogue'}[user.stats.class] }}  + a.btn.btn-danger.btn-xs(ng-click='changeClass(null)')=env.t('changeClass') + small 3 + table.table.table-striped + tr + td + strong + {{user.stats.points}}  + =env.t('unallocated') +   + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('levelPopover')) + td + tr + td(colspan=2) + fieldset.auto-allocate + .checkbox + label + input(type='checkbox', ng-model='user.preferences.automaticAllocation', ng-change='set({"preferences.automaticAllocation": user.preferences.automaticAllocation?true: false})', ng-click='set({"preferences.allocationMode":"taskbased"})') + =env.t('autoAllocation') + |  + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('autoAllocationPop')) + form(ng-show='user.preferences.automaticAllocation',style='margin-left:1em') + .radio + label + input(type='radio', name='allocationMode', value='flat', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "flat"})') + =env.t('evenAllocation') + |  + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('evenAllocationPop')) + .radio + label + input(type='radio', name='allocationMode', value='classbased', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "classbased"})') + =env.t('classAllocation') + |  + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('classAllocationPop')) + .radio + label + input(type='radio', name='allocationMode', value='taskbased', ng-model='user.preferences.allocationMode', ng-change='set({"preferences.allocationMode": "taskbased"})') + =env.t('taskAllocation') + |  + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('taskAllocationPop')) + div(ng-show='user.preferences.automaticAllocation && !(user.preferences.allocationMode === "taskbased") && (user.stats.points > 0)') + a.btn.btn-primary.btn-xs(ng-click='user.ops.allocateNow({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('distributePointsPop')) + span.glyphicon.glyphicon-download +   + =env.t('distributePoints') + tr + td= env.t('allocateCon') + ' {{user.stats.con}}' + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("con")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateConPop')) + + tr + td= env.t('allocateInt') + ' {{user.stats.int}}' + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("int")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateIntPop')) + + tr + td= env.t('allocatePer') + ' {{user.stats.per}}' + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("per")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocatePerPop')) + + tr + td= env.t('allocateStr') + ' {{user.stats.str}}' + td + a.btn.btn-primary(ng-show='user.stats.points', ng-click='allocate("str")', popover-trigger='mouseenter', popover-placement='right', popover=env.t('allocateStrPop')) + + + div(ng-class='user.flags.classSelected && !user.preferences.disableClasses ? "col-md-4" : "col-md-6"') + include ../shared/profiles/achievements script(id='partials/options.profile.profile.html', type='text/ng-template') - button.btn.btn-default(ng-click='_editing.profile = true', ng-show='!_editing.profile')= env.t('edit') - button.btn.btn-primary(ng-click='save()', ng-show='_editing.profile')= env.t('save') - div(ng-show='!_editing.profile') - h4=env.t('displayName') - span(ng-show='profile.profile.name') {{profile.profile.name}} - span.muted(ng-hide='profile.profile.name') -  - =env.t('none') - |  - + .container-fluid + .row + .col-md-12(ng-show='!_editing.profile') + button.btn.btn-default(ng-click='_editing.profile = true', ng-show='!_editing.profile')= env.t('edit') + h4=env.t('displayName') + span(ng-show='profile.profile.name') {{profile.profile.name}} + span.muted(ng-hide='profile.profile.name') -  + =env.t('none') + |  - - h4=env.t('displayPhoto') - img(ng-show='profile.profile.imageUrl', ng-src='{{profile.profile.imageUrl}}') - span.muted(ng-hide='profile.profile.imageUrl') -  - =env.t('none') - |  - + h4=env.t('displayPhoto') + img(ng-show='profile.profile.imageUrl', ng-src='{{profile.profile.imageUrl}}') + span.muted(ng-hide='profile.profile.imageUrl') -  + =env.t('none') + |  - - h4=env.t('displayBlurb') - markdown(ng-show='profile.profile.blurb', ng-model='profile.profile.blurb') - span.muted(ng-hide='profile.profile.blurb') -  - =env.t('none') - |  - - //{{profile.profile.blurb | linky:'_blank'}} + h4=env.t('displayBlurb') + markdown(ng-show='profile.profile.blurb', ng-model='profile.profile.blurb') + span.muted(ng-hide='profile.profile.blurb') -  + =env.t('none') + |  - + //{{profile.profile.blurb | linky:'_blank'}} - div.whatever-options(ng-show='_editing.profile') - // TODO use photo-upload instead: https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/xMmADvxBOak - .control-group.option-large - label.control-label=env.t('displayName') - input.option-content(type='text', placeholder=env.t('fullName'), ng-model='editingProfile.name') - .control-group.option-large - label.control-label=env.t('photoUrl') - input.option-content(type='url', ng-model='editingProfile.imageUrl', placeholder=env.t('imageUrl')) - .control-group.option-large - label.control-label=env.t('displayBlurb') - textarea.option-content(style='height:15em;', placeholder=env.t('displayBlurb'), ng-model='editingProfile.blurb') - include ../shared/formatting-help + form.col-md-4(ng-show='_editing.profile', ng-submit='save()') + input.btn.btn-primary(type='submit', value=env.t('save')) + + // TODO use photo-upload instead: https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/xMmADvxBOak + .form-group + label=env.t('displayName') + input.form-control(type='text', placeholder=env.t('fullName'), ng-model='editingProfile.name') + .form-group + label=env.t('photoUrl') + input.form-control(type='url', ng-model='editingProfile.imageUrl', placeholder=env.t('imageUrl')) + .form-group + label=env.t('displayBlurb') + textarea.form-control(rows=5, placeholder=env.t('displayBlurb'), ng-model='editingProfile.blurb') + include ../shared/formatting-help script(id='partials/options.profile.html', type="text/ng-template") ul.nav.nav-tabs diff --git a/views/options/settings.jade b/views/options/settings.jade index 7026c558da..1befe51f21 100644 --- a/views/options/settings.jade +++ b/views/options/settings.jade @@ -17,102 +17,108 @@ script(id='partials/options.settings.html', type="text/ng-template") div(ui-view) script(type='text/ng-template', id='partials/options.settings.settings.html') - .row-fluid - .personal-options.span6 - h2=env.t('settings') - h4=env.t('customDayStart') - .option-group.option-short - input.option-content.option-time(type='number', min='0', max='24', ng-model='user.preferences.dayStart', ng-change='saveDayStart()') - span.input-suffix= ':00 (' + env.t('24HrClock') + ')' - div - small - div=env.t('clockInfo') - .alert.alert-danger - =env.t('subWarning1') - a(href='https://github.com/HabitRPG/habitrpg/issues/1057' target='_blank')=env.t('subWarning2') - =env.t('subWarning3') - hr - h4=env.t('language') - select(ng-model='language.code', ng-options='lang.code as lang.name for lang in avalaibleLanguages', ng-change='changeLanguage()') - hr - h4=env.t('misc') - label.checkbox - input(type='checkbox', ng-click='set({"preferences.hideHeader":user.preferences.hideHeader?false:true})', ng-checked='user.preferences.hideHeader!==true') - =env.t('showHeader') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('showHeaderPop')) - label.checkbox - input(type='checkbox', ng-click='toggleStickyHeader()', ng-checked='user.preferences.stickyHeader!==false') - =env.t('stickyHeader') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('stickyHeaderPop')) - label.checkbox - input(type='checkbox', ng-model='user.preferences.newTaskEdit', ng-change='set({"preferences.newTaskEdit": user.preferences.newTaskEdit?true: false})') - =env.t('newTaskEdit') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('newTaskEditPop')) - label.checkbox - input(type='checkbox', ng-model='user.preferences.tagsCollapsed', ng-change='set({"preferences.tagsCollapsed": user.preferences.tagsCollapsed?true: false})') - =env.t('startCollapsed') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startCollapsedPop')) - label.checkbox - input(type='checkbox', ng-model='user.preferences.advancedCollapsed', ng-change='set({"preferences.advancedCollapsed": user.preferences.advancedCollapsed?true: false})') - =env.t('startAdvCollapsed') -   - i.icon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startAdvCollapsedPop')) - button.btn(ng-click='showTour()', popover-placement='right', popover-trigger='mouseenter', popover=env.t('restartTour'))= env.t('showTour') - br - button.btn(ng-click='showBailey()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('showBaileyPop'))= env.t('showBailey') - br - button.btn(ng-click='modals.restore = true', popover-trigger='mouseenter', popover-placement='right', popover=env.t('fixValPop'))= env.t('fixVal') - div(ng-if='user.preferences.disableClasses==true') - button.btn(ng-click='user.ops.changeClass({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('enableClassPop'))= env.t('enableClass') - div(ng-if='!user.preferences.disableClasses && user.flags.classSelected') - button.btn(ng-click='showClassesTour()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('classTourPop'))= env.t('showClass') - - //- Why is ng-if='user.auth.local' validating for users *without* user.auth.local (facebook users)? adding .username here for extra - div(ng-if='user.auth.local.username') + .containter-fluid + .row + .personal-options.col-md-6 + h2=env.t('settings') + h4=env.t('customDayStart') + .form-group + input.form-control(type='number', min='0', max='24', ng-model='user.preferences.dayStart', ng-change='saveDayStart()') + span.input-suffix= ':00 (' + env.t('24HrClock') + ')' + div + small + div=env.t('clockInfo') + .alert.alert-danger.alert-sm + =env.t('subWarning1') + |  + a(href='https://github.com/HabitRPG/habitrpg/issues/1057' target='_blank')=env.t('subWarning2') + |  + =env.t('subWarning3') hr - h4=env.t('changePass') - form(ng-submit='changePassword(changePass)', ng-show='user.auth.local') - .control-group - input(type='password', placeholder=env.t('oldPass'), ng-model='changePass.oldPassword', required) - .control-group - input(type='password', placeholder=env.t('newPass'), ng-model='changePass.newPassword', required) - .control-group - input(type='password', placeholder=env.t('confirmPass'), ng-model='changePass.confirmNewPassword', required) - input.btn(type='submit', value=env.t('submit')) + h4=env.t('language') + select(ng-model='language.code', ng-options='lang.code as lang.name for lang in avalaibleLanguages', ng-change='changeLanguage()') + hr + h4=env.t('misc') + .checkbox + label + input(type='checkbox', ng-click='set({"preferences.hideHeader":user.preferences.hideHeader?false:true})', ng-checked='user.preferences.hideHeader!==true') + =env.t('showHeader') +   + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('showHeaderPop')) + .checkbox + label + input(type='checkbox', ng-click='toggleStickyHeader()', ng-checked='user.preferences.stickyHeader!==false') + =env.t('stickyHeader') +   + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('stickyHeaderPop')) + .checkbox + label + input(type='checkbox', ng-model='user.preferences.newTaskEdit', ng-change='set({"preferences.newTaskEdit": user.preferences.newTaskEdit?true: false})') + =env.t('newTaskEdit') +   + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('newTaskEditPop')) + .checkbox + label + input(type='checkbox', ng-model='user.preferences.tagsCollapsed', ng-change='set({"preferences.tagsCollapsed": user.preferences.tagsCollapsed?true: false})') + =env.t('startCollapsed') +   + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startCollapsedPop')) + .checkbox + label + input(type='checkbox', ng-model='user.preferences.advancedCollapsed', ng-change='set({"preferences.advancedCollapsed": user.preferences.advancedCollapsed?true: false})') + =env.t('startAdvCollapsed') +   + span.glyphicon.glyphicon-question-sign(popover-trigger='mouseenter', popover-placement='right', popover=env.t('startAdvCollapsedPop')) + button.btn.btn-default(ng-click='showTour()', popover-placement='right', popover-trigger='mouseenter', popover=env.t('restartTour'))= env.t('showTour') + button.btn.btn-default(ng-click='showBailey()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('showBaileyPop'))= env.t('showBailey') + button.btn.btn-default(ng-click='openRestoreModal()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('fixValPop'))= env.t('fixVal') + button.btn.btn-default(ng-if='user.preferences.disableClasses==true', ng-click='user.ops.changeClass({})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('enableClassPop'))= env.t('enableClass') + button.btn.btn-default(ng-if='!user.preferences.disableClasses && user.flags.classSelected', ng-click='showClassesTour()', popover-trigger='mouseenter', popover-placement='right', popover=env.t('classTourPop'))= env.t('showClass') - hr - h4=env.t('dangerZone') - a.btn.btn-danger(ng-click='modals.reset = true', popover-trigger='mouseenter', popover-placement='right', popover=env.t('resetAccPop'))= env.t('resetAccount') - a.btn.btn-danger(ng-click='modals.delete = true', popover-trigger='mouseenter', popover=env.t('deleteAccPop'))= env.t('deleteAccount') + //- Why is ng-if='user.auth.local' validating for users *without* user.auth.local (facebook users)? adding .username here for extra + div(ng-if='user.auth.local.username') + hr + h4=env.t('changePass') + form(ng-submit='changePassword(changePass)', ng-show='user.auth.local') + .form-group + input.form-control(type='password', placeholder=env.t('oldPass'), ng-model='changePass.oldPassword', required) + .form-group + input.form-control(type='password', placeholder=env.t('newPass'), ng-model='changePass.newPassword', required) + .form-group + input.form-control(type='password', placeholder=env.t('confirmPass'), ng-model='changePass.confirmNewPassword', required) + input.btn.btn-default(type='submit', value=env.t('submit')) + + hr + h4=env.t('dangerZone') + a.btn.btn-danger(ng-click='openModal("reset", {controller:"SettingsCtrl"})', popover-trigger='mouseenter', popover-placement='right', popover=env.t('resetAccPop'))= env.t('resetAccount') + a.btn.btn-danger(ng-click='openModal("delete", {controller:"SettingsCtrl"})', popover-trigger='mouseenter', popover=env.t('deleteAccPop'))= env.t('deleteAccount') script(type='text/ng-template', id='partials/options.settings.api.html') - .row.fluid - .span6 - h2=env.t('API') - small=env.t('APIText') - h6=env.t('userId') - pre.prettyprint {{user.id}} - h6=env.t('APIToken') - pre.prettyprint {{user.apiToken}} - h6=env.t('qrCode') - img(src='https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl=%7B%22address%22%3A%22https%3A%2F%2Fhabitrpg.com%22%2C%22user%22%3A%22{{user.id}}%22%2C%22key%22%3A%22{{user.apiToken}}%22%7D&choe=UTF-8&chld=L', alt='qrcode') + .containter-fluid + .row + .col-md-6 + h2=env.t('API') + small=env.t('APIText') + h6=env.t('userId') + pre.prettyprint {{user.id}} + h6=env.t('APIToken') + pre.prettyprint {{user.apiToken}} + h6=env.t('qrCode') + img(src='https://chart.googleapis.com/chart?cht=qr&chs=200x200&chl=%7B%22address%22%3A%22https%3A%2F%2Fhabitrpg.com%22%2C%22user%22%3A%22{{user.id}}%22%2C%22key%22%3A%22{{user.apiToken}}%22%7D&choe=UTF-8&chld=L', alt='qrcode') script(id='partials/options.settings.export.html', type="text/ng-template") - .row.fluid - .span6 - h2=env.t('dataExport') - small=env.t('saveData') - h4=env.t('habitHistory') - =env.t('exportHistory') - a(href="/export/history.csv")= ' ' + env.t('csv') - h4=env.t('userData') - =env.t('exportUserData') - a(href="/export/userdata.xml")= ' ' + env.t('xml') + ' ' - a(href="/export/userdata.json")= env.t('json') + .containter-fluid + .row + .col-md-6 + h2=env.t('dataExport') + small=env.t('saveData') + h4=env.t('habitHistory') + =env.t('exportHistory') + a(href="/export/history.csv")= ' ' + env.t('csv') + h4=env.t('userData') + =env.t('exportUserData') + a(href="/export/userdata.xml")= ' ' + env.t('xml') + ' ' + a(href="/export/userdata.json")= env.t('json') script(id='partials/options.settings.subscription.perks.html',type='text/ng-template') table.table.table-striped @@ -153,7 +159,9 @@ script(id='partials/options.settings.subscription.html',type='text/ng-template') div(ng-if='user.purchased.plan.customerId') .well - div(style='font-size:22px') - i.icon-ok=env.t('subscribed') + p.lead + =env.t('subscribed') + |   + span.glyphicon.glyphicon-ok div(ng-include="'partials/options.settings.subscription.perks.html'") - .btn.btn-small.btn-danger(ng-click='cancelSubscription()')=env.t('cancelSub') \ No newline at end of file + .btn.btn-sm.btn-danger(ng-click='cancelSubscription()')=env.t('cancelSub') diff --git a/views/options/social/challenge-box.jade b/views/options/social/challenge-box.jade index 5b7254bbc7..758a28ece3 100644 --- a/views/options/social/challenge-box.jade +++ b/views/options/social/challenge-box.jade @@ -1,18 +1,23 @@ // ------ Challenges ------- -.modal(style='position: relative;top: auto;left: auto;right: auto;margin: 0 auto 20px;z-index: 1;max-width: 100%;') - .modal-header - h3=env.t('challenges') +.panel.panel-default + .panel-heading + h3.panel-title=env.t('challenges') a.pull-right(target='_blank', href='https://trello.com/card/challenges-individual-party-guild-public/50e5d3684fe3a7266b0036d6/58') - i.icon-question-sign - .modal-body(bindonce='group.challenges') + span.glyphicon.glyphicon-question-sign + .panel-body.modal-fixed-height(bindonce='group.challenges') div(ng-if='group.challenges.length > 0') table.table.table-striped tr(ng-repeat='challenge in group.challenges') td a(ui-sref='options.social.challenges.detail({cid:challenge._id})') {{challenge.name}} div(ng-if='group.challenges.length == 0') - p=env.t('noChallenges') - a(class='label' ui-sref='options.social.challenges') - i.icon-bullhorn + p + |  + =env.t('noChallenges') + |  + a.label.label-primary(ui-sref='options.social.challenges') + span.glyphicon.glyphicon-bullhorn + |  =env.t('challenges') + |  =env.t('toCreate') diff --git a/views/options/social/challenges.jade b/views/options/social/challenges.jade index ae90ff588e..748349de64 100644 --- a/views/options/social/challenges.jade +++ b/views/options/social/challenges.jade @@ -1,5 +1,5 @@ script(type='text/ng-template', id='partials/options.social.challenges.detail.close.html') - a.btn.btn-small.btn-danger(ng-click="delete(closingChal)")=env.t('delete') + a.btn.btn-sm.btn-danger(ng-click="delete(closingChal)")=env.t('delete') h5= '- ' + env.t('or') + ' -' select(ui-select2, ng-required=true, ng-model='closingChal.winner', data-placeholder=env.t('selectWinner'), ng-change='selectWinner(closingChal)', ) option(value='') @@ -9,42 +9,44 @@ script(type='text/ng-template', id='partials/options.social.challenges.detail.cl a(ng-click='cancelClosing(closingChal)')=env.t('cancel') script(type='text/ng-template', id='partials/options.social.challenges.detail.member.html') - .modal.wide-modal - .modal-header - button.close(type='button', ng-click='$state.go("^")', aria-hidden='true') × - h3 {{obj.profile.name}} - .modal-body - habitrpg-tasks(main=false) - .modal-footer - a.btn(ng-click='$state.go("^")')=env.t('close') + .modal.bs-modal-lg(style='display: block') + .modal-dialog.modal-lg + .modal-content + .modal-header + button.close(type='button', ng-click='$state.go("^")', aria-hidden='true') × + h3 {{obj.profile.name}} + .modal-body + habitrpg-tasks(main=false) + .modal-footer + a.btn.btn-default(ng-click='$state.go("^")')=env.t('close') script(type='text/ng-template', id='partials/options.social.challenges.detail.html') // Edit button div(bindonce='challenge', ng-if='challenge.leader==user._id') - ul.unstyled - li(ng-hide='challenge._locked==false') - button.btn.btn-small.btn-default(ng-click='challenge._locked = false')=env.t('edit') - button.btn.btn-small.btn-warning(ng-click='close(challenge, $event)', tooltip=env.t('deleteOrSelect'))=env.t('endChallenge') - li(ng-show='challenge._locked==false') - button.btn.btn-small.btn-primary(ng-click='save(challenge)')=env.t('save') - button.btn.btn-small.btn-default(ng-click='challenge._locked=true')=env.t('cancel') + div(ng-hide='challenge._locked==false') + button.btn.btn-sm.btn-default(ng-click='challenge._locked = false')=env.t('edit') + button.btn.btn-sm.btn-warning(ng-click='close(challenge, $event)', tooltip=env.t('deleteOrSelect'))=env.t('endChallenge') - div(ng-show='challenge._locked==false') - .-options - input.option-content(type='text', ng-model='challenge.name') - .option-medium - input.option-content(type='text', minlength="3", maxlength="16", ng-model='challenge.shortName', placeholder=env.t('challengeTag'), required) - textarea.option-content(cols='3', placeholder=env.t('challengeDescr'), ng-model='challenge.description') + form(ng-show='challenge._locked==false') + .form-group(ng-show='challenge._locked==false') + button.btn.btn-sm.btn-primary(ng-click='save(challenge)')=env.t('save') + button.btn.btn-sm.btn-default(ng-click='challenge._locked=true')=env.t('cancel') + .form-group + input.form-control(type='text', ng-model='challenge.name') + .form-group + input.form-control(type='text', minlength="3", maxlength="16", ng-model='challenge.shortName', placeholder=env.t('challengeTag'), required) + .form-group + textarea.form-control(cols='3', placeholder=env.t('challengeDescr'), ng-model='challenge.description') hr .well(bindonce='challenge', ng-if='challenge.description') markdown(ng-model='challenge.description') - i.icon-question-sign(popover=env.t('challengeDiscription'), popover-trigger='mouseenter', popover-placement='right') + span.glyphicon.glyphicon-question-sign(popover=env.t('challengeDiscription'), popover-trigger='mouseenter', popover-placement='right') habitrpg-tasks(obj='challenge', main=false) // Member List div(bindonce='challenge', ng-if='challenge.members.length > 0') - a.btn.btn-primary.btn-small.pull-right(href='/api/v2/challenges/{{challenge._id}}/csv') + a.btn.btn-primary.btn-sm.pull-right(href='/api/v2/challenges/{{challenge._id}}/csv') =env.t('exportChallengeCSV') h3=env.t('hows') menu @@ -62,94 +64,102 @@ script(type='text/ng-template', id='partials/options.social.challenges.detail.ht div(ui-view) script(type='text/ng-template', id='partials/options.social.challenges.html') - .row-fluid - .span2.well#challenges-filters - h3=env.t('filter') + ':' - h4=env.t('groups') - ul.unstyled - a.btn.btn-mini.pull-left(ng-click='selectAll()')=env.t('all') - a.btn.btn-mini(ng-click='selectNone()')=env.t('none') - br - label.checkbox(ng-repeat='group in groupsFilter') - input(type='checkbox', ng-model='search.group[group._id]') - | {{group.name}} - h4=env.t('membership') - label.radio - input(type='radio', name='search-participation-radio', ng-click='search._isMember = true') - =env.t('participating') - label.radio - input(type='radio', name='search-participation-radio', ng-click='search._isMember = false') - =env.t('notParticipating') - label.radio - input(type='radio', name='search-participation-radio', ng-click='search._isMember = undefined', checked='checked') - =env.t('either') - .span10 - // Creation form - button.btn.btn-success#create-challenge-btn(ng-click='create()', ng-hide='newChallenge')=env.t('createChallenge') - .create-challenge-from.well(ng-if='newChallenge') - form(ng-submit='save(newChallenge)') - div - input.btn.btn-success(type='submit', value=env.t('save')) - input.btn.btn-danger(type='button', ng-click='discard()', value=env.t('discard')) - select(ng-model='newChallenge.group', ng-required='required', name='Group', ng-options='g._id as g.name for g in groups') - .challenge-options - input.option-content(type='text', ng-model='newChallenge.name', placeholder=env.t('challengeTitle'), required='required') + .container-fluid + .row + .col-md-2.well#challenges-filters + h3=env.t('filter') + ':' + h4=env.t('groups') + ul.list-unstyled + a.btn.btn-xs.btn-default.pull-left(ng-click='selectAll()')=env.t('all') + a.btn.btn-xs.btn-default(ng-click='selectNone()')=env.t('none') + br + .checkbox(ng-repeat='group in groupsFilter') + label + input(type='checkbox', ng-model='search.group[group._id]') + | {{group.name}} + h4=env.t('membership') + .radio + label + input(type='radio', name='search-participation-radio', ng-click='search._isMember = true') + =env.t('participating') + .radio + label + input(type='radio', name='search-participation-radio', ng-click='search._isMember = false') + =env.t('notParticipating') + .radio + label + input(type='radio', name='search-participation-radio', ng-click='search._isMember = undefined', checked='checked') + =env.t('either') + .col-md-10 + // Creation form + button.btn.btn-success#create-challenge-btn(ng-click='create()', ng-hide='newChallenge')=env.t('createChallenge') + .create-challenge-from.well(ng-if='newChallenge') + form(ng-submit='save(newChallenge)') + div + input.btn.btn-success(type='submit', value=env.t('save')) + input.btn.btn-danger(type='button', ng-click='discard()', value=env.t('discard')) + select(ng-model='newChallenge.group', ng-required='required', name='Group', ng-options='g._id as g.name for g in groups') + .challenge-options + .form-group + input.form-control(type='text', ng-model='newChallenge.name', placeholder=env.t('challengeTitle'), required='required') - .option-medium - input.option-content(type='text', minlength="3", maxlength="16", ng-model='newChallenge.shortName', placeholder=env.t('challengeTag'), required) - a(target='_blank', href='http://habitrpg.wikia.com/wiki/Tags') - i.icon-question-sign(popover=env.t('challengeTagPop'), popover-trigger='mouseenter', popover-placement='right') + .form-group + input.form-control(type='text', minlength="3", maxlength="16", ng-model='newChallenge.shortName', placeholder=env.t('challengeTag'), required) + a(target='_blank', href='http://habitrpg.wikia.com/wiki/Tags') + span.glyphicon.glyphicon-question-sign(popover=env.t('challengeTagPop'), popover-trigger='mouseenter', popover-placement='right') - textarea.option-content(cols='3', placeholder=env.t('challengeDescr'), ng-model='newChallenge.description') + .form-group + textarea.form-control(cols='3', placeholder=env.t('challengeDescr'), ng-model='newChallenge.description') - //- what's going on here? - br - br + //- what's going on here? + br + br - .option-medium - input.option-content(type='number', min="{{newChallenge.group=='habitrpg' ? 1 : 0}}", max="{{maxPrize}}", ng-model='newChallenge.prize', placeholder=env.t('prize')) - span.input-suffix.Pet_Currency_Gem1x.inline-gems - i.icon-question-sign(popover=env.t('prizePop'), popover-trigger='mouseenter', popover-placement='right') - span(ng-show='newChallenge.group=="habitrpg"') - !=env.t('publicChallenges') + .form-group + input.form-control(type='number', min="{{newChallenge.group=='habitrpg' ? 1 : 0}}", max="{{maxPrize}}", ng-model='newChallenge.prize', placeholder=env.t('prize')) + span.input-suffix.Pet_Currency_Gem1x.inline-gems + span.glyphicon.glyphicon-question-sign(popover=env.t('prizePop'), popover-trigger='mouseenter', popover-placement='right') + span(ng-show='newChallenge.group=="habitrpg"') + !=env.t('publicChallenges') - .option-medium(ng-if='user.contributor.admin') - label.checkbox - input(type='checkbox', ng-model='newChallenge.official') - =env.t('officialChallenge') + .form-group(ng-if='user.contributor.admin') + .checkbox + label + input(type='checkbox', ng-model='newChallenge.official') + =env.t('officialChallenge') - habitrpg-tasks(main=false, obj='newChallenge') + habitrpg-tasks(main=false, obj='newChallenge') - // Challenges list - .accordion-group(ng-repeat='challenge in challenges|filter:filterChallenges') - .accordion-heading - ul.pull-right.challenge-accordion-header-specs - li(ng-if='challenge.official') - span.label.label-success=env.t('officialChallenge') - li {{challenge.group.name}} - li - =env.t('by') - strong {{challenge.leader.profile.name}} - li - | {{challenge.memberCount}} - = ' ' + env.t('participants') - li(ng-show='challenge.prize') - // prize - table(ng-show='challenge.prize') - tr - td {{challenge.prize}} - td - .Pet_Currency_Gem1x(style='margin-left:0px') - td=env.t('prize') - li - // leave / join - a.btn.btn-small.btn-danger(ng-show='challenge._isMember', ng-click='clickLeave(challenge, $event)') - i.icon-ban-circle - =env.t('leave') - a.btn.btn-small.btn-success(ng-hide='challenge._isMember', ng-click='join(challenge)') - i.icon-ok - =env.t('join') - a.accordion-toggle(ng-click='toggle(challenge._id)') {{challenge.name}} - .accordion-body(ng-class='{collapse: !$stateParams.cid == challenge._id}') - .accordion-inner(ng-if='$stateParams.cid == challenge._id') - div(ui-view) + // Challenges list + .panel-group + .panel.panel-default(ng-repeat='challenge in challenges|filter:filterChallenges') + .panel-heading + ul.pull-right.challenge-accordion-header-specs + li(ng-if='challenge.official') + span.label.label-success=env.t('officialChallenge') + li {{challenge.group.name}} + li + =env.t('by') + strong {{challenge.leader.profile.name}} + li + | {{challenge.memberCount}} + = ' ' + env.t('participants') + li(ng-show='challenge.prize') + p + // prize + | {{challenge.prize}}  + span.inline-block.Pet_Currency_Gem1x + |   + =env.t('prize') + li + // leave / join + a.btn.btn-sm.btn-danger(ng-show='challenge._isMember', ng-click='clickLeave(challenge, $event)') + span.glyphicon.glyphicon-ban-circle + =env.t('leave') + a.btn.btn-sm.btn-success(ng-hide='challenge._isMember', ng-click='join(challenge)') + span.glyphicon.glyphicon-ok + =env.t('join') + a.accordion-toggle(ng-click='toggle(challenge._id)') {{challenge.name}} + .panel-body(ng-class='{collapse: !$stateParams.cid == challenge._id}') + .accordion-inner(ng-if='$stateParams.cid == challenge._id') + div(ui-view) diff --git a/views/options/social/chat-box.jade b/views/options/social/chat-box.jade index e2abc37737..e1831531de 100644 --- a/views/options/social/chat-box.jade +++ b/views/options/social/chat-box.jade @@ -1,16 +1,14 @@ form.chat-form(ng-submit='postChat(group,message.content)') - .-options - //FIXME ng-model makes this painfully slow! using jquery for now, which is really non-angular-like - .control-group.option-large(ng-controller='AutocompleteCtrl') - textarea.chat-textarea.option-content(style='height:6em;', ui-keypress='{13:"postChat(group,message.content)"}', ng-model='message.content', updateinterval='250', flag='@', at-user, auto-complete) - span.user-list(ng-show='!isAtListHidden') - ul.list-at-user - li(bindonce='response', ng-repeat='user in response | filter:query.text | limitTo: 5', ng-click='autoComplete(user)') - span.username.label(class='label-contributor-{{user.contributor.level}}', ng-class='{"label-npc": user.backer.npc}') {{user.user}} - table.pull-right - tr - td - input.btn.chat-btn(type='submit', value=env.t('sendChat'), ng-class='{disabled: _sending == true}') - td - button.btn(type="button", ng-click='sync(group)', tooltip=env.t('toolTipMsg')) - i(class='pull-right icon-refresh') + div(ng-controller='AutocompleteCtrl') + textarea.form-control(rows=4, ui-keypress='{13:"postChat(group,message.content)"}', ng-model='message.content', updateinterval='250', flag='@', at-user, auto-complete) + span.user-list(ng-show='!isAtListHidden') + ul.list-at-user + li(bindonce='response', ng-repeat='user in response | filter:query.text | limitTo: 5', ng-click='autoComplete(user)') + span.username.label.label-default(ng-class='userLevelStyle(user)') {{user.user}} + .row + .col-md-9 + include ../../shared/formatting-help + .col-md-3 + input.btn.btn-default.chat-btn(type='submit', value=env.t('sendChat'), ng-class='{disabled: _sending == true}') + button.btn.btn-default(type="button", ng-click='sync(group)', tooltip=env.t('toolTipMsg')) + span.glyphicon.glyphicon-refresh.pull-right \ No newline at end of file diff --git a/views/options/social/chat-message.jade b/views/options/social/chat-message.jade index 6aab142028..6377032d6f 100644 --- a/views/options/social/chat-message.jade +++ b/views/options/social/chat-message.jade @@ -1,14 +1,16 @@ li.chat-message(bindonce='group.chat', ng-repeat='message in group.chat', bo-class='{highlight: isUserMentioned(user,message), "own-message": user._id == message.uuid}') div(class='scrollable-message') - a.label.chat-message(class='hidden-label') - span {{message.user}}  - markdown(ng-model='message.text', style='padding:0px 3px') + a.label.label-default.chat-message(class='hidden-label') + span {{message.user}} + |   + markdown(ng-model='message.text') | - - span.muted.time(from-now='message.timestamp',style='padding:0px 3px') - span(style='padding:0px 3px') - a.label(ng-show='countExists(message.likes)',ng-class='{"label-success":message.likes[user._id]}',ng-click='likeChatMessage(group,message)') +{{countExists(message.likes)}} - a.chat-plus-one.muted(ng-show='!countExists(message.likes)',ng-click='likeChatMessage(group,message)',style='background-color:#eee;padding:3px 3px 0px 3px') +1 - a(bo-show='user.contributor.admin || message.uuid == user.id', ng-click='deleteChatMessage(group, message)',style='padding: 0px 3px') - i.icon-trash(tooltip=env.t('delete')) - a.label.chat-message(bo-show='message.user', class='float-label', bo-class='{"label-npc": message.backer.npc, "label-contributor-{{message.contributor.level}}":message.contributor.level}', ng-click='clickMember(message.uuid, true)') + span.muted.time(from-now='message.timestamp') + span + a.label.label-default(ng-show='countExists(message.likes)', ng-class='{"label-success":message.likes[user._id]}', ng-click='likeChatMessage(group,message)') +{{countExists(message.likes)}} + a.chat-plus-one.muted(ng-show='!countExists(message.likes)', ng-click='likeChatMessage(group,message)') +1 + |   + a(bo-show='user.contributor.admin || message.uuid == user.id', ng-click='deleteChatMessage(group, message)') + span.glyphicon.glyphicon-trash(tooltip=env.t('delete')) + a.label.label-default.chat-message(bo-show='message.user', class='float-label', bo-class='{"label-npc": message.backer.npc, "label-contributor-{{message.contributor.level}}":message.contributor.level}', ng-click='clickMember(message.uuid, true)') span(tooltip='{{contribText(message.contributor, message.backer)}}') {{message.user}}  diff --git a/views/options/social/create-group.jade b/views/options/social/create-group.jade index edf985d1a4..bd2563cd76 100644 --- a/views/options/social/create-group.jade +++ b/views/options/social/create-group.jade @@ -1,27 +1,27 @@ -form.form-horizontal(ng-submit='create(newGroup)') - .control-group.whatever-options - .control-group - label.control-label(for='new-group-name') - | {{text}}  - =env.t('name') - .controls - input#new-group-name.input-medium.option-content(required, type='text', placeholder='{{text}} Name', ng-model='newGroup.name') - .control-group - label.control-label(for='new-group-description')=env.t('description') - .controls - textarea#new-group-description.option-content(cols='3', placeholder=env.t('description'), ng-model='newGroup.description') - .control-group(ng-show='type=="guild"') - .controls - label.radio - input(type='radio', name='new-group-privacy', value='public', ng-model='newGroup.privacy') - =env.t('public') - label.radio - input(type='radio', name='new-group-privacy', value='private', ng-model='newGroup.privacy') - =env.t('inviteOnly') - input.btn(type='submit', ng-disabled='!newGroup.privacy && !newGroup.name', value=env.t('create')) +.container-fluid + .row + form.col-md-12.form-horizontal(ng-submit='create(newGroup)') + .form-group + label.control-label(for='new-group-name') + | {{text}}  + =env.t('name') + input.form-control#new-group-name.input-medium.option-content(required, type='text', placeholder=('{{text}} ' + env.t('name')), ng-model='newGroup.name') + .form-group + label(for='new-group-description')=env.t('description') + textarea.form-control#new-group-description.option-content(cols='3', placeholder=env.t('description'), ng-model='newGroup.description') + .form-group(ng-show='type=="guild"') + .radio + label + input(type='radio', name='new-group-privacy', value='public', ng-model='newGroup.privacy') + =env.t('public') + .radio + label + input(type='radio', name='new-group-privacy', value='private', ng-model='newGroup.privacy') + =env.t('inviteOnly') + br + input.btn.btn-default(type='submit', ng-disabled='!newGroup.privacy && !newGroup.name', value=env.t('create')) span.gem-cost= '4 ' + env.t('gems') p small=env.t('gemCost') - .control-group(ng-show='type=="party"') - .controls - input.btn(type='submit', value=env.t('create')) + .form-group(ng-show='type=="party"') + input.btn.btn-default.form-control(type='submit', value=env.t('create')) diff --git a/views/options/social/group.jade b/views/options/social/group.jade index 3235cea7a9..918c25732e 100644 --- a/views/options/social/group.jade +++ b/views/options/social/group.jade @@ -4,174 +4,177 @@ a.pull-right.gem-wallet(popover-trigger='mouseenter', popover-title=env.t('guild .Pet_Currency_Gem2x.Gems | {{group.balance * 4 | number:0 }} =env.t('guildGems') -.row-fluid - .span4 +.container-fluid + .row + .col-md-4 - // ------ Bosses ------- - .modal.inline-modal(bindonce='group', ng-if='group.type==="party" && group.quest.key') - .modal-header - h3(ng-if='group.quest.active==false')=env.t('questInvitation') + '{{Content.quests[group.quest.key].text}}' - h3(ng-if='group.quest.active==true') {{Content.quests[group.quest.key].text}} - .modal-body - div(ng-if='group.quest.active==false') - table.table.table-striped - tr(ng-repeat='member in group.members') - td {{member.profile.name}} - td {{group.quest.members[member._id] === true ? env.t('accepted') : group.quest.members[member._id] === false ? env.t('rejected') : env.t('pending')}} - hr - .npc_ian.pull-left - p=env.t('questStart') - button.btn.btn-small.btn-warning(ng-if='!group.quest.leader || group.quest.leader==user._id', ng-click='party.$questAccept({"force":true})')=env.t('begin') - //-TODO Cancel button - //-TODO Both force-start & cancel should only be available to quest-initiator - - div(ng-if='group.quest.active==true') - div(ng-if='Content.quests[group.quest.key].boss') - div(class="quest_{{group.quest.key}}") - //- - .progress(style="height:10px") - .bar(style='width: {{Shared.percent(group.quest.hp, Content.quests[group.quest.key].hp)}}%;') - span.meter-text - i.icon-heart - | {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}} - .hero-stats - .meter.health(title=env.t('bossHP')) - .bar(style='width: {{Shared.percent(group.quest.progress.hp, Content.quests[group.quest.key].boss.hp)}}%;') - span.meter-text - i.icon-heart - | {{group.quest.progress.hp | number:0}} / {{Content.quests[group.quest.key].boss.hp}} - - div(ng-if='Content.quests[group.quest.key].collect') - h4=env.t('collected') + ':' + // ------ Bosses ------- + .panel.panel-default(bindonce='group', ng-if='group.type==="party" && group.quest.key') + .panel-heading + h3.panel-title(ng-if='group.quest.active==false')=env.t('questInvitation') + ' {{Content.quests[group.quest.key].text}}' + h3.panel-title(ng-if='group.quest.active==true') {{Content.quests[group.quest.key].text}} + .panel-body.modal-fixed-height + div(ng-if='group.quest.active==false') table.table.table-striped - tr(ng-repeat='(k,v) in group.quest.progress.collect', class='quest_collected_{{v >= Content.quests[group.quest.key].collect[k].count}}') - td - div.pull-left(class='quest_{{group.quest.key}}_{{k}}') - | {{Content.quests[group.quest.key].collect[k].text}} - td - {{v}} / {{Content.quests[group.quest.key].collect[k].count}} + tr(ng-repeat='member in group.members') + td {{member.profile.name}} + td {{group.quest.members[member._id] === true ? env.t('accepted') : group.quest.members[member._id] === false ? env.t('rejected') : env.t('pending')}} + hr + .npc_ian.pull-left + p=env.t('questStart') + button.btn.btn-sm.btn-warning(ng-if='!group.quest.leader || group.quest.leader==user._id', ng-click='party.$questAccept({"force":true})')=env.t('begin') + //-TODO Cancel button + //-TODO Both force-start & cancel should only be available to quest-initiator - div(ng-bind-html='Content.quests[group.quest.key].notes') + div(ng-if='group.quest.active==true') + div(ng-if='Content.quests[group.quest.key].boss') + div(class="quest_{{group.quest.key}}") + //- + .progress + .bar(style='width: {{Shared.percent(group.quest.hp, Content.quests[group.quest.key].hp)}}%;') + span.meter-text + span.glyphicon.glyphicon-heart + | {{group.quest.hp | number:0}} / {{Content.quests[group.quest.key].hp}} + .hero-stats + .meter.health(title=env.t('bossHP')) + .bar(style='width: {{Shared.percent(group.quest.progress.hp, Content.quests[group.quest.key].boss.hp)}}%;') + span.meter-text + span.glyphicon.glyphicon-heart + | {{group.quest.progress.hp | number:0}} / {{Content.quests[group.quest.key].boss.hp}} + + div(ng-if='Content.quests[group.quest.key].collect') + h4=env.t('collected') + ':' + table.table.table-striped + tr(ng-repeat='(k,v) in group.quest.progress.collect', class='quest_collected_{{v >= Content.quests[group.quest.key].collect[k].count}}') + td + div.pull-left(class='quest_{{group.quest.key}}_{{k}}') + | {{Content.quests[group.quest.key].collect[k].text}} + td + {{v}} / {{Content.quests[group.quest.key].collect[k].count}} + + div(ng-bind-html='Content.quests[group.quest.key].notes') + hr + h5=env.t('participants') + table.table.table-striped + tr(ng-repeat='(k,v) in group.members', ng-show='group.quest.members[v._id]') + td {{v.profile.name}} + quest-rewards(key='{{group.quest.key}}') + + div(ng-if='Content.quests[group.quest.key].boss') + .npc_ian.pull-left + p!=env.t('bossDmg1') + br + p=env.t('bossDmg2') + + div(ng-if='Content.quests[group.quest.key].collect') + .npc_ian.pull-left + p=env.t('bossColl1') + br + p=env.t('bossColl2') + + + button.btn.btn-xs.btn-danger(ng-if='!group.quest.leader || group.quest.leader==user._id', ng-click='questAbort()')=env.t('abort') + + // ------ Information ------- + .panel.panel-default + .panel-heading(bindonce='group') + h3.panel-title + | {{group.name}} + span(ng-if='group.leader == user.id') + button.btn.btn-primary.pull-right(ng-click='save(group)', ng-show='group._editing')=env.t('save') + button.btn.btn-default.pull-right(ng-click='group._editing = true', ng-hide='group._editing')=env.t('editGroup') + + .panel-body + form(ng-show='group._editing') + .form-group + label=env.t('groupName') + input.form-control(type='text', ng-model='group.name', placeholder=env.t('groupName')) + .form-group + label=env.t('description') + textarea.form-control(rows=6, ng-model='group.description') + include ../../shared/formatting-help + .form-group + label=env.t('logoUrl') + input.form-control(type='url', placeholder=env.t('logoUrl'), ng-model='group.logo') + + h4=env.t('assignLeader') + select#group-leader-selection(ng-model='group._newLeader', ng-options='member.profile.name for member in group.members') + + div(ng-show='!group._editing') + img.pull-right(ng-show='group.logo', ng-src='{{group.logo}}') + markdown(ng-model='group.description') hr - h5=env.t('participants') + small.muted Group ID: {{group._id}} + + include ./challenge-box + + // ------ Members ------- + .panel.panel-default + .panel-heading + h3.panel-title=env.t('members') + .panel-body.modal-fixed-height + span + =env.t('partyList') + |  + select#partyOrder( + ng-model='user.party.order', + ng-controller='ChatCtrl', + ng-options='k as v for (k , v) in partyOrderChoices', + ng-change='set({"party.order": user.party.order})' + ) + table.table.table-striped(bindonce='group') + tr(ng-repeat='member in group.members') + td + // allow leaders to ban members + div(ng-show='group.leader == user.id && user.id!=member._id') + a(ng-click='removeMember(group, member, true)') + span.glyphicon.glyphicon-ban-circle(tooltip=env.t('banTip')) + a + span(ng-class='{"badge badge-info": group.leader==member._id}', ng-click='clickMember(member._id, true)') + | {{member.profile.name}} + tr(ng-if='group.memberCount > group.members.length') + td + |{{group.memberCount - group.members.length}} + = ' ' + env.t('moreMembers') + | + + h4(ng-show='group.invites.length > 0')=env.t('invited') table.table.table-striped - tr(ng-repeat='(k,v) in group.members', ng-show='group.quest.members[v._id]') - td {{v.profile.name}} - quest-rewards(key='{{group.quest.key}}') - - div(ng-if='Content.quests[group.quest.key].boss') - .npc_ian.pull-left - p!=env.t('bossDmg1') - br - p=env.t('bossDmg2') - - div(ng-if='Content.quests[group.quest.key].collect') - .npc_ian.pull-left - p=env.t('bossColl1') - br - p=env.t('bossColl2') + tr(ng-repeat='invite in group.invites') + td + // allow leaders to ban members + div(ng-show='group.leader == user.id') + a(ng-click='removeMember(group, invite, false)') + span.glyphicon.glyphicon-ban-circle(tooltip=env.t('banTip')) + a + span(ng-click='clickMember(invite._id, true)') + | {{invite.profile.name}} + .panel-footer + form.form-inline(ng-submit='invite(group)') + //.alert.alert-danger(ng-show='_groupError') {{_groupError}} + .form-group + input.form-control(type='text', placeholder=env.t('userId'), ng-model='group.invitee') +   + input.btn.btn-default(type='submit', value=env.t('invite')) - button.btn.btn-mini.btn-danger(ng-if='!group.quest.leader || group.quest.leader==user._id', ng-click='questAbort()')=env.t('abort') + a.btn.btn-danger(data-id='{{group.id}}', ng-click='clickLeave(group, $event)')=env.t('leave') - // ------ Information ------- - .modal.inline-modal - .modal-header(bindonce='group') - span(ng-if='group.leader == user.id') - button.btn.btn-primary.pull-right(ng-click='save(group)', ng-show='group._editing')=env.t('save') - button.btn.btn-default.pull-right(ng-click='group._editing = true', ng-hide='group._editing')=env.t('editGroup') - h3 {{group.name}} - .modal-body - div.whatever-options(ng-show='group._editing') - .control-group.option-large - label.control-labe=env.t('groupName') - input.option-content(type='text', ng-model='group.name', placeholder=env.t('groupName')) - .control-group.option-large - label.control-label=env.t('description') - textarea.option-content(style='height:15em;', placeholder=env.t('groupDescr'), ng-model='group.description') - .control-group.option-large - label.control-label=env.t('logoUrl') - input.option-content(type='url', placeholder=env.t('logoUrl'), ng-model='group.logo') + .col-md-8 + div() + textarea.form-control(ng-show='group._editing', rows=6, placeholder=env.t('leaderMsg'), ng-model='group.leaderMessage') + table(ng-show='group.leaderMessage') + tr + td + .popover.static-popover.fade.right.in.wide-popover + .arrow + h3.popover-title {{Members.members[group.leader].profile.name}} + .popover-content + markdown(ng-model='group.leaderMessage') + div(ng-controller='ChatCtrl') + h3=env.t('chat') + include ./chat-box - h4=env.t('assignLeader') - select#group-leader-selection(ng-model='group._newLeader', ng-options='member.profile.name for member in group.members') - - div(ng-show='!group._editing') - img.pull-right(ng-show='group.logo', style='max-width:150px', ng-src='{{group.logo}}') - markdown(ng-model='group.description') - hr - small.muted Group ID: {{group._id}} - - include ./challenge-box - - // ------ Members ------- - .modal.inline-modal - .modal-header - h3=env.t('members') - .modal-body - span - =env.t('partyList') - select#partyOrder( - style='width:140px', - ng-model='user.party.order', - ng-controller='ChatCtrl', - ng-options='k as v for (k , v) in partyOrderChoices', - ng-change='set({"party.order": user.party.order})' - ) - table.table.table-striped(bindonce='group') - tr(ng-repeat='member in group.members') - td - // allow leaders to ban members - div(ng-show='group.leader == user.id && user.id!=member._id') - a(ng-click='removeMember(group, member, true)') - i.icon-ban-circle(tooltip=env.t('banTip')) - a - span(ng-class='{"badge badge-info": group.leader==member._id}', ng-click='clickMember(member._id, true)') - | {{member.profile.name}} - tr(ng-if='group.memberCount > group.members.length') - td - |{{group.memberCount - group.members.length}} - = ' ' + env.t('moreMembers') - | - - h4(ng-show='group.invites.length > 0')=env.t('invited') - table.table.table-striped - tr(ng-repeat='invite in group.invites') - td - // allow leaders to ban members - div(ng-show='group.leader == user.id') - a(ng-click='removeMember(group, invite, false)') - i.icon-ban-circle(tooltip=env.t('banTip')) - a - span(ng-click='clickMember(invite._id, true)') - | {{invite.profile.name}} - .modal-footer.whatever-options - form.form-inline(ng-submit='invite(group)') - //.alert.alert-danger(ng-show='_groupError') {{_groupError}} - .control-group.option-large - input.option-content(type='text', placeholder=env.t('userId'), ng-model='group.invitee') - input.btn(type='submit', value=env.t('invite')) - - - a.btn.btn-danger(data-id='{{group.id}}', ng-click='clickLeave(group, $event)')=env.t('leave') - - .span8 - div.blah-options(ng-show='group._editing') - .option-large - textarea.option-content(style='height:15em;', placeholder=env.t('leaderMsg'), ng-model='group.leaderMessage') - table(ng-show='group.leaderMessage') - tr - td - .popover.static-popover.fade.right.in.wide-popover - .arrow - h3.popover-title {{Members.members[group.leader].profile.name}} - .popover-content - markdown(ng-model='group.leaderMessage') - div(ng-controller='ChatCtrl') - h3=env.t('chat') - include ./chat-box - include ../../shared/formatting-help - - ul.unstyled.tavern-chat - include ./chat-message + ul.list-unstyled.tavern-chat + include ./chat-message diff --git a/views/options/social/hall.jade b/views/options/social/hall.jade index adebd5e3ee..708a972da2 100644 --- a/views/options/social/hall.jade +++ b/views/options/social/hall.jade @@ -12,83 +12,83 @@ script(type='text/ng-template', id='partials/options.social.hall.html') div(ui-view) script(type='text/ng-template', id='partials/options.social.hall.heroes.html') - div.well(ng-if='user.contributor.admin') + .well(ng-if='user.contributor.admin') h2=env.t('rewardUser') - form.form-horizontal(ng-submit='loadHero(_heroID)') - .-options - .option-group.option-large - input.option-content(type='text', ng-model='_heroID', placeholder=env.t('UUID')) - button.btn(type='submit')=env.t('loadUser') - form.form-horizontal(ng-show='hero', ng-submit='saveHero(hero)') + form(ng-submit='loadHero(_heroID)') + .form-group + input.form-control(type='text', ng-model='_heroID', placeholder=env.t('UUID')) + .form-group + input.btn.btn-default(type='submit')=env.t('loadUser') + form(ng-show='hero', ng-submit='saveHero(hero)') h3 {{hero.profile.name}} - .-options - .control-group.option-large - input.option-content(type='text', ng-model='hero.contributor.text', placeholder=env.t('contribTitle')) - .control-group.option-medium - input.option-content(type='number', step="any", ng-model='hero.contributor.level') - span.input-suffix=env.t('contribLevel') - br - small=env.t('contribHallText') - a(target='_blank', href='https://trello.com/c/wkFzONhE/277-contributor-gear')=env.t('moreDetails') - .control-group.option-large - textarea.option-content(style='height:15em;', placeholder=env.t('contributions'), ng-model='hero.contributor.contributions') - //-include ../../shared/formatting-help - .control-group.option-medium - label.checkbox + .form-group + input.form-control(type='text', ng-model='hero.contributor.text', placeholder=env.t('contribTitle')) + .form-group + label=env.t('contribLevel') + input.form-control(type='number', step="any", ng-model='hero.contributor.level') + small=env.t('contribHallText') + a(target='_blank', href='https://trello.com/c/wkFzONhE/277-contributor-gear')=env.t('moreDetails') + .form-group + textarea.form-control(cols=5, placeholder=env.t('contributions'), ng-model='hero.contributor.contributions') + include ../../shared/formatting-help + .form-group + .checkbox + label input(type='checkbox', ng-model='hero.contributor.admin') =env.t('admin') hr - .control-group.option-medium - input.option-content(type='number', step="any", ng-model='hero.balance') - span.input-suffix=env.t('balance') - p - small!= '`user.balance`' + env.t('notGems') + .form-group + label=env.t('balance') + input.form-control(type='number', step="any", ng-model='hero.balance') + small!= '`user.balance`' + env.t('notGems') - .control-group.option-medium - label.checkbox - input(type='checkbox', ng-model='hero.purchased.ads') - =env.t('hideAds') + .form-group + .checkbox + label + input(type='checkbox', ng-model='hero.purchased.ads') + =env.t('hideAds') - // h4 Backer Status - // Add backer stuff like tier, disable adds, etcs - button.btn-primary(type='submit')=env.t('save') + // h4 Backer Status + // Add backer stuff like tier, disable adds, etcs + .form-group + input.form-control.btn.btn-primary(type='submit')=env.t('save') table.table.table-striped - tr - thead - tr - th=env.t('name') - th(ng-if='user.contributor.admin')=env.t('UUID') - th=env.t('contribLevel') - th=env.t('title') - th=env.t('contributions') - tr(ng-repeat='hero in heroes') - td - span(ng-if='hero.contributor.admin',popover=env.t('gamemaster'),popover-trigger='mouseenter',popover-placement='right') - a.label(class='label-contributor-{{hero.contributor.level}}', ng-class='{"label-npc": hero.backer.npc}', ng-click='clickMember(hero._id, true)') - | {{hero.profile.name}}  - i.icon-star.icon-white - span(ng-if='!hero.contributor.admin') - a.label(class='label-contributor-{{hero.contributor.level}}', ng-class='{"label-npc": hero.backer.npc}', ng-click='clickMember(hero._id, true)') {{hero.profile.name}} - td(ng-if='user.contributor.admin') {{hero._id}} - td {{hero.contributor.level}} - td {{hero.contributor.text}} - td - markdown(ng-model='hero.contributor.contributions', target='_blank') + thead + tr + th=env.t('name') + th(ng-if='user.contributor.admin')=env.t('UUID') + th=env.t('contribLevel') + th=env.t('title') + th=env.t('contributions') + tbody + tr(ng-repeat='hero in heroes') + td + span(ng-if='hero.contributor.admin',popover=env.t('gamemaster'),popover-trigger='mouseenter',popover-placement='right') + a.label.label-default(ng-class='userLevelStyle(hero)', ng-click='clickMember(hero._id, true)') + | {{hero.profile.name}}  + span.glyphicon.glyphicon-star + span(ng-if='!hero.contributor.admin') + a.label.label-default(ng-class='userLevelStyle(hero)', ng-click='clickMember(hero._id, true)') {{hero.profile.name}} + td(ng-if='user.contributor.admin') {{hero._id}} + td {{hero.contributor.level}} + td {{hero.contributor.text}} + td + markdown(ng-model='hero.contributor.contributions', target='_blank') script(type='text/ng-template', id='partials/options.social.hall.patrons.html') table.table.table-striped(infinite-scroll="loadMore()") - tr - thead - tr - th=env.t('name') - th(ng-if='user.contributor.admin')=env.t('UUID') - th=env.t('backerTier') - tr(ng-repeat='patron in patrons') - td - a.label(class='label-contributor-{{patron.contributor.level}}', ng-class='{"label-npc": patron.backer.npc}', ng-click='clickMember(patron._id, true)') {{patron.profile.name}} - td(ng-if='user.contributor.admin') {{patron._id}} - td {{patron.backer.tier}} + thead + tr + th=env.t('name') + th(ng-if='user.contributor.admin')=env.t('UUID') + th=env.t('backerTier') + tbody + tr(ng-repeat='patron in patrons') + td + a.label.label-default(ng-class='userLevelStyle(patron)', ng-click='clickMember(patron._id, true)') {{patron.profile.name}} + td(ng-if='user.contributor.admin') {{patron._id}} + td {{patron.backer.tier}} diff --git a/views/options/social/index.jade b/views/options/social/index.jade index 73f3cd773f..8d64335454 100644 --- a/views/options/social/index.jade +++ b/views/options/social/index.jade @@ -7,13 +7,12 @@ include ./hall.jade script(type='text/ng-template', id='partials/options.social.tavern.html') include ./tavern - script(type='text/ng-template', id='partials/options.social.party.html') div(ng-show='group._id') include ./group div(ng-hide='group._id') div(ng-show='user.invitations.party.id') - h2=env.t('invitedTo') + '{{user.invitations.party.name}}' + h2=env.t('invitedTo') + ' {{user.invitations.party.name}}' a.btn.btn-success(data-type='party', ng-click='join(user.invitations.party)')=env.t('accept') a.btn.btn-danger(ng-click='reject()')=env.t('reject') div(ng-hide='user.invitations.party.id') @@ -30,12 +29,12 @@ script(type='text/ng-template', id='partials/options.social.party.html') script(type='text/ng-template', id='partials/options.social.guilds.public.html') div(ng-repeat='invitation in user.invitations.guilds') - h3=env.t('invitedTo') + '{{invitation.name}}' + h3=env.t('invitedTo') + ' {{invitation.name}}' a.btn.btn-success(data-type='guild', ng-click='join(invitation)')=env.t('accept') a.btn.btn-danger(ng-click='reject(invitation)')=env.t('reject') // Public Groups - .options-group.option-large.whatever-options - input.option-content(type='text',ng-model='guildSearch', placeholder=env.t('search')) + .col-md-3 + input.form-control(type='text',ng-model='guildSearch', placeholder=env.t('search')) table.table.table-striped tr(ng-repeat='group in groups.public | filter:guildSearch') td @@ -43,11 +42,11 @@ script(type='text/ng-template', id='partials/options.social.guilds.public.html') li='{{group.memberCount}} ' + env.t('members') li // join / leave - a.btn.btn-small.btn-danger(ng-show='group._isMember', ng-click='clickLeave(group, $event)') - i.icon-ban-circle + a.btn.btn-sm.btn-danger(ng-show='group._isMember', ng-click='clickLeave(group, $event)') + span.glyphicon.glyphicon-ban-circle =env.t('leave') - a.btn.btn-small.btn-success(ng-hide='group._isMember', ng-click='join(group)') - i.icon-ok + a.btn.btn-sm.btn-success(ng-hide='group._isMember', ng-click='join(group)') + span.glyphicon.glyphicon-ok =env.t('join') h4 {{group.name}} p {{group.description}} @@ -78,7 +77,8 @@ script(type='text/ng-template', id='partials/options.social.html') ul.nav.nav-tabs li(ng-class="{ active: $state.includes('options.social.tavern') }") a(ui-sref='options.social.tavern') - i.icon-eye-close + span.glyphicon.glyphicon-eye-close + |   =env.t('tavern') li(ng-class="{ active: $state.includes('options.social.party') }") a(ui-sref='options.social.party') @@ -88,11 +88,13 @@ script(type='text/ng-template', id='partials/options.social.html') =env.t('guilds') li(ng-class="{ active: $state.includes('options.social.challenges') }") a(ui-sref='options.social.challenges') - i.icon-bullhorn + span.glyphicon.glyphicon-bullhorn + |   =env.t('challenges') li(ng-class="{ active: $state.includes('options.social.hall') }") a(ui-sref='options.social.hall') - i.icon-flag + span.glyphicon.glyphicon-flag + |   =env.t('hall') .tab-content diff --git a/views/options/social/tavern.jade b/views/options/social/tavern.jade index 349065f739..8fe0cf317b 100644 --- a/views/options/social/tavern.jade +++ b/views/options/social/tavern.jade @@ -1,6 +1,6 @@ -.row-fluid - .span4 - .tavern-pane +.container-fluid + .row + .col-md-4 table tr td @@ -13,115 +13,112 @@ .popover-content =env.t('danielText') div - button.btn.btn-large.btn-success(ng-class='{active: user.preferences.sleep}', ng-click='User.user.ops.sleep({})') + button.btn.btn-lg.btn-success(ng-class='{active: user.preferences.sleep}', ng-click='User.user.ops.sleep({})') span(ng-show='user.preferences.sleep')=env.t('innCheckOut') span(ng-hide='user.preferences.sleep')=env.t('innCheckIn') - .alert.alert-info(ng-show='user.preferences.sleep') - =env.t('innText') + .alert.alert-info(ng-show='user.preferences.sleep') + =env.t('innText') - // Resources - .modal(style='position: relative;top: auto;left: auto;right: auto;margin: 0 auto 20px;z-index: 1;max-width: 100%;') - .modal-header - h3=env.t('resources') - .modal-body - table.table.table-striped - tr - td - a(target='_blank', href='http://habitrpg.wikia.com/wiki/Board:Looking_for_a_Group_(LFG)')=env.t('lfgPosts') - tr - td - a(target='_blank', href='https://vimeo.com/57654086')=env.t('tutorial') - tr - td - a(target='_blank', href='http://habitrpg.wikia.com/wiki/FAQ')=env.t('FAQ') - tr - td - a(target='_blank', href='http://habitrpg.wikia.com/wiki/Glossary')=env.t('glossary') - tr - td - a(target='_blank', href='https://github.com/HabitRPG/habitrpg/issues')=env.t('reportAP') - tr - td - a(target='_blank', href='https://trello.com/c/8gzGlle8')=env.t('requestAF') - tr - td - a(target='_blank', href='http://habitrpg.wikia.com/wiki/Special:Forum')=env.t('community') + // Resources + .panel.panel-default + .panel-heading + h3.panel-title=env.t('resources') + .panel-body + table.table.table-striped + tr + td + a(target='_blank', href='http://habitrpg.wikia.com/wiki/Board:The_Archery_-_LFG')=env.t('lfgPosts') + tr + td + a(target='_blank', href='https://vimeo.com/57654086')=env.t('tutorial') + tr + td + a(target='_blank', href='http://habitrpg.wikia.com/wiki/FAQ')=env.t('FAQ') + tr + td + a(target='_blank', href='http://habitrpg.wikia.com/wiki/Glossary')=env.t('glossary') + tr + td + a(target='_blank', href='https://github.com/HabitRPG/habitrpg/issues/2760')=env.t('reportAP') + tr + td + a(target='_blank', href='https://trello.com/c/8gzGlle8')= ' ' + env.t('requestAF') + tr + td + a(target='_blank', href='http://habitrpg.wikia.com/wiki/Special:Forum')=env.t('community') - // Player Tiers - .modal(style='position: relative;top: auto;left: auto;right: auto;margin: 0 auto 20px;z-index: 1;max-width: 100%;', popover=env.t('tierPop'), popover-trigger="mouseenter", popover-placement="right") - .modal-header - h3=env.t('playerTiers') - .modal-body - small - | - =env.t('visitHeroes') - |
    - | - =env.t('conLearn') - |
    - | - =env.t('conLearnHow') - | - table.table.table-striped - tr - td - a.label.label-contributor-1(ng-click='toggleUserTier($event)')=env.t('friendBadge') - div(style='display:none;') - p - span.achievement.achievement-firefox - !=env.t('friendFirst') - hr - p - span.shop-sprite.item-img(class='shop_armor_special_1') - !=env.t('friendSecond') - tr - td - a.label.label-contributor-3(ng-click='toggleUserTier($event)')=env.t('eliteBadge') - div(style='display:none;') - p - span.shop-sprite.item-img(class='shop_head_special_1') - !=env.t('eliteThird') - hr - p - span.shop-sprite.item-img(class='shop_weapon_special_1') - !=env.t('eliteFourth') - tr - td - a.label.label-contributor-5(ng-click='toggleUserTier($event)')=env.t('championBadge') - div(style='display:none;') - p - span.shop-sprite.item-img(class='shop_shield_special_1') - !=env.t('championFifth') - hr - p - div(class='Pet-Dragon-Hydra pull-left') - !=env.t('championSixth') - tr - td - a.label.label-contributor-7(ng-click='toggleUserTier($event)')=env.t('legendaryBadge') - div(style='display:none;') - p - !=env.t('legSeventh') - tr - td - a.label.label-contributor-8(ng-click='toggleUserTier($event)')=env.t('heroicBadge') - div(style='display:none;') - p=env.t('heroicText') - tr - td - a.label.label-npc(ng-click='toggleUserTier($event)')=env.t('npc') - div(style='display:none;') - p=env.t('npcText') + // Player Tiers + .panel.panel-default(popover=env.t('tierPop'), popover-trigger="mouseenter", popover-placement="right") + .panel-heading + h3.panel-title=env.t('playerTiers') + .panel-body + small + | + =env.t('visitHeroes') + |
    + | + =env.t('conLearn') + |
    + | + =env.t('conLearnHow') + | + table.table.table-striped.panel-tiers + tr + td + a.label.label-contributor-1(ng-click='toggleUserTier($event)')=env.t('friend') + ' (1-2)' + div + p + span.achievement.achievement-firefox + !=env.t('friendFirst') + hr + p + span.shop-sprite.item-img(class='shop_armor_special_1') + !=env.t('friendSecond') + tr + td + a.label.label-contributor-3(ng-click='toggleUserTier($event)')=env.t('elite') + ' (3-4)' + div + p + span.shop-sprite.item-img(class='shop_head_special_1') + !=env.t('eliteThird') + hr + p + span.shop-sprite.item-img(class='shop_weapon_special_1') + !=env.t('eliteFourth') + tr + td + a.label.label-contributor-5(ng-click='toggleUserTier($event)')=env.t('champion') + ' (5-6)' + div + p + span.shop-sprite.item-img(class='shop_shield_special_1') + !=env.t('championFifth') + hr + p + div(class='Pet-Dragon-Hydra pull-left') + !=env.t('championSixth') + tr + td + a.label.label-contributor-7(ng-click='toggleUserTier($event)')=env.t('legendary') + ' (7)' + div + p + !=env.t('legSeventh') + tr + td + a.label.label-contributor-8(ng-click='toggleUserTier($event)')=env.t('heroic') + div + p=env.t('heroicText') + tr + td + a.label.label-npc(ng-click='toggleUserTier($event)')=env.t('npc') + div + p=env.t('npcText') - include ./challenge-box + include ./challenge-box - .span8(ng-controller='ChatCtrl') - h3=env.t('tavernTalk') - include ./chat-box - small(style='position: relative; top: -20px; left: 25px;') - include ../../shared/formatting-help - small(style='position: relative; top: 18px; left: 0px') - alert.alert-info - !=env.t('tavernAlert1') + ' ' + env.t('tavernAlert2') + '.' - ul.unstyled.tavern-chat - include ./chat-message + .col-md-8.tavern(ng-controller='ChatCtrl') + h3=env.t('tavernTalk') + include ./chat-box + .alert.alert-info.alert-sm + != ' ' + env.t('tavernAlert1') + ' ' + env.t('tavernAlert2') + '.' + ul.list-unstyled.tavern-chat + include ./chat-message diff --git a/views/shared/footer.jade b/views/shared/footer.jade index d85a8ce8f7..216b641b13 100644 --- a/views/shared/footer.jade +++ b/views/shared/footer.jade @@ -1,20 +1,21 @@ footer.footer(ng-controller='FooterCtrl') .container .row - .span3 + .col-sm-3 h4=env.t('footerMobile') - ul.unstyled + ul.list-unstyled li a(href='https://itunes.apple.com/us/app/habitrpg/id689569235?mt=8', target='_blank')=env.t('mobileIOS') li a(href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg', target='_blank')=env.t('mobileAndroid') - .span3 + .col-sm-3 h4=env.t('footerCompany') - ul.unstyled - if (!env.isFrontPage) + ul.list-unstyled + if (!env.isStaticPage) li - .btn.btn-small.btn-success(ng-click='modals.buyGems = true') - i.icon-heart.icon-white + .btn.btn-sm.btn-success(ng-click='openModal("buyGems",{track:"Gems > Donate"})') + span.glyphicon.glyphicon-heart + |  =env.t('companyDonate') li a(href='/static/about')=env.t('companyAbout') @@ -28,9 +29,9 @@ footer.footer(ng-controller='FooterCtrl') a(href='/static/privacy')=env.t('companyPrivacy') li a(href='/static/terms')=env.t('companyTerms') - .span3 + .col-sm-3 h4=env.t('footerCommunity') - ul.unstyled + ul.list-unstyled li a(target='_blank', href='https://github.com/HabitRPG/habitrpg/issues')=env.t('communityBug') li @@ -47,8 +48,8 @@ footer.footer(ng-controller='FooterCtrl') a(target='_blank', href='https://www.facebook.com/Habitrpg')=env.t('communityFacebook') li a(target='_blank', href='http://www.reddit.com/r/habitrpg/')=env.t('communityReddit') - .span3 - if (env.NODE_ENV=='production') + .col-sm-3 + if (env.NODE_ENV == 'production' && !env.IS_MOBILE) h4=env.t('footerSocial') .addthis_toolbox.addthis_default_style(addthis:url='https://habitrpg.com', addthis:title=env.t('socialTitle')) table @@ -59,21 +60,17 @@ footer.footer(ng-controller='FooterCtrl') td a.addthis_button_tweet(tw:via='habitrpg') tr - td(align='left') + td iframe(src='/bower_components/github-buttons/github-btn.html?user=lefnire&repo=habitrpg&type=watch&count=true', allowtransparency='true', frameborder='0', scrolling='0', width='85px', height='20px') tr td a.addthis_button_google_plusone(g:plusone:size='medium') - else + else if(env.NODE_ENV != 'production' && !env.isStaticPage) h4 Debug - ul.unstyled - li - a.btn(ng-click='addMissedDay()') +1 Missed Day - li - a.btn(ng-click='addTenGems()') +10 Gems - li - a.btn(ng-click='addLevelsAndGold()') +Exp +GP - li - a.btn(ng-click='addOneLevel()') +1 Level + .btn-group-vertical + a.btn.btn-default(ng-click='addMissedDay()') +1 Missed Day + a.btn.btn-default(ng-click='addTenGems()') +10 Gems + a.btn.btn-default(ng-click='addLevelsAndGold()') +Exp +GP + a.btn.btn-default(ng-click='addOneLevel()') +1 Level div(ng-init='deferredScripts()') diff --git a/views/shared/gems.jade b/views/shared/gems.jade index 71c3c8e411..9b78852cb8 100644 --- a/views/shared/gems.jade +++ b/views/shared/gems.jade @@ -1,4 +1,4 @@ -a.pull-right.gem-wallet(ng-click='modals.buyGems = true', popover-trigger='mouseenter', popover-title=env.t('gems'), popover=env.t('gemsWhatFor'), popover-placement='bottom') +a.pull-right.gem-wallet(ng-click='openModal("buyGems",{track:"Gems > Wallet"})', popover-trigger='mouseenter', popover-title=env.t('gems'), popover=env.t('gemsWhatFor'), popover-placement='bottom') span.task-action-btn.tile.flush.bright.add-gems-btn + span.task-action-btn.tile.flush.neutral .Pet_Currency_Gem2x.Gems diff --git a/views/shared/header/avatar.jade b/views/shared/header/avatar.jade index ef266bbb2f..971560d5af 100644 --- a/views/shared/header/avatar.jade +++ b/views/shared/header/avatar.jade @@ -3,7 +3,7 @@ Removing it will remove the user's name, but will allow more room for mounts & helms. IMO this is the lesser of two evils, revisit //-figure.herobox(ng-click='clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: profile.id==user.id, hasPet: profile.items.currentPet}', data-level='{{profile.stats.lvl}}', data-uid='{{profile.id}}', rel='popover', data-placement='bottom', data-trigger='hover', data-html='true', data-content="
    Level: {{profile.stats.lvl}}
    GP: {{profile.stats.gp | number:0}}
    {{count(profile.items.pets)}} / 90 Pets Found
    ") -figure.herobox(ng-click='spell ? castEnd(profile, "user", $event) : clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: (user._id==profile._id && !user.items.currentMount), hasPet: profile.items.currentMount, "cast-target": applyingAction, isLeader: party.leader==profile._id}') +figure.herobox(ng-click='spell ? castEnd(profile, "user", $event) : clickMember(profile._id)', data-name='{{profile.profile.name}}', ng-class='{isUser: user._id==profile._id, hasPet:profile.items.currentPet, hasMount:profile.items.currentMount, "cast-target": applyingAction, isLeader: party.leader==profile._id}') .character-sprites // Mount Body span(ng-if='profile.items.currentMount', class='Mount_Body_{{profile.items.currentMount}}') @@ -43,8 +43,10 @@ figure.herobox(ng-click='spell ? castEnd(profile, "user", $event) : clickMember( span(ng-class='{zzz:profile.preferences.sleep}') // FIXME handle @minimal, this might have to be a directive span.current-pet(class='Pet-{{profile.items.currentPet}}', ng-show='profile.items.currentPet && !minimal') - .avatar-level(ng-class='userLevelStyle(profile,"label")') - i.icon-circle-arrow-up.icon-white(ng-show='profile.stats.buffs.str || profile.stats.buffs.per || profile.stats.buffs.con || profile.stats.buffs.int || profile.stats.buffs.stealth', tooltip=env.t('buffed'), style='margin-right:5px;') + .avatar-level.label.label-default(ng-class='userLevelStyle(profile)') + span.glyphicon.glyphicon-circle-arrow-up(ng-show='profile.stats.buffs.str || profile.stats.buffs.per || profile.stats.buffs.con || profile.stats.buffs.int || profile.stats.buffs.stealth', tooltip=env.t('buffed'), style='margin-right:5px;') + |  =env.t('lvl') - | {{profile.stats.lvl}} - i.icon-plus-sign.icon-white(ng-show='profile.achievements.rebirths', tooltip=env.t('reborn', {reLevel: "{{profile.achievements.rebirthLevel}}"}), style='margin-left:5px') + | {{profile.stats.lvl}} + |  + span.glyphicon.glyphicon-plus-sign(ng-show='profile.achievements.rebirths', tooltip=env.t('reborn', {reLevel: "{{profile.achievements.rebirthLevel}}"})) diff --git a/views/shared/header/header.jade b/views/shared/header/header.jade index 63ea79962a..6481d1a5e6 100644 --- a/views/shared/header/header.jade +++ b/views/shared/header/header.jade @@ -1,7 +1,5 @@ .header-wrap(ng-controller='HeaderCtrl') - a.label.undo-button(x-bind='click:undo', ng-show='_undo')=env.t('undo') - div(ng-if='!user.preferences.hideHeader') - include menu + a.label.label-default.undo-button(x-bind='click:undo', ng-show='_undo')=env.t('undo') header.site-header(ng-hide='user.preferences.hideHeader', role='banner', data-partysize='{{party.members.length>1 ? truarr(party.members.length) : 0}}') // avatar @@ -13,27 +11,28 @@ .meter.health(title=env.t('health')) .bar(style='width: {{Shared.percent(user.stats.hp, 50)}}%;') span.meter-text - i.icon-heart + span.glyphicon.glyphicon-heart + |   | {{Math.ceil(user.stats.hp)}} / 50 .meter.experience(title=env.t('experience')) .bar(style='width: {{Shared.percent(user.stats.exp,Shared.tnl(user.stats.lvl))}}%;') span.meter-text - i.icon-star + span.glyphicon.glyphicon-star + |   | {{Math.floor(user.stats.exp)}} / {{Shared.tnl(user.stats.lvl) | number:0}} // FIXME doesn't look great here, but the "Experience" CSS title rollover covers it where it was before span(ng-show='user.history.exp') a(ng-click='toggleChart("exp")', tooltip=env.t('progress')) - i.icon-signal + |   + span.glyphicon.glyphicon-signal .meter.mana(title='Mana', ng-if='user.flags.classSelected && !user.preferences.disableClasses') .bar(style='width: {{user.stats.mp / user._statsComputed.maxMP * 100}}%;') span.meter-text - i.icon-fire + span.glyphicon.glyphicon-fire + |   | {{Math.floor(user.stats.mp)}} / {{user._statsComputed.maxMP}} // party span(ng-controller='PartyCtrl') .herobox-wrap(ng-repeat='profile in partyMinusSelf') include avatar - - .npc_bailey.npc_bailey_head(ng-show='user.flags.newStuff', tooltip=env.t('psst'), tooltip-placement='top', ng-click='modals.newStuff=true') - diff --git a/views/shared/header/menu.jade b/views/shared/header/menu.jade index 6933ffbe98..ff9cbbc285 100644 --- a/views/shared/header/menu.jade +++ b/views/shared/header/menu.jade @@ -1,36 +1,132 @@ -.user-menu(ng-controller='AuthCtrl') - button.task-action-btn.tile.solid(ng-hide='authenticated()', style='cursor: pointer;', ng-click="modals.login = true")=env.t('loginAndReg') - ul.nav.site-nav(ng-show='authenticated()') - li.flyout - h1.task-action-btn.tile.solid.user-reporter {{user.profile.name}} - ul.flyout-content.nav.stacked - li - a.task-action-btn.tile.solid - span(ng-show='$state.includes("options")', ui-sref='tasks') - i.icon-ok - =env.t('tasks') - span(ng-show='$state.includes("tasks")', ui-sref='options') - i.icon.icon-wrench - =env.t('options') - li - a.task-action-btn.tile.solid(href="http://habitrpg.wikia.com/wiki/FAQ", target='_blank') - i.icon.icon-book - =env.t('FAQ') - li - a.task-action-btn.tile.solid(href="https://vimeo.com/57654086", target='_blank') - i.icon.icon-film - =env.t('tutorials') - li - a.task-action-btn.tile.solid(ng-click='User.sync()') - i.icon.icon-refresh - =env.t('sync') - li - a.task-action-btn.tile.solid(ng-click='logout()') - i.icon-share-alt - =env.t('logout') - // party invitation notification - a(ng-show='user.party.invitation', style='cursor: pointer;', x-bind='click:gotoPartyChat') - i.icon-user - // party chat notification - a(ng-show='newChatMessages(_party.chat,user.party.lastMessageSeen)', style='cursor: pointer;', x-bind='click:gotoPartyChat') - i.icon-comment(tooltip=env.t('partyNotification')) +a.collapse-toolbar(popover='Expand Toolbar',popover-placement='left',popover-trigger='mouseenter',ng-if='user.preferences.toolbarCollapsed',ng-click='set({"preferences.toolbarCollapsed":false})') + span.glyphicon.glyphicon-chevron-down + +nav.navbar.navbar-inverse(ng-controller='AuthCtrl',style='min-height:0px;margin-bottom:0px;border:0;border-radius:0px',ng-if='!user.preferences.toolbarCollapsed') + + ul.nav.navbar-nav.pull-right + li + a(popover='Collapse Toolbar',popover-placement='left',popover-trigger='mouseenter',,ng-if='!user.preferences.toolbarCollapsed',ng-click='set({"preferences.toolbarCollapsed":true})') + span.glyphicon.glyphicon-chevron-up.white + + .navbar-header + button.navbar-toggle(type='button', ng-click='isNavbarCollapsed = !isNavbarCollapsed', ng-init='isNavbarCollapsed = true') + span.sr-only Toggle navigation + span.icon-bar + span.icon-bar + span.icon-bar + .collapse.navbar-collapse(collapse="isNavbarCollapsed") + ul.nav.navbar-nav + + li + a(ui-sref='tasks',popover=env.t('tasks'),popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-check.white + li.dropdown + a.dropdown-toggle(popover='User',popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-user.white + span.caret + ul.dropdown-menu + li + a(ui-sref='options.profile.avatar')=env.t('avatar') + li + a(ui-sref='options.profile.stats')=env.t('stats') + li + a(ui-sref='options.profile.profile')=env.t('profile') + li + a(ng-click='logout()') + span.glyphicon.glyphicon-share-alt + |   + =env.t('logout') + li.dropdown + a.dropdown-toggle(popover='Social',popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-heart.white + span.caret + ul.dropdown-menu + li + a(ui-sref='options.social.tavern')=env.t('tavern') + li + a(ui-sref='options.social.party')=env.t('party') + li + a(ui-sref='options.social.guilds')=env.t('guilds') + li + a(ui-sref='options.social.challenges')=env.t('challenges') + li + a(ui-sref='options.social.hall')=env.t('hall') + li.dropdown + a.dropdown-toggle(popover='Inventory',popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-gift.white + span.caret + ul.dropdown-menu + li + a(ui-sref='options.inventory.drops')=env.t('market') + li + a(ui-sref='options.inventory.pets')=env.t('mounts') + li + a(ui-sref='options.inventory.mounts')=env.t('mounts') + li + a(ui-sref='options.inventory.equipment')=env.t('equipment') + li.dropdown + a.dropdown-toggle(popover='Settings',popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-wrench.white + span.caret + ul.dropdown-menu + li + a(ui-sref='options.settings.settings')=env.t('site') + li + a(ui-sref='options.settings.api')=env.t('API') + li + a(ui-sref='options.settings.export')=env.t('export') + li + a(ui-sref='options.settings.subscription')=env.t('subscription') + li.dropdown + a.dropdown-toggle(popover='Help',popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-question-sign.white + span.caret + ul.dropdown-menu + li + a(href="http://habitrpg.wikia.com/wiki/FAQ", target='_blank') + span.glyphicon.glyphicon-book + |   + =env.t('FAQ') + li + a(href="https://vimeo.com/57654086", target='_blank') + span.glyphicon.glyphicon-film + //|   Tutorials is too long to stay in the menu if we add a space + =env.t('tutorials') + li + a(ng-click='User.sync()',popover=env.t('sync'),popover-placement='right',popover-trigger='mouseenter') + span.glyphicon.glyphicon-refresh.white + //-|   + //-=env.t('sync') + + // Separator + li + a(style='border-right:1px solid #999;cursor:default;padding-left:0px;padding-right:0px')   + + li + a.pull-right.gem-wallet(style='padding-top:8px;padding-bottom:0px;',ng-click='openModal("buyGems",{track:"Gems > Toolbar"})', popover-trigger='mouseenter', popover-title=env.t('gems'), popover=env.t('gemsWhatFor'), popover-placement='bottom',popover-append-to-body='true') + //span.task-action-btn.tile.flush.bright.add-gems-btn + + span.task-action-btn.tile.flush.neutral + .Pet_Currency_Gem2x.Gems + | {{user.balance * 4 | number:0}} + li + button.btn.btn-primary(style='margin-top:8px;margin-bottom:0px',ng-if='!user.purchased.plan.customerId',ui-sref='options.settings.subscription',popover-trigger='mouseenter',popover-placement='bottom',popover-title='Subscriptions',popover='Disable ads, buy gems with gold, monthly mystery item, retain progress history, double daily drop-caps, supports the devs. Click for more info.',popover-append-to-body='true') Subscribe + button.btn.btn-default(style='margin-top:8px;margin-bottom:0px',ng-if='user.purchased.plan.customerId',ui-sref='options.settings.subscription',popover-trigger='mouseenter',popover-placement='bottom',popover='Click to manage subscription',popover-append-to-body='true') Subscribed + + li(ng-if='user.flags.newStuff') + div.toolbar-bailey-container + |  + .npc_bailey.npc_bailey_head(popover=env.t('psst'), popover-trigger='mouseenter', popover-placement='right', ng-click='openModal("newStuff")') + + // Invitations + li(ng-if='user.invitations.party.id') + a(ui-sref='options.social.party') + span.glyphicon.glyphicon-user.white(popover='Invited to {{user.invitations.party.name}}',popover-placement='bottom',popover-trigger='mouseenter',popover-append-to-body='true') + li(ng-repeat='guild in user.invitations.guilds') + a(ui-sref='options.social.guilds') + span.glyphicon.glyphicon-user.white(popover='Invited to {{guid.name}}',popover-placement='bottom',popover-trigger='mouseenter',popover-append-to-body='true') + // Chat notifs + li(ng-repeat='(k,v) in user.newMessages', ng-if='v.value') + a(ng-click='k==party._id ? $state.go("options.social.party") : $state.go("options.social.guilds.detail",{gid:k})') + span.glyphicon.glyphicon-comment.white(popover='New message in "{{v.name}}"',popover-placement='bottom',popover-trigger='mouseenter',popover-append-to-body='true') + + diff --git a/views/shared/modals/achievements.jade b/views/shared/modals/achievements.jade index ed4ba27b57..ca884290f7 100644 --- a/views/shared/modals/achievements.jade +++ b/views/shared/modals/achievements.jade @@ -1,52 +1,53 @@ // Streak -div(modal='modals.achievements.streak') +script(id='modals/achievements/streak.html', type='text/ng-template') .modal-header - h3=env.t('modalAchievement') + h4=env.t('modalAchievement') .modal-body .achievement.achievement-thermometer =env.t('streakerAchievement') .modal-footer - button.btn.btn-default.cancel(ng-click='modals.achievements.streak = false')=env.t('ok') + button.btn.btn-default(ng-click='$close()')=env.t('ok') // Max Gear -div(modal='modals.achievements.ultimateGear') +script(id='modals/achievements/ultimateGear.html', type='text/ng-template') .modal-header - h3=env.t('modalAchievement') + h4=env.t('modalAchievement') .modal-body .achievement.achievement-armor =env.t('gearAchievement') .modal-footer - button.btn.btn-default.cancel(ng-click='modals.achievements.ultimateGear = false')=env.t('ok') + button.btn.btn-default(ng-click='$close()')=env.t('ok') // Beast Master -div(modal='modals.achievements.beastMaster') +script(id='modals/achievements/beastMaster.html', type='text/ng-template') .modal-header - h3=env.t('modalAchievement') + h4=env.t('modalAchievement') .modal-body p .achievement.achievement-rat =env.t('beastAchievement') .modal-footer - button.btn.btn-default.cancel(ng-click='modals.achievements.beastMaster = false')=env.t('ok') + button.btn.btn-default(ng-click='$close()')=env.t('ok') // Contributor -div(modal='user.flags.contributor') +// activated by user.flags.contributor +script(id='modals/achievements/contributor.html', type='text/ng-template') .modal-header - h3=env.t('modalContribAchievement') + h4=env.t('modalContribAchievement') .modal-body .npc_justin.float-left p !=env.t('contribModal', {name: "{{user.profile.name}}", level: "{{user.contributor.level}}"}) a(href='http://habitrpg.wikia.com/wiki/Contributor_Rewards' target='_blank')=env.t('contribLink') .modal-footer - button.btn.btn-default.cancel(ng-click='set({"flags.contributor":false})')=env.t('ok') + button.btn.btn-default(ng-click='set({"flags.contributor":false}); $close()')=env.t('ok') //Rebirth -div(modal='modals.achievements.rebirth') +script(id='modals/achievements/rebirth.html', type='text/ng-template') .modal-header - h3=env.t('modalAchievement') + h4=env.t('modalAchievement') .modal-body .achievement.achievement-sun =env.t('rebirthAchievement', {number: "{{user.achievements.rebirths}}", level: "{{user.achievements.rebirthLevel}}"}) .modal-footer - button.btn.btn-default.cancel(ng-click='modals.achievements.rebirth = false')=env.t('ok') + button.btn.btn-default(ng-click='$close()')=env.t('ok') diff --git a/views/shared/modals/buy-gems.jade b/views/shared/modals/buy-gems.jade index 63b5127abb..6b7aa884c5 100644 --- a/views/shared/modals/buy-gems.jade +++ b/views/shared/modals/buy-gems.jade @@ -1,4 +1,5 @@ -div(modal='modals.buyGems') +//Why is shown? Because of script here at line 24!!!!!!!!! +script(id='modals/buyGems.html', type='text/ng-template') .modal-body .buy-gems include ../gems @@ -18,14 +19,17 @@ div(modal='modals.buyGems') =env.t('USD') tr td - .btn.btn-primary(ng-click='showStripe()',style='margin-left:10px;')=env.t('payWithCard') - div(style='float:left;') - //-script(src='/bower_components/JavaScriptButtons/dist/paypal-button.min.js?merchant=#{env.PAYPAL_MERCHANT}', data-button='buynow', data-name=env.t('paypalText'), data-quantity='1', data-amount='5', data-currency='USD', data-tax='0', data-callback='#{env.BASE_URL}/api/v2/user/buy-gems/paypal-ipn', data-env="#{env.NODE_ENV == 'production' ? '' : 'sandbox'}", data-custom='?uid={{user._id}}&apiToken={{user.apiToken}}', data-return='#{env.BASE_URL}', data-rm='1', data-no_shipping='1') - script(src='/bower_components/JavaScriptButtons/dist/paypal-button.min.js?merchant=#{env.PAYPAL_MERCHANT}', data-button='buynow', data-name=env.t('paypalText'), data-quantity='1', data-amount='5', data-currency='USD', data-tax='0', data-callback='#{env.BASE_URL}/api/v2/user/buy-gems/paypal-ipn', data-custom='?uid={{user._id}}&apiToken={{user.apiToken}}', data-return='#{env.BASE_URL}', data-rm='1', data-no_shipping='1') - p - small.muted(style='line-height:10px;')=env.t('payNote') + .row + .col-md-6 + .btn.btn-primary(ng-click='showStripe()')=env.t('payWithCard') + .col-md-6 + //-script(src='/bower_components/JavaScriptButtons/dist/paypal-button.min.js?merchant=#{env.PAYPAL_MERCHANT}', data-button='buynow', data-name=env.t('paypalText'), data-quantity='1', data-amount='5', data-currency='USD', data-tax='0', data-callback='#{env.BASE_URL}/api/v2/user/buy-gems/paypal-ipn', data-custom='?uid={{user._id}}&apiToken={{user.apiToken}}', data-return='#{env.BASE_URL}', data-rm='1', data-no_shipping='1') + div#custom-paypal-button.paypal-button(ng-init='initPayPalButton()') + .row + .col-md-12 + small.muted=env.t('payNote') div(ng-include="'partials/options.settings.subscription.html'") .modal-footer - button.btn.btn-default.cancel(ng-click='modals.buyGems = false')=env.t('cancel') + button.btn.btn-default(ng-click='$close()')=env.t('cancel') diff --git a/views/shared/modals/classes.jade b/views/shared/modals/classes.jade index 220b13c213..e6ccdb5a10 100644 --- a/views/shared/modals/classes.jade +++ b/views/shared/modals/classes.jade @@ -1,75 +1,78 @@ -.modal(ng-if='!user.flags.classSelected && user.stats.lvl >= 10', data-backdrop=true, ng-controller='UserCtrl') +script(type='text/ng-template', id='modals/chooseClass.html') .modal-header - h3=env.t('chooseClass1') + h4 + |  + =env.t('chooseClass1') a(href='http://habitrpg.wikia.com/wiki/Class_System' target='_blank')=env.t('chooseClass2') .modal-body.select-class - .row-fluid - .span3(ng-click='selectedClass = "warrior"') - h5 - a(href='http://habitrpg.wikia.com/wiki/Warrior' target='_blank')=env.t('warrior') - figure.herobox(ng-class='{"selected-class": selectedClass=="warrior"}') - .character-sprites - span(class='skin_{{user.preferences.skin}}') - span(class='{{user.preferences.size}}_armor_warrior_5') - span(class='head_0') - span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') - span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') - span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') - span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') - span(class='head_warrior_5') - span(class='shield_warrior_5') - span(class='weapon_warrior_6') - .span3(ng-click='selectedClass = "wizard"') - h5 - a(href='http://habitrpg.wikia.com/wiki/Mage' target='_blank')=env.t('mage') - figure.herobox(ng-class='{"selected-class": selectedClass=="wizard"}') - .character-sprites - span(class='skin_{{user.preferences.skin}}') - span(class='{{user.preferences.size}}_armor_wizard_5') - span(class='head_0') - span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') - span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') - span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') - span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') - span(class='head_wizard_5') - span(class='shield_wizard_5') - span(class='weapon_wizard_6') - .span3(ng-click='selectedClass = "rogue"') - h5 - a(href='http://habitrpg.wikia.com/wiki/Rogue' target='_blank')=env.t('rogue') - figure.herobox(ng-class='{"selected-class": selectedClass=="rogue"}') - .character-sprites - span(class='skin_{{user.preferences.skin}}') - span(class='{{user.preferences.size}}_armor_rogue_5') - span(class='head_0') - span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') - span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') - span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') - span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') - span(class='head_rogue_5') - span(class='shield_rogue_6') - span(class='weapon_rogue_6') - .span3(ng-click='selectedClass = "healer"') - h5 - a(href='http://habitrpg.wikia.com/wiki/Healer' target='_blank')=env.t('healer') - figure.herobox(ng-class='{"selected-class": selectedClass=="healer"}') - .character-sprites - span(class='skin_{{user.preferences.skin}}') - span(class='{{user.preferences.size}}_armor_healer_5') - span(class='head_0') - span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') - span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') - span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') - span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') - span(class='head_healer_5') - span(class='shield_healer_5') - span(class='weapon_healer_6') - br - .well(ng-show='selectedClass=="warrior"')=env.t('warriorText') - .well(ng-show='selectedClass=="wizard"')=env.t('mageText') - .well(ng-show='selectedClass=="rogue"')=env.t('rogueText') - .well(ng-show='selectedClass=="healer"')=env.t('healerText') + .container-fluid + .row + .col-md-3(ng-click='selectedClass = "warrior"') + h5 + a(href='http://habitrpg.wikia.com/wiki/Warrior' target='_blank')=env.t('warrior') + figure.herobox(ng-class='{"selected-class": selectedClass=="warrior"}') + .character-sprites + span(class='skin_{{user.preferences.skin}}') + span(class='{{user.preferences.size}}_armor_warrior_5') + span(class='head_0') + span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') + span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') + span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') + span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') + span(class='head_warrior_5') + span(class='shield_warrior_5') + span(class='weapon_warrior_6') + .col-md-3(ng-click='selectedClass = "wizard"') + h5 + a(href='http://habitrpg.wikia.com/wiki/Mage' target='_blank')=env.t('mage') + figure.herobox(ng-class='{"selected-class": selectedClass=="wizard"}') + .character-sprites + span(class='skin_{{user.preferences.skin}}') + span(class='{{user.preferences.size}}_armor_wizard_5') + span(class='head_0') + span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') + span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') + span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') + span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') + span(class='head_wizard_5') + span(class='shield_wizard_5') + span(class='weapon_wizard_6') + .col-md-3(ng-click='selectedClass = "rogue"') + h5 + a(href='http://habitrpg.wikia.com/wiki/Rogue' target='_blank')=env.t('rogue') + figure.herobox(ng-class='{"selected-class": selectedClass=="rogue"}') + .character-sprites + span(class='skin_{{user.preferences.skin}}') + span(class='{{user.preferences.size}}_armor_rogue_5') + span(class='head_0') + span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') + span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') + span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') + span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') + span(class='head_rogue_5') + span(class='shield_rogue_6') + span(class='weapon_rogue_6') + .col-md-3(ng-click='selectedClass = "healer"') + h5 + a(href='http://habitrpg.wikia.com/wiki/Healer' target='_blank')=env.t('healer') + figure.herobox(ng-class='{"selected-class": selectedClass=="healer"}') + .character-sprites + span(class='skin_{{user.preferences.skin}}') + span(class='{{user.preferences.size}}_armor_healer_5') + span(class='head_0') + span(class='hair_base_{{user.preferences.hair.base}}_{{user.preferences.hair.color}}') + span(class='hair_bangs_{{user.preferences.hair.bangs}}_{{user.preferences.hair.color}}') + span(class='hair_beard_{{user.preferences.hair.beard}}_{{user.preferences.hair.color}}') + span(class='hair_mustache_{{user.preferences.hair.mustache}}_{{user.preferences.hair.color}}') + span(class='head_healer_5') + span(class='shield_healer_5') + span(class='weapon_healer_6') + br + .well(ng-show='selectedClass=="warrior"')=env.t('warriorText') + .well(ng-show='selectedClass=="wizard"')=env.t('mageText') + .well(ng-show='selectedClass=="rogue"')=env.t('rogueText') + .well(ng-show='selectedClass=="healer"')=env.t('healerText') .modal-footer - button.btn.btn-small.btn-danger.cancel(ng-click='user.ops.disableClasses({})', popover-placement='top', popover-trigger='mouseenter', popover=env.t('optOutText'))=env.t('optOut') - button.btn.btn-primary.cancel(ng-click='selectedClass && changeClass(selectedClass)')=env.t('select') \ No newline at end of file + button.btn.btn-sm.btn-danger(ng-click='user.ops.disableClasses({}); $close()', popover-placement='top', popover-trigger='mouseenter', popover=env.t('optOutText'))=env.t('optOut') + button.btn.btn-primary(ng-disabled='!selectedClass' ng-click='changeClass(selectedClass); $close()')=env.t('select') \ No newline at end of file diff --git a/views/shared/modals/death.jade b/views/shared/modals/death.jade index 2c346e11a8..4ab50771a3 100644 --- a/views/shared/modals/death.jade +++ b/views/shared/modals/death.jade @@ -1,14 +1,16 @@ -div(modal='user.stats.hp <= 0', options='{backdrop:true, keyboard:false, backdropClick:false}') - .modal-body - .row-fluid - .span4 - figure.notification-character - .GrimReaper - .span8 - h2=env.t('youDied') - .row-fluid - .span4 - p - a.btn.btn-danger.btn-large.notification-action(ng-click='user.ops.revive({})')=env.t('continue') - .span8 - p=env.t('dieText') +//div(modal='user.stats.hp <= 0', options='{backdrop:true, keyboard:false, backdropClick:false}') +script(type='text/ng-template', id='modals/death.html') + .modal-body.death-modal + .container-fluid + .row + .col-md-3 + figure + .GrimReaper + .col-md-9 + h2=env.t('youDied') + .row + .col-md-3 + p + a.btn.btn-danger.btn-lg(ng-click='user.ops.revive({}); $close()')=env.t('continue') + .col-md-9 + p=env.t('dieText') diff --git a/views/shared/modals/drops.jade b/views/shared/modals/drops.jade index e423e2d68a..fcb91e808a 100644 --- a/views/shared/modals/drops.jade +++ b/views/shared/modals/drops.jade @@ -1,23 +1,15 @@ -div(modal='modals.dropsEnabled') +script(type='text/ng-template', id='modals/dropsEnabled.html') .modal-header - h3=env.t('dropsEnabled') + h4=env.t('dropsEnabled') .modal-body p - span.item-drop-icon(class='Pet_Egg_Wolf', style='margin-left: 0px') + figure + .item-drop-icon(class='Pet_Egg_Wolf') span!=env.t('firstDrop', {eggText: "{{Content.eggs.Wolf.text}}", eggNotes: "{{Content.eggs.Wolf.notes}}"}) br p - span.item-drop-icon(class='Pet_Currency_Gem') + figure + .item-drop-icon(class='Pet_Currency_Gem') span!=env.t('useGems') .modal-footer - button.btn.btn-default.cancel(ng-click='modals.dropsEnabled = false')=env.t('close') - -div(modal='modals.drop') - .modal-header - h3=env.t('itemDrop') - .modal-body - p - span.item-drop-icon(class='Pet_{{user._tmp.drop.type}}_{{user._tmp.drop.key}}') - | {{user._tmp.drop.dialog}} - .modal-footer - button.btn.btn-default.cancel(ng-click='modals.drop = false')=env.t('close') \ No newline at end of file + button.btn.btn-default(ng-click='$close()')=env.t('close') \ No newline at end of file diff --git a/views/shared/modals/index.jade b/views/shared/modals/index.jade index db481615bb..c16838f72e 100644 --- a/views/shared/modals/index.jade +++ b/views/shared/modals/index.jade @@ -1,5 +1,4 @@ include ./achievements -include ./login include ./reroll include ./death include ./new-stuff @@ -9,4 +8,5 @@ include ./settings include ./drops include ./classes include ./quests -include ./rebirth \ No newline at end of file +include ./rebirth +include ./limited \ No newline at end of file diff --git a/views/shared/modals/limited.jade b/views/shared/modals/limited.jade new file mode 100644 index 0000000000..8d2d31ce90 --- /dev/null +++ b/views/shared/modals/limited.jade @@ -0,0 +1,30 @@ +//Valentine +script(id='modals/valentine.html', type='text/ng-template') + .modal-header + h4 + .inventory_special_valentine.pull-right + =env.t('valentineCard') + .modal-body + .bg-info(style='padding:10px') + p To: {{user.profile.name}}, From: {{user.items.special.valentineReceived[0]}} + hr + div(ng-switch='Math.floor(Math.random()*3)') + ul.list-unstyled(ng-switch-when='0') + li "Roses are red + li My Dailies are blue + li I'm happy that I'm + li In a Party with you!" + ul.list-unstyled(ng-switch-when='1') + li "Roses are red + li Violets are nice + li Let's get together + li And fight against Vice!" + ul.list-unstyled(ng-switch-default) + li "Roses are red + li This poem style is old + li I hope that you like this + li Cuz it cost ten gold." + p + small For enduring such a saccharine poem, you both receive the "Adoring Friends" badge! + .modal-footer + button.btn.btn-default(ng-click='user.ops.readValentine({});$close()')=env.t('ok') \ No newline at end of file diff --git a/views/shared/modals/login.jade b/views/shared/modals/login.jade deleted file mode 100644 index 9245d18535..0000000000 --- a/views/shared/modals/login.jade +++ /dev/null @@ -1,42 +0,0 @@ -// This file was moved to /views/static/front.jade, as we're requiring login / registration from front page now - keep this file around until we're sure of that decision - -//-div(modal='modals.login') - .modal-header - h3 Login / Register - .modal-body - //a(href='/auth/facebook') - img(src='/img/facebook-login-register.jpeg', alt='Login / Register With Facebook') - //h3 Or - tabset - tab(heading='Login') - form(ng-controller='AuthCtrl', ng-submit='auth()') - .control-group - input(type='text', ng-model='loginUsername', placeholder='{{useUUID ? \'UUID\' : \'Username\'}}') - .control-group - input(type='{{useUUID ? "text" : "password"}}', ng-model='loginPassword', placeholder='{{useUUID ? \'API Token\' : \'Password\'}}') - .control-group - label.checkbox - input(type='checkbox', ng-click='useUUID = !useUUID') - | Use UUID / API Token (For Facebook Users) - .control-group - input.btn.btn-primary(type='submit', value='Login') - - tab(heading='Register') - form(ng-controller='AuthCtrl', ng-submit='register()', name='registrationForm') - .control-group - input(type='text', ng-model='registerVals.username', placeholder='Username', required) - .control-group - input(type='email', ng-model='registerVals.email', placeholder='Email', required) - .control-group - input(type='password', ng-model='registerVals.password', placeholder='Password', required) - .control-group - input(type='password', ng-model='registerVals.confirmPassword', placeholder='Password Confirm', required) - .control-group - input.btn.btn-primary(type='submit', value='Register') - - - .modal-footer - button.btn.btn-warning.cancel(ng-click='modals.login = false') Cancel - - diff --git a/views/shared/modals/members.jade b/views/shared/modals/members.jade index 925d9267df..3937fd1fd5 100644 --- a/views/shared/modals/members.jade +++ b/views/shared/modals/members.jade @@ -1,26 +1,30 @@ -div(ng-controller='MemberModalCtrl') - #memberModal(modal='modals.member') - .modal-header(bindonce='profile') - h3 - span {{profile.profile.name}} - span(ng-if='profile.contributor.level') - {{contribText(profile.contributor, profile.backer)}} - .modal-body(bindonce='profile') - .row-fluid - .span6 +script(type='text/ng-template', id='modals/member.html') + .modal-header(bindonce='profile') + h4 + span {{profile.profile.name}} + span(ng-if='profile.contributor.level') - {{contribText(profile.contributor, profile.backer)}} + .modal-body(bindonce='profile') + .container-fluid + .row + .col-md-6 img(ng-show='profile.profile.imageUrl', ng-src='{{profile.profile.imageUrl}}') markdown(ng-show='profile.profile.blurb', ng-model='profile.profile.blurb') - ul.muted.unstyled(ng-if='profile.auth.timestamps', style='margin-top:10px;') + ul.muted.list-unstyled(ng-if='profile.auth.timestamps') li {{profile._id}} - li(ng-show='profile.auth.timestamps.created')=env.t('memberSince') + li(ng-show='profile.auth.timestamps.created') + |  + =env.t('memberSince') | {{timestamp(profile.auth.timestamps.created)}} - - li(ng-show='profile.auth.timestamps.loggedin')=env.t('lastLoggedIn') + li(ng-show='profile.auth.timestamps.loggedin') + |  + =env.t('lastLoggedIn') | {{timestamp(profile.auth.timestamps.loggedin)}} - h3=env.t('stats') - .label.label-info {{ {warrior:'Warrior',wizard:'Mage',healer:'Healer',rogue:'Rogue'}[user.stats.class] }} + .label.label-info {{ {warrior:'Warrior',wizard:'Mage',healer:'Healer',rogue:'Rogue'}[profile.stats.class] }} include ../profiles/stats - .span6 + .col-md-6 include ../header/avatar h3 Achievements include ../profiles/achievements - .modal-footer - button.btn.btn-default(ng-click='modals.member = false')=env.t('ok') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('ok') \ No newline at end of file diff --git a/views/shared/modals/new-stuff.jade b/views/shared/modals/new-stuff.jade index f6601c3bb6..4e9d44abe9 100644 --- a/views/shared/modals/new-stuff.jade +++ b/views/shared/modals/new-stuff.jade @@ -1,20 +1,7 @@ -//-div(modal='!user.flags.mathUpdates') +script(type='text/ng-template', id='modals/newStuff.html') .modal-header - h3 Math Updates - .modal-body - .npc_tyler.pull-left(style='margin-right:10px') - p Hey all, Tyler here. Sorry for the rocky numbers (Exp, GP, etc) with the Class System roll-out! We're tweaking the math today, and things should be mostly finalized by tomorrow. Follow our progress here, and please do chime in! - br - p We've also changed classes to unlock at lvl 10 instead of 5, to prevent overwhelming our new users. And since users have expressed confusion, we'll be improving UI and clarifification. Stay tuned. - br - p iOS users, iTunes is on vacation until 12/28. Until we can push the updated app, you'll need to sync periodically to get the corrected numbers (app has old math, server saves the correct numbers). Android goes out ASAP. More info on Trello. - .modal-footer - button.btn.btn-default.cancel(ng-click='User.set({"flags.mathUpdates":true})') Dismiss - -div(modal='modals.newStuff') - .modal-header - h3 New Stuff! - .modal-body + h4 New Stuff! + .modal-body.new-stuff-modal.modal-fixed-height table tr td @@ -25,12 +12,68 @@ div(modal='modals.newStuff') h3.popover-title a(target='_blank', href='https://twitter.com/Mihakuu') Bailey .popover-content - h5 Vice - p You awaken after the Winter Wonderland festivities and birthday celebrations with a smile. It's been a snowy, cheerful couple months, and the NPCs have finally returned to their normal attire. But today something is very wrong. Shadowy whisps cover the ground of Habitica, the sky has darkened. At the tavern you hear @DanielTheBard struming dark tales on his lute, and @Baconsaur peering into a mug, grumbling about her mounts swallowed in the shadows. They speak of the same thing: Vice, a dark an terrible foe. This new boss arc is a 3-part quest that requires level 30 to begin. Bring your strongest party members, and don't miss your dailies - there's a powerful weapon at the end! + + h5 Happy Valentine's Day! + p Help motivate all of the lovely people in your life by sending them a caring valentine. Valentines can be purchased for 10 gold from the Item Store. For spreading love and joy throughout the community, both the giver AND the receiver get a coveted "adoring friends" badge. Hooray! p - small.muted by @baconsaur & @DanielTheBard + small.muted By Lemoness and zoebeagle + hr p - small.muted 02/01/2014 + small.muted 02/13/2014 + + hr + h5 02/12/2014 + table.table.table-striped + tr + td + h5 Chat & Invite Notifications + p Chat & group-invitation notifications are back! Miss them? They currently work for all chat updates in parties & guilds. Any devs willing to jump into @tagging in Tavern, see here. + tr + td + h5 Toolbar + p In order to make room for these notifs, we added a toolbar above the header. You can collapse the toolbar (far-right icon), but take care as Bailey notifs are inside the toolbar! + hr + h5 02/07/2014 + table.table.table-striped + tr + td + h5 February Mystery Item + p + .pull-right.inventory_present + | We're excited to announce a new feature a s a big thank-you to the awesome people who subscribe to HabitRPG! Every month, all subscribers will now receive a limited-edition mystery item! The mystery item will be a stats-free costume piece (like the Absurd Party Robes) that will only be available to the people who are subscribers each month. The February 2014 item will be revealed on the 23rd to everyone, but all people who are subscribers during the month of February will receive it. Subscribe now, get excited, and thank you so much for helping to support HabitRPG! We love you. + tr + td + h5 Critical Hammer Of Bug-Crushing + p + .pull-right.weapon_special_critical + | Some of you may have noticed that we periodically have some bugs that are nastier than the norm - the dreaded critical bugs. These monstrous apparitions have been snapping at the heels of many a player. For updates on what we're currently working on to improve site stability, read this link - and then jump in to help! Not only will programming assistance reward you with the usual contributor levels, but if you actually manage to fix a bug marked "critical," you will now receive the Critical Hammer of Bug-Crushing as your reward! + tr + td + + h5 Rainbow Hair Colors + p + .pull-right.customize-option.hair_bangs_1_rainbow + | Want to spruce up your avatar? Rainbow hair colors are now available! Dye your luscious locks purple, green, or even rainbow-striped, and passersby will look at you with envy. + tr + td + h5 Stability Update + p We've stabilized the site a lot (we're still working out kinks, but we're way better now). Follow the progress here, but here are some workarounds for now: + ul + li Click slower. VersionError is caused by clicking things off too fast (we're working on a fix). + li If you see an error, refresh before proceeding. + + p + small.muted By Lemoness, mariahm, crystalphoenix, aiseant, zoebeagle, cole, lefnire + + hr + h5 02/01/2014 + table.table.table-striped + tr + td + h5 Vice + p You awaken after the Winter Wonderland festivities and birthday celebrations with a smile. It's been a snowy, cheerful couple months, and the NPCs have finally returned to their normal attire. But today something is very wrong. Shadowy whisps cover the ground of Habitica, the sky has darkened. At the tavern you hear @DanielTheBard struming dark tales on his lute, and @Baconsaur peering into a mug, grumbling about her mounts swallowed in the shadows. They speak of the same thing: Vice, a dark an terrible foe. This new boss arc is a 3-part quest that requires level 30 to begin. Bring your strongest party members, and don't miss your dailies - there's a powerful weapon at the end! + p + small.muted by @baconsaur & @DanielTheBard hr h5 01/30/2014 @@ -466,5 +509,5 @@ div(modal='modals.newStuff') a(href='https://trello.com/card/custom-day-start/50e5d3684fe3a7266b0036d6/15') Add custom day start .modal-footer - button.btn.btn-default.cancel(ng-click='modals.newStuff = false') Cool - button.btn.btn-warning.cancel(ng-click='dismissAlert()') Dismiss This Alert \ No newline at end of file + button.btn.btn-default(ng-click='$close()') Cool + button.btn.btn-warning(ng-click='dismissAlert(); $close()') Dismiss This Alert diff --git a/views/shared/modals/quests.jade b/views/shared/modals/quests.jade index 2950fab26e..8c0838feca 100644 --- a/views/shared/modals/quests.jade +++ b/views/shared/modals/quests.jade @@ -11,50 +11,49 @@ script(id='partials/options.social.party.quest-rewards.html', type='text/ng-temp td {{quest.drop.gp}} =env.t('gold') -div(modal='user.party.quest.completed', ng-controller='InventoryCtrl') +script(type='text/ng-template', id='modals/questCompleted.html') .modal-header - h3 "{{Content.quests[user.party.quest.completed].text}}" + h4 "{{Content.quests[user.party.quest.completed].text}}" =env.t('completed') .modal-body p {{Content.quests[user.party.quest.completed].completion}} quest-rewards(key='{{user.party.quest.completed}}', header=env.t('youReceived')) .modal-footer - button.btn.btn-default.btn-primary(ng-click='set({"party.quest.completed":""})')=env.t('accept') + button.btn.btn-primary(ng-click='set({"party.quest.completed":""}); $close()')=env.t('accept') -div(modal='modals.showQuest', ng-controller='InventoryCtrl') +script(type='text/ng-template', id='modals/showQuest.html') .modal-header - h3 {{selectedQuest.text}} + h4 {{selectedQuest.text}} .modal-body .pull-right(class='quest_{{selectedQuest.key}}') div(ng-bind-html='selectedQuest.notes') quest-rewards(key='{{selectedQuest.key}}') - hr - div(style='clear:left;clear:right') - .npc_ian.pull-left - p=env.t('questSend') + hr + .npc_ian.pull-left + p=env.t('questSend') .modal-footer - button.btn.btn-default.btn-small.btn-cancel(ng-click='closeQuest()')=env.t('cancel') - button.btn.btn-default.btn-primary(ng-click='questInit()')=env.t('inviteParty') + button.btn.btn-default(ng-click='closeQuest(); $close()')=env.t('cancel') + button.btn.btn-primary(ng-click='questInit(); $close()')=env.t('inviteParty') -div(modal='modals.buyQuest', ng-controller='InventoryCtrl') +script(type='text/ng-template', id='modals/buyQuest.html') .modal-header - h3 {{selectedQuest.text}} + h4 {{selectedQuest.text}} .modal-body .pull-right(class='quest_{{selectedQuest.key}}') div(ng-bind-html='selectedQuest.notes') quest-rewards(key='{{selectedQuest.key}}') .modal-footer - button.btn.btn-large.cancel(ng-click='closeQuest()')=env.t('neverMind') - button.btn.btn-default.btn-large.btn-primary(ng-click='purchase("quests", quest);closeQuest()')=env.t('buyQuest') + button.btn.btn-default(ng-click='closeQuest(); $close()')=env.t('neverMind') + button.btn.btn-primary(ng-click='purchase("quests", quest); closeQuest(); $close()')=env.t('buyQuest') -div(modal='party.quest.key && !party.quest.active && !questHold && party.quest.members[user._id] == undefined') +script(type='text/ng-template', id='modals/questInvitation.html') .modal-header - h3=env.t('questInvitation') - | {{Content.quests[party.quest.key].text}} + h4=env.t('questInvitation') + | {{Content.quests[party.quest.key].text}} .modal-body div(ng-bind-html='Content.quests[party.quest.key].notes') quest-rewards(key='{{party.quest.key}}') .modal-footer - button.btn.btn-default.btn-small.btn-cancel(ng-click='questHold = true')=env.t('askLater') - button.btn.btn-default.btn-small.btn-cancel(ng-click='party.$questReject()')=env.t('reject') - button.btn.btn-default.btn-primary(ng-click='party.$questAccept()')=env.t('accept') + button.btn.btn-default(ng-click='questHold = true; $close()')=env.t('askLater') + button.btn.btn-default(ng-click='party.$questReject(); $close()')=env.t('reject') + button.btn.btn-primary(ng-click='party.$questAccept(); $close()')=env.t('accept') diff --git a/views/shared/modals/rebirth.jade b/views/shared/modals/rebirth.jade index bbc79630cf..bb11a3ec48 100644 --- a/views/shared/modals/rebirth.jade +++ b/views/shared/modals/rebirth.jade @@ -1,46 +1,51 @@ // Created by Sabe on 12/22/13. -div(modal='modals.rebirthEnabled') - .modal-header - h3=env.t('rebirthNew') - .modal-body - .rebirth_orb.pull-left(style='margin-right: 5px') - p - span=env.t('rebirthUnlock') - .modal-footer - button.btn.btn-default.cancel(ng-click='modals.rebirthEnabled = false')=env.t('close') +script(type='text/ng-template', id='modals/rebirthEnabled.html') + .modal-header + h4=env.t('rebirthNew') + .modal-body + figure + .rebirth_orb + p + span=env.t('rebirthUnlock') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('close') -div(modal='modals.rebirth') - .modal-header - h3=env.t('rebirthBegin') - .modal-body - include ../gems - .rebirth_orb.pull-left(style='margin-right: 5px') - p=env.t('rebirthStartOver') - br - .unordered - li=env.t('rebirthAdvList1') - li=env.t('rebirthAdvList2') - li=env.t('rebirthAdvList3') - li=env.t('rebirthAdvList4') - br - p=env.t('rebirthInherit') - .unordered - li=env.t('rebirthInList1') - li=env.t('rebirthInList2') - li=env.t('rebirthInList3') - li=env.t('rebirthInList4') - li=env.t('rebirthInList5') - br - .achievement-sun.pull-left(style='margin-right: 5px') - p=env.t('rebirthEarnAchievement') - .modal-footer - button.btn.btn-large.cancel(ng-click='modals.rebirth = false')=env.t('neverMind') - span(ng-if='user.balance < 2') - a.btn.btn-success.btn-large(ng-click="modals.buyGems = true")=env.t('buyMoreGems') - span.gem-cost=env.t('notEnoughGems') - span(ng-if='user.balance >= 2', ng-controller='SettingsCtrl') - a.btn.btn-danger.btn-large(ng-click='rebirth()')=env.t('beReborn') - span.gem-cost - | 8  - =env.t('gems') +script(type='text/ng-template', id='modals/rebirth.html') + .modal-header + h4=env.t('rebirthBegin') + .modal-body + include ../gems + figure + .rebirth_orb + p=env.t('rebirthStartOver') + br + ul.list-unstyled + li=env.t('rebirthAdvList1') + li=env.t('rebirthAdvList2') + li=env.t('rebirthAdvList3') + li=env.t('rebirthAdvList4') + br + p=env.t('rebirthInherit') + ul.list-unstyled + li=env.t('rebirthInList1') + li=env.t('rebirthInList2') + li=env.t('rebirthInList3') + li=env.t('rebirthInList4') + li=env.t('rebirthInList5') + br + + p + span.vertical-align.inline-block.achievement-sun + |  + =env.t('rebirthEarnAchievement') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('neverMind') + span(ng-if='user.balance < 2') + a.btn.btn-success(ng-click='openModal("buyGems",{track:"Gems > Rebirth"})')=env.t('buyMoreGems') + span.gem-cost=env.t('notEnoughGems') + span(ng-if='user.balance >= 2', ng-controller='SettingsCtrl') + a.btn.btn-danger(ng-click='$close(); rebirth()')=env.t('beReborn') + span.gem-cost + | 8  + =env.t('gems') diff --git a/views/shared/modals/reroll.jade b/views/shared/modals/reroll.jade index 24dba8d7ff..5bfa2dacd5 100644 --- a/views/shared/modals/reroll.jade +++ b/views/shared/modals/reroll.jade @@ -1,18 +1,18 @@ // Re-Roll modal -div(modal='modals.reroll') +script(type='text/ng-template', id='modals/reroll.html') .modal-header - h3=env.t('fortify') + h4=env.t('fortify') .modal-body include ../gems p=env.t('fortifyText') .modal-footer - button.btn.btn-large.cancel(ng-click='modals.reroll = false')=env.t('neverMind') + button.btn.btn-default(ng-click='$close()')=env.t('neverMind') span(ng-if='user.balance < 1') - a.btn.btn-success.btn-large(ng-click="modals.buyGems = true")=env.t('buyMoreGems') + a.btn.btn-success(ng-click='openModal("buyGems",{track:{"Gems > Reroll"}})')=env.t('buyMoreGems') span.gem-cost=env.t('notEnoughGems') span(ng-if='user.balance >= 1', ng-controller='SettingsCtrl') - a.btn.btn-danger.btn-large(ng-click='reroll()')=env.t('fortify') + a.btn.btn-danger(ng-click='$close(); reroll()')=env.t('fortify') span.gem-cost | 4  = env.t('gems') diff --git a/views/shared/modals/settings.jade b/views/shared/modals/settings.jade index d4c04fdf38..15db914df2 100644 --- a/views/shared/modals/settings.jade +++ b/views/shared/modals/settings.jade @@ -1,59 +1,65 @@ -div(ng-controller='SettingsCtrl') - div(modal='modals.reset') - .modal-header - h3=env.t('resetAccount') - .modal-body - p=env.t('resetText1') - p=env.t('resetText2') - .modal-footer - button.btn.btn-default.cancel(ng-click='modals.reset = false')=env.t('neverMind') - button.btn.btn-danger(data-dismiss='modal', ng-click='reset()')=env.t('resetDo') +script(type='text/ng-template', id='modals/reset.html') + .modal-header + h4=env.t('resetAccount') + .modal-body + p=env.t('resetText1') + p=env.t('resetText2') + .modal-footer + button.btn.btn-default(ng-click='$close();')=env.t('neverMind') + button.btn.btn-danger(ng-click='$close(); reset()')=env.t('resetDo') - div(modal='modals.restore') - .modal-header - h3=env.t('fixValues') - .modal-body - p=env.t('fixValuesText1') - p=env.t('fixValuesText2') - div.restore-options - form#restore-form.form-horizontal - h3=env.t('stats') - .option-group.option-medium - input.option-content(type='number', step="any", data-for='stats.hp', ng-model='restoreValues.stats.hp') - span.input-suffix=env.t('health') - .option-group.option-medium - input.option-content(type='number', step="any", data-for='stats.exp', ng-model='restoreValues.stats.exp') - span.input-suffix=env.t('experience') - .option-group.option-medium - input.option-content(type='number', step="any", data-for='stats.gp', ng-model='restoreValues.stats.gp') - //input.option-content(type='number', step="any", data-for='stats.gp', ng-model='restoreValues.stats.gp',disabled) - span.input-suffix=env.t('gold') - //-p.alert - small=env.t('disabledWinterEvent') - .option-group.option-medium - input.option-content(type='number', step="any", data-for='stats.mp', ng-model='restoreValues.stats.mp') - span.input-suffix=env.t('mana') - .option-group.option-medium - input.option-content(type='number', data-for='stats.lvl', ng-model='restoreValues.stats.lvl') - span.input-suffix=env.t('level') - h3=env.t('achievements') - .option-group.option-medium - input.option-content(type='number', data-for='achievements.streak', ng-model='restoreValues.achievements.streak') - span.input-suffix=env.t('fix21Streaks') - //- This is causing too many problems for users - h3=env.t('other') - a.btn.btn-small.btn-warning(ng-controller='FooterCtrl', ng-click='addMissedDay()')=env.t('triggerDay') - .modal-footer - button.btn.btn-default.cancel(ng-click='modals.restore = false')=env.t('discardChanges') - button.btn.btn-primary(ng-click='restore()')=env.t('saveAndClose') +script(type='text/ng-template', id='modals/restore.html') + .modal-header + h4=env.t('fixValues') + .modal-body + p=env.t('fixValuesText1') + p=env.t('fixValuesText2') + form.form-horizontal + h3=env.t('stats') + .form-group + .col-md-6 + input.form-control(type='number', step="any", data-for='stats.hp', ng-model='$parent.restoreValues.stats.hp') + label.control-label=env.t('health') + .form-group + .col-md-6 + input.form-control(type='number', step="any", data-for='stats.exp', ng-model='$parent.restoreValues.stats.exp') + label.control-label=env.t('experience') + .form-group + .col-md-6 + input.form-control(type='number', step="any", data-for='stats.gp', ng-model='$parent.restoreValues.stats.gp') + //input.form-control(type='number', step="any", data-for='stats.gp', ng-model='restoreValues.stats.gp',disabled) + label.control-label=env.t('gold') + //-p.alert + small=env.t('disabledWinterEvent') + .form-group + .col-md-6 + input.form-control(type='number', step="any", data-for='stats.mp', ng-model='$parent.restoreValues.stats.mp') + label.control-label=env.t('mana') + .form-group + .col-md-6 + input.form-control(type='number', data-for='stats.lvl', ng-model='$parent.restoreValues.stats.lvl') + label.control-label=env.t('level') + h3=env.t('achievements') + .form-group + .col-md-6 + input.form-control(type='number', data-for='achievements.streak', ng-model='$parent.restoreValues.achievements.streak') + label.control-label=env.t('fix21Streaks') + //- This is causing too many problems for users + h3=env.t('other') + a.btn.btn-sm.btn-warning(ng-controller='FooterCtrl', ng-click='addMissedDay()')=env.t('triggerDay') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('discardChanges') + button.btn.btn-primary(ng-click='restore(); $close();')=env.t('saveAndClose') - div(modal='modals.delete') - .modal-header - h3=env.t('deleteAccount') - .modal-body - p!=env.t('deleteText') - p - input(type='text', ng-model='_deleteAccount') - .modal-footer - button.btn.btn-default(ng-click='modals.delete = false')=env.t('neverMind') - button.btn.btn-danger.btn-small(ng-disabled='_deleteAccount != "DELETE"', ng-click='delete()')=env.t('deleteDo') +script(type='text/ng-template', id='modals/delete.html') + .modal-header + h4=env.t('deleteAccount') + .modal-body + p!=env.t('deleteText') + br + .row + .col-md-6 + input.form-control(type='text', ng-model='_deleteAccount') + .modal-footer + button.btn.btn-default(ng-click='$close()')=env.t('neverMind') + button.btn.btn-danger(ng-disabled='_deleteAccount != "DELETE"', ng-click='$close(); delete()')=env.t('deleteDo') diff --git a/views/shared/profiles/achievements.jade b/views/shared/profiles/achievements.jade index 2ef6789a86..d18e0c739f 100644 --- a/views/shared/profiles/achievements.jade +++ b/views/shared/profiles/achievements.jade @@ -1,133 +1,141 @@ -.achievements-container - //
    - //
    - //
    - //
    - //
    - //
    - //
    - //
    - //
    - //
    - //
    - //
    +//
    +//
    +//
    +//
    +//
    +//
    +//
    +//
    +//
    +//
    +//
    +//
    - div(ng-if='profile.backer.npc') - .achievement.achievement-helm +div(ng-if='profile.backer.npc') + .achievement.achievement-helm + h5 + span.label.label-npc + | {{profile.backer.npc}} + =env.t('npc') + small=env.t('npcText') + hr + +div(ng-if='profile.contributor.level || user._id == profile._id') + .achievement.achievement-firefox(ng-if='profile.contributor.level') + div(ng-class='{muted: !profile.contributor.level}') h5 - span.label.label-npc - | {{profile.backer.npc}} - =env.t('npc') - small=env.t('npcText') - hr - - div(ng-if='profile.contributor.level || user._id == profile._id') - .achievement.achievement-firefox(ng-if='profile.contributor.level') - div(ng-class='{muted: !profile.contributor.level}') - h5 - span.label(ng-if='profile.contributor.level', class='label-contributor-{{profile.contributor.level}}') {{contribText(profile.contributor, profile.backer)}} - span.label(ng-if='!profile.contributor.level')=env.t('contribName') - small - =env.t('contribText') - a(href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG', target='_blank')=env.t('readMore') - | . - hr - - div(ng-if='profile.backer.tier') - .achievement.achievement-heart - h5=env.t('kickstartName', {tier: "{{profile.backer.tier}}"}) - small=env.t('kickstartText') - hr - - div(ng-if='profile.achievements.streak || user._id == profile._id') - .achievement.achievement-thermometer(ng-show='profile.achievements.streak') - div(ng-class='{muted: !profile.achievements.streak}') - h5 {{profile.achievements.streak || 0 }} - =env.t('streakName') - small=env.t('streakText', {streaks: "{{profile.achievements.streak || 0 }}"}) - hr - - div(ng-if='profile.achievements.perfect || user._id == profile._id') - .achievement.achievement-perfect(ng-show='profile.achievements.perfect') - div(ng-class='{muted: !profile.achievements.perfect}') - h5 {{profile.achievements.perfect || 0 }} - =env.t('perfectName') - small=env.t('perfectText', {perfects: "{{profile.achievements.perfect || 0 }}"}) - hr - - div(ng-if='profile.achievements.ultimateGear || user._id == profile._id') - .achievement.achievement-armor(ng-show='profile.achievements.ultimateGear') - div(ng-class='{muted: !profile.achievements.ultimateGear}') - h5=env.t('ultimGearName') - small=env.t('ultimGearText') - hr - - div(ng-if='profile.achievements.beastMaster || user._id == profile._id') - .achievement.achievement-rat(ng-show='profile.achievements.beastMaster') - div(ng-class='{muted: !profile.achievements.beastMaster}') - h5=env.t('beastMastName') - small=env.t('beastMastText') - hr - - div(ng-if='profile.achievements.rebirths') - .achievement.achievement-sun - h5(ng-if='profile.achievements.rebirths == 1')=env.t('rebirthBegan') - h5(ng-if='profile.achievements.rebirths > 1') - =env.t('rebirthText', {rebirths: "{{profile.achievements.rebirths}}"}) + span.label.label-default(ng-if='profile.contributor.level', class='label-contributor-{{profile.contributor.level}}') {{contribText(profile.contributor, profile.backer)}} + span.label.label-default(ng-if='!profile.contributor.level')=env.t('contribName') small - =env.t('rebirthOrb') - | {{profile.achievements.rebirthLevel}}. - hr + =env.t('contribText') + a(href='http://habitrpg.wikia.com/wiki/Contributing_to_HabitRPG', target='_blank')=env.t('readMore') + | . + hr - div(ng-if='profile.achievements.helpedHabit') - .achievement.achievement-tree - h5=env.t('helped') - small - =env.t('helpedText1') - a(href='http://community.habitrpg.com/node/290', target='_blank')=env.t('helpedText2') - hr +div(ng-if='profile.backer.tier') + .achievement.achievement-heart + h5=env.t('kickstartName', {tier: "{{profile.backer.tier}}"}) + small=env.t('kickstartText') + hr - div(ng-if='profile.achievements.originalUser || profile.achievements.veteran') - .achievement.achievement-cake - div(ng-if='profile.achievements.veteran') - h5=env.t('veteran') - small=env.t('veteranText') - div(ng-if='profile.achievements.originalUser') - h5=env.t('originalUser') - small!=env.t('originalUserText') - hr +div(ng-if='profile.achievements.streak || user._id == profile._id') + .achievement.achievement-thermometer(ng-show='profile.achievements.streak') + div(ng-class='{muted: !profile.achievements.streak}') + h5 + |{{profile.achievements.streak || 0 }}  + =env.t('streakName') + small=env.t('streakText', {streaks: "{{profile.achievements.streak || 0 }}"}) + hr - div(ng-if='profile.achievements.challenges || user._id == profile._id') - // This is a very strange icon to use. revisit - .achievement.achievement-karaoke(ng-show='profile.achievements.challenges') - div(ng-class='{muted: !profile.achievements.challenges}') - h5=env.t('challengeWinner') - table.table.table-striped - tr(ng-repeat='chal in profile.achievements.challenges') - td {{chal}} - hr +div(ng-if='profile.achievements.perfect || user._id == profile._id') + .achievement.achievement-perfect(ng-show='profile.achievements.perfect') + div(ng-class='{muted: !profile.achievements.perfect}') + h5 + |{{profile.achievements.perfect || 0 }}  + =env.t('perfectName') + small=env.t('perfectText', {perfects: "{{profile.achievements.perfect || 0 }}"}) + hr - div(ng-if='profile.achievements.quests || user._id == profile._id') - .achievement.achievement-alien(ng-show='profile.achievements.quests') - div(ng-class='{muted: !profile.achievements.quests}') - h5=env.t('completedQuests') - table.table.table-striped - tr(ng-repeat='(k,v) in profile.achievements.quests') - td {{Content.quests[k].text}} - td x{{v}} - hr +div(ng-if='profile.achievements.ultimateGear || user._id == profile._id') + .achievement.achievement-armor(ng-show='profile.achievements.ultimateGear') + div(ng-class='{muted: !profile.achievements.ultimateGear}') + h5=env.t('ultimGearName') + small=env.t('ultimGearText') + hr - div(ng-if='profile.achievements.snowball') - .achievement.achievement-snowball - h5=env.t('annoyingFriends') - small - =env.t('annoyingFriendsText', {snowballs: "{{profile.achievements.snowball}}"}) - hr +div(ng-if='profile.achievements.beastMaster || user._id == profile._id') + .achievement.achievement-rat(ng-show='profile.achievements.beastMaster') + div(ng-class='{muted: !profile.achievements.beastMaster}') + h5=env.t('beastMastName') + small=env.t('beastMastText') + hr - div(ng-if='profile.achievements.habitBirthday') - .achievement.achievement-habitBirthday - h5=env.t('habitBirthday') - small - =env.t('habitBirthdayText') - hr +div(ng-if='profile.achievements.rebirths') + .achievement.achievement-sun + h5(ng-if='profile.achievements.rebirths == 1')=env.t('rebirthBegan') + h5(ng-if='profile.achievements.rebirths > 1') + =env.t('rebirthText', {rebirths: "{{profile.achievements.rebirths}}"}) + small + =env.t('rebirthOrb') + | {{profile.achievements.rebirthLevel}}. + hr +div(ng-if='profile.achievements.helpedHabit') + .achievement.achievement-tree + h5=env.t('helped') + small + =env.t('helpedText1') + |  + a(href='http://community.habitrpg.com/node/290', target='_blank')=env.t('helpedText2') + hr + +div(ng-if='profile.achievements.originalUser || profile.achievements.veteran') + .achievement.achievement-cake + div(ng-if='profile.achievements.veteran') + h5=env.t('veteran') + small=env.t('veteranText') + div(ng-if='profile.achievements.originalUser') + h5=env.t('originalUser') + small!=env.t('originalUserText') + hr + +div(ng-if='profile.achievements.challenges || user._id == profile._id') + // This is a very strange icon to use. revisit + .achievement.achievement-karaoke(ng-show='profile.achievements.challenges') + div(ng-class='{muted: !profile.achievements.challenges}') + h5=env.t('challengeWinner') + table.table.table-striped + tr(ng-repeat='chal in profile.achievements.challenges') + td {{chal}} + hr + +div(ng-if='profile.achievements.quests || user._id == profile._id') + .achievement.achievement-alien(ng-show='profile.achievements.quests') + div(ng-class='{muted: !profile.achievements.quests}') + h5=env.t('completedQuests') + table.table.table-striped + tr(ng-repeat='(k,v) in profile.achievements.quests') + td {{Content.quests[k].text}} + td x{{v}} + hr + +div(ng-if='profile.achievements.snowball') + .achievement.achievement-snowball + h5=env.t('annoyingFriends') + small + =env.t('annoyingFriendsText', {snowballs: "{{profile.achievements.snowball}}"}) + hr + +div(ng-if='profile.achievements.habitBirthday') + .achievement.achievement-habitBirthday + h5=env.t('habitBirthday') + small + =env.t('habitBirthdayText') + hr + +div(ng-if='profile.achievements.valentine') + .achievement.achievement-valentine + h5=env.t('adoringFriends') + small + =env.t('adoringFriendsText', {cards: "{{profile.achievements.valentine}}"}) + hr diff --git a/views/shared/profiles/stats.jade b/views/shared/profiles/stats.jade index ea9a921c01..4f3addef0d 100644 --- a/views/shared/profiles/stats.jade +++ b/views/shared/profiles/stats.jade @@ -29,49 +29,54 @@ h4=env.t('attributes') table.table.table-striped tr(ng-repeat='(k,v) in {str:"Strength (STR)",int:"Intelligence (INT)",con:"Constitution (CON)",per:"Perception (PER)"}') td - i.icon-question-sign(ng-show='k=="str"', popover-title=env.t('strength'), popover-placement='right', popover=env.t('strengthText'), popover-trigger='mouseenter', style='margin-right:3px') - i.icon-question-sign(ng-show='k=="con"', popover-title=env.t('constitution'), popover-placement='right', popover=env.t('conText') popover-trigger='mouseenter', style='margin-right:3px') - i.icon-question-sign(ng-show='k=="per"', popover-title=env.t('perception'), popover-placement='right', popover=env.t('perText'), popover-trigger='mouseenter', style='margin-right:3px') - i.icon-question-sign(ng-show='k=="int"', popover-title=env.t('intelligence'), popover-placement='right', popover=env.t('intText'), popover-trigger='mouseenter', style='margin-right:3px') + span.glyphicon.glyphicon-question-sign(ng-show='k=="str"', popover-title=env.t('strength'), popover-placement='right', popover=env.t('strengthText'), popover-trigger='mouseenter') + |   + span.glyphicon.glyphicon-question-sign(ng-show='k=="con"', popover-title=env.t('constitution'), popover-placement='right', popover=env.t('conText') popover-trigger='mouseenter') + |   + span.glyphicon.glyphicon-question-sign(ng-show='k=="per"', popover-title=env.t('perception'), popover-placement='right', popover=env.t('perText'), popover-trigger='mouseenter') + |   + span.glyphicon.glyphicon-question-sign(ng-show='k=="int"', popover-title=env.t('intelligence'), popover-placement='right', popover=env.t('intText'), popover-trigger='mouseenter') + |   strong {{v}}: {{profile._statsComputed[k]}} td - ul(style='list-style-type:none') + ul.list-unstyled li(ng-show='profile.stats.lvl > 1') - i.icon-question-sign(popover-title=env.t('levelBonus'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('levelBonusText')) + span.glyphicon.glyphicon-question-sign(popover-title=env.t('levelBonus'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('levelBonusText')) | =env.t('level') |: {{(profile.stats.lvl - 1) / 2}}  li(ng-show='Content.gear.flat[profile.items.gear.equipped.weapon][k] + Content.gear.flat[profile.items.gear.equipped.armor][k] + Content.gear.flat[profile.items.gear.equipped.head][k] + Content.gear.flat[profile.items.gear.equipped.shield][k] > 0') - i.icon-question-sign(popover-title=env.t('equipment'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('equipmentBonusText')) + span.glyphicon.glyphicon-question-sign(popover-title=env.t('equipment'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('equipmentBonusText')) | =env.t('equipment') |: {{Content.gear.flat[profile.items.gear.equipped.weapon][k] + Content.gear.flat[profile.items.gear.equipped.armor][k] + Content.gear.flat[profile.items.gear.equipped.head][k] + Content.gear.flat[profile.items.gear.equipped.shield][k] || 0}}  li(ng-show='profile._statsComputed[k] - profile.stats.buffs[k] - ((profile.stats.lvl - 1) / 2) - Content.gear.flat[profile.items.gear.equipped.weapon][k] - Content.gear.flat[profile.items.gear.equipped.armor][k] - Content.gear.flat[profile.items.gear.equipped.head][k] - Content.gear.flat[profile.items.gear.equipped.shield][k] - profile.stats[k] > 0') - i.icon-question-sign(popover-title=env.t('classBonus'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('classBonusText')) + span.glyphicon.glyphicon-question-sign(popover-title=env.t('classBonus'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('classBonusText')) | =env.t('classEquipBonus') |: {{profile._statsComputed[k] - profile.stats.buffs[k] - ((profile.stats.lvl - 1) / 2) - Content.gear.flat[profile.items.gear.equipped.weapon][k] - Content.gear.flat[profile.items.gear.equipped.armor][k] - Content.gear.flat[profile.items.gear.equipped.head][k] - Content.gear.flat[profile.items.gear.equipped.shield][k] - profile.stats[k]}}  li(ng-show='profile.stats[k] > 0') - i.icon-question-sign(popover-title=env.t('allocatedPoints'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('allocatedPointsText')) + span.glyphicon.glyphicon-question-sign(popover-title=env.t('allocatedPoints'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('allocatedPointsText')) | =env.t('allocated') |: {{profile.stats[k] || 0}}  li(ng-show='profile.stats.buffs[k] > 0') - i.icon-question-sign(popover-title=env.t('buffs'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('buffsText')) + span.glyphicon.glyphicon-question-sign(popover-title=env.t('buffs'), popover-trigger='mouseenter', popover-placement='top', popover=env.t('buffsText')) | =env.t('buffs') |: {{profile.stats.buffs[k] || 0}}  tr(ng-if='profile.stats.buffs.stealth') td + i.icon-question-sign(popover-title=env.t('stealth'), popover-trigger='mouseenter', popover-placement='right', popover=env.t('stealthNewDay')) +   strong=env.t('stealth') - |: {{profile.stats.buffs.stealth}}  - i.icon-question-sign(popover-title=env.t('stealth'), popover-trigger='mouseenter', popover-placement='right', popover=env.t('stealthNewDay')) + |: {{profile.stats.buffs.stealth}} td tr(ng-if='profile.stats.buffs.streaks') td + i.icon-question-sign(popover-title=env.t('streaksFrozen'), popover-trigger='mouseenter', popover-placement='right', popover=env.t('streaksFrozenText')) +   strong=env.t('streaksFrozen') - | - i.icon-question-sign(popover-title=env.t('streaksFrozen'), popover-trigger='mouseenter', popover-placement='right', popover=env.t('streaksFrozenText')) td h4(ng-show='user.flags.dropsEnabled')=env.t('pets') diff --git a/views/shared/tasks/lists.jade b/views/shared/tasks/lists.jade index bd50b3042a..dc9010d279 100644 --- a/views/shared/tasks/lists.jade +++ b/views/shared/tasks/lists.jade @@ -2,122 +2,137 @@ // parts of the app. The alternative would be to create new scopes for different containing sections, but that // started to get unwieldy script(id='templates/habitrpg-tasks.html', type="text/ng-template") - .grid - .module(bindonce='lists', ng-repeat='list in lists', bo-class='{"rewards-module": list.type==="reward"}') - .task-column(class='{{list.type}}s') + .tasks-lists.container-fluid + .row + .col-md-3.col-sm-6(bindonce='lists', ng-repeat='list in lists', bo-class='{"rewards-module": list.type==="reward"}') + .task-column(class='{{list.type}}s') - // Todos export/graph options - span.option-box.pull-right(bo-if='main && list.type=="todo"') - a.option-action(ng-show='obj.history.todos', ng-click='toggleChart("todos")', tooltip=env.t('progress')) - i.icon-signal - //a.option-action(ng-href='/v1/users/{{user.id}}/calendar.ics?apiToken={{user.apiToken}}', tooltip='iCal') - a.option-action(ng-click='notPorted()', tooltip='iCal', ng-show='false') - i.icon-calendar - // + // Todos export/graph options + span.option-box.pull-right(bo-if='main && list.type=="todo"') + a.option-action(ng-show='obj.history.todos', ng-click='toggleChart("todos")', tooltip=env.t('progress')) + span.glyphicon.glyphicon-signal + //a.option-action(ng-href='/v1/users/{{user.id}}/calendar.ics?apiToken={{user.apiToken}}', tooltip='iCal') + a.option-action(ng-click='notPorted()', tooltip='iCal', ng-show='false') + span.glyphicon.glyphicon-calendar + // - // Gold & Gems - span.option-box.pull-right.wallet(bo-if='main && list.type=="reward"') - .money - | {{Shared.gold(user.stats.gp)}} - span.shop_gold(tooltip=env.t('gold')) - .money - | {{Shared.silver(user.stats.gp)}} - span.shop_silver(tooltip=env.t('silver')) + // Gold & Gems + span.option-box.pull-right.wallet(bo-if='main && list.type=="reward"') + .money + | {{Shared.gold(user.stats.gp)}} + span.shop_gold(tooltip=env.t('gold')) + .money + | {{Shared.silver(user.stats.gp)}} + span.shop_silver(tooltip=env.t('silver')) - // Header - h2.task-column_title {{list.header}} + // Header + h2.task-column_title {{list.header}} - // Todo Chart - .todos-chart(bo-if='list.type == "todo"', ng-show='charts.todos') + // Todo Chart + .todos-chart(bo-if='list.type == "todo"', ng-show='charts.todos') - // Add New - form.addtask-form.form-inline.new-task-form(name='new{{list.type}}form', ng-hide='obj._locked || (list.showCompleted && list.type=="todo")', ng-submit='addTask(obj[list.type+"s"],list)') - span.addtask-field - input(type='text', ng-model='list.newTask', placeholder='{{list.placeHolder}}', required) - input.addtask-btn(type='submit', value='+', ng-disabled='new{{list.type}}form.$invalid') - hr + // Add New + form.addtask-form.form-inline.new-task-form(name='new{{list.type}}form', ng-hide='obj._locked || (list.showCompleted && list.type=="todo")', ng-submit='addTask(obj[list.type+"s"],list)') + span.addtask-field + input(type='text', ng-model='list.newTask', placeholder='{{list.placeHolder}}', required) + input.addtask-btn(type='submit', value='+', ng-disabled='new{{list.type}}form.$invalid') + hr - // Actual List - ul(class='{{list.type}}s main-list', ng-show='obj[list.type + "s"].length > 0', habitrpg-sortable) - include ./task + // Actual List + ul(class='{{list.type}}s main-list', ng-show='obj[list.type + "s"].length > 0', habitrpg-sortable) + include ./task - // Static Rewards - ul.items.rewards(ng-if='main && list.type=="reward" && user.flags.itemsEnabled') - li.task.reward-item(ng-repeat='item in itemStore',popover-trigger='mouseenter', popover-placement='top', popover='{{item.notes}}') - // right-hand side control buttons - .task-meta-controls - span.task-notes - i.icon-comment - //left-hand size commands - .task-controls - a.money.btn-buy.item-btn(ng-click='buy(item)') - span.reward-cost {{item.value}} - span.shop_gold - // main content - span(bo-class='{"shop_{{item.key}} shop-sprite item-img": true}') - p.task-text {{item.text}} + // Static Rewards + ul.items.rewards(ng-if='main && list.type=="reward" && user.flags.itemsEnabled') + li.task.reward-item(ng-repeat='item in itemStore',popover-trigger='mouseenter', popover-placement='top', popover='{{item.notes}}') + // right-hand side control buttons + .task-meta-controls + span.task-notes + span.glyphicon.glyphicon-comment + //left-hand size commands + .task-controls + a.money.btn-buy.item-btn(ng-click='buy(item)') + span.reward-cost {{item.value}} + span.shop_gold + // main content + span(bo-class='{"shop_{{item.key}} shop-sprite item-img": true}') + p.task-text {{item.text}} - // Winter Event - ul.items.rewards(ng-if='main && list.type=="reward" && (user.items.special.snowball>0 || user.stats.buffs.snowball)') - li.task.reward-item(ng-if='user.items.special.snowball>0',popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.snowball.notes}}') - .task-meta-controls - span.task-notes - i.icon-comment - //left-hand size commands - .task-controls - a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.snowball)') - span.reward-cost {{user.items.special.snowball}} - span.shop_snowball(style='display:inline-block;vertical-align:top;') - // main content - p.task-text {{Content.spells.special.snowball.text}} - li.task.reward-item(ng-if='user.stats.buffs.snowball',popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.salt.notes}}') - .task-meta-controls - span.task-notes - i.icon-comment - //left-hand size commands - .task-controls - a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.salt)') - span.reward-cost {{Content.spells.special.salt.value}} - span.shop_gold - // main content - span.shop_salt.shop-sprite.item-img - p.task-text {{Content.spells.special.salt.text}} + // Events + ul.items.rewards(ng-if='main && list.type=="reward"') - // Spells - ul.items(ng-if='main && list.type=="reward" && user.stats.class && !user.preferences.disableClasses') - li.task.reward-item(ng-repeat='(k,spell) in Content.spells[user.stats.class]', ng-if='user.stats.lvl >= spell.lvl',popover-trigger='mouseenter', popover-placement='top', popover='{{spell.notes}}') - .task-meta-controls - span.task-notes - i.icon-comment - //left-hand size commands - .task-controls - a.money.btn-buy.item-btn(ng-click='castStart(spell)') - span.reward-cost - strong {{spell.mana}} - =env.t('mp') - // main content - span(ng-class='{"shop_{{spell.key}} shop-sprite item-img": true}') - p.task-text {{spell.text}} + li.task.reward-item(ng-if='user.items.special.snowball>0',popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.snowball.notes}}') + .task-meta-controls + span.task-notes + span.glyphicon.glyphicon-comment + //left-hand size commands + .task-controls + a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.snowball)') + span.reward-cost {{user.items.special.snowball}} + span.shop_snowball(style='display:inline-block;vertical-align:top;') + // main content + p.task-text {{Content.spells.special.snowball.text}} - br + li.task.reward-item(ng-if='user.stats.buffs.snowball',popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.salt.notes}}') + .task-meta-controls + span.task-notes + span.glyphicon.glyphicon-comment + //left-hand size commands + .task-controls + a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.salt)') + span.reward-cost {{Content.spells.special.salt.value}} + span.shop_gold + // main content + span.shop_salt.shop-sprite.item-img + p.task-text {{Content.spells.special.salt.text}} - // Ads - div(bo-if='main && !user.purchased.ads && !user.purchased.plan.customerId && list.type!="reward"') - span.pull-right - a(ui-sref='options.settings.subscription', popover=env.t('removeAds'), popover-trigger='mouseenter') - i.icon-remove - // Habit3 - ins.adsbygoogle(ng-init='initAds()', style='display: inline-block; width: 234px; height: 60px;', data-ad-client='ca-pub-3242350243827794', data-ad-slot='9529624576') + li.task.reward-item(popover-trigger='mouseenter', popover-placement='top', popover='{{Content.spells.special.valentine.notes}}') + .task-meta-controls + span.task-notes + span.glyphicon.glyphicon-comment + //left-hand size commands + .task-controls + a.money.btn-buy.item-btn(ng-click='castStart(Content.spells.special.valentine)') + span.reward-cost {{Content.spells.special.valentine.value}} + span.shop_gold + span.inventory_special_valentine.shop-sprite.item-img + p.task-text {{Content.spells.special.valentine.text}} - // Todo Tabs - div(bo-if='main && list.type=="todo"', bo-class='{"tabbable tabs-below": list.type=="todo"}') - div(ng-show='list.showCompleted') - .alert - =env.t('lotOfToDos') - button.task-action-btn.tile.spacious.bright(ng-click='user.ops.clearCompleted({})')=env.t('clearCompleted') - // remaining/completed tabs - ul.nav.nav-tabs - li(ng-class='{active: !list.showCompleted}') - a(ng-click='list.showCompleted = false')=env.t('remaining') - li(ng-class='{active: list.showCompleted}') - a(ng-click='list.showCompleted= true')=env.t('complete') \ No newline at end of file + // Spells + ul.items(ng-if='main && list.type=="reward" && user.stats.class && !user.preferences.disableClasses') + li.task.reward-item(ng-repeat='(k,spell) in Content.spells[user.stats.class]', ng-if='user.stats.lvl >= spell.lvl',popover-trigger='mouseenter', popover-placement='top', popover='{{spell.notes}}') + .task-meta-controls + span.task-notes + span.glyphicon.glyphicon-comment + //left-hand size commands + .task-controls + a.money.btn-buy.item-btn(ng-click='castStart(spell)') + span.reward-cost + strong {{spell.mana}} + =env.t('mp') + // main content + span(ng-class='{"shop_{{spell.key}} shop-sprite item-img": true}') + p.task-text {{spell.text}} + + br + + // Ads + div(bo-if='main && !user.purchased.ads && !user.purchased.plan.customerId && list.type!="reward"') + span.pull-right + a(ui-sref='options.settings.subscription', popover=env.t('removeAds'), popover-trigger='mouseenter') + span.glyphicon.glyphicon-remove + // Habit3 + ins.adsbygoogle(ng-init='initAds()', style='display: inline-block; width: 234px; height: 60px;', data-ad-client='ca-pub-3242350243827794', data-ad-slot='9529624576') + + // Todo Tabs + div(bo-if='main && list.type=="todo"', bo-class='{"tabbable tabs-below": list.type=="todo"}') + div(ng-show='list.showCompleted') + .alert + =env.t('lotOfToDos') + button.task-action-btn.tile.spacious.bright(ng-click='user.ops.clearCompleted({})')=env.t('clearCompleted') + // remaining/completed tabs + ul.nav.nav-tabs + li(ng-class='{active: !list.showCompleted}') + a(ng-click='list.showCompleted = false')=env.t('remaining') + li(ng-class='{active: list.showCompleted}') + a(ng-click='list.showCompleted= true')=env.t('complete') \ No newline at end of file diff --git a/views/shared/tasks/task.jade b/views/shared/tasks/task.jade index 5fd8d64baa..6d0e515648 100644 --- a/views/shared/tasks/task.jade +++ b/views/shared/tasks/task.jade @@ -3,44 +3,53 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s .task-meta-controls // Due Date - span.task-date(ng-if='task.type=="todo" && task.date', style='margin:1px') - span(ng-class='{"label label-important":moment(task.date).isBefore(_today, "days")}') {{moment(task.date).format('MM/DD')}} + span(ng-if='task.type=="todo" && task.date') + span(ng-class='{"label label-danger":moment(task.date).isBefore(_today, "days")}') {{moment(task.date).format('MM/DD')}} // Streak - span(ng-show='task.streak') {{task.streak}} + |   + span(ng-show='task.streak') {{task.streak}}  span(tooltip=env.t('streakCounter')) - i.icon-forward + span.glyphicon.glyphicon-forward + |   // Icons only available if you own the tasks (aka, hidden from challenge stats) span(ng-if='!obj._locked') - a.badge(ng-if='task.checklist[0]', ng-class='{"badge-success":checklistCompletion(task.checklist) == task.checklist.length}', ng-click='collapseChecklist(task)', style='margin:1px') + a.badge(ng-if='task.checklist[0]', ng-class='{"badge-success":checklistCompletion(task.checklist) == task.checklist.length}', ng-click='collapseChecklist(task)') {{checklistCompletion(task.checklist)}}/{{task.checklist.length}} - - i.icon-tags(tooltip='{{appliedTags(user.tags, task.tags)}}', ng-hide='Shared.noTags(task.tags)') + span.glyphicon.glyphicon-tags(tooltip='{{Shared.appliedTags(user.tags, task.tags)}}', ng-hide='Shared.noTags(task.tags)') // edit a(ng-hide='task._editing', ng-click='editTask(task)', tooltip=env.t('edit')) - i.icon-pencil(ng-hide='task._editing') - // cancel + |   + span.glyphicon.glyphicon-pencil(ng-hide='task._editing') + |   a(ng-hide='!task._editing', ng-click='editTask(task)', tooltip=env.t('cancel')) - i.icon-remove(ng-hide='!task._editing') + span.glyphicon.glyphicon-remove(ng-hide='!task._editing') + |   // save a(ng-hide='!task._editing', ng-click='editTask(task);saveTask(task)', tooltip=env.t('save')) - i.icon-ok(ng-hide='!task._editing') + span.glyphicon.glyphicon-ok(ng-hide='!task._editing') + |   //challenges span(ng-if='task.challenge.id') span(ng-if='task.challenge.broken') - i.icon-bullhorn(style='background-color:red;', ng-click='task._editing = true', tooltip=env.t('brokenChaLink') tooltip-placement='right') + span.glyphicon.glyphicon-bullhorn(style='background-color:red;', ng-click='task._editing = true', tooltip=env.t('brokenChaLink') tooltip-placement='right') + |   span(ng-if='!task.challenge.broken') - i.icon-bullhorn(tooltip=env.t('challenge')) + span.glyphicon.glyphicon-bullhorn(tooltip=env.t('challenge')) + |   // delete a(ng-if='!task.challenge.id', ng-click='removeTask(obj[list.type+"s"], $index)', tooltip=env.t('delete')) - i.icon-trash + span.glyphicon.glyphicon-trash + |   // chart a(ng-show='task.history', ng-click='toggleChart(obj._id+task.id, task)', tooltip=env.t('progress')) - i.icon-signal + span.glyphicon.glyphicon-signal + |   // notes span.task-notes(ng-show='task.notes && !task._editing') - i.icon-comment + span.glyphicon.glyphicon-comment + |   // left-hand side checkbox .task-controls.task-primary @@ -83,10 +92,12 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s p=env.t('brokenTask') p a(ng-click='unlink(task, "keep")')=env.t('keepIt') - |  |  + |    a(ng-click="removeTask(obj[list.type+'s'], $index)")=env.t('removeIt') div(ng-if='task.challenge.broken=="CHALLENGE_DELETED"') - p=env.t('brokenChallenge') + p + |  + =env.t('brokenChallenge') p a(ng-click='unlink(task, "keep-all")')=env.t('keepThem') |  |  @@ -107,19 +118,20 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s // Checklists div(ng-if='!$state.includes("options.social.challenges")') - a.btn.btn-small(ng-if='!task.checklist[0] && (task.type=="daily" || task.type=="todo")',ng-click='addChecklist(task)') - i.icon-tasks + button.btn.btn-default.btn-sm(ng-if='!task.checklist[0] && (task.type=="daily" || task.type=="todo")',ng-click='addChecklist(task)') + span.glyphicon.glyphicon-tasks + |     =env.t('addChecklist') form.checklist-form(ng-if='task.checklist') fieldset.option-group.task-checklist(ng-if='!$state.includes("options.social.challenges")') legend.option-title =env.t('checklist') - |   - i.icon.icon-question-sign(popover=env.t('checklistText'),popover-trigger='mouseenter',popover-placement='bottom') - ul.unstyled + |     + span.glyphicon.glyphicon-question-sign(popover=env.t('checklistText'),popover-trigger='mouseenter',popover-placement='bottom') + ul.list-unstyled li(ng-repeat='item in task.checklist') a.pull-right.checklist-icons(ng-click='removeChecklistItem(task,$event,$index,true)') - i.icon-trash(tooltip=env.t('delete')) + span.glyphicon.glyphicon-trash(tooltip=env.t('delete')) input(type='checkbox',ng-model='item.completed',ng-change='saveTask(task,true)') input.inline-edit(type='text',ng-model='item.text',ui-keydown="{'8 46':'removeChecklistItem(task,$event,$index)'}",ui-keyup="{'13':'addChecklistItem(task,$event,$index)','38 40':'navigateChecklist(task,$index,$event)'}")//-,ng-blur='saveTask(task,true)') @@ -181,7 +193,8 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s fieldset.option-group.advanced-option(ng-class="{visuallyhidden: task._advanced}") legend.option-title a.priority-multiplier-help(href='https://trello.com/card/priority-multiplier/50e5d3684fe3a7266b0036d6/17', target='_blank', popover-title=env.t('difficultyHelpTitle'), popover-trigger='mouseenter', popover=env.t('difficultyHelpContent')) - i.icon-question-sign + span.glyphicon.glyphicon-question-sign + |   =env.t('difficulty') .task-controls.tile-group.priority-multiplier button.task-action-btn.tile(type='button', ng-class='{active: task.priority==1 || !task.priority}', ng-click='task.challenge.id || (task.priority=1)')=env.t('easy') @@ -194,13 +207,13 @@ li(bindonce='list', bo-id='"task-"+task.id', ng-repeat='task in obj[list.type+"s div(ng-if='(user.preferences.allocationMode == "taskbased" && user.preferences.automaticAllocation) || $state.is("options.social.challenges")') legend.option-title.pull-left=env.t('attributes') - .task-controls.tile-group(style='text-align:left') + .task-controls.tile-group.task-attributes button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="str"}', ng-click='task.attribute="str"')=env.t('physical') button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="int"}', ng-click='task.attribute="int"')=env.t('mental') button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="con"}', ng-click='task.attribute="con"')=env.t('social') button.task-action-btn.tile(type='button', ng-class='{active: task.attribute=="per"}', ng-click='task.attribute="per"', popover=env.t('otherExamples'), popover-trigger='mouseenter', popover-placement='top')=env.t('other') |  - i.icon-question-sign + span.glyphicon.glyphicon-question-sign button.task-action-btn.tile.spacious(type='submit')=env.t('saveAndClose') diff --git a/views/static/about.jade b/views/static/about.jade index dfe6a20451..b86dd503ef 100644 --- a/views/static/about.jade +++ b/views/static/about.jade @@ -8,15 +8,79 @@ block title title=env.t('companyAbout') block content - .container - p(style='height:600;') - iframe(src='//player.vimeo.com/video/76557040', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') - p(style='height:600;') - iframe(src='//player.vimeo.com/video/57654086', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') - p(style='height:600;') - iframe(src='//player.vimeo.com/video/79172253', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') - p(style='height:600;') - iframe(src='//player.vimeo.com/video/79172327', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') - p(style='height:600;') - iframe(src='//player.vimeo.com/video/79172363', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') + .row#about-page(ng-controller='AboutCtrl') + .col-md-12#aboutPage + .marketing + h1 Improve Your Habits By Playing A Game + .row + .col-md-6 + a.gallery(href='/marketing/screenshot.png', title='Improve Your Habits By Playing A Game') + img(src='/marketing/screenshot.png') + p.lead HabitRPG is a video game to help you improve real life habits. It "gamifies" your life by turning all your tasks (habits, dailies, and to-dos) into little monsters you have to conquor. The better you are at this, the more you progress in the game. If you slip up in life, your character starts backsliding in the game. + .col-md-6 + + a.gallery(href='/marketing/gear.png', title='Get Sweet Gear') + img(src='/marketing/gear.png') + p.lead Get Sweet Gear. Improve your habits to build up your avatar. Show off the sweet gear you've earned + + a.gallery(href='/marketing/drops.png', title='Find Random Prizes') + img(src='/marketing/drops.png',style='max-height:200px') + p.lead Find Random Prizes. For some, it's the gamble which motivates them, a system called "stochastic rewarding". HabitRPG accomodates all reinforcement styles: positive, negative, predictable, and random. + + // TODO achievements + + hr.clearfix + + h1 Compete With Friends, Join Interest Groups + .row + .col-md-6 + a.gallery(href='/marketing/guild.png', title='Compete With Friends, Join Interest Groups') + img(src='/marketing/guild.png') + p.lead While you can solo-play HabitRPG, the lights really turn on when you start collaborating, competing, and holding eachother accountable. The most effective part of any self-improvement program is social accountability, and what better an environment for accountability and competition than a video game? + + a.gallery(href='/bower_components/habitrpg-shared/img/sprites/spritesmith/quests/quest_vice3.png', title='Bosses') + img(src='/bower_components/habitrpg-shared/img/sprites/spritesmith/quests/quest_vice3.png') + p.lead Fight Bosses. What's a Role Playing Game without battles? Fight bosses with your party. Bosses are "super accountability mode" - a day you miss the gym is a day the boss hurts everyone. + .col-md-6 + a.gallery(href='/marketing/challenge.png', title='Challenges') + img(src='/marketing/challenge.png') + p.lead Challenges let you compete with friends and strangers. Whoever does the best at the end of a challenge wins special prizes. + + hr.clearfix + + h1 Apps + .row + .col-md-6 + a.gallery(href='/marketing/android_iphone.png', title='iPhone & Android') + img(src='/marketing/android_iphone.png',style='box-shadow:none;') + p.lead The iPhone & Android apps let you take care of business on the go. We realize that logging into the website to click buttons can be a drag. + .col-md-6 + a.gallery(href='/marketing/integration.png', title='iPhone & Android') + img(src='/marketing/integration.png') + p.lead Other 3rd Party Tools tie HabitRPG into various aspects of your life. Our API provides easy integration for things like the Chrome Extension, for which you lose points when browsing unproductive websites, and gain points when on productive ones. See more here + + hr.clearfix + + h1 Organizational Use + .row + .col-md-6 + h3 Gamification In Education + img.pull-left(src='/marketing/education.png') + p.lead Education is one of the best sectors for gamification. We all know how glued to phones and games students are these days, harness that power! Pit your students against eachother in friendly competition. Reward good behavior with rare prizes. Watch their grades and behavior soar. + .col-md-6 + h3 Gamification In Health and Wellness + img.pull-left(src='/marketing/wellness.png') + p.lead Health care costs are on the rise, and something's gotta give. Hundreds of programs are built to reduce costs and improve wellness. We believe HabitRPG can pave a substantial path towards healthy lifestyles. + .row + .col-md-6.col-md-offset-3 + h3 Gamify Everything + img(src='/marketing/lefnire.png') + p.lead + | Want to gamify your life?  + a.btn.btn-primary(ng-click='playButtonClick()') Play + | Interested in running a group in education, wellness, and more?  + a.btn.btn-primary(href='/static/plans',target='_blank') Contact Us + | Want to learn more?  + a.btn.btn-primary(href='/static/videos') Watch Videos + diff --git a/views/static/api.jade b/views/static/api.jade index 16b55d8277..276f3bee20 100644 --- a/views/static/api.jade +++ b/views/static/api.jade @@ -16,7 +16,7 @@ html script(src='/bower_components/swagger-ui/dist/lib/swagger.js', type='text/javascript') script(src='/bower_components/swagger-ui/dist/swagger-ui.js', type='text/javascript') script(src='/bower_components/swagger-ui/dist/lib/highlight.7.3.pack.js', type='text/javascript') - script(type='text/javascript') + script(type='text/javascript'). $(function () { window.swaggerUi = new SwaggerUi({ url: "/api/v2/api-docs", diff --git a/views/static/front.jade b/views/static/front.jade index f669785860..9674899373 100644 --- a/views/static/front.jade +++ b/views/static/front.jade @@ -8,38 +8,41 @@ block title title=env.t('titleFront') block content - #wrap - //-include ./header - .marketing - //we need to use something that's not jumbotron for this, but still keep it centered - //could someone write something else to make it pretty? - img(src='/bower_components/habitrpg-shared/img/logo/habitrpg_pixel.png', alt='HabitRPG logo') - //this image needs to be replaced by something more enticing, that shows off the features of hRPG - //while acting similarly to a logo - h1(style='margin:10px 0 20px 0;font-size:40px')=env.t('tagline') - p - button.btn.btn-primary.btn-large.frontpage-play-button(ng-click='playButtonClick()')=env.t('playButton') - hr - img(src='/marketing/devices.png') - //we'd want the tagline centered, for sure, and a bit more pop, but without using jumbotron - //it could also be part of the image, as long as the alt text included it - //in fact, I think I really want it on the image, rather than as text, but language issues - br - p=env.t('landingp1') - h2=env.t('landingp2header') - //images in these parts could be useful, too - //if there's a language workaround, image headers? people like pictures! - p=env.t('landingp2') - h2=env.t('landingp3header') - //I'm not sold on "Consquences as the title here. Anyone got a better idea? - p=env.t('landingp3') - h2=env.t('landingp4header') - p=env.t('landingp4') - //- TODO - h2=env.t('landingend') - p - =env.t('landingend2') - a(href="FEATURESPAGEHERE")=env.t('landingfeatureslink') - =env.t('landingend3') - a(href="ENTERPRISEPAGEHERE")=env.t('landingadminlink') - =env.t('landingend4') + .marketing + //we need to use something that's not jumbotron for this, but still keep it centered + //could someone write something else to make it pretty? + img(src='/bower_components/habitrpg-shared/img/logo/habitrpg_pixel.png', alt='HabitRPG logo') + //this image needs to be replaced by something more enticing, that shows off the features of hRPG + //while acting similarly to a logo + h1#tagline=env.t('tagline') + p.lead + button.btn.btn-primary.btn-lg#frontpage-play-button(ng-click='playButtonClick()')=env.t('playButton') + hr + img(src='/marketing/devices.png') + //we'd want the tagline centered, for sure, and a bit more pop, but without using jumbotron + //it could also be part of the image, as long as the alt text included it + //in fact, I think I really want it on the image, rather than as text, but language issues + br + p.lead=env.t('landingp1') + h2=env.t('landingp2header') + //images in these parts could be useful, too + //if there's a language workaround, image headers? people like pictures! + p.lead + =env.t('landingp2') + |  + h2=env.t('landingp3header') + //I'm not sold on "Consquences as the title here. Anyone got a better idea? + p.lead + =env.t('landingp3') + |  + h2=env.t('landingp4header') + p.lead=env.t('landingp4') + //- TODO + h2=env.t('landingend') + p.lead + =env.t('landingend2') + a(href="FEATURESPAGEHERE")=env.t('landingfeatureslink') + =env.t('landingend3') + a(href="ENTERPRISEPAGEHERE")=env.t('landingadminlink') + |  + =env.t('landingend4') diff --git a/views/static/header.jade b/views/static/header.jade deleted file mode 100644 index 6a9223a5e0..0000000000 --- a/views/static/header.jade +++ /dev/null @@ -1,30 +0,0 @@ -// - diff --git a/views/static/layout.jade b/views/static/layout.jade index 5492c2365f..1d4593760f 100644 --- a/views/static/layout.jade +++ b/views/static/layout.jade @@ -1,41 +1,48 @@ //Trick needed to pass 'env' to ./layout block vars doctype 5 -html +html(ng-app='habitrpgStatic') head block title title=env.t('titleIndex') - link(rel='shortcut icon', href='#{env.getBuildUrl("favicon.ico")}?v=2') + script(type='text/javascript'). + window.env = !{JSON.stringify(env)}; + + link(rel='shortcut icon', href='#{env.getBuildUrl("favicon.ico")}?v=3') meta(charset='utf-8') meta(name='viewport', content='width=device-width, initial-scale=1.0') != env.getManifestFiles("static") - script(type='text/javascript'). - $.getScript("//s7.addthis.com/js/250/addthis_widget.js#pubid=lefnire"); - body - div(ng-app='habitrpgStatic',ng-controller='AuthCtrl',style='margin-top:60px;') + div(ng-controller='AuthCtrl') include ./login-modal - .navbar.navbar-inverse.navbar.navbar-inverse.navbar-fixed-top - .navbar-inner - .container - a.pull-right.btn.btn-primary(ng-click='playButtonClick()')=env.t('playButton') + nav.navbar.navbar-inverse.navbar-fixed-top + .navbar-header + button.navbar-toggle(type='button', ng-click='isNavbarCollapsed = !isNavbarCollapsed', ng-init='isNavbarCollapsed = true') + span.sr-only Toggle navigation + span.icon-bar + span.icon-bar + span.icon-bar + a.navbar-brand(href='/static/front') HabitRPG + .collapse.navbar-collapse(collapse="isNavbarCollapsed") + ul.nav.navbar-nav + li(class='#{menuItem=="about" ? "active" : ""}') + //a(href='/static/about')=env.t('learnMore') + a(href='/static/about') About + li(class='#{menuItem=="videos" ? "active" : ""}') + a(href='/static/videos') Videos + li + a(href='http://blog.habitrpg.com/')=env.t('companyBlog') + li(class='#{menuItem=="plans" ? "active" : ""}') + a(href='/static/plans')=env.t('groupPlans') + //li(class='#{menuItem=="contact" ? "active" : ""}') + a(href='/static/contact')=env.t('contact') - ul.nav - li(class='#{menuItem=="home" ? "active" : ""}') - a(href='/static/front')=env.t('home') - li(class='#{menuItem=="about" ? "active" : ""}') - a(href='/static/about')=env.t('learnMore') - li - a(href='http://blog.habitrpg.com/')=env.t('companyBlog') - li(class='#{menuItem=="plans" ? "active" : ""}') - a(href='/static/plans')=env.t('groupPlans') - //li(class='#{menuItem=="contact" ? "active" : ""}') - a(href='/static/contact')=env.t('contact') + button#header-play-button.btn.btn-primary.navbar-btn.navbar-right(ng-click='playButtonClick()')=env.t('playButton') .container block content diff --git a/views/static/login-modal.jade b/views/static/login-modal.jade index c9710af000..0201b35504 100644 --- a/views/static/login-modal.jade +++ b/views/static/login-modal.jade @@ -1,69 +1,73 @@ -.modal.fade#login-modal(style='display:none') - .modal-dialog - .modal-content - .modal-header - button.close(type='button', data-dismiss='modal', aria-hidden='true') × - h4.modal-title=env.t('loginAndReg') - .modal-body - a(href='/auth/facebook') - img(src='/bower_components/habitrpg-shared/img/facebook-login-register.jpeg', alt=env.t('loginFacebookAlt')) - //can we add in google auth? I like google auth - h3 Or - ul.nav.nav-tabs - li.active - a(data-toggle='tab',data-target='#login-tab')=env.t('login') - li - a(data-toggle='tab',data-target='#register-tab')=env.t('register') +script(id='modals/login.html', type='text/ng-template') + .modal-header + button.close(type='button', ng-click='$close()') × + h4.modal-title=env.t('loginAndReg') + .modal-body(ng-controller='AuthCtrl') + a(href='/auth/facebook') + img(src='/bower_components/habitrpg-shared/img/facebook-login-register.jpeg', alt=env.t('loginFacebookAlt')) + //can we add in google auth? I like google auth + h3 Or + ul.nav.nav-tabs + li.active + a(data-toggle='tab',data-target='#login-tab')=env.t('login') + li + a(data-toggle='tab',data-target='#register-tab')=env.t('register') - .tab-content - .tab-pane.active#login-tab - form(ng-submit='auth()', method='POST') - .control-group - input(type='text', ng-model='loginUsername', placeholder=env.t('username'), name='username') - .control-group - input(type='password', ng-model='loginPassword', placeholder=env.t('password'), name='password') - //-.control-group - label.checkbox - input(type='checkbox', ng-click='useUUID = !useUUID') - =env.t('useUUID') - .control-group - input.btn.btn-primary(type='submit', value=env.t('login')) + .tab-content + .tab-pane.active#login-tab + form(ng-submit='auth()', method='POST') + .form-group + input.form-control(type='text', ng-model='loginUsername', placeholder=env.t('username'), name='username') + .form-group + input.form-control(type='password', ng-model='loginPassword', placeholder=env.t('password'), name='password') + //-.control-group + label.checkbox + input(type='checkbox', ng-click='useUUID = !useUUID') + =env.t('useUUID') + .form-group + input.btn.btn-primary(type='submit', value=env.t('login')) - small=env.t('passMan') + small=env.t('passMan') + br + br - // good god accordions have html ceremony - #forgot-password-accordion.accordion - .accordion-group - .accordion-heading - a.accordion-toggle(data-toggle='collapse', data-parent='#forgot-password-accordion', href='#forgot-password-group')=env.t('forgotPass') - #forgot-password-group.accordion-body.collapse - .accordion-inner - form#derby-auth-password-reset(ng-submit='passwordReset(passwordResetEmail)') - h3=env.t('emailNewPass') - //.alert.alert-success {.success.passwordReset} - //.control-group.{#if..errors.passwordReset}error{/} - .control-group - input(type='text', name='email', placeholder=env.t('email') , ng-model='passwordResetEmail') - //span.help-inline {.errors.passwordReset} - input.btn(type='submit', value=env.t('submit')) + // good god accordions have html ceremony + .panel-group#forgot-password + .panel.panel-default + .panel-heading + h3.panel-title + a(data-toggle="collapse" data-parent="#forgot-password" href="#forgot-password-panel")=env.t('forgotPass') + + .panel-body.collapse#forgot-password-panel + form(ng-submit='passwordReset(passwordResetEmail)') + //.alert.alert-success {.success.passwordReset} + //.control-group.{#if..errors.passwordReset}error{/} + h3=env.t('emailNewPass') + .form-group + input.form-control(type='text', name='email', placeholder=env.t('email') , ng-model='passwordResetEmail') + //span.help-inline {.errors.passwordReset} + .form-group + input.btn.btn-default(type='submit', value=env.t('submit')) - - - - .tab-pane#register-tab - form(ng-submit='register()', name='registrationForm') - .control-group - input(type='text', ng-model='registerVals.username', placeholder=env.t('username'), required) - .control-group - input(type='email', ng-model='registerVals.email', placeholder=env.t('email'), required) - .control-group - input(type='password', ng-model='registerVals.password', placeholder=env.t('password'), required) - .control-group - input(type='password', ng-model='registerVals.confirmPassword', placeholder=env.t('passConfirm'), required) - .control-group - small=env.t('accept1Terms') - a(href='/static/terms' target='_blank')=env.t('terms') - =env.t('accept2Terms') - a(href='/static/privacy' target='_blank')=env.t('privacy') - | . - input.btn.btn-primary(type='submit', value=env.t('register')) + .tab-pane#register-tab + form(ng-submit='register()', name='registrationForm') + .form-group + input.form-control(type='text', ng-model='registerVals.username', placeholder=env.t('username'), required) + .form-group + input.form-control(type='email', ng-model='registerVals.email', placeholder=env.t('email'), required) + .form-group + input.form-control(type='password', ng-model='registerVals.password', placeholder=env.t('password'), required) + .form-group + input.form-control(type='password', ng-model='registerVals.confirmPassword', placeholder=env.t('passConfirm'), required) + .form-group + small + =env.t('accept1Terms') + |  + a(href='/static/terms' target='_blank')=env.t('terms') + |  + =env.t('accept2Terms') + |  + a(href='/static/privacy' target='_blank')=env.t('privacy') + | . + .form-group + input.btn.btn-primary(type='submit', value=env.t('register')) diff --git a/views/static/plans.jade b/views/static/plans.jade index 5f88d21333..a7971fd3ab 100644 --- a/views/static/plans.jade +++ b/views/static/plans.jade @@ -8,90 +8,97 @@ block title title=env.t('groupPlans') block content - h2=env.t('groupPlans') + .row(ng-controller='PlansCtrl') + .col-md-12 + h2=env.t('groupPlans') - p - =env.t('indivPlan1') - a(href='http://habitrpg.wikia.com/wiki/Guilds' target='_blank')=env.t('guilds') - = ' ' + env.t('and') + ' ' - a(href='http://habitrpg.wikia.com/wiki/Challenges' target='_blank')=env.t('challenges') - =env.t('indivPlan2') - p - =env.t('groupText1') - a(href='http://habitrpg.wikia.com/wiki/Habitica' target='_blank')=env.t('habitica') - |.  - =env.t('groupText2') + p + =env.t('indivPlan1') + |  + a(href='http://habitrpg.wikia.com/wiki/Guilds' target='_blank')=env.t('guilds') + = ' ' + env.t('and') + ' ' + a(href='http://habitrpg.wikia.com/wiki/Challenges' target='_blank')=env.t('challenges') + |  + =env.t('indivPlan2') + p + =env.t('groupText1') + |  + a(href='http://habitrpg.wikia.com/wiki/Habitica' target='_blank')=env.t('habitica') + |.  + =env.t('groupText2') - .subscription-features - table.table.table-striped - tr - th.feature-name - th.feature-name.muted=env.t('planFamily') - th.feature-name.muted=env.t('planGroup') - th.feature-name=env.t('organization') - tr - th - span.dashed-underline(popover=env.t('organizationSubText'),popover-trigger='mouseenter',popover-placement='right')=env.t('organizationSub') - td.muted - i.icon-ok - td.muted - i.icon-ok - td - i.icon-ok - tr - th - span.dashed-underline(popover=env.t('dedicatedHostText'),popover-trigger='mouseenter',popover-placement='right')=env.t('dedicatedHost') - td - td - td - i.icon-ok - tr - th - span.dashed-underline(popover=env.t('customDomainText'),popover-trigger='mouseenter',popover-placement='right')=env.t('customDomain') - td - td - td - i.icon-ok - tr - th - span.dashed-underline(popover=env.t('maxPlayersText'),popover-trigger='mouseenter',popover-placement='right')=env.t('maxPlayers') - td.muted 10 - td.muted 75 - td=env.t('unlimited') - tr - th - span.dashed-underline(popover=env.t('priSupportText'),popover-trigger='mouseenter',popover-placement='right')=env.t('priSupport') - td - td - td - i.icon-ok - tr - th - span.dashed-underline(popover=env.t('timeSupportText'),popover-trigger='mouseenter',popover-placement='right')=env.t('timeSupport') - td.muted - - td.muted 5 - td 10 - tr - th - h5=env.t('gameFeatures') + ':' - ul - li=env.t('gameNoAds') - li - span.dashed-underline(popover=env.t('gold2GemText'),popover-trigger='mouseenter',popover-placement='right')=env.t('gold2Gem') - li - span.dashed-underline(popover=env.t('infiniteGemText'),popover-trigger='mouseenter',popover-placement='right')=env.t('infiniteGem') - td.muted - i.icon-ok - td.muted - i.icon-ok - td - i.icon-ok - tr - th - //| Price - td - a.btn.muted(href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover=env.t('notYetPlan'),popover-placement='right',popover-trigger='mouseenter')=env.t('contactUs') - td - a.btn.muted(href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover=env.t('notYetPlan'),popover-placement='right',popover-trigger='mouseenter')=env.t('contactUs') - td - a.btn.btn-primary(href='https://docs.google.com/forms/d/12Jqj_8f3oQS0B3ZUHewHbK61uLjBdzBeB0zyEqB9lxM/viewform')=env.t('contactUs') \ No newline at end of file + .subscription-features + table.table.table-striped + thead + tr + th.feature-name + th.feature-name.muted=env.t('planFamily') + th.feature-name.muted=env.t('planGroup') + th.feature-name=env.t('organization') + tbody + tr + th + span.dashed-underline(popover=env.t('organizationSubText'),popover-trigger='mouseenter',popover-placement='right')=env.t('organizationSub') + td.muted + span.glyphicon.glyphicon-ok + td.muted + span.glyphicon.glyphicon-ok + td + span.glyphicon.glyphicon-ok + tr + th + span.dashed-underline(popover=env.t('dedicatedHostText'),popover-trigger='mouseenter',popover-placement='right')=env.t('dedicatedHost') + td + td + td + span.glyphicon.glyphicon-ok + tr + th + span.dashed-underline(popover=env.t('customDomainText'),popover-trigger='mouseenter',popover-placement='right')=env.t('customDomain') + td + td + td + span.glyphicon.glyphicon-ok + tr + th + span.dashed-underline(popover=env.t('maxPlayersText'),popover-trigger='mouseenter',popover-placement='right')=env.t('maxPlayers') + td.muted 10 + td.muted 75 + td=env.t('unlimited') + tr + th + span.dashed-underline(popover=env.t('priSupportText'),popover-trigger='mouseenter',popover-placement='right')=env.t('priSupport') + td + td + td + span.glyphicon.glyphicon-ok + tr + th + span.dashed-underline(popover=env.t('timeSupportText'),popover-trigger='mouseenter',popover-placement='right')=env.t('timeSupport') + td.muted - + td.muted 5 + td 10 + tr + th + h5=env.t('gameFeatures') + ':' + ul + li=env.t('gameNoAds') + li + span.dashed-underline(popover=env.t('gold2GemText'),popover-trigger='mouseenter',popover-placement='right')=env.t('gold2Gem') + li + span.dashed-underline(popover=env.t('infiniteGemText'),popover-trigger='mouseenter',popover-placement='right')=env.t('infiniteGem') + td.muted + span.glyphicon.glyphicon-ok + td.muted + span.glyphicon.glyphicon-ok + td + span.glyphicon.glyphicon-ok + tr + th + //| Price + td + a.btn.btn-default.muted(ng-click='clickContact()', href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover=env.t('notYetPlan'),popover-placement='right',popover-trigger='mouseenter')=env.t('contactUs') + td + a.btn.btn-default.muted(ng-click='clickContact()', href='https://docs.google.com/forms/d/17torT7OlxgtbHAPdBDRQNG2lTdIQyrGXk4O2YXvUMig/viewform',popover=env.t('notYetPlan'),popover-placement='right',popover-trigger='mouseenter')=env.t('contactUs') + td + a.btn.btn-primary(ng-click='clickContact()', href='https://docs.google.com/forms/d/12Jqj_8f3oQS0B3ZUHewHbK61uLjBdzBeB0zyEqB9lxM/viewform')=env.t('contactUs') diff --git a/views/static/privacy.jade b/views/static/privacy.jade index f51601be32..5a6519dec9 100644 --- a/views/static/privacy.jade +++ b/views/static/privacy.jade @@ -7,9 +7,8 @@ block title title HabitRPG | Privacy block content - #wrap - //app:header:header - .container + .row + .col-md-12 .page-header h1 Privacy Policy p.pagemeta @@ -306,5 +305,4 @@ block content br | Telephone: 1-805-975-5236 p - #home_static_footer | © 2012 OCDevel \ No newline at end of file diff --git a/views/static/terms.jade b/views/static/terms.jade index 527b2589d5..4e3b0cb5e1 100644 --- a/views/static/terms.jade +++ b/views/static/terms.jade @@ -7,9 +7,8 @@ block title title HabitRPG | Terms block content - #wrap - //app:header:header - .container + .row + .col-md-12 .page-header h1 Terms of Use p diff --git a/views/static/videos.jade b/views/static/videos.jade new file mode 100644 index 0000000000..6b4d161f09 --- /dev/null +++ b/views/static/videos.jade @@ -0,0 +1,18 @@ +extends ./layout + +block vars + - var layoutEnv = env + - var menuItem = 'about' + +block title + title=env.t('companyAbout') + +block content + .row + .col-md-12#aboutPage + iframe(src='//player.vimeo.com/video/76557040', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') + iframe(src='//player.vimeo.com/video/57654086', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') + iframe(src='//player.vimeo.com/video/79172253', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') + iframe(src='//player.vimeo.com/video/79172327', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') + iframe(src='//player.vimeo.com/video/79172363', width='100%', height='539', frameborder='0', webkitallowfullscreen='', mozallowfullscreen='', allowfullscreen='') +