Restructure static files into a single directory

This commit is contained in:
Andrew Miner
2015-03-08 15:05:24 -07:00
parent 997b37ab7d
commit 0a6c185965
5053 changed files with 383 additions and 175 deletions
@@ -0,0 +1,124 @@
###
Crafting Guide - base_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
views = require '../views'
########################################################################################################################
module.exports = class BaseController extends Backbone.View
constructor: (options={})->
@tryRefresh = _.debounce @_tryRefresh, 100
Object.defineProperty this, 'model', get:@getModel, set:@setModel
@_model = null
@_rendered = false
@_parent = options.parent
@_children = []
Object.defineProperty this, 'rendered', get:-> return @_rendered
@_loadTemplate options.templateName
super options
# Public Methods ###############################################################################
addChild: (Controller, atSelector, options={})->
options.el = @$(atSelector)[0]
options.parent = this
child = new Controller options
child.render()
@_children.push child
return child
refresh: ->
logger.verbose => "#{this} refreshing"
routeLinkClick: (event)->
href = $(event.currentTarget).attr 'href'
router.navigate href, trigger:true
return false
# Event Methods ################################################################################
onDidModelChange: ->
@tryRefresh()
onDidModelSync: ->
@tryRefresh()
onWillRender: -> # do nothing
onDidRender: ->
@tryRefresh()
onWillShow: -> # do nothing
onDidShow: -> # do nothing
onWillChangeModel: (oldModel, newModel)->
if oldModel?.on?
@stopListening oldModel
if newModel?.on?
@listenTo newModel, 'sync', (e)=> @onDidModelSync e
@listenTo newModel, 'change', (e)=> @onDidModelChange e
return true
# Property Methods #############################################################################
getModel: ->
return @_model
setModel: (newModel)->
return if @model is newModel
return unless @onWillChangeModel @_model, newModel
@_model = newModel
@tryRefresh()
# Backbone.View Overrides ######################################################################
events: ->
return {}
render: (options={})->
return this unless not @_rendered or options.force
data = (@model?.toHash? and @model.toHash()) or @model or {}
if not @_template?
logger.error => "Default render called for #{@constructor.name} without a template"
return this
logger.verbose => "#{this} rendering with data: #{data}"
@onWillRender()
$oldEl = @$el
$newEl = Backbone.$(@_template(data))
if $oldEl
$oldEl.replaceWith $newEl
$newEl.addClass $oldEl.attr 'class'
@setElement $newEl
@_rendered = true
@onDidRender()
return this
# Object Overrides #############################################################################
toString: ->
return "#{@constructor.name}.#{@cid}"
# Private Methods ##############################################################################
_loadTemplate: (templateName)->
if templateName?
@_template = views[templateName]
_tryRefresh: ->
return unless @_rendered
@refresh()
@@ -0,0 +1,58 @@
###
Crafting Guide - browse_page_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
PageController = require './page_controller'
{Duration} = require '../constants'
{Event} = require '../constants'
ModController = require './mod_controller'
########################################################################################################################
module.exports = class BrowsePageController extends PageController
constructor: (options={})->
if not options.modPack? then throw new Error 'options.modPack is required'
options.templateName = 'browse_page'
super options
@modPack = options.modPack
@modPack.on Event.change, => @tryRefresh()
# PageController Overrides #####################################################################
getTitle: ->
return 'Browse'
# BaseController Overrides #####################################################################
onDidRender: ->
@$modContainer = @$('.mods')
super
refresh: ->
@_controllers ?= []
controllerIndex = 0
@modPack.eachMod (mod)=>
return unless mod.enabled
controller = @_controllers[controllerIndex]
if not controller?
controller = new ModController model:mod
controller.render()
@$modContainer.append controller.$el
else
controller.model = mod
controllerIndex += 1
while @_controllers.length > controllerIndex
controller = @_controllers.pop()
controller.$el.addClass 'removing'
controller.$el.one Event.transitionEnd -> controller.$el.remove()
super
@@ -0,0 +1,33 @@
###
Crafting Guide - configure_page_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
PageController = require './page_controller'
ModPackController = require './mod_pack_controller'
########################################################################################################################
module.exports = class ConfigurePageController extends PageController
constructor: (options={})->
if not options.modPack? then throw new Error 'options.modPack is required'
if not options.storage? then throw new Error 'options.storage is required'
options.templateName = 'configure_page'
super options
@modPack = options.modPack
@storage = options.storage
# PageController Overrides #####################################################################
getTitle: ->
return "Configure"
# BaseController Overrides #####################################################################
onDidRender: ->
@modPackController = @addChild ModPackController, '.view__mod_pack', model:@modPack, storage:@storage
super
@@ -0,0 +1,113 @@
###
Crafting Guide - craft_page_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
CraftingTableController = require './crafting_table_controller'
CraftPage = require '../models/craft_page'
{Event} = require '../constants'
ImageLoader = require './image_loader'
InventoryController = require './inventory_controller'
ModPackController = require './mod_pack_controller'
NameFinder = require '../models/name_finder'
PageController = require './page_controller'
Storage = require '../models/storage'
{Text} = require '../constants'
{Url} = require '../constants'
########################################################################################################################
module.exports = class CraftPageController extends PageController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.model ?= new CraftPage modPack:options.modPack
options.storage ?= new Storage storage:window.localStorage
options.templateName = 'craft_page'
super options
@imageLoader = options.imageLoader
@storage = options.storage
# Event Methods ################################################################################
onToolsBoxToggled: ->
@model.plan.includingTools = @$('.includeTools:checked').length isnt 0
# PageController Overrides #####################################################################
getTitle: ->
return 'Craft'
# BaseController Overrides #####################################################################
onWillRender: ->
@storage.register 'crafting-plan', @model.plan, 'includingTools'
@model.plan.have.clear()
@model.plan.have.parse @storage.load('crafting-plan:have')
super
onDidRender: ->
@wantController = @addChild InventoryController, '.want',
editable: true
icon: '/images/fishing_rod.png'
imageLoader: @imageLoader
model: @model.plan.want
modPack: @model.modPack
onChange: => @_updateLocation()
title: 'Items you want'
@haveController = @addChild InventoryController, '.have',
editable: true,
imageLoader: @imageLoader
model: @model.plan.have
modPack: @model.modPack
onChange: => @_saveHaveInventory()
nameFinder: new NameFinder @model.modPack, includeGatherable:true
title: 'Items you have'
@needController = @addChild InventoryController, '.need',
editable: false
icon: '/images/boots.png'
imageLoader: @imageLoader
model: @model.plan.need
modPack: @model.modPack
title: "Items you'll need"
@craftingTableController = @addChild CraftingTableController, '.view__crafting_table',
imageLoader: @imageLoader
model: @model.table
modPack: @model.modPack
@$('.want .toolbar').append '<label><input class="includeTools" type="checkbox"> include tools</label>'
@$includeToolsBox = @$('.includeTools')
super
refresh: ->
if @model.plan.includingTools
@$includeToolsBox.attr 'checked', 'checked'
else
@$includeToolsBox.removeAttr 'checked'
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'change .includeTools': 'onToolsBoxToggled'
# Private Methods ##############################################################################
_saveHaveInventory: ->
@storage.store 'crafting-plan:have', @model.plan.have.unparse()
_updateLocation: ->
text = @model.plan.want.unparse()
url = Url.crafting inventoryText:text
router.navigate url
@@ -0,0 +1,74 @@
###
Crafting Guide - crafting_grid_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
{Event} = require '../constants'
ImageLoader = require './image_loader'
########################################################################################################################
module.exports = class CraftingGridController extends BaseController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.templateName = 'crafting_grid'
super options
@_imageLoader = options.imageLoader
@_modPack = options.modPack
@_slotCount = 9
@_modPack.on Event.change, => @tryRefresh()
# BaseController Methods #######################################################################
onDidRender: ->
@slots = []
for el in @$('td')
$el = $(el)
@slots.push a:$el.find('a'), img:$el.find('img')
super
refresh: ->
for index in [0...@slots.length]
slot = @slots[index]
slot.a.addClass 'empty'
slot.a.removeAttr 'href'
slot.img.attr 'src', '/images/empty.png'
slot.img.removeAttr 'alt'
display = @_getItemDisplayAt index
if display?
slot.a.removeClass 'empty'
slot.a.attr 'href', display.itemUrl
slot.a.attr 'title', display.itemName
@_imageLoader.load display.iconUrl, slot.img
slot.img.attr 'alt', display.itemName
@$el.tooltip show:{delay:Duration.snap, duration:Duration.fast}
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click td a': 'routeLinkClick'
# Private Methods ##############################################################################
_getItemDisplayAt: (slot)->
if slot >= @_slotCount then throw new Error "slot (#{slot}) must be less than #{@_slotCount}"
return null unless @model?
itemSlug = @model.getItemSlugAt slot
return null unless itemSlug?
itemDisplay = @_modPack.findItemDisplay itemSlug
return itemDisplay
@@ -0,0 +1,94 @@
###
Crafting Guide - crafting_table_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
{Event} = require '../constants'
ImageLoader = require './image_loader'
MinimalRecipeController = require './minimal_recipe_controller'
########################################################################################################################
module.exports = class CraftingTableController extends BaseController
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 = 'crafting_table'
super options
@imageLoader = options.imageLoader
@modPack = options.modPack
# Event Methods ################################################################################
onNextClicked: ->
@model.stepIndex += 1
onPrevClicked: ->
@model.stepIndex -= 1
onReportProblem: ->
itemList = @model.plan.want.unparse()
toolsMessage = if @model.plan.includingTools then '(including tools)' else ''
message = "When I was on step #{@model.stepIndex + 1} of making:
\n\n#{itemList}#{toolsMessage}\n\nI noticed that...\n"
global.feedbackController.enterFeedback message
# BaseController Overrides #####################################################################
onDidRender: ->
@recipeController = @addChild MinimalRecipeController, '.view__minimal_recipe',
imageLoader: @imageLoader
modPack: @modPack
@$next = @$('.next')
@$prev = @$('.prev')
@$problemControl = @$('.problem')
@$title = @$('h2 p')
@$tool = @$('.tool p')
@$multiplier = $('<p class="multiplier"></p>')
@$('.output').append @$multiplier
@defaultTitle = @$title.html()
super
refresh: ->
@$prev.removeClass 'enabled'
@$next.removeClass 'enabled'
if @model.hasSteps
if @model.hasPrevStep then @$prev.addClass 'enabled'
if @model.hasNextStep then @$next.addClass 'enabled'
@$title.html "Step #{@model.stepIndex + 1} of #{@model.stepCount}"
else
@$title.html @defaultTitle
currentStep = @model.currentStep
@recipeController.model = currentStep?.recipe
if currentStep?.multiplier > 1
@$multiplier.html "×#{currentStep.multiplier}"
else
@$multiplier.html ''
if not (@model.hasSteps and global.feedbackController?)
@$problemControl.hide duration:Duration.snap
else
@$problemControl.show duration:Duration.snap
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click .next': 'onNextClicked'
'click .prev': 'onPrevClicked'
'click .problem a': 'onReportProblem'
@@ -0,0 +1,112 @@
###
Crafting Guide - feedback_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
EmailClient = require '../models/email_client'
{Key} = require '../constants'
########################################################################################################################
module.exports = class FeedbackController extends BaseController
constructor: (options={})->
options.model ?= new EmailClient
options.templateName = 'feedback'
super options
Object.defineProperty this, 'isOpen', get:-> @$el.offset().left is 0
# Public Methods ###############################################################################
enterFeedback: (message)->
promise = w(true)
if not @isOpen then promise = @onToggle()
promise.then => @$commentField.val message
# Event Methods ################################################################################
onSendClicked: ->
return unless @$commentField.val().length > 0
@$sendButton.attr 'disabled', 'disabled'
message =
subject:'Crafting Guide Feedback'
body:
"""
url: #{window.location.href}
name: #{@$nameField.val()}
email: #{@$emailField.val()}
comment:
#{@$commentField.val()}
"""
@model.send(message)
.then =>
@onToggle()
@$error.slideUp duration:Duration.normal
.catch (error)=>
@$error.slideDown duration:Duration.normal
.finally =>
@$sendButton.removeAttr 'disabled'
onTextChanged: (event)->
if @$commentField.val().length > 0
@$sendButton.removeAttr 'disabled'
else
@$sendButton.attr 'disabled', 'disabled'
onToggle: ->
w.promise (resolve)=>
@$screen.off 'click'
if @isOpen
@$screen.css display:'none'
@$el.animate {left:-@$el.outerWidth()}, duration:Duration.fast, complete:=>
@$commentField.val ''
@$('input, textarea').blur()
resolve()
else
@$screen.on 'click', => @onToggle()
@$screen.css display:'block'
@$el.animate {left:0}, duration:Duration.fast, complete:=>
if @$nameField.val().length is 0
@$nameField.focus()
else if @$emailField.val().length is 0
@$emailField.focus()
else
@$commentField.focus()
resolve()
# BaseController Overrides #####################################################################
onDidRender: ->
@$screen = $('.view__screen')
@$commentField = @$('textarea[name="comment"]')
@$emailField = @$('input[name="email"]')
@$error = @$('.error')
@$nameField = @$('input[name="name"]')
@$sendButton = @$('button[name="send"]')
windowHeight = window.innerHeight
@$el.delay(Duration.slow).animate {left:-@$el.outerWidth()}, Duration.normal
@onTextChanged()
super
# Backbone.View Methods ########################################################################
events: ->
return _.extend super,
'click .label': 'onToggle'
'click button': 'onSendClicked'
'input textarea': 'onTextChanged'
'keyup textarea': 'onTextChanged'
@@ -0,0 +1,95 @@
###
Crafting Guide - full_recipe_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
CraftingGridController = require './crafting_grid_controller'
{Duration} = require '../constants'
ImageLoader = require './image_loader'
Inventory = require '../models/inventory'
InventoryTableController = require './inventory_table_controller'
StringBuilder = require '../models/string_builder'
########################################################################################################################
module.exports = class FullRecipeController extends BaseController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.templateName = 'full_recipe'
super options
@imageLoader = options.imageLoader
@modPack = options.modPack
# BaseController Overrides #####################################################################
onDidRender: ->
@gridController = @addChild CraftingGridController, '.view__crafting_grid',
modPack: @modPack
imageLoader: @imageLoader
@inputController = @addChild InventoryTableController, '.input .view__inventory_table',
editable: false
imageLoader: @imageLoader
model: new Inventory
modPack: @modPack
@outputController = @addChild InventoryTableController, '.output .view__inventory_table',
editable: false
imageLoader: @imageLoader
model: new Inventory
modPack: @modPack
@$toolContainer = @$('.tool')
super
refresh: ->
@gridController.model = @model
@$el.tooltip show:{delay:Duration.normal, duration:Duration.normal}
@_refreshInputs()
@_refreshOutputs()
@_refreshTools()
super
# Backbone.View Methods ########################################################################
events: ->
return _.extend super,
'click a': 'routeLinkClick'
# Private Methods ##############################################################################
_refreshInputs: ->
inputs = @inputController.model
inputs.clear()
if @model?
for stack in @model.input
inputs.add stack.itemSlug, stack.quantity
_refreshOutputs: ->
outputs = @outputController.model
outputs.clear()
if @model?
for stack in @model.output
outputs.add stack.itemSlug, stack.quantity
_refreshTools: ->
@$toolContainer.empty()
return unless @model?
builder = new StringBuilder
builder.loop @model.tools, delimiter:', ', onEach:(b, stack)=>
display = @modPack.findItemDisplay stack.itemSlug
b.push "<a href=\"#{display.itemUrl}\">#{display.itemName}</a>"
@$toolContainer.html builder.toString()
@@ -0,0 +1,60 @@
###
Crafting Guide - header_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
########################################################################################################################
module.exports = class HeaderController extends BaseController
constructor: (options={})->
super options
# Event Methods ################################################################################
onLogoClicked: ->
router.navigate '/', trigger:true
return false
onNavItemClicked: (event)->
return true unless $(event.currentTarget).attr('href')?
router.navigate $(event.currentTarget).attr('href'), trigger:true
return false
# BaseController Overrides #####################################################################
render: ->
# Overriding render because the header is already part of the stock page layout, and we
# don't need to replace it here.
@_rendered = true
@$navLinks = ($(el) for el in @$('.navBar a'))
zIndex = @$navLinks.length + 100
for $navLink in @$navLinks
$navLink.css 'z-index', zIndex--
refresh: ->
for $navLink in @$navLinks
linkPage = $navLink.data('page')
if @model is linkPage
$navLink.addClass 'selected'
else
$navLink.removeClass 'selected'
if @model.indexOf(linkPage) isnt -1
$navLink.find('.dot').css 'opacity': 1;
else
$navLink.find('.dot').css 'opacity': 0;
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click a.logo': 'onLogoClicked'
'click .navBar a': 'onNavItemClicked'
@@ -0,0 +1,22 @@
###
Crafting Guide - home_page_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
PageController = require './page_controller'
########################################################################################################################
module.exports = class HomeController extends PageController
constructor: (options={})->
options.templateName = 'home_page'
super options
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click a': 'routeLinkClick'
+105
View File
@@ -0,0 +1,105 @@
###
Crafting Guide - image_loader.coffee
Copyright (c) 2014 by Redwood Labs
All rights reserved.
###
{Duration} = require '../constants'
########################################################################################################################
module.exports = class ImageLoader
constructor: (options={})->
options.defaultUrl ?= null
if not options.defaultUrl?
options.onLoading ?= -> @hide()
options.onLoad ?= -> @show()
@defaultUrl = options.defaultUrl
@onLoading = options.onLoading
@onLoad = options.onLoad
@_images = {}
if @defaultUrl
@_defaultImage = new Image
@_defaultImage.src = @defaultUrl
# Class Methods ################################################################################
@load: (imageUrl, $el, options={}) ->
loader = new ImageLoader options
loader.load $el, options
return loader
# Public Methods ###############################################################################
isLoaded: (imageUrl)->
data = @_images[imageUrl]
return false unless data?
return data.isLoaded
load: (imageUrl, $el)->
return if not $el
data = @preload imageUrl
if $el.attr('src')?
return if $el.attr('src').indexOf(imageUrl) isnt -1
if @onLoading? then @onLoading.call $el
$el.data 'isLoading', true
$el.data 'isLoaded', false
if data.isLoaded
@_loadImageIntoElement data.imageUrl, $el
else
if @defaultUrl?
$el.attr 'src', @defaultUrl
else
$el.removeAttr 'src'
if data.elements.indexOf($el) is -1 then data.elements.push $el
return this
preload: (imageUrl)->
data = @_images[imageUrl]
if not data?
data = imageUrl:imageUrl, elements:[], image:new Image, isLoaded:false
@_images[imageUrl] = data
data.image = new Image
data.image.onload = => @_onImageLoaded data
data.image.onerror = => @_onImageError data
data.image.src = imageUrl
return data
# Private Methods ##############################################################################
_onImageLoaded: (data)->
for $el in data.elements
@_loadImageIntoElement data.imageUrl, $el
data.elements = []
data.isLoaded = true
_onImageError: (data)->
if @defaultUrl?
data.imageUrl = @defaultUrl
for $el in data.elements
@_loadImageIntoElement @defaultUrl, $el
data.elements = []
data.isLoaded = true
_loadImageIntoElement: (imageUrl, $el)->
$el.data 'isLoading', false
$el.data 'isLoaded', true
$el.attr 'src', imageUrl
if @onLoad? then @onLoad.call $el
@@ -0,0 +1,181 @@
###
Crafting Guide - inventory_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
{Event} = require '../constants'
{Key} = require '../constants'
ImageLoader = require './image_loader'
NameFinder = require '../models/name_finder'
StackController = require './stack_controller'
########################################################################################################################
module.exports = class InventoryController extends BaseController
@MAX_QUANTITY = 9999
@ONLY_DIGITS = /^[0-9]*$/
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'
@editable = options.editable ?= true
@icon = options.icon ?= '/images/chest_front.png'
@imageLoader = options.imageLoader
@modPack = options.modPack
@nameFinder = options.nameFinder ?= new NameFinder options.modPack
@onChange = options.onChange ?= -> # do nothing
@title = options.title ?= 'Inventory'
options.templateName = 'inventory'
super options
@_stackControllers = []
@listenTo @modPack, Event.change, => @refresh()
# Event Methods ################################################################################
onAddButtonClicked: ->
if @$nameField.val().trim().length is 0
@$nameField.focus()
return
item = @modPack.findItemByName @$nameField.val()
return unless item?
@model.add item.slug, 1
@$nameField.val ''
@$scrollbox.scrollTop @$scrollbox.prop 'scrollHeight'
@$nameField.autocomplete 'close'
@onChange()
onClearButtonClicked: ->
@model.clear()
@onChange()
onItemSelected: ->
func = =>
@onNameFieldChanged()
@onAddButtonClicked()
@$nameField.blur()
setTimeout func, 10 # needed to allow the autocomplete to finish
return true
onNameFieldBlur: ->
item = @modPack.findItemByName @$nameField.val()
@$nameField.val if item? then item.name else ''
@onNameFieldChanged()
onNameFieldChanged: ->
@_refreshButtonState()
onNameFieldFocused: ->
@$nameField.val ''
@$nameField.autocomplete 'search'
onNameFieldKeyUp: (event)->
if event.which is Key.Return
@onAddButtonClicked()
# BaseController Overrides #####################################################################
onDidRender: ->
@$addButton = @$('button[name="add"]')
@$clearButton = @$('button[name="clear"]')
@$icon = @$('.icon')
@$editPanel = @$('.edit')
@$nameField = @$('input[name="name"]')
@$scrollbox = @$('.scrollbox')
@$table = @$('table')
@$toolbar = @$('.toolbar')
@$title = @$('h2 p')
super
refresh: ->
@$editPanel.css display:(if @editable then 'table-row' else 'none')
@$toolbar.css display:(if @editable then 'block' else 'none')
@$scrollbox.css bottom:(if @editable then @$toolbar.height() else '0')
@$icon.attr 'src', @icon
@$title.html @title
@_refreshStacks()
@_refreshNameAutocomplete()
@_refreshButtonState()
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'blur input[name="name"]': 'onNameFieldBlur'
'click button[name="add"]': 'onAddButtonClicked'
'click button[name="clear"]': 'onClearButtonClicked'
'focus input[name="name"]': 'onNameFieldFocused'
'input input[name="name"]': 'onNameFieldChanged'
'keyup input[name="name"]': 'onNameFieldKeyUp'
# Private Methods ##############################################################################
_refreshButtonState: ->
if @model.isEmpty then @$clearButton.attr('disabled', 'disabled') else @$clearButton.removeAttr('disabled')
noText = @$nameField.val().trim().length is 0
itemValid = @modPack.findItemByName(@$nameField.val())?
disable = not (itemValid or noText)
if disable then @$addButton.attr('disabled', 'disabled') else @$addButton.removeAttr('disabled')
_refreshNameAutocomplete: ->
onChanged = => @onNameFieldChanged()
onSelected = => @onItemSelected()
@$nameField.autocomplete
source: (request, callback)=> callback @nameFinder.search request.term
delay: 0
minLength: 0
change: onChanged
close: onChanged
select: onSelected
_refreshStacks: ->
@_stackControllers ?= []
index = 0
$lastRow = @$table.find 'tr:last-child'
@model.each (stack)=>
controller = @_stackControllers[index]
if not controller?
controller = new StackController
editable: @editable
imageLoader: @imageLoader
model: stack
modPack: @modPack
onChange: @onChange
onRemove: if not @editable then null else (stack)=> @_removeStack(stack)
controller.render()
controller.$el.hide()
controller.$el.insertBefore $lastRow
controller.$el.slideDown duration:Duration.fast
@_stackControllers.push controller
else
controller.model = stack
index += 1
while @_stackControllers.length > index
controller = @_stackControllers.pop()
controller.$el.fadeOut duration:Duration.fast, complete:-> @remove()
_removeStack: (stack)->
@model.remove stack.itemSlug, stack.quantity
@@ -0,0 +1,184 @@
###
Crafting Guide - inventory_table_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
ImageLoader = require './image_loader'
ItemSlug = require '../models/item_slug'
{Key} = require '../constants'
NameFinder = require '../models/name_finder'
StackController = require './stack_controller'
########################################################################################################################
module.exports = class InventoryTableController extends BaseController
@ONLY_DIGITS = /^[0-9]*$/
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'
@editable = options.editable ?= true
@imageLoader = options.imageLoader
@modPack = options.modPack
@nameFinder = options.nameFinder ?= new NameFinder options.modPack
@onChange = options.onChange ?= -> # do nothing
options.templateName = 'inventory_table'
super options
@_stackControllers = []
@listenTo @modPack, 'change', => @refresh()
# Event Methods ################################################################################
onAddButtonClicked: ->
name = @modpack.findItemByName @$nameField.val()
return unless item?
@model.add item.slug, parseInt(@$quantityField.val())
@$nameField.val ''
@$quantityField.val '1'
@$scrollbox.scrollTop @$scrollbox.prop 'scrollHeight'
@$nameField.autocomplete 'close'
@onChange()
onClearButtonClicked: ->
@model.clear()
@onChange()
onItemSelected: ->
func = =>
@onNameFieldChanged()
@onAddButtonClicked()
@$nameField.blur()
setTimeout func, 10 # needed to allow the autocomplete to finish
return true
onNameFieldBlur: ->
item = @modPack.findItemByName @$nameField.val()
@$nameField.val if item? then item.name else ''
@onNameFieldChanged()
onNameFieldChanged: ->
item = @modPack.findItemByName @$nameField.val()
@_updateButtonState()
onNameFieldFocused: ->
@$nameField.val ''
@$nameField.autocomplete('search')
onNameFieldKeyUp: (event)->
if event.which is Key.Return
@onAddButtonClicked()
onQuantityFieldBlur: ->
value = @$quantityField.val().replace /[^0-9]/g, ''
if value.length is 0 then value = '1'
value = Math.min value, 64
@$quantityField.val value
@onQuantityFieldChanged()
onQuantityFieldChanged: ->
if not @$quantityField.val().match /^[0-9]*$/
@$quantityField.addClass 'error', 0
@$quantityField.addClass 'error-new', 0
@$quantityField.removeClass 'error-new', Duration.slow
@$quantityField.focus()
return
@$quantityField.removeClass 'error', Duration.normal
@$quantityField.removeClass 'error-new', Duration.normal
@_updateButtonState()
onQuantityFieldFocused: ->
@$quantityField.val ''
# BaseController Overrides #####################################################################
onDidRender: ->
@$addButton = @$('button[name="add"]')
@$clearButton = @$('button[name="clear"]')
@$editPanel = @$('.edit')
@$nameField = @$('input[name="name"]')
@$quantityField = @$('input[name="quantity"]')
@$scrollbox = @$('.scrollbox')
@$table = @$('table')
@$toolbar = @$('.toolbar')
super
refresh: ->
@$editPanel.css display:(if @editable then 'table-row' else 'none')
@$toolbar.css display:(if @editable then 'block' else 'none')
@$scrollbox.css bottom:(if @editable then @$toolbar.height() else '0')
if _.isEmpty(@$quantityField.val()) then @$quantityField.val '1'
@$table.find('tr:not(:last-child)').remove()
$lastRow = @$table.find 'tr:last-child'
@_stackControllers = []
@model.each (stack)=>
controller = new StackController
editable: @editable
imageLoader: @imageLoader
model: stack
modPack: @modPack
onRemove: if not @editable then null else (stack)=> @_removeStack(stack)
controller.render()
controller.$el.insertBefore $lastRow
@_stackControllers.push controller
@_updateNameAutocomplete()
@_updateButtonState()
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'blur input[name="name"]': 'onNameFieldBlur'
'blur input[name="quantity"]': 'onQuantityFieldBlur'
'click button[name="add"]': 'onAddButtonClicked'
'click button[name="clear"]': 'onClearButtonClicked'
'focus input[name="name"]': 'onNameFieldFocused'
'focus input[name="quantity"]': 'onQuantityFieldFocused'
'input input[name="name"]': 'onNameFieldChanged'
'input input[name="quantity"]': 'onQuantityFieldChanged'
'keyup input[name="name"]': 'onNameFieldKeyUp'
# Private Methods ##############################################################################
_removeStack: (stack)->
@model.remove stack.itemSlug, stack.quantity
_updateNameAutocomplete: ->
onChanged = => @onNameFieldChanged()
onSelected = => @onItemSelected()
@$nameField.autocomplete
source: (request, callback)=> callback @nameFinder.search request.term
delay: 0
minLength: 0
change: onChanged
close: onChanged
select: onSelected
_updateButtonState: ->
if @model.isEmpty then @$clearButton.attr('disabled', 'disabled') else @$clearButton.removeAttr('disabled')
itemValid = @modPack.findItemByName(@$nameField.val())?
quantityValid = @$quantityField.val().match(InventoryTableController.ONLY_DIGITS)
disable = not (itemValid and quantityValid)
if disable then @$addButton.attr('disabled', 'disabled') else @$addButton.removeAttr('disabled')
@@ -0,0 +1,43 @@
###
Crafting Guide - item_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
########################################################################################################################
module.exports = class ItemController extends BaseController
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 = 'item'
super options
@_imageLoader = options.imageLoader
@_modPack = options.modPack
# BaseController Overrides #####################################################################
onDidRender: ->
@$icon = @$('img')
@$name = @$('.itemName')
@$nameLink = @$('a')
super
refresh: ->
display = @_modPack.findItemDisplay @model.slug
@_imageLoader.load display.iconUrl, @$icon
@$name.html display.itemName
@$nameLink.attr 'href', display.itemUrl
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click a': 'routeLinkClick'
@@ -0,0 +1,84 @@
###
Crafting Guide - item_group_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
{Event} = require '../constants'
Item = require '../models/item'
ItemController = require './item_controller'
########################################################################################################################
module.exports = class ItemGroupController extends BaseController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.title ?= ''
options.templateName = 'item_group'
super options
@_delayStep = 20
@_imageLoader = options.imageLoader
@_itemControllers = []
@_modPack = options.modPack
@_title = options.title
Object.defineProperties this,
title: {get:@getTitle, set:@setTitle}
# Property Methods #############################################################################
getTitle: ->
return @_title
setTitle: (title)->
@_title = title
@tryRefresh()
# BaseController Overrides #####################################################################
onDidRender: ->
@$title = @$('h2')
@$items = @$('.panel')
super
refresh: ->
@$title.html @_title
@_refreshItems()
super
# Private Methods ##############################################################################
_createItemController: (item, delay)->
controller = new ItemController imageLoader:@_imageLoader, model:item, modPack:@_modPack
@_itemControllers.push controller
attachController = =>
controller.render()
@$items.append controller.$el
_.delay attachController, delay
_refreshItems: ->
controllerIndex = 0
delay = 0
if @model?
for item in @model
controller = @_itemControllers[controllerIndex]
if not controller?
@_createItemController item, delay
delay += @_delayStep
else
controller.model = item
controller.refresh()
controllerIndex += 1
while @_itemControllers.length > controllerIndex
controller = @_itemControllers.pop()
controller.$el.slideUp duration:Duration.normal, complete:-> @remove()
@@ -0,0 +1,174 @@
###
Crafting Guide - item_page_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
{Duration} = require '../constants'
{Event} = require '../constants'
FullRecipeController = require './full_recipe_controller'
ImageLoader = require './image_loader'
Item = require '../models/item'
ItemGroupController = require './item_group_controller'
ItemPage = require '../models/item_page'
ItemSlug = require '../models/item_slug'
PageController = require './page_controller'
{Text} = require '../constants'
{Url} = require '../constants'
########################################################################################################################
module.exports = class ItemPageController extends PageController
constructor: (options={})->
if not options.itemSlug? then throw new Error 'options.itemSlug is required'
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.model ?= new ItemPage modPack:options.modPack
options.templateName ?= 'item_page'
super options
@imageLoader = options.imageLoader
@modPack = options.modPack
@_itemSlug = options.itemSlug
@modPack.on Event.change, => @tryRefresh()
# PageController Overrides #####################################################################
getTitle: ->
return @model.item?.name
# BaseController Overrides #####################################################################
onDidRender: ->
@_usedAsToolToMakeController = @addChild ItemGroupController, '.usedAsToolToMake .view__item_group',
imageLoader: @imageLoader
modPack: @modPack
@_similarItemsController = @addChild ItemGroupController, '.similar .view__item_group',
imageLoader: @imageLoader
modPack: @modPack
@_usedToMakeController = @addChild ItemGroupController, '.usedToMake .view__item_group',
imageLoader: @imageLoader
modPack: @modPack
@$usedAsToolToMakeSection = @$('.usedAsToolToMake')
@$byline = @$('.byline')
@$bylineLink = @$('.byline a')
@$craftingPlanLink = @$('a.craftingPlan')
@$name = @$('h1.name')
@$recipeContainer = @$('.recipes .panel')
@$recipesSection = @$('.recipes')
@$similarSection = @$('.similar')
@$titleImage = @$('.titleImage img')
@$usedToMakeSection = @$('.usedToMake')
super
refresh: ->
@_resolveItemSlug()
if @model.item?
display = @modPack.findItemDisplay @model.item.slug
@$craftingPlanLink.attr href:display.craftingUrl
@$craftingPlanLink.fadeIn duration:Duration.fast
@imageLoader.load display.iconUrl, @$titleImage
@$name.html display.itemName
@$el.slideDown duration:Duration.normal
else
@$el.slideUp duration:Duration.normal
@_refreshByline()
@_refreshRecipes()
@_refreshSimilarItems()
@_refreshUsedAsToolToMake()
@_refreshUsedToMake()
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click a.craftingPlan': 'routeLinkClick'
'click .byline a': 'routeLinkClick'
# Private Methods ##############################################################################
_refreshUsedAsToolToMake: ->
@_usedAsToolToMakeController.title = 'Used as Tool to Make'
@_usedAsToolToMakeController.model = @model.findToolForRecipes()
if @_usedAsToolToMakeController.model?
@$usedAsToolToMakeSection.slideDown duration:Duration.normal
else
@$usedAsToolToMakeSection.slideUp duration:Duration.normal
_refreshByline: ->
mod = @model.item?.modVersion?.mod
if mod?.name?.length > 0
@$bylineLink.attr 'href', Url.mod modSlug:mod.slug
@$bylineLink.html mod.name
@$byline.fadeIn duration:Duration.fast
else
@$byline.fadeOut duration:Duration.fast
_refreshUsedToMake: ->
@_usedToMakeController.title = 'Used to Make'
@_usedToMakeController.model = @model.findComponentInItems()
if @_usedToMakeController.model?
@$usedToMakeSection.slideDown duration:Duration.normal
else
@$usedToMakeSection.slideUp duration:Duration.normal
_refreshRecipes: ->
@_recipeControllers ?= []
index = 0
recipes = @model.findRecipes()
if recipes?
@$recipesSection.slideDown duration:Duration.normal
for recipe in @model.findRecipes()
controller = @_recipeControllers[index]
if not controller?
controller = new FullRecipeController imageLoader:@imageLoader, modPack:@modPack, model:recipe
@_recipeControllers.push controller
controller.render()
@$recipeContainer.append controller.$el
else
controller.model = recipe
index++
else
@$recipesSection.slideUp duration:Duration.normal
while @_recipeControllers.length > index
controller = @_recipeControllers.pop()
controller.$el.slideUp duration:Duration.normal, complete:-> controller.$el.remove()
_refreshSimilarItems: ->
group = @model.item?.group
if group? and group isnt Item.Group.Other
@_similarItemsController.title = "Other #{group}"
@_similarItemsController.model = @model.findSimilarItems()
else
@_similarItemsController.model = null
if @_similarItemsController.model?
@$similarSection.slideDown duration:Duration.normal
else
@$similarSection.slideUp duration:Duration.normal
_resolveItemSlug: ->
item = @modPack.findItem @_itemSlug, includeDisabled:false
if item? and not ItemSlug.equal item.slug, @_itemSlug
router.navigate Url.item(modSlug:item.slug.mod, itemSlug:item.slug.item), trigger:true
return
@model.item = item
@@ -0,0 +1,80 @@
###
Crafting Guide - minimal_recipe_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
CraftingGridController = require './crafting_grid_controller'
{Duration} = require '../constants'
ImageLoader = require './image_loader'
StringBuilder = require '../models/string_builder'
########################################################################################################################
module.exports = class MinimalRecipeController extends BaseController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.templateName = 'minimal_recipe'
super options
@imageLoader = options.imageLoader
@modPack = options.modPack
# BaseController Overrides #####################################################################
onDidRender: ->
@gridController = @addChild CraftingGridController, '.view__crafting_grid',
modPack: @modPack
imageLoader: @imageLoader
@$outputImg = @$('.output img')
@$outputLink = @$('.output a')
@$outputQuantity = @$('.quantity')
@$toolContainer = @$('.tool')
super
refresh: ->
@gridController.model = @model
@$outputImg.attr 'src', '/images/empty.png'
@$outputImg.removeAttr 'alt'
@$outputLink.removeAttr 'href'
@$outputQuantity.html ''
if @model?
outputStack = @model.output[0]
if outputStack?
display = @modPack.findItemDisplay outputStack.itemSlug
@$outputLink.attr 'href', display.itemUrl
@$outputLink.attr 'title', display.itemName
@$outputImg.attr 'alt', display.itemName
@$outputQuantity.html outputStack.quantity if outputStack.quantity > 1
@imageLoader.load display.iconUrl, @$outputImg
@$el.tooltip show:{delay:Duration.snap, duration:Duration.fast}
@_refreshTools()
super
# Backbone.View Methods ########################################################################
events: ->
return _.extend super,
'click a': 'routeLinkClick'
# Private Methods ##############################################################################
_refreshTools: ->
@$toolContainer.empty()
return unless @model?
builder = new StringBuilder
builder.loop @model.tools, delimiter:', ', onEach:(b, stack)=>
display = @modPack.findItemDisplay stack.itemSlug
b.push "<a href=\"#{display.itemUrl}\">#{display.itemName}</a>"
@$toolContainer.html builder.toString()
@@ -0,0 +1,43 @@
###
Crafting Guide - mod_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Url} = require '../constants'
########################################################################################################################
module.exports = class ModController extends BaseController
constructor: (options={})->
options.templateName = 'mod'
super options
# BaseController Overrides #####################################################################
onDidRender: ->
@$link = @$('a')
@$logo = @$('.logo')
@$name = @$('.name p')
@$description = @$('.description p')
super
refresh: ->
if @model?
@$el.removeClass 'empty'
@$link.attr 'href', Url.mod modSlug:@model.slug
@$logo.attr 'src', Url.modIcon modSlug:@model.slug
@$name.html @model.name
@$description.html @model.description
else
@$el.addClass 'empty'
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click a': 'routeLinkClick'
@@ -0,0 +1,77 @@
###
Crafting Guide - mod_pack_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{DefaultMods} = require '../constants'
{Duration} = require '../constants'
Mod = require '../models/mod'
ModSelectorController = require './mod_selector_controller'
########################################################################################################################
module.exports = class ModPackController extends BaseController
constructor: (options={})->
if not options.model? then throw new Error 'options.model is required'
options.templateName = 'mod_pack'
super options
@_controllers = []
@storage = options.storage
# Event Methods ################################################################################
onSuggestModClicked: ->
return unless global.feedbackController?
global.feedbackController.enterFeedback 'Please add mod:\n\n'
# BaseController Overrides #####################################################################
onDidRender: ->
@$mods = @$('.mods')
@$toolbar = @$('.toolbar')
super
refresh: ->
if not @model?
@_controllers = []
@$('table tr').remove()
return
index = 0
mods = @model.getMods()
while index < Math.min @_controllers.length, mods.length
controller = @_controllers[index]
controller.model = mods[index]
index++
while @_controllers.length < mods.length
controller = new ModSelectorController model:mods[index], storage:@storage
controller.render()
@_controllers.push controller
controller.$el.hide duration:0
@$mods.append controller.$el
controller.$el.slideDown duration:Duration.normal
index++
while @_controllers.length > mods.length
controller = @_controllers.pop()
controller.$el.slideUp duration:Duration.normal, complete:-> controller.$el.remove()
if global.feedbackController?
@$toolbar.show duration:0
else
@$toolbar.hide duration:0
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'click button[name="suggestMod"]': 'onSuggestModClicked'
@@ -0,0 +1,153 @@
###
Crafting Guide - mod_page_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
{Duration} = require '../constants'
Item = require '../models/item'
ItemGroupController = require './item_group_controller'
Mod = require '../models/mod'
ModPack = require '../models/mod_pack'
PageController = require './page_controller'
{Text} = require '../constants'
{Url} = require '../constants'
########################################################################################################################
module.exports = class ModPageController extends PageController
constructor: (options={})->
if not options.imageLoader? then throw new Error 'options.imageLoader is required'
if not options.modPack? then throw new Error 'options.modPack is required'
options.delayStep ?= 10
options.templateName = 'mod_page'
super options
@imageLoader = options.imageLoader
@modPack = options.modPack
@_delayStep = options.delayStep
@_effectiveModVersion = null
@_groupControllers = []
Object.defineProperties this,
effectiveModVersion: {get:@_getEffectiveModVersion}
# Event Methods ################################################################################
onVersionChanged: ->
version = @$versionSelector.val()
modVersion = @model.getModVersion version
@_effectiveModVersion = modVersion
if modVersion? then modVersion.fetch()
@refresh()
# PageController Overrides #####################################################################
getTitle: ->
return @model?.name
# BaseController Overrides #####################################################################
onDidRender: ->
@$name = @$('.name')
@$byline = @$('.byline p')
@$description = @$('.description p')
@$documentationLink = @$('.documentation')
@$downloadLink = @$('.download')
@$homePageLink = @$('.homePage')
@$groupContainer = @$('.itemGroups')
@$titleImage = @$('.titleImage img')
@$versionSelector = @$('select.version')
super
refresh: ->
if @model?
@$name.html @model.name
@$byline.html "by #{@model.author}"
@$titleImage.attr 'src', Url.modIcon modSlug:@model.slug
@$description.html @model.description
@$el.slideDown duration:Duration.normal
else
@$el.slideUp duration:Duration.normal
@_refreshLink @$homePageLink, @model.homePageUrl
@_refreshLink @$documentationLink, @model.documentationUrl
@_refreshLink @$downloadLink, @model.downloadUrl
@_refreshItemGroups()
@_refreshVersions()
super
# Backbone.View Methods ########################################################################
events:
'change select.version': 'onVersionChanged'
# Private Methods ##############################################################################
_getEffectiveModVersion: ->
return @_effectiveModVersion if @_effectiveModVersion?
return null unless @model?
modVersion = @model.activeModVersion
modVersion ?= @model.getModVersion Mod.Version.Latest
modVersion.fetch() if modVersion?
return modVersion
_refreshItemGroups: ->
groupIndex = 0
modVersion = @effectiveModVersion
if modVersion?
modVersion.eachGroup (group)=>
controller = @_groupControllers[groupIndex]
items = modVersion.allItemsInGroup group
if not controller?
title = if group is Item.Group.Other then 'Items' else group
controller = new ItemGroupController
imageLoader: @imageLoader
model: items
modPack: @modPack
title: title
controller.render()
@$groupContainer.append controller.$el
@_groupControllers[groupIndex] = controller
else
controller.modVersion = modVersion
controller.model = items
controller.refresh()
groupIndex++
while @_groupControllers.length > groupIndex + 1
controller = @_groupControllers.pop()
controller.$el.slideUp duration:Duration.normal, -> @remove()
_refreshLink: ($link, url)->
if url?
$link.slideDown duration:Duration.normal
$link.attr 'href', url
else
$link.slideUp duration:Duration.normal
_refreshVersions: ->
@$versionSelector.empty()
return unless @model?
effectiveModVersion = @effectiveModVersion
versionCount = 0
@model.eachModVersion (modVersion)=>
option = $("<option value=\"#{modVersion.version}\">#{modVersion.version}</option>")
if modVersion is effectiveModVersion
option.attr 'selected', 'selected'
@$versionSelector.append option
versionCount++
if versionCount <= 1
@$versionSelector.attr 'disabled', 'disabled'
else
@$versionSelector.removeAttr 'disabled'
@@ -0,0 +1,70 @@
###
Crafting Guide - mod_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Event} = require '../constants'
Mod = require '../models/mod'
{RequiredMods} = require '../constants'
{Url} = require '../constants'
########################################################################################################################
module.exports = class ModSelectorController extends BaseController
constructor: (options={})->
if not options.model? then throw new Error 'options.model is required'
options.templateName = 'mod_selector'
super options
@_storage = options.storage
# Event Methods ################################################################################
onVersionChanged: ->
@model.activeVersion = @$version.val()
# BaseController Overrides #####################################################################
onDidRender: ->
@$version = @$('select')
@$nameLink = @$('.name a')
@$nameText = @$('.name p')
@$description = @$('.description p')
super
refresh: ->
if @model.activeVersion is Mod.Version.None
@$el.addClass 'disabled'
else
@$el.removeClass 'disabled'
@$version.empty()
if not (@model.slug in RequiredMods)
option = $("<option value=\"none\">Disabled</option>")
if @model.activeVersion is Mod.Version.None
option.attr 'selected', 'selected'
@$version.append option
@model.eachModVersion (modVersion)=>
option = $("<option value=\"#{modVersion.version}\">#{modVersion.version}</option>")
if modVersion is @model.activeModVersion
option.attr 'selected', 'selected'
@$version.append option
@$nameLink.attr 'href', Url.mod(modSlug:@model.slug)
@$nameText.html @model.name
@$description.html @model.description
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'change select': 'onVersionChanged'
'click a': 'routeLinkClick'
@@ -0,0 +1,37 @@
###
Crafting Guide - page_controller.coffee
Copyright (c) 2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Text} = require '../constants'
########################################################################################################################
module.exports = class PageController extends BaseController
constructor: (options={})->
super options
# Public Methods ###############################################################################
getTitle: ->
# subclasses should override this to return the page-specific portion of the title
return null
# BaseController Overrides #####################################################################
refresh: ->
title = @getTitle()
title = if title? then title.trim() else ''
if title.length > 0
title += " | #{Text.title}"
else
title = Text.title
$('title').html title
super
@@ -0,0 +1,25 @@
###
Crafting Guide - recipe_step_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
########################################################################################################################
module.exports = class RecipeStepController extends BaseController
constructor: (options={})->
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 = 'recipe_step'
super options
# BaseController Overrides #####################################################################
onDidRender: ->
@$slotImages = (@$slots.push $(el) for el in @$('.table-slot img'))
@$output = @$('.output')
super
@@ -0,0 +1,125 @@
###
Crafting Guide - stack_controller.coffee
Copyright (c) 2014-2015 by Redwood Labs
All rights reserved.
###
BaseController = require './base_controller'
{Duration} = require '../constants'
{Event} = require '../constants'
ImageLoader = require './image_loader'
{Key} = require '../constants'
########################################################################################################################
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.editable ?= false
options.onChange ?= -> # do nothing
options.onRemove ?= (stack)-> # do nothing
options.templateName = 'stack'
super options
@editable = options.editable
@modPack = options.modPack
@onChange = options.onChange
@onRemove = options.onRemove
@_imageLoader = options.imageLoader
@modPack.on Event.change, => @tryRefresh()
# Event Methods ################################################################################
onQuantityFieldBlur: ->
quantityText = @$quantityField.val().trim()
if quantityText.length is 0
quantity = @_priorValue
else if not quantityText.match /^[0-9]*$/
quantity = 1
else
quantity = parseInt quantityText, 10
if _.isNaN quantity then quantity = 1
quantity = Math.min quantity, StackController.MAX_QUANTITY
quantity = Math.max 1, quantity
if quantity?
@$quantityField.val "#{quantity}"
@$quantityField.removeClass 'error', Duration.snap
@$quantityField.removeClass 'error-new', Duration.snap
@model.quantity = quantity
@onChange()
onQuantityFieldChanged: ->
quantityText = @$quantityField.val().trim()
if not quantityText.match /^[0-9]*$/
@$quantityField.addClass 'error', 0
@$quantityField.addClass 'error-new', 0
@$quantityField.removeClass 'error-new', Duration.fast
else
@$quantityField.removeClass 'error', Duration.snap
@$quantityField.removeClass 'error-new', Duration.snap
onQuantityFieldFocused: ->
if @editable
@_priorValue = @model.quantity
@$quantityField.val ''
else
@$quantityField.blur()
onQuantityKeyUp: (event)->
if event.which is Key.Return
@$quantityField.blur()
onRemoveClicked: ->
@onRemove @model
@onChange()
# BaseController Overrides #####################################################################
onDidRender: ->
@$action = @$('.action')
@$image = @$('.icon img')
@$nameLink = @$('.name a')
@$quantityField = @$('.quantity input')
@$removeButton = @$('button.remove')
super
refresh: ->
display = @modPack.findItemDisplay @model.itemSlug
@_imageLoader.load display.iconUrl, @$image
@$nameLink.html display.itemName
@$nameLink.attr 'href', display.itemUrl
@$quantityField.val @model.quantity
if @editable
@$quantityField.removeAttr 'readonly'
@$quantityField.addClass 'editable'
else
@$quantityField.attr 'readonly', 'readonly'
@$quantityField.removeClass 'editable'
@$action.css display:(if @editable then 'table-cell' else 'none')
super
# Backbone.View Overrides ######################################################################
events: ->
return _.extend super,
'blur .quantity input': 'onQuantityFieldBlur'
'click button.remove': 'onRemoveClicked'
'click .name a': 'routeLinkClick'
'focus .quantity input': 'onQuantityFieldFocused'
'input .quantity input': 'onQuantityFieldChanged'
'keyup .quantity input': 'onQuantityKeyUp'