mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-04-14 11:36:45 +00:00
starts cleaning up the repo
This commit is contained in:
parent
e8fa22bf93
commit
e145439b0b
19 changed files with 1 additions and 1174 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -16,7 +16,7 @@ newrelic_agent.log
|
|||
.bower-cache
|
||||
.vagrant
|
||||
Vagrantfile
|
||||
|
||||
TODO
|
||||
*.log
|
||||
src/*/*.map
|
||||
src/*/*/*.map
|
||||
|
|
|
|||
1
.nvmrc
1
.nvmrc
|
|
@ -1 +0,0 @@
|
|||
0.10.40
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<a name="">HabitRPG</a>
|
||||
# (2014-01-28)
|
||||
|
||||
|
||||
## Documentation
|
||||
|
||||
- **rebirth:** Bullet point about repurchase of limited ed gear after Rebirth
|
||||
([d3f4a561](https://github.com/habitrpg/habitrpg/commits/d3f4a561fdf137e5d8f406bae03be4fef1caff22))
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **#2003:** healer gear not showing
|
||||
([949cd97b](https://github.com/habitrpg/habitrpg/commits/949cd97b91b42e9450eba559bbfea17e239ab100))
|
||||
- **#2375:** merge in @SabreCat's stats.jade changes "More elegant show/hide setup for attribute bonuses"
|
||||
([518f200a](https://github.com/habitrpg/habitrpg/commits/518f200a8fc7373b44ed7d7b5f016d921b0746bd))
|
||||
- **beastmaster:** fixes #2557, adds opacity to previously-owned pets after they're mounted. You can earn them back again
|
||||
([5caaff1c](https://github.com/habitrpg/habitrpg/commits/5caaff1cea1a68fe572e7ddf4aac50248b13df5d))
|
||||
- **bosses:** don't reset progress.up when starting a new quest. We want to be able to carry over damage from the same day a boss battle begins, even if the dailies were completed before battle-start. Fixes #2168
|
||||
([4efd0f5e](https://github.com/habitrpg/habitrpg/commits/4efd0f5ed8708f2491dd483f93e3d7a268a6337d))
|
||||
- **classes:**
|
||||
- misc fixes
|
||||
([d2121a85](https://github.com/habitrpg/habitrpg/commits/d2121a858716cb5a532a53ee9c5a1adaa74a7f69))
|
||||
- misc class fixes (not @snicker, ng-if on item store since we dynamically swap it sometimes)
|
||||
([478be611](https://github.com/habitrpg/habitrpg/commits/478be6111337cd200374f7f31b959725c6a0b945))
|
||||
- **find_uniq_user:** fix
|
||||
([ecbe780e](https://github.com/habitrpg/habitrpg/commits/ecbe780e70549b1470504efe052f238c89a9db14))
|
||||
- **mounts:** Move avatar upward when mounted regardless of pet
|
||||
([bc1adeb1](https://github.com/habitrpg/habitrpg/commits/bc1adeb1277103a5ca1f756e175ed68bbe837a2f))
|
||||
- **nodemon:** ignore CHANGELOG.md on watch
|
||||
([d6c55952](https://github.com/habitrpg/habitrpg/commits/d6c55952da8b49f36e9d8e4570d80931d081343d))
|
||||
- **party:** Round boss health up instead of to nearest integer
|
||||
([626da568](https://github.com/habitrpg/habitrpg/commits/626da5681f5ea95700f8ddf40587c7184926971c),
|
||||
[#2504](https://github.com/habitrpg/habitrpg/issues/2504))
|
||||
- **paypal:** fixes #2492, remove environment check for now, only have production-mode option. revisit
|
||||
([1dc68112](https://github.com/habitrpg/habitrpg/commits/1dc68112d131e4ebdec32ddff938eb6311d6565f))
|
||||
- **profile:** fix bug where empty profile displayed on username click
|
||||
([0579c432](https://github.com/habitrpg/habitrpg/commits/0579c432489c4a038e8c9f95ea3b285f5abc146f),
|
||||
[#2465](https://github.com/habitrpg/habitrpg/issues/2465))
|
||||
- **quests:**
|
||||
- bug fix to multi-drop
|
||||
([f478d10c](https://github.com/habitrpg/habitrpg/commits/f478d10c20f816cd104b3f0da814c189957f45f5))
|
||||
- list multiple rewards in dialog
|
||||
([e48c7277](https://github.com/habitrpg/habitrpg/commits/e48c7277f8256cf827790aece51e897fe0439374))
|
||||
- **settings:** reintroduce space between captions and help bubbles stripped during localization
|
||||
([5ddf09fe](https://github.com/habitrpg/habitrpg/commits/5ddf09fe13c7f8d844c8c47be0fb8f8b2fd1df33))
|
||||
- **spells:**
|
||||
- more $rootScope spell-casting bug fixes
|
||||
([47bd6dcb](https://github.com/habitrpg/habitrpg/commits/47bd6dcb79778d90d6f3ddeb003c3d8e45433333))
|
||||
- add some spells tests, don't send up body to spell paths
|
||||
([e0646bb9](https://github.com/habitrpg/habitrpg/commits/e0646bb98d44b6874b5259107c9be5fa34c58933))
|
||||
- some $rootScope.applying action fixes so cast-ending is immediate instead of waiting on response. Also, slim down party population to the essentials to avoid RequestEntityTooLarge
|
||||
([c6f7ab8a](https://github.com/habitrpg/habitrpg/commits/c6f7ab8a5c6f4e382208a928b90ba5f4eba9cd37))
|
||||
- <ESC> to cancel spell-casting
|
||||
([a1df41ad](https://github.com/habitrpg/habitrpg/commits/a1df41ad8165cd9eb6d2d5d59c7fe404edde716c))
|
||||
- **stable:** show hatchable combo when petOwned>0 (fyi @deilann)
|
||||
([51bff238](https://github.com/habitrpg/habitrpg/commits/51bff23885ca0080e7e71ff752daa0950ae923ae))
|
||||
- **stats:** Better layout for attribute point allocation
|
||||
([d782fc6b](https://github.com/habitrpg/habitrpg/commits/d782fc6b6a3cd7e90d327c93a5764626b2990c74))
|
||||
- **tests:**
|
||||
- include select2 in test manifest
|
||||
([38b4cea7](https://github.com/habitrpg/habitrpg/commits/38b4cea73299f51c4db7f6b2eb12533d219745f8))
|
||||
- don't use cluster in tests, else we get "connection refused"
|
||||
([7a479098](https://github.com/habitrpg/habitrpg/commits/7a479098dc6535654e322c737d80813790967941)
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
<a name="">My app - Changelog</a>
|
||||
# (2015-05-08)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- **Spring:** WHO IS LESLIE
|
||||
([6685e935](watch/commits/6685e93554a1274dedb55dae054e787fb80eb440))
|
||||
- **invite-friends:** text should be valid for both parties and guilds
|
||||
([54e82a14](watch/commits/54e82a14a252c3b9923449a7ef7cee6033a5d160))
|
||||
- **mystery:** It's 2015 now, Sabe
|
||||
([00252f20](watch/commits/00252f200481f06de9bccd1e55275d5366b03919))
|
||||
- **scoring:** move gainMP into score
|
||||
([2fc0cb8f](watch/commits/2fc0cb8fa1b3b5975c16653cb110be2f03b5427e))
|
||||
- **slimes:** Tweaks and missing sprite
|
||||
([7d1a58ca](watch/commits/7d1a58ca002af9dac19aba65d6465bd23b28d649))
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- **Spring:** Flung
|
||||
([d50d4ad8](watch/commits/d50d4ad8bb0f89e39ceb6562e0f8f392f94b5444))
|
||||
- **emails:** add support for weekly recap emails
|
||||
([37f7db3c](watch/commits/37f7db3c4e3859d03fd55a44e63819e273a06442))
|
||||
- **i18n:** upload japanese, serbian and chinese (taiwan)
|
||||
([ee7ba19e](watch/commits/ee7ba19ed17e72b33cbef8a324266617d384f852))
|
||||
- **mystery:** April Subscribee Items
|
||||
([7a7fc968](watch/commits/7a7fc96818ffd7f92738e8c6cc8a59e48d60597d))
|
||||
- **pets:** Slime Quest
|
||||
([f13c6cba](watch/commits/f13c6cba0026c645b19a0b1355ba2c5b27f80878))
|
||||
- **quests:** Boss damage from Habits
|
||||
([43dcded0](watch/commits/43dcded051b602d8a4efc30eef45365abfd238b4))
|
||||
- **scoring:** MP gain from Habits and Dailies
|
||||
([7b22244f](watch/commits/7b22244f0123cf649c9f2aada0811f35a565688d))
|
||||
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
# What's This?
|
||||
|
||||
I'm consolidating @litenull's rewrite branch with the old code, and removing files from both sources once they've been
|
||||
successfully merged into the new platform. While @litenull's "from scratch" approach was really clean, it will take
|
||||
us longer to implemente all the original features. This approach will (1) let us leverage code we already have, (2) merge
|
||||
in litenull's hard work from the last few weeks.
|
||||
|
||||
Once this archive/ directory is completely empty, we should be fully merged and ready to deploy the rewrite!
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
_ = require 'lodash'
|
||||
moment = require 'moment'
|
||||
|
||||
###
|
||||
Loads JavaScript files from public/vendor/*
|
||||
Use require() to min / concatinate for faster page load
|
||||
###
|
||||
loadJavaScripts = (model) ->
|
||||
|
||||
# Turns out you can't have expressions in browserify require() statements
|
||||
#vendor = '../../public/vendor'
|
||||
#require "#{vendor}/jquery-ui-1.10.2/jquery-1.9.1"
|
||||
|
||||
###
|
||||
Internal Scripts
|
||||
###
|
||||
require "../vendor/jquery.cookie.min.js"
|
||||
require "../vendor/bootstrap/js/bootstrap.min.js"
|
||||
require "../vendor/datepicker/js/bootstrap-datepicker"
|
||||
require "../vendor/bootstrap-tour/bootstrap-tour"
|
||||
|
||||
unless (model.get('_mobileDevice') is true)
|
||||
require "../vendor/sticky"
|
||||
|
||||
# note: external script loading is handled in app.on('render') near the bottom of this file (see https://groups.google.com/forum/?fromgroups=#!topic/derbyjs/x8FwdTLEuXo)
|
||||
|
||||
|
||||
# jquery sticky header on scroll, no need for position fixed
|
||||
initStickyHeader = (model) ->
|
||||
$('.header-wrap').sticky({topSpacing:0})
|
||||
|
||||
|
||||
module.exports.app = (appExports, model, app) ->
|
||||
|
||||
app.on 'render', (ctx) ->
|
||||
#restoreRefs(model)
|
||||
unless model.get('_mobileDevice')
|
||||
setupTooltips(model)
|
||||
initStickyHeader(model)
|
||||
setupSortable(model)
|
||||
|
||||
$('.datepicker').datepicker({autoclose:true, todayBtn:true})
|
||||
.on 'changeDate', (ev) ->
|
||||
#for some reason selecting a date doesn't fire a change event on the field, meaning our changes aren't saved
|
||||
model.at(ev.target).set 'date', moment(ev.date).format('MM/DD/YYYY')
|
||||
|
||||
|
|
@ -1,239 +0,0 @@
|
|||
_ = require 'lodash'
|
||||
{helpers} = require 'habitrpg-shared'
|
||||
async = require 'async'
|
||||
|
||||
module.exports.app = (app) ->
|
||||
|
||||
###
|
||||
Sync any updates to challenges since last refresh. Do it after cron, so we don't punish them for new tasks
|
||||
This is challenge->user sync. user->challenge happens when user interacts with their tasks
|
||||
###
|
||||
app.on 'ready', (model) ->
|
||||
window.setTimeout ->
|
||||
_.each model.get('groups'), (g) ->
|
||||
if (@uid in g.members) and g.challenges
|
||||
_.each(g.challenges, ->app.challenges.syncChalToUser g)
|
||||
true
|
||||
, 500
|
||||
|
||||
###
|
||||
Sync user to challenge (when they score, add to statistics)
|
||||
###
|
||||
app.model.on "change", "_page.user.priv.tasks.*.value", (id, value, previous, passed) ->
|
||||
### Sync to challenge, but do it later ###
|
||||
async.nextTick =>
|
||||
model = app.model
|
||||
ctx = {model: model}
|
||||
task = model.at "_page.user.priv.tasks.#{id}"
|
||||
tobj = task.get()
|
||||
pub = model.get "_page.user.pub"
|
||||
|
||||
if (chalTask = helpers.taskInChallenge.call ctx, tobj)? and chalTask.get()
|
||||
chalTask.increment "value", value - previous
|
||||
chal = model.at "groups.#{tobj.group.id}.challenges.#{tobj.challenge}"
|
||||
chalUser = -> helpers.indexedAt.call(ctx, chal.path(), 'members', {id:pub.id})
|
||||
cu = chalUser()
|
||||
unless cu?.get()
|
||||
chal.push "members", {id: pub.id, name: model.get(pub.profile.name)}
|
||||
cu = model.at chalUser()
|
||||
else
|
||||
cu.set 'name', pub.profile.name # update their name incase it changed
|
||||
cu.set "#{tobj.type}s.#{tobj.id}",
|
||||
value: tobj.value
|
||||
history: tobj.history
|
||||
|
||||
###
|
||||
Render graphs for user scores when the "Challenges" tab is clicked
|
||||
###
|
||||
|
||||
###
|
||||
TODO
|
||||
1) on main tab click or party
|
||||
* sort & render graphs for party
|
||||
2) guild -> all guilds
|
||||
3) public -> all public
|
||||
###
|
||||
|
||||
|
||||
###
|
||||
$('#profile-challenges-tab-link').on 'shown', ->
|
||||
async.each _.toArray(model.get('groups')), (g) ->
|
||||
async.each _.toArray(g.challenges), (chal) ->
|
||||
async.each _.toArray(chal.tasks), (task) ->
|
||||
async.each _.toArray(chal.members), (member) ->
|
||||
if (history = member?["#{task.type}s"]?[task.id]?.history) and !!history
|
||||
data = google.visualization.arrayToDataTable _.map(history, (h)-> [h.date,h.value])
|
||||
options =
|
||||
backgroundColor: { fill:'transparent' }
|
||||
width: 150
|
||||
height: 50
|
||||
chartArea: width: '80%', height: '80%'
|
||||
axisTitlePosition: 'none'
|
||||
legend: position: 'bottom'
|
||||
hAxis: gridlines: color: 'transparent' # since you can't seem to *remove* gridlines...
|
||||
vAxis: gridlines: color: 'transparent'
|
||||
chart = new google.visualization.LineChart $(".challenge-#{chal.id}-member-#{member.id}-history-#{task.id}")[0]
|
||||
chart.draw(data, options)
|
||||
###
|
||||
|
||||
app.fn
|
||||
challenges:
|
||||
|
||||
###
|
||||
Create
|
||||
###
|
||||
create: (e,el) ->
|
||||
[type, gid] = [$(el).attr('data-type'), $(el).attr('data-gid')]
|
||||
cid = @model.id()
|
||||
@model.set '_page.new.challenge',
|
||||
id: cid
|
||||
name: ''
|
||||
habits: []
|
||||
dailys: []
|
||||
todos: []
|
||||
rewards: []
|
||||
user:
|
||||
uid: @uid
|
||||
name: @pub.get('profile.name')
|
||||
group: {type, id:gid}
|
||||
timestamp: +new Date
|
||||
|
||||
###
|
||||
Save
|
||||
###
|
||||
save: ->
|
||||
newChal = @model.get('_page.new.challenge')
|
||||
[gid, cid] = [newChal.group.id, newChal.id]
|
||||
@model.push "_page.lists.challenges.#{gid}", newChal, ->
|
||||
app.browser.growlNotification('Challenge Created','success')
|
||||
app.challenges.discard()
|
||||
app.browser.resetDom() # something is going absolutely haywire here, all model data at end of reflist after chal created
|
||||
|
||||
###
|
||||
Toggle Edit
|
||||
###
|
||||
toggleEdit: (e, el) ->
|
||||
path = "_page.editing.challenges.#{$(el).attr('data-id')}"
|
||||
@model.set path, !@model.get(path)
|
||||
|
||||
###
|
||||
Discard
|
||||
###
|
||||
discard: ->
|
||||
@model.del '_page.new.challenge'
|
||||
|
||||
###
|
||||
Delete
|
||||
###
|
||||
delete: (e) ->
|
||||
return unless confirm("Delete challenge, are you sure?") is true
|
||||
e.at().remove()
|
||||
|
||||
###
|
||||
Add challenge name as a tag for user
|
||||
###
|
||||
syncChalToUser: (chal) ->
|
||||
return unless chal
|
||||
### Sync tags ###
|
||||
tags = @priv.get('tags') or []
|
||||
idx = _.findIndex tags, {id: chal.id}
|
||||
if ~idx and (tags[idx].name isnt chal.name)
|
||||
### update the name - it's been changed since ###
|
||||
@priv.set "tags.#{idx}.name", chal.name
|
||||
else
|
||||
@priv.push 'tags', {id: chal.id, name: chal.name, challenge: true}
|
||||
|
||||
tags = {}; tags[chal.id] = true
|
||||
_.each chal.habits.concat(chal.dailys.concat(chal.todos.concat(chal.rewards))), (task) =>
|
||||
_.defaults task, { tags, challenge: chal.id, group: {id: chal.group.id, type: chal.group.type} }
|
||||
path = "tasks.#{task.id}"
|
||||
if @priv.get path
|
||||
@priv.set path, _.defaults(@priv.get(path), task)
|
||||
else
|
||||
@model.push "_page.lists.tasks.#{@uid}.#{task.type}s", task
|
||||
true
|
||||
|
||||
###
|
||||
Subscribe
|
||||
###
|
||||
subscribe: (e) ->
|
||||
chal = e.get()
|
||||
### Add all challenge's tasks to user's tasks ###
|
||||
currChallenges = @pub.get('challenges')
|
||||
@pub.unshift('challenges', chal.id) unless currChallenges and ~currChallenges.indexOf(chal.id)
|
||||
e.at().push "members",
|
||||
id: @uid
|
||||
name: @pub.get('profile.name')
|
||||
app.challenges.syncChalToUser(chal)
|
||||
|
||||
###
|
||||
--------------------------
|
||||
Unsubscribe functions
|
||||
--------------------------
|
||||
###
|
||||
|
||||
unsubscribe: (chal, keep=true) ->
|
||||
|
||||
### Remove challenge from user ###
|
||||
i = @pub.get('challenges')?.indexOf(chal.id)
|
||||
if i? and ~i
|
||||
@pub.remove("challenges", i, 1)
|
||||
|
||||
### Remove user from challenge ###
|
||||
if ~(i = _.findIndex chal.members, {id: @uid})
|
||||
@model.remove "groups.#{chal.group.id}.challenges.#{chal.id}.members", i, 1
|
||||
|
||||
### Remove tasks from user ###
|
||||
async.each chal.habits.concat(chal.dailys.concat(chal.todos.concat(chal.rewards))), (task) =>
|
||||
if keep is true
|
||||
@priv.del "tasks.#{task.id}.challenge"
|
||||
else
|
||||
path = "_page.lists.tasks.#{@uid}.#{task.type}s"
|
||||
if ~(i = _.findIndex(@model.get(path), {id:task.id}))
|
||||
@model.remove(path, i, 1)
|
||||
true
|
||||
|
||||
taskUnsubscribe: (e, el) ->
|
||||
|
||||
###
|
||||
since the challenge was deleted, we don't have its data to unsubscribe from - but we have the vestiges on the task
|
||||
FIXME this is a really dumb way of doing this
|
||||
###
|
||||
tasks = @priv.get('tasks')
|
||||
tobj = tasks[$(el).attr("data-tid")]
|
||||
deletedChal =
|
||||
id: tobj.challenge
|
||||
members: [@uid]
|
||||
habits: _.where tasks, {type: 'habit', challenge: tobj.challenge}
|
||||
dailys: _.where tasks, {type: 'daily', challenge: tobj.challenge}
|
||||
todos: _.where tasks, {type: 'todo', challenge: tobj.challenge}
|
||||
rewards: _.where tasks, {type: 'reward', challenge: tobj.challenge}
|
||||
|
||||
switch $(el).attr('data-action')
|
||||
when 'keep'
|
||||
@priv.del "tasks.#{tobj.id}.challenge"
|
||||
@priv.del "tasks.#{tobj.id}.group"
|
||||
when 'keep-all'
|
||||
app.challenges.unsubscribe.call @, deletedChal, true
|
||||
when 'remove'
|
||||
path = "_page.lists.tasks.#{@uid}.#{tobj.type}s"
|
||||
if ~(i = _.findIndex @model.get(path), {id: tobj.id})
|
||||
@model.remove path, i
|
||||
when 'remove-all'
|
||||
app.challenges.unsubscribe.call @, deletedChal, false
|
||||
|
||||
challengeUnsubscribe: (e, el) ->
|
||||
$(el).popover('destroy').popover({
|
||||
html: true
|
||||
placement: 'top'
|
||||
trigger: 'manual'
|
||||
title: 'Unsubscribe From Challenge And:'
|
||||
content: """
|
||||
<a class=challenge-unsubscribe-and-remove>Remove Tasks</a><br/>
|
||||
<a class=challenge-unsubscribe-and-keep>Keep Tasks</a><br/>
|
||||
<a class=challenge-unsubscribe-cancel>Cancel</a><br/>
|
||||
"""
|
||||
}).popover('show')
|
||||
$('.challenge-unsubscribe-and-remove').click => app.challenges.unsubscribe.call @, e.get(), false
|
||||
$('.challenge-unsubscribe-and-keep').click => app.challenges.unsubscribe.call @, e.get(), true
|
||||
$('[class^=challenge-unsubscribe]').click => $(el).popover('destroy')
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
_ = require 'lodash'
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
||||
appExports.filtersDeleteTag = (e, el) ->
|
||||
tags = user.get('tags')
|
||||
tag = e.at "_user.tags." + $(el).attr('data-index')
|
||||
tagId = tag.get('id')
|
||||
|
||||
###something got corrupted, let's clear the corrupt tags###
|
||||
unless tagId
|
||||
user.set 'tags', _.filter( tags, ((t)-> t?.id) )
|
||||
user.set 'filters', {}
|
||||
return
|
||||
|
||||
model.del "_user.filters.#{tagId}"
|
||||
tag.remove()
|
||||
|
||||
### remove tag from all tasks###
|
||||
_.each user.get("tasks"), (task) -> user.del "tasks.#{task.id}.tags.#{tagId}"; true
|
||||
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
_ = require('lodash')
|
||||
helpers = require('habitrpg-shared/script/helpers')
|
||||
|
||||
module.exports.app = (appExports, model, app) ->
|
||||
browser = require './browser'
|
||||
|
||||
_currentTime = model.at '_currentTime'
|
||||
_currentTime.setNull +new Date
|
||||
# Every 60 seconds, reset the current time so that the chat can update relative times
|
||||
setInterval (->_currentTime.set +new Date), 60000
|
||||
|
||||
appExports.groupCreate = (e,el) ->
|
||||
type = $(el).attr('data-type')
|
||||
newGroup =
|
||||
name: model.get("_new.group.name")
|
||||
description: model.get("_new.group.description")
|
||||
leader: user.get('id')
|
||||
members: [user.get('id')]
|
||||
type: type
|
||||
|
||||
# parties - free
|
||||
if type is 'party'
|
||||
return model.add 'groups', newGroup, ->location.reload()
|
||||
|
||||
# guilds - 4G
|
||||
unless user.get('balance') >= 1
|
||||
return $('#more-gems-modal').modal 'show'
|
||||
if confirm "Create Guild for 4 Gems?"
|
||||
newGroup.privacy = (model.get("_new.group.privacy") || 'public') if type is 'guild'
|
||||
newGroup.balance = 1 # they spent $ to open the guild, it goes into their guild bank
|
||||
model.add 'groups', newGroup, ->
|
||||
user.incr 'balance', -1, ->location.reload()
|
||||
|
||||
appExports.toggleGroupEdit = (e, el) ->
|
||||
path = "_editing.groups.#{$(el).attr('data-gid')}"
|
||||
model.set path, !model.get(path)
|
||||
|
||||
appExports.toggleLeaderMessageEdit = (e, el) ->
|
||||
path = "_editing.leaderMessage.#{$(el).attr('data-gid')}"
|
||||
model.set path, !model.get(path)
|
||||
|
||||
appExports.groupAddWebsite = (e, el) ->
|
||||
test = e.get()
|
||||
e.at().unshift 'websites', model.get('_newGroupWebsite')
|
||||
model.del '_newGroupWebsite'
|
||||
|
||||
appExports.groupInvite = (e,el) ->
|
||||
uid = model.get('_groupInvitee').replace(/[\s"]/g, '')
|
||||
model.set '_groupInvitee', ''
|
||||
return if _.isEmpty(uid)
|
||||
|
||||
model.query('users').publicInfo([uid]).fetch (err, profiles) ->
|
||||
throw err if err
|
||||
profile = profiles.at(0).get()
|
||||
return model.set("_groupError", "User with id #{uid} not found.") unless profile
|
||||
model.query('groups').withMember(uid).fetch (err, g) ->
|
||||
throw err if err
|
||||
group = e.get(); groups = g.get()
|
||||
{type, name} = group; gid = group.id
|
||||
groupError = (msg) -> model.set("_groupError", msg)
|
||||
invite = ->
|
||||
$.bootstrapGrowl "Invitation Sent."
|
||||
switch type
|
||||
when 'guild' then model.push "users.#{uid}.invitations.guilds", {id:gid, name}, ->location.reload()
|
||||
when 'party' then model.set "users.#{uid}.invitations.party", {id:gid, name}, ->location.reload()
|
||||
|
||||
switch type
|
||||
when 'guild'
|
||||
if profile.invitations?.guilds and _.find(profile.invitations.guilds, {id:gid})
|
||||
return groupError("User already invited to that group")
|
||||
else if uid in group.members
|
||||
return groupError("User already in that group")
|
||||
else invite()
|
||||
when 'party'
|
||||
if profile.invitations?.party
|
||||
return groupError("User already pending invitation.")
|
||||
else if _.find(groups, {type:'party'})
|
||||
return groupError("User already in a party.")
|
||||
else invite()
|
||||
|
||||
|
||||
appExports.acceptInvitation = (e,el) ->
|
||||
gid = e.get('id')
|
||||
if $(el).attr('data-type') is 'party'
|
||||
user.set 'invitations.party', null, ->joinGroup(gid)
|
||||
else
|
||||
e.at().remove ->joinGroup(gid)
|
||||
|
||||
appExports.rejectInvitation = (e, el) ->
|
||||
clear = -> browser.resetDom(model)
|
||||
if e.at().path().indexOf('party') != -1
|
||||
model.del e.at().path(), clear
|
||||
else e.at().remove clear
|
||||
|
||||
appExports.groupLeave = (e,el) ->
|
||||
if confirm("Leave this group, are you sure?") is true
|
||||
uid = user.get('id')
|
||||
group = model.at "groups.#{$(el).attr('data-id')}"
|
||||
index = group.get('members').indexOf(uid)
|
||||
if index != -1
|
||||
group.remove 'members', index, 1, ->
|
||||
updated = group.get()
|
||||
# last member out, delete the party
|
||||
if _.isEmpty(updated.members) and (updated.type is 'party')
|
||||
group.del ->location.reload()
|
||||
# assign new leader, so the party is editable #TODO allow old leader to assign new leader, this is just random
|
||||
else if (updated.leader is uid)
|
||||
group.set "leader", updated.members[0], ->location.reload()
|
||||
else location.reload()
|
||||
|
||||
###
|
||||
Chat Functionality
|
||||
###
|
||||
|
||||
model.on 'unshift', '_party.chat', -> $('.chat-message').tooltip()
|
||||
model.on 'unshift', '_habitrpg.chat', -> $('.chat-message').tooltip()
|
||||
|
||||
appExports.chatKeyup = (e, el, next) ->
|
||||
return next() unless e.keyCode is 13
|
||||
appExports.sendChat(e, el)
|
||||
|
||||
appExports.deleteChatMessage = (e) ->
|
||||
if confirm("Delete chat message?") is true
|
||||
e.at().remove() #requires the {#with}
|
||||
|
||||
app.on 'render', (ctx) ->
|
||||
$('#party-tab-link').on 'shown', (e) ->
|
||||
messages = model.get('_party.chat')
|
||||
return false unless messages?.length > 0
|
||||
model.set '_user.party.lastMessageSeen', messages[0].id
|
||||
|
||||
appExports.gotoPartyChat = ->
|
||||
model.set '_gamePane', true, ->
|
||||
$('#party-tab-link').tab('show')
|
||||
|
||||
appExports.assignGroupLeader = (e, el) ->
|
||||
newLeader = model.get('_new.groupLeader')
|
||||
if newLeader and (confirm("Assign new leader, you sure?") is true)
|
||||
e.at().set('leader', newLeader, ->browser.resetDom(model)) if newLeader
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
i18n = require 'derby-i18n'
|
||||
|
||||
i18n.plurals.add 'he', (n) -> n
|
||||
i18n.plurals.add 'bg', (n) -> n
|
||||
i18n.plurals.add 'nl', (n) -> n
|
||||
|
||||
module.exports = i18n
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# Translations
|
||||
i18n = require './i18n'
|
||||
i18n.localize app,
|
||||
availableLocales: ['en', 'he', 'bg', 'nl']
|
||||
defaultLocale: 'en'
|
||||
urlScheme: false
|
||||
checkHeader: true
|
||||
|
||||
|
||||
# ========== CONTROLLER FUNCTIONS ==========
|
||||
|
||||
ready (model) ->
|
||||
misc.fixCorruptUser(model) # https://github.com/lefnire/habitrpg/issues/634
|
||||
|
||||
# used for things like remove website, chat, etc
|
||||
exports.removeAt = (e, el) ->
|
||||
if (confirmMessage = $(el).attr 'data-confirm')?
|
||||
return unless confirm(confirmMessage) is true
|
||||
e.at().remove()
|
||||
browser.resetDom(model) if $(el).attr('data-refresh')
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
items = require 'habitrpg-shared/script/items'
|
||||
_ = require 'lodash'
|
||||
|
||||
updateStore = (model) ->
|
||||
nextItems = items.updateStore(model.get('_user'))
|
||||
_.each nextItems, (v,k) -> model.set("_items.next.#{k}",v); true
|
||||
|
||||
###
|
||||
server exports
|
||||
###
|
||||
module.exports.server = (model) ->
|
||||
model.set '_items', items.items
|
||||
updateStore(model)
|
||||
|
||||
###
|
||||
app exports
|
||||
###
|
||||
module.exports.app = (appExports, model) ->
|
||||
misc = require './misc'
|
||||
|
||||
model.on "set", "_user.items.*", -> updateStore(model)
|
||||
|
||||
appExports.buyItem = (e, el) ->
|
||||
misc.batchTxn model, (uObj, paths) ->
|
||||
ret = items.buyItem uObj, $(el).attr('data-type'), {paths}
|
||||
alert("Not enough GP") if ret is false
|
||||
|
||||
appExports.activateRewardsTab = ->
|
||||
model.set '_activeTabRewards', true
|
||||
model.set '_activeTabPets', false
|
||||
appExports.activatePetsTab = ->
|
||||
model.set '_activeTabPets', true
|
||||
model.set '_activeTabRewards', false
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
_ = require 'lodash'
|
||||
algos = require 'habitrpg-shared/script/algos'
|
||||
items = require('habitrpg-shared/script/items').items
|
||||
helpers = require('habitrpg-shared/script/helpers')
|
||||
|
||||
#TODO put this in habitrpg-shared
|
||||
###
|
||||
We can't always use refLists, but we often still need to get a positional path by id: eg, users.1234.tasks.5678.value
|
||||
For arrays (which use indexes, not id-paths), here's a helper function so we can run indexedPath('users',:user.id,'tasks',:task.id,'value)
|
||||
###
|
||||
indexedPath = ->
|
||||
_.reduce arguments, (m,v) =>
|
||||
return v if !m #first iteration
|
||||
return "#{m}.#{v}" if _.isString v #string paths
|
||||
return "#{m}." + _.findIndex(@model.get(m),v)
|
||||
, ''
|
||||
|
||||
taskInChallenge = (task) ->
|
||||
return undefined unless task?.challenge
|
||||
@model.at indexedPath.call(@, "groups.#{task.group.id}.challenges", {id:task.challenge}, "#{task.type}s", {id:task.id})
|
||||
|
||||
###
|
||||
algos.score wrapper for habitrpg-helpers to work in Derby. We need to do model.set() instead of simply setting the
|
||||
object properties, and it's very difficult to diff the two objects and find dot-separated paths to set. So we to first
|
||||
clone our user object (if we don't do that, it screws with model.on() listeners, ping Tyler for an explaination),
|
||||
perform the updates while tracking paths, then all the values at those paths
|
||||
###
|
||||
module.exports.score = (model, taskId, direction, allowUndo=false) ->
|
||||
drop = undefined
|
||||
delta = batchTxn model, (uObj, paths) ->
|
||||
tObj = uObj.tasks[taskId]
|
||||
|
||||
# Stuff for undo
|
||||
if allowUndo
|
||||
tObjBefore = _.cloneDeep tObj
|
||||
tObjBefore.completed = !tObjBefore.completed if tObjBefore.type in ['daily', 'todo']
|
||||
previousUndo = model.get('_undo')
|
||||
clearTimeout(previousUndo.timeoutId) if previousUndo?.timeoutId
|
||||
timeoutId = setTimeout (-> model.del('_undo')), 20000
|
||||
model.set '_undo', {stats:_.cloneDeep(uObj.stats), task:tObjBefore, timeoutId: timeoutId}
|
||||
|
||||
delta = algos.score(uObj, tObj, direction, {paths})
|
||||
model.set('_streakBonus', uObj._tmp.streakBonus) if uObj._tmp?.streakBonus
|
||||
drop = uObj._tmp?.drop
|
||||
|
||||
# Update challenge statistics
|
||||
# FIXME put this in it's own batchTxn, make batchTxn model.at() ref aware (not just _user)
|
||||
# FIXME use reflists for users & challenges
|
||||
if (chalTask = taskInChallenge.call({model}, tObj)) and chalTask?.get()
|
||||
model._dontPersist = false
|
||||
chalTask.incr "value", delta
|
||||
chal = model.at indexedPath.call({model}, "groups.#{tObj.group.id}.challenges", {id:tObj.challenge})
|
||||
chalUser = -> indexedPath.call({model}, chal.path(), 'users', {id:uObj.id})
|
||||
cu = model.at chalUser()
|
||||
unless cu?.get()
|
||||
chal.push "users", {id: uObj.id, name: helpers.username(uObj.auth, uObj.profile?.name)}
|
||||
cu = model.at chalUser()
|
||||
else
|
||||
cu.set 'name', helpers.username(uObj.auth, uObj.profile?.name) # update their name incase it changed
|
||||
cu.set "#{tObj.type}s.#{tObj.id}",
|
||||
value: tObj.value
|
||||
history: tObj.history
|
||||
model._dontPersist = true
|
||||
, done:->
|
||||
if drop and $?
|
||||
model.set '_drop', drop
|
||||
$('#item-dropped-modal').modal 'show'
|
||||
|
||||
delta
|
||||
|
||||
###
|
||||
Cleanup task-corruption (null tasks, rogue/invisible tasks, etc)
|
||||
Obviously none of this should be happening, but we'll stop-gap until we can find & fix
|
||||
Gotta love refLists! see https://github.com/lefnire/habitrpg/issues/803 & https://github.com/lefnire/habitrpg/issues/6343
|
||||
###
|
||||
module.exports.fixCorruptUser = (model) ->
|
||||
user = model.at('_user')
|
||||
tasks = user.get('tasks')
|
||||
|
||||
## Remove corrupted tasks
|
||||
_.each tasks, (task, key) ->
|
||||
unless task?.id? and task?.type?
|
||||
user.del("tasks.#{key}")
|
||||
delete tasks[key]
|
||||
true
|
||||
resetDom = false
|
||||
batchTxn model, (uObj, paths, batch) ->
|
||||
## fix https://github.com/lefnire/habitrpg/issues/1086
|
||||
uniqPets = _.uniq(uObj.items.pets)
|
||||
batch.set('items.pets', uniqPets) if !_.isEqual(uniqPets, uObj.items.pets)
|
||||
|
||||
if uObj.invitations?.guilds
|
||||
uniqInvites = _.uniq(uObj.invitations.guilds)
|
||||
batch.set('invitations.guilds', uniqInvites) if !_.isEqual(uniqInvites, uObj.invitations.guilds)
|
||||
|
||||
## Task List Cleanup
|
||||
['habit','daily','todo','reward'].forEach (type) ->
|
||||
|
||||
# 1. remove duplicates
|
||||
# 2. restore missing zombie tasks back into list
|
||||
idList = uObj["#{type}Ids"]
|
||||
taskIds = _.pluck( _.where(tasks, {type}), 'id')
|
||||
union = _.union idList, taskIds
|
||||
|
||||
# 2. remove empty (grey) tasks
|
||||
preened = _.filter union, (id) -> id and _.contains(taskIds, id)
|
||||
|
||||
# There were indeed issues found, set the new list
|
||||
if !_.isEqual(idList, preened)
|
||||
batch.set("#{type}Ids", preened)
|
||||
console.error uObj.id + "'s #{type}s were corrupt."
|
||||
true
|
||||
resetDom = !_.isEmpty(paths)
|
||||
require('./browser').resetDom(model) if resetDom
|
||||
|
||||
module.exports.viewHelpers = (view) ->
|
||||
|
||||
#misc
|
||||
view.fn "percent", (x, y) ->
|
||||
x=1 if x==0
|
||||
Math.round(x/y*100)
|
||||
view.fn 'indexOf', (str1, str2) ->
|
||||
return false unless str1 && str2
|
||||
str1.indexOf(str2) != -1
|
||||
view.fn "round", Math.round
|
||||
view.fn "floor", Math.floor
|
||||
view.fn "ceil", Math.ceil
|
||||
view.fn "truarr", (num) -> num-1
|
||||
view.fn 'count', (arr) -> arr?.length or 0
|
||||
view.fn 'int',
|
||||
get: (num) -> num
|
||||
set: (num) -> [parseInt(num)]
|
||||
view.fn 'indexedPath', indexedPath
|
||||
|
||||
|
||||
#iCal
|
||||
view.fn "encodeiCalLink", helpers.encodeiCalLink
|
||||
|
||||
#User
|
||||
view.fn "gems", (balance) -> balance * 4
|
||||
|
||||
#Challenges
|
||||
view.fn 'taskInChallenge', (task) ->
|
||||
taskInChallenge.call(@,task)?.get()
|
||||
view.fn 'taskAttrFromChallenge', (task, attr) ->
|
||||
taskInChallenge.call(@,task)?.get(attr)
|
||||
view.fn 'brokenChallengeLink', (task) ->
|
||||
task?.challenge and !(taskInChallenge.call(@,task)?.get())
|
||||
|
||||
view.fn 'challengeMemberScore', (member, tType, tid) ->
|
||||
Math.round(member["#{tType}s"]?[tid]?.value)
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
_ = require 'lodash'
|
||||
{ randomVal } = require 'habitrpg-shared/script/helpers'
|
||||
{ pets, hatchingPotions } = require('habitrpg-shared/script/items').items
|
||||
|
||||
###
|
||||
app exports
|
||||
###
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at '_user'
|
||||
|
||||
appExports.chooseEgg = (e, el) ->
|
||||
model.ref '_hatchEgg', e.at()
|
||||
|
||||
appExports.hatchEgg = (e, el) ->
|
||||
hatchingPotionName = $(el).children('select').val()
|
||||
myHatchingPotion = user.get 'items.hatchingPotions'
|
||||
egg = model.get '_hatchEgg'
|
||||
eggs = user.get 'items.eggs'
|
||||
myPets = user.get 'items.pets'
|
||||
|
||||
hatchingPotionIdx = myHatchingPotion.indexOf hatchingPotionName
|
||||
eggIdx = eggs.indexOf egg
|
||||
|
||||
return alert "You don't own that hatching potion yet, complete more tasks!" if hatchingPotionIdx is -1
|
||||
return alert "You don't own that egg yet, complete more tasks!" if eggIdx is -1
|
||||
return alert "You already have that pet, hatch a different combo." if myPets and myPets.indexOf("#{egg.name}-#{hatchingPotionName}") != -1
|
||||
|
||||
user.push 'items.pets', egg.name + '-' + hatchingPotionName, ->
|
||||
eggs.splice eggIdx, 1
|
||||
myHatchingPotion.splice hatchingPotionIdx, 1
|
||||
user.set 'items.eggs', eggs
|
||||
user.set 'items.hatchingPotions', myHatchingPotion
|
||||
|
||||
alert 'Your egg hatched! Visit your stable to equip your pet.'
|
||||
|
||||
#FIXME Bug: this removes from the array properly in the browser, but on refresh is has removed all items from the arrays
|
||||
# user.remove 'items.hatchingPotions', hatchingPotionIdx, 1
|
||||
# user.remove 'items.eggs', eggIdx, 1
|
||||
|
||||
appExports.choosePet = (e, el, next) ->
|
||||
petStr = $(el).attr('data-pet')
|
||||
|
||||
return next() if user.get('items.pets').indexOf(petStr) == -1
|
||||
# If user's pet is already active, deselect it
|
||||
return user.set 'items.currentPet', {} if user.get('items.currentPet.str') is petStr
|
||||
|
||||
[name, modifier] = petStr.split('-')
|
||||
pet = _.find pets, {name: name}
|
||||
pet.modifier = modifier
|
||||
pet.str = petStr
|
||||
user.set 'items.currentPet', pet
|
||||
|
||||
appExports.buyHatchingPotion = (e, el) ->
|
||||
name = $(el).attr 'data-hatchingPotion'
|
||||
newHatchingPotion = _.find hatchingPotions, {name: name}
|
||||
gems = user.get('balance') * 4
|
||||
if gems >= newHatchingPotion.value
|
||||
if confirm "Buy this hatching potion with #{newHatchingPotion.value} of your #{gems} Gems?"
|
||||
user.push 'items.hatchingPotions', newHatchingPotion.name
|
||||
user.set 'balance', (gems - newHatchingPotion.value) / 4
|
||||
else
|
||||
$('#more-gems-modal').modal 'show'
|
||||
|
||||
appExports.buyEgg = (e, el) ->
|
||||
name = $(el).attr 'data-egg'
|
||||
newEgg = _.find pets, {name: name}
|
||||
gems = user.get('balance') * 4
|
||||
if gems >= newEgg.value
|
||||
if confirm "Buy this egg with #{newEgg.value} of your #{gems} Gems?"
|
||||
user.push 'items.eggs', newEgg
|
||||
user.set 'balance', (gems - newEgg.value) / 4
|
||||
else
|
||||
$('#more-gems-modal').modal 'show'
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
algos = require 'habitrpg-shared/script/algos'
|
||||
helpers = require 'habitrpg-shared/script/helpers'
|
||||
_ = require 'lodash'
|
||||
moment = require 'moment'
|
||||
misc = require './misc'
|
||||
|
||||
appExports.clearCompleted = (e, el) ->
|
||||
completedIds = _.pluck( _.where(model.get('_todoList'), {completed:true}), 'id')
|
||||
todoIds = user.get('todoIds')
|
||||
_.each completedIds, (id) -> user.del "tasks.#{id}"; true
|
||||
user.set 'todoIds', _.difference(todoIds, completedIds)
|
||||
|
||||
|
||||
###
|
||||
Undo
|
||||
###
|
||||
appExports.undo = () ->
|
||||
undo = model.get '_undo'
|
||||
clearTimeout(undo.timeoutId) if undo?.timeoutId
|
||||
model.del '_undo'
|
||||
_.each undo.stats, (val, key) -> user.set "stats.#{key}", val; true
|
||||
taskPath = "tasks.#{undo.task.id}"
|
||||
_.each undo.task, (val, key) ->
|
||||
return true if key in ['id', 'type'] # strange bugs in this world: https://workflowy.com/shared/a53582ea-43d6-bcce-c719-e134f9bf71fd/
|
||||
if key is 'completed'
|
||||
user.pass({cron:true}).set("#{taskPath}.completed",val)
|
||||
else
|
||||
user.set "#{taskPath}.#{key}", val
|
||||
true
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
_ = require 'lodash'
|
||||
{ randomVal } = require 'habitrpg-shared/script/helpers'
|
||||
{ pets, hatchingPotions } = require('habitrpg-shared/script/items').items
|
||||
|
||||
###
|
||||
Listeners to enabled flags, set notifications to the user when they've unlocked features
|
||||
###
|
||||
|
||||
module.exports.app = (appExports, model) ->
|
||||
user = model.at('_user')
|
||||
|
||||
alreadyShown = (before, after) -> !(!before and after is true)
|
||||
|
||||
showPopover = (selector, title, html, placement='bottom') ->
|
||||
$(selector).popover('destroy')
|
||||
html += " <a href='#' onClick=\"$('#{selector}').popover('hide');return false;\">[Close]</a>"
|
||||
$(selector).popover({
|
||||
title: title
|
||||
placement: placement
|
||||
trigger: 'manual'
|
||||
html: true
|
||||
content: html
|
||||
}).popover 'show'
|
||||
|
||||
|
||||
user.on 'set', 'flags.customizationsNotification', (after, before) ->
|
||||
return if alreadyShown(before,after)
|
||||
$('.main-herobox').popover('destroy') #remove previous popovers
|
||||
html = "Click your avatar to customize your appearance."
|
||||
showPopover '.main-herobox', 'Customize Your Avatar', html, 'bottom'
|
||||
|
||||
user.on 'set', 'flags.itemsEnabled', (after, before) ->
|
||||
return if alreadyShown(before,after)
|
||||
html = """
|
||||
<img src='/vendor/BrowserQuest/client/img/1/chest.png' />
|
||||
Congratulations, you have unlocked the Item Store! You can now buy weapons, armor, potions, etc. Read each item's comment for more information.
|
||||
"""
|
||||
showPopover 'div.rewards', 'Item Store Unlocked', html, 'left'
|
||||
|
||||
user.on 'set', 'flags.petsEnabled', (after, before) ->
|
||||
return if alreadyShown(before,after)
|
||||
html = """
|
||||
<img src='/img/sprites/wolf_border.png' style='width:30px;height:30px;float:left;padding-right:5px' />
|
||||
You have unlocked Pets! You can now buy pets with Gems (note, you replenish Gems with real-life money - so chose your pets wisely!)
|
||||
"""
|
||||
showPopover '#rewardsTabs', 'Pets Unlocked', html, 'left'
|
||||
|
||||
user.on 'set', 'flags.partyEnabled', (after, before) ->
|
||||
return if user.get('party.current') or alreadyShown(before,after)
|
||||
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'
|
||||
|
||||
user.on 'set', 'flags.dropsEnabled', (after, before) ->
|
||||
return if alreadyShown(before,after)
|
||||
|
||||
egg = randomVal pets
|
||||
|
||||
dontPersist = model._dontPersist
|
||||
|
||||
model._dontPersist = false
|
||||
user.push 'items.eggs', egg
|
||||
model._dontPersist = dontPersist
|
||||
|
||||
$('#drops-enabled-modal').modal 'show'
|
||||
|
||||
user.on 'push', 'items.pets', (after, before) ->
|
||||
return if user.get('achievements.beastMaster')
|
||||
if before >= 90 # evidently before is the count?
|
||||
dontPersist = model._dontPersist; model._dontPersist = false
|
||||
user.set 'achievements.beastMaster', true, (-> model._dontPersist = dontPersist)
|
||||
$('#beastmaster-achievement-modal').modal('show')
|
||||
|
||||
user.on 'set', 'items.*', (after, before) ->
|
||||
return if user.get('achievements.ultimateGear')
|
||||
items = user.get('items')
|
||||
if parseInt(items.weapon) >= 6 and parseInt(items.armor) >= 5 and parseInt(items.head) >= 5 and parseInt(items.shield) >= 5
|
||||
dontPersist = model._dontPersist; model._dontPersist = false
|
||||
user.set 'achievements.ultimateGear', true, (->model._dontPersist = dontPersist)
|
||||
$('#max-gear-achievement-modal').modal('show')
|
||||
|
||||
user.on 'set', 'tasks.*.streak', (id, after, before) ->
|
||||
if after > 0
|
||||
|
||||
# 21-day streak, as per the old philosophy of doign a thing 21-days in a row makes a habit
|
||||
if (after % 21) is 0
|
||||
dontPersist = model._dontPersist; model._dontPersist = false
|
||||
user.incr 'achievements.streak', 1, (-> model._dontPersist = dontPersist)
|
||||
$('#streak-achievement-modal').modal('show')
|
||||
|
||||
# they're undoing a task at the 21 mark, take back their badge
|
||||
else if (before - after is 1) and (before % 21 is 0)
|
||||
dontPersist = model._dontPersist; model._dontPersist = false
|
||||
user.incr 'achievements.streak', -1, (-> model._dontPersist = dontPersist)
|
||||
25
newrelic.js
25
newrelic.js
|
|
@ -1,25 +0,0 @@
|
|||
/**
|
||||
* New Relic agent configuration.
|
||||
*
|
||||
* See lib/config.defaults.js in the agent distribution for a more complete
|
||||
* description of configuration variables and their potential values.
|
||||
*/
|
||||
var nconf = require('nconf')
|
||||
exports.config = {
|
||||
/**
|
||||
* Array of application names.
|
||||
*/
|
||||
app_name : ['HabitRPG'],
|
||||
/**
|
||||
* Your New Relic license key.
|
||||
*/
|
||||
license_key : nconf.get('NEW_RELIC_LICENSE_KEY'),
|
||||
logging : {
|
||||
/**
|
||||
* Level at which to log. 'trace' is most useful to New Relic when diagnosing
|
||||
* issues with the agent, 'info' and higher will impose the least overhead on
|
||||
* production applications.
|
||||
*/
|
||||
level : 'warning'
|
||||
}
|
||||
};
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Git COMMIT-MSG hook for validating commit message
|
||||
* From: https://github.com/angular/angular.js
|
||||
* See https://docs.google.com/document/d/1rk04jEuGfk9kYzfqCuOlPTSJw3hEDZJTBN5E5f1SALo/edit
|
||||
*
|
||||
* Installation:
|
||||
* >> cd <angular-repo>
|
||||
* >> ln -s ../../validate-commit-msg.js .git/hooks/commit-msg
|
||||
*/
|
||||
var fs = require('fs');
|
||||
var util = require('util');
|
||||
|
||||
|
||||
var MAX_LENGTH = 999;
|
||||
var PATTERN = /^(?:fixup!\s*)?(\w*)(\(([\w\$\.\-\*/]*)\))?\: (.*)$/;
|
||||
var IGNORED = /^WIP\:/;
|
||||
var TYPES = {
|
||||
feat: true,
|
||||
fix: true,
|
||||
docs: true,
|
||||
style: true,
|
||||
refactor: true,
|
||||
perf: true,
|
||||
test: true,
|
||||
chore: true,
|
||||
revert: true,
|
||||
'interface': true,
|
||||
i18n: true
|
||||
};
|
||||
|
||||
|
||||
var error = function() {
|
||||
// gitx does not display it
|
||||
// http://gitx.lighthouseapp.com/projects/17830/tickets/294-feature-display-hook-error-message-when-hook-fails
|
||||
// https://groups.google.com/group/gitx/browse_thread/thread/a03bcab60844b812
|
||||
console.error('INVALID COMMIT MSG: ' + util.format.apply(null, arguments));
|
||||
};
|
||||
|
||||
|
||||
var validateMessage = function(message) {
|
||||
var isValid = true;
|
||||
|
||||
if (IGNORED.test(message)) {
|
||||
console.log('Commit message validation ignored.');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (message.length > MAX_LENGTH) {
|
||||
error('is longer than %d characters !', MAX_LENGTH);
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
var match = PATTERN.exec(message);
|
||||
|
||||
if (!match) {
|
||||
error('does not match "<type>(<scope>): <subject>" ! was: ' + message);
|
||||
return false;
|
||||
}
|
||||
|
||||
var type = match[1];
|
||||
var scope = match[3];
|
||||
var subject = match[4];
|
||||
|
||||
if (!TYPES.hasOwnProperty(type)) {
|
||||
error('"%s" is not allowed type !', type);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Some more ideas, do want anything like this ?
|
||||
// - allow only specific scopes (eg. fix(docs) should not be allowed ?
|
||||
// - auto correct the type to lower case ?
|
||||
// - auto correct first letter of the subject to lower case ?
|
||||
// - auto add empty line after subject ?
|
||||
// - auto remove empty () ?
|
||||
// - auto correct typos in type ?
|
||||
// - store incorrect messages, so that we can learn
|
||||
|
||||
return isValid;
|
||||
};
|
||||
|
||||
|
||||
var firstLineFromBuffer = function(buffer) {
|
||||
return buffer.toString().split('\n').shift();
|
||||
};
|
||||
|
||||
|
||||
|
||||
// publish for testing
|
||||
exports.validateMessage = validateMessage;
|
||||
|
||||
// hacky start if not run by jasmine :-D
|
||||
if (process.argv.join('').indexOf('jasmine-node') === -1) {
|
||||
var commitMsgFile = process.argv[2];
|
||||
var incorrectLogFile = commitMsgFile.replace('COMMIT_EDITMSG', 'logs/incorrect-commit-msgs');
|
||||
|
||||
fs.readFile(commitMsgFile, function(err, buffer) {
|
||||
var msg = firstLineFromBuffer(buffer);
|
||||
|
||||
if (!validateMessage(msg)) {
|
||||
fs.appendFile(incorrectLogFile, msg + '\n', function() {
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
describe('validate-commit-msg.js', function() {
|
||||
var m = require('./validate-commit-msg');
|
||||
var errors = [];
|
||||
var logs = [];
|
||||
|
||||
var VALID = true;
|
||||
var INVALID = false;
|
||||
|
||||
beforeEach(function() {
|
||||
errors.length = 0;
|
||||
logs.length = 0;
|
||||
|
||||
spyOn(console, 'error').andCallFake(function(msg) {
|
||||
errors.push(msg.replace(/\x1B\[\d+m/g, '')); // uncolor
|
||||
});
|
||||
|
||||
spyOn(console, 'log').andCallFake(function(msg) {
|
||||
logs.push(msg.replace(/\x1B\[\d+m/g, '')); // uncolor
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateMessage', function() {
|
||||
|
||||
it('should be valid', function() {
|
||||
expect(m.validateMessage('fixup! fix($compile): something')).toBe(VALID);
|
||||
expect(m.validateMessage('fix($compile): something')).toBe(VALID);
|
||||
expect(m.validateMessage('feat($location): something')).toBe(VALID);
|
||||
expect(m.validateMessage('docs($filter): something')).toBe(VALID);
|
||||
expect(m.validateMessage('style($http): something')).toBe(VALID);
|
||||
expect(m.validateMessage('refactor($httpBackend): something')).toBe(VALID);
|
||||
expect(m.validateMessage('test($resource): something')).toBe(VALID);
|
||||
expect(m.validateMessage('chore($controller): something')).toBe(VALID);
|
||||
expect(m.validateMessage('chore(foo-bar): something')).toBe(VALID);
|
||||
expect(m.validateMessage('chore(*): something')).toBe(VALID);
|
||||
expect(m.validateMessage('chore(guide/location): something')).toBe(VALID);
|
||||
expect(m.validateMessage('revert(foo): something')).toBe(VALID);
|
||||
expect(m.validateMessage('i18n(translate): something')).toBe(VALID);
|
||||
expect(m.validateMessage('interface(ui-change): something')).toBe(VALID);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
it('should validate "<type>(<scope>): <subject>" format', function() {
|
||||
var msg = 'not correct format';
|
||||
|
||||
expect(m.validateMessage(msg)).toBe(INVALID);
|
||||
expect(errors).toEqual(['INVALID COMMIT MSG: does not match "<type>(<scope>): <subject>" ! was: not correct format']);
|
||||
});
|
||||
|
||||
|
||||
it('should validate type', function() {
|
||||
expect(m.validateMessage('weird($filter): something')).toBe(INVALID);
|
||||
expect(errors).toEqual(['INVALID COMMIT MSG: "weird" is not allowed type !']);
|
||||
});
|
||||
|
||||
|
||||
it('should allow empty scope', function() {
|
||||
expect(m.validateMessage('fix: blablabla')).toBe(VALID);
|
||||
});
|
||||
|
||||
|
||||
it('should allow dot in scope', function() {
|
||||
expect(m.validateMessage('chore(mocks.$httpBackend): something')).toBe(VALID);
|
||||
});
|
||||
|
||||
|
||||
it('should ignore msg prefixed with "WIP: "', function() {
|
||||
expect(m.validateMessage('WIP: bullshit')).toBe(VALID);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue