* Convert build to use a single JavaScript file on the site (instead of downloading each library separately). This also means that all global variables (e.g., `$` for JQuery, `_` for Underscore.js) can be removed. * Remove unused scripts and/or move them to a more appropriate repo * Update the `deploy` script to work with CircleCI's automation * Update markdown display code to allow images to be located with the markdown data files instead of the pre-rendered index pages
169 lines
5.8 KiB
CoffeeScript
169 lines
5.8 KiB
CoffeeScript
###
|
|
Crafting Guide - stack_controller.coffee
|
|
|
|
Copyright (c) 2014-2015 by Redwood Labs
|
|
All rights reserved.
|
|
###
|
|
|
|
BaseController = require './base_controller'
|
|
ImageLoader = require './image_loader'
|
|
_ = require 'underscore'
|
|
{Duration} = require '../constants'
|
|
{Event} = require '../constants'
|
|
{Key} = require '../constants'
|
|
|
|
########################################################################################################################
|
|
|
|
###
|
|
Events:
|
|
change(this)
|
|
change:quantity(this, oldQuantity, newQuantity)
|
|
button:first(this, type)
|
|
button:second(this, type)
|
|
###
|
|
module.exports = class StackController extends BaseController
|
|
|
|
@MAX_QUANTITY = 9999
|
|
|
|
constructor: (options={})->
|
|
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
|
|
if not options.model? then throw new Error 'options.model is required'
|
|
if not options.modPack? then throw new Error 'options.modPack is required'
|
|
|
|
options.templateName = 'stack'
|
|
super options
|
|
|
|
@editable = options.editable ?= false
|
|
@firstButtonType = options.firstButtonType ?= null
|
|
@imageLoader = options.imageLoader
|
|
@modPack = options.modPack
|
|
@secondButtonType = options.secondButtonType ?= null
|
|
@shouldEnableButton = options.shouldEnableButton ?= (model, button)-> true
|
|
|
|
@modPack.on Event.change, => @tryRefresh()
|
|
|
|
# Event Methods ################################################################################
|
|
|
|
onFirstButtonClicked: (event)->
|
|
@trigger Event.button.first, this, @firstButtonType
|
|
return false
|
|
|
|
onQuantityFieldBlur: ->
|
|
return unless @editable
|
|
|
|
@$bubble.removeClass 'editing'
|
|
|
|
oldQuantity = @_priorValue
|
|
quantityText = @$quantityField.val().trim()
|
|
if quantityText.length is 0
|
|
newQuantity = oldQuantity
|
|
else if not quantityText.match /^[0-9]*$/
|
|
newQuantity = 1
|
|
else
|
|
newQuantity = parseInt quantityText, 10
|
|
if _.isNaN newQuantity then newQuantity = 1
|
|
|
|
newQuantity = Math.min newQuantity, StackController.MAX_QUANTITY
|
|
newQuantity = Math.max 1, newQuantity
|
|
|
|
if newQuantity? and newQuantity isnt oldQuantity
|
|
@$quantityField.val "#{newQuantity}"
|
|
@$bubble.removeClass 'error', Duration.snap
|
|
@$bubble.removeClass 'error-new', Duration.snap
|
|
|
|
@model.quantity = newQuantity
|
|
@trigger Event.change + ':quantity', this, oldQuantity, newQuantity
|
|
@trigger Event.change, this
|
|
|
|
onQuantityFieldChanged: ->
|
|
return unless @editable
|
|
|
|
quantityText = @$quantityField.val().trim()
|
|
if not quantityText.match /^[0-9]*$/
|
|
@$bubble.addClass 'error', 0
|
|
@$bubble.addClass 'error-new', 0
|
|
@$bubble.removeClass 'error-new', Duration.fast
|
|
else
|
|
@$bubble.removeClass 'error', Duration.snap
|
|
@$bubble.removeClass 'error-new', Duration.snap
|
|
|
|
onQuantityFieldFocused: ->
|
|
|
|
if @editable
|
|
@$bubble.addClass 'editing'
|
|
@_priorValue = @model.quantity
|
|
@$quantityField.val ''
|
|
else
|
|
@$quantityField.blur()
|
|
|
|
onQuantityKeyUp: (event)->
|
|
return unless @editable
|
|
|
|
if event.which is Key.Return
|
|
@$quantityField.blur()
|
|
|
|
onSecondButtonClicked: (event)->
|
|
event.preventDefault()
|
|
@trigger Event.button.second, this, @secondButtonType
|
|
|
|
# BaseController Overrides #####################################################################
|
|
|
|
onDidRender: ->
|
|
@$action = @$('.action')
|
|
@$bubble = @$('.bubble')
|
|
@$firstButton = @$('button.first')
|
|
@$image = @$('.icon img')
|
|
@$nameLink = @$('.name a')
|
|
@$quantityField = @$('.quantity input')
|
|
@$secondButton = @$('button.second')
|
|
super
|
|
|
|
refresh: ->
|
|
if not @model? then throw new Error "must have a model to render"
|
|
|
|
display = @modPack.findItemDisplay @model.itemSlug
|
|
|
|
@imageLoader.load display.iconUrl, @$image
|
|
@$nameLink.html display.itemName
|
|
@$nameLink.attr 'href', display.itemUrl
|
|
@$quantityField.val @model.quantity
|
|
|
|
@_updateButtonType @$firstButton, @firstButtonType
|
|
@_updateButtonType @$secondButton, @secondButtonType
|
|
|
|
@$firstButton.prop('disabled', not @shouldEnableButton(@model, 1)) if @firstButtonType?
|
|
@$secondButton.prop('disabled', not @shouldEnableButton(@model, 2)) if @secondButtonType?
|
|
|
|
if @editable
|
|
@$quantityField.removeAttr 'readonly'
|
|
@$bubble.addClass 'editable'
|
|
else
|
|
@$quantityField.attr 'readonly', 'readonly'
|
|
@$bubble.removeClass 'editable'
|
|
|
|
super
|
|
|
|
# Backbone.View Overrides ######################################################################
|
|
|
|
events: ->
|
|
return _.extend super,
|
|
'blur .quantity input': 'onQuantityFieldBlur'
|
|
'click .name a': 'routeLinkClick'
|
|
'click button.first': 'onFirstButtonClicked'
|
|
'click button.second': 'onSecondButtonClicked'
|
|
'focus .quantity input': 'onQuantityFieldFocused'
|
|
'input .quantity input': 'onQuantityFieldChanged'
|
|
'keyup .quantity input': 'onQuantityKeyUp'
|
|
|
|
# Private Methods ##############################################################################
|
|
|
|
_updateButtonType: ($button, type)->
|
|
for currentType in ['check', 'down', 'remove', 'up']
|
|
$button.removeClass currentType
|
|
|
|
if type?
|
|
$button.css 'display', null
|
|
$button.addClass type
|
|
else
|
|
$button.css 'display', 'none'
|