2013-02-06 19:12:05 +00:00
_ = require ' underscore '
2013-02-17 18:58:16 +00:00
moment = require ' moment '
2013-02-06 18:59:56 +00:00
2013-01-20 22:55:45 +00:00
# ##
2013-04-09 17:20:59 +00:00
Loads JavaScript files from public / vendor / *
Use require ( ) to min / concatinate for faster page load
2013-01-20 22:55:45 +00:00
# ##
2013-02-08 07:01:48 +00:00
loadJavaScripts = (model) ->
2013-01-20 22:55:45 +00:00
2013-04-09 17:20:59 +00:00
# Turns out you can't have expressions in browserify require() statements
#vendor = '../../public/vendor'
2013-04-09 17:43:26 +00:00
#require "#{vendor}/jquery-ui-1.10.2/jquery-1.9.1"
2013-04-09 17:20:59 +00:00
# ##
Internal Scripts
# ##
2013-04-09 17:43:26 +00:00
require " ../../public/vendor/jquery-ui-1.10.2/jquery-1.9.1 "
2013-04-09 17:20:59 +00:00
require " ../../public/vendor/jquery.cookie.min "
require " ../../public/vendor/bootstrap/js/bootstrap.min "
require " ../../public/vendor/jquery.bootstrap-growl.min "
require " ../../public/vendor/datepicker/js/bootstrap-datepicker "
require " ../../public/vendor/bootstrap-tour/bootstrap-tour "
2013-04-10 15:49:00 +00:00
unless ( model . get ( ' _mobileDevice ' ) is true )
2013-04-09 17:43:26 +00:00
require " ../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.core "
require " ../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.widget "
require " ../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.mouse "
require " ../../public/vendor/jquery-ui-1.10.2/ui/jquery.ui.sortable "
2013-04-09 17:20:59 +00:00
require " ../../public/vendor/sticky "
2013-04-10 15:49:00 +00:00
# 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)
2013-01-20 22:55:45 +00:00
# ##
Setup jQuery UI Sortable
# ##
2013-02-08 07:01:48 +00:00
setupSortable = (model) ->
2013-04-10 15:49:00 +00:00
unless ( model . get ( ' _mobileDevice ' ) is true ) #don't do sortable on mobile
2013-03-01 23:02:53 +00:00
_ . each [ ' habit ' , ' daily ' , ' todo ' , ' reward ' ] , (type) ->
2013-01-20 22:55:45 +00:00
$ ( " ul. #{ type } s " ) . sortable
dropOnEmpty: false
cursor: " move "
items: " li "
scroll: true
axis: ' y '
update: (e, ui) ->
item = ui . item [ 0 ]
domId = item . id
id = item . getAttribute ' data-id '
to = $ ( " ul. #{ type } s " ) . children ( ) . index ( item )
# Use the Derby ignore option to suppress the normal move event
# binding, since jQuery UI will move the element in the DOM.
# Also, note that refList index arguments can either be an index
# or the item's id property
model . at ( " _ #{ type } List " ) . pass ( ignore: domId ) . move { id } , to
2013-05-02 14:01:11 +00:00
setupTooltips = module.exports.setupTooltips = ->
2013-01-20 22:55:45 +00:00
$ ( ' [rel=tooltip] ' ) . tooltip ( )
$ ( ' [rel=popover] ' ) . popover ( )
2013-05-02 14:01:11 +00:00
$ ( ' .popover-auto-show ' ) . popover ( ' show ' )
2013-01-20 22:55:45 +00:00
2013-03-05 23:58:15 +00:00
$ ( ' .priority-multiplier-help ' ) . popover
title: " How difficult is this task? "
trigger: " hover "
content: " This multiplies its point value. Use sparingly, rely instead on our organic value-adjustment algorithms. But some tasks are grossly more valuable (Write Thesis vs Floss Teeth). Click for more info. "
2013-02-08 07:01:48 +00:00
setupTour = (model) ->
2013-02-08 06:26:17 +00:00
tourSteps = [
{
2013-03-23 01:11:29 +00:00
element: " .main-herobox "
2013-02-08 06:26:17 +00:00
title: " Welcome to HabitRPG "
content: " Welcome to HabitRPG, a habit-tracker which treats your goals like a Role Playing Game. "
}
{
element: " # bars "
title: " Achieve goals and level up "
content: " As you accomplish goals, you level up. If you fail your goals, you lose hit points. Lose all your HP and you die. "
}
{
element: " ul.habits "
title: " Habits "
content: " Habits are goals that you constantly track. "
placement: " bottom "
}
{
element: " ul.dailys "
title: " Dailies "
content: " Dailies are goals that you want to complete once a day. "
placement: " bottom "
}
{
element: " ul.todos "
title: " Todos "
content: " Todos are one-off goals which need to be completed eventually. "
placement: " bottom "
}
{
element: " ul.rewards "
title: " Rewards "
content: " As you complete goals, you earn gold to buy rewards. Buy them liberally - rewards are integral in forming good habits. "
placement: " bottom "
}
{
element: " ul.habits li:first-child "
title: " Hover over comments "
content: " Different task-types have special properties. Hover over each task ' s comment for more information. When you ' re ready to get started, delete the existing tasks and add your own. "
placement: " right "
}
]
2013-03-26 19:54:34 +00:00
$ ( ' .main-herobox ' ) . popover ( ' destroy ' ) #remove previous popovers
2013-01-20 22:55:45 +00:00
tour = new Tour ( )
2013-02-08 23:37:26 +00:00
_ . each tourSteps , (step) ->
2013-03-04 17:47:33 +00:00
tour . addStep _ . defaults step , { html : true }
tour._current = 0 if isNaN ( tour . _current ) #bootstrap-tour bug
2013-01-20 22:55:45 +00:00
tour . start ( )
2013-03-18 20:48:47 +00:00
# jquery sticky header on scroll, no need for position fixed
initStickyHeader = (model) ->
2013-03-19 03:42:03 +00:00
$ ( ' .header-wrap ' ) . sticky ( { topSpacing : 0 } )
2013-03-18 20:48:47 +00:00
2013-01-20 21:52:56 +00:00
# ##
Sets up " +1 Exp " , " Level Up " , etc notifications
# ##
2013-02-08 07:01:48 +00:00
setupGrowlNotifications = (model) ->
2013-01-20 21:52:56 +00:00
return unless jQuery ? # Only run this in the browser
user = model . at ' _user '
statsNotification = (html, type) ->
#don't show notifications if user dead
2013-02-05 03:35:07 +00:00
return if user . get ( ' stats.lvl ' ) == 0
2013-01-20 21:52:56 +00:00
$ . bootstrapGrowl html ,
2013-02-16 17:02:55 +00:00
ele: ' # notification-area ' ,
2013-03-02 20:02:57 +00:00
type: type # (null, 'info', 'error', 'success', 'gp', 'xp', 'hp', 'lvl','death')
2013-01-20 21:52:56 +00:00
top_offset: 20
align: ' right ' # ('left', 'right', or 'center')
width: 250 # (integer, or 'auto')
delay: 3000
allow_dismiss: true
stackup_spacing: 10 # spacing between consecutive stacecked growls.
# Setup listeners which trigger notifications
2013-02-05 03:35:07 +00:00
user . on ' set ' , ' stats.hp ' , (captures, args) ->
2013-01-20 21:52:56 +00:00
num = captures - args
rounded = Math . abs ( num . toFixed ( 1 ) )
if num < 0
2013-02-28 23:09:12 +00:00
statsNotification " <i class= ' icon-heart ' ></i> - #{ rounded } HP " , ' hp ' # lost hp from purchase
else if num > 0
statsNotification " <i class= ' icon-heart ' ></i> + #{ rounded } HP " , ' hp ' # gained hp from potion/level?
2013-03-06 17:02:08 +00:00
user . on ' set ' , ' stats.exp ' , (captures, args, isLocal, silent=false) ->
# unless silent
num = captures - args
rounded = Math . abs ( num . toFixed ( 1 ) )
2013-03-08 00:10:11 +00:00
if num < 0 and num > - 50 # TODO fix hackey negative notification supress
2013-03-06 17:02:08 +00:00
statsNotification " <i class= ' icon-star ' ></i> - #{ rounded } XP " , ' xp '
else if num > 0
statsNotification " <i class= ' icon-star ' ></i> + #{ rounded } XP " , ' xp '
2013-02-28 23:09:12 +00:00
2013-05-04 09:11:06 +00:00
# ##
Show " + 5 {gold_coin} 3 {silver_coin} "
# ##
showCoins = (money) ->
absolute = Math . abs ( money )
2013-02-28 23:09:12 +00:00
gold = Math . floor ( absolute )
silver = Math . floor ( ( absolute - gold ) * 100 )
if gold and silver > 0
2013-05-04 09:11:06 +00:00
return " #{ gold } <i class= ' icon-gold ' ></i> #{ silver } <i class= ' icon-silver ' ></i> "
2013-02-28 23:09:12 +00:00
else if gold > 0
2013-05-04 09:11:06 +00:00
return " #{ gold } <i class= ' icon-gold ' ></i> "
2013-02-28 23:09:12 +00:00
else if silver > 0
2013-05-04 09:11:06 +00:00
return " #{ silver } <i class= ' icon-silver ' ></i> "
user . on ' set ' , ' stats.gp ' , (captures, args) ->
money = captures - args
sign = if money < 0 then ' - ' else ' + '
statsNotification " #{ sign } #{ showCoins ( money ) } " , ' gp '
# Append Bonus
bonus = model . get ( ' _streakBonus ' )
if money > 0 and bonus
statsNotification " #{ showCoins ( bonus ) } Streak Bonus! "
model . del ( ' _streakBonus ' )
2013-01-20 21:52:56 +00:00
2013-02-05 03:35:07 +00:00
user . on ' set ' , ' stats.lvl ' , (captures, args) ->
2013-01-20 21:52:56 +00:00
if captures > args
2013-03-02 20:02:57 +00:00
if captures is 1 and args is 0
2013-03-06 17:02:08 +00:00
statsNotification ' <i class= " icon-death " ></i> You died! Game over. ' , ' death '
2013-03-02 20:02:57 +00:00
else
2013-03-02 20:12:18 +00:00
statsNotification ' <i class= " icon-chevron-up " ></i> Level Up! ' , ' lvl '
2013-03-04 16:17:17 +00:00
module.exports.resetDom = (model) ->
2013-03-17 00:02:01 +00:00
DERBY . app . dom . clear ( )
DERBY . app . view . render ( model , DERBY . app . view . _lastRender . ns , DERBY . app . view . _lastRender . context ) ;
2013-03-04 16:17:17 +00:00
2013-03-31 01:45:57 +00:00
# Note, Google Analyatics giving beef if in this file. Moved back to index.html. It's ok, it's async - really the
# syncronous requires up top are what benefit the most from this file.
2013-05-01 12:07:42 +00:00
googleAnalytics = (model) ->
if model . flags . nodeEnv is ' production '
window . _gaq = [ [ " _setAccount " , " UA-33510635-1 " ] , [ " _setDomainName " , " habitrpg.com " ] , [ " _trackPageview " ] ]
$ . getScript ( ( if " https: " is document . location . protocol then " https://ssl " else " http://www " ) ) + " .google-analytics.com/ga.js "
amazonAffiliate = (model) ->
if model . get ( ' _loggedIn ' ) and ( model . get ( ' _user.flags.ads ' ) != ' hide ' )
$ . getScript ( ' //wms.assoc-amazon.com/20070822/US/js/link-enhancer-common.js?tag=ha0d2-20 ' ) . fail ->
$ ( ' body ' ) . append ( ' <img src= " //wms.assoc-amazon.com/20070822/US/img/noscript.gif?tag=ha0d2-20 " alt= " " /> ' )
googleCharts = ->
$ . getScript " //www.google.com/jsapi " , ->
# Specifying callback in options param is vital! Otherwise you get blank screen, see http://stackoverflow.com/a/12200566/362790
google . load " visualization " , " 1 " , { packages : [ " corechart " ] , callback: -> }
2013-03-04 16:17:17 +00:00
module.exports.app = (appExports, model, app) ->
loadJavaScripts ( model )
2013-04-08 00:29:13 +00:00
setupGrowlNotifications ( model ) unless model . get ( ' _mobileDevice ' )
2013-03-04 16:17:17 +00:00
app . on ' render ' , (ctx) ->
#restoreRefs(model)
setupSortable ( model )
setupTooltips ( model )
2013-03-05 19:02:46 +00:00
setupTour ( model )
2013-04-08 00:29:13 +00:00
initStickyHeader ( model ) unless model . get ( ' _mobileDevice ' )
2013-03-04 16:17:17 +00:00
$ ( ' .datepicker ' ) . datepicker ( { autoclose : true , todayBtn : true } )
. on ' changeDate ' , (ev) ->
2013-03-31 01:45:57 +00:00
#for some reason selecting a date doesn't fire a change event on the field, meaning our changes aren't saved
2013-04-18 02:51:43 +00:00
model . at ( ev . target ) . set ' date ' , moment ( ev . date ) . format ( ' MM/DD/YYYY ' )
2013-04-10 15:49:00 +00:00
# ##
External Scripts
JS files not needed right away ( google charts ) or entirely optional ( analytics )
Each file getsload asyncronously via $ . getScript , so it doesn ' t bog page-load
These need to be handled in app . on ( ' render ' ) , see https : / / groups . google . com / forum / ? fromgroups = #!topic/derbyjs/x8FwdTLEuXo
# ##
$ . getScript ( ' //checkout.stripe.com/v2/checkout.js ' )
unless ( model . get ( ' _mobileDevice ' ) is true )
$ . getScript ( " //s7.addthis.com/js/250/addthis_widget.js # pubid=lefnire " )
2013-05-01 12:07:42 +00:00
googleCharts ( )
googleAnalytics ( model )
amazonAffiliate ( model )